Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/subr_rman.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
* at some point in the future, particularly if we want to support 36-bit | * at some point in the future, particularly if we want to support 36-bit | ||||
* addresses on IA32 hardware. | * addresses on IA32 hardware. | ||||
*/ | */ | ||||
struct resource_i { | struct resource_i { | ||||
struct resource r_r; | struct resource r_r; | ||||
TAILQ_ENTRY(resource_i) r_link; | TAILQ_ENTRY(resource_i) r_link; | ||||
LIST_ENTRY(resource_i) r_sharelink; | LIST_ENTRY(resource_i) r_sharelink; | ||||
LIST_HEAD(, resource_i) *r_sharehead; | LIST_HEAD(, resource_i) *r_sharehead; | ||||
u_long r_start; /* index of the first entry in this resource */ | rman_res_t r_start; /* index of the first entry in this resource */ | ||||
u_long r_end; /* index of the last entry (inclusive) */ | rman_res_t r_end; /* index of the last entry (inclusive) */ | ||||
u_int r_flags; | u_int r_flags; | ||||
void *r_virtual; /* virtual address of this resource */ | void *r_virtual; /* virtual address of this resource */ | ||||
struct device *r_dev; /* device which has allocated this resource */ | struct device *r_dev; /* device which has allocated this resource */ | ||||
struct rman *r_rm; /* resource manager from whence this came */ | struct rman *r_rm; /* resource manager from whence this came */ | ||||
int r_rid; /* optional rid for this resource. */ | int r_rid; /* optional rid for this resource. */ | ||||
}; | }; | ||||
static int rman_debug = 0; | static int rman_debug = 0; | ||||
Show All 27 Lines | |||||
if (once == 0) { | if (once == 0) { | ||||
once = 1; | once = 1; | ||||
TAILQ_INIT(&rman_head); | TAILQ_INIT(&rman_head); | ||||
mtx_init(&rman_mtx, "rman head", NULL, MTX_DEF); | mtx_init(&rman_mtx, "rman head", NULL, MTX_DEF); | ||||
} | } | ||||
if (rm->rm_start == 0 && rm->rm_end == 0) | if (rm->rm_start == 0 && rm->rm_end == 0) | ||||
rm->rm_end = ~0ul; | rm->rm_end = ~0; | ||||
if (rm->rm_type == RMAN_UNINIT) | if (rm->rm_type == RMAN_UNINIT) | ||||
panic("rman_init"); | panic("rman_init"); | ||||
if (rm->rm_type == RMAN_GAUGE) | if (rm->rm_type == RMAN_GAUGE) | ||||
panic("implement RMAN_GAUGE"); | panic("implement RMAN_GAUGE"); | ||||
TAILQ_INIT(&rm->rm_list); | TAILQ_INIT(&rm->rm_list); | ||||
rm->rm_mtx = malloc(sizeof *rm->rm_mtx, M_RMAN, M_NOWAIT | M_ZERO); | rm->rm_mtx = malloc(sizeof *rm->rm_mtx, M_RMAN, M_NOWAIT | M_ZERO); | ||||
if (rm->rm_mtx == NULL) | if (rm->rm_mtx == NULL) | ||||
return ENOMEM; | return ENOMEM; | ||||
mtx_init(rm->rm_mtx, "rman", NULL, MTX_DEF); | mtx_init(rm->rm_mtx, "rman", NULL, MTX_DEF); | ||||
mtx_lock(&rman_mtx); | mtx_lock(&rman_mtx); | ||||
TAILQ_INSERT_TAIL(&rman_head, rm, rm_link); | TAILQ_INSERT_TAIL(&rman_head, rm, rm_link); | ||||
mtx_unlock(&rman_mtx); | mtx_unlock(&rman_mtx); | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
rman_manage_region(struct rman *rm, u_long start, u_long end) | rman_manage_region(struct rman *rm, rman_res_t start, rman_res_t end) | ||||
{ | { | ||||
struct resource_i *r, *s, *t; | struct resource_i *r, *s, *t; | ||||
int rv = 0; | int rv = 0; | ||||
DPRINTF(("rman_manage_region: <%s> request: start %#lx, end %#lx\n", | DPRINTF(("rman_manage_region: <%s> request: start %#jx, end %#jx\n", | ||||
rm->rm_descr, start, end)); | rm->rm_descr, start, end)); | ||||
if (start < rm->rm_start || end > rm->rm_end) | if (start < rm->rm_start || end > rm->rm_end) | ||||
return EINVAL; | return EINVAL; | ||||
r = int_alloc_resource(M_NOWAIT); | r = int_alloc_resource(M_NOWAIT); | ||||
if (r == NULL) | if (r == NULL) | ||||
return ENOMEM; | return ENOMEM; | ||||
r->r_start = start; | r->r_start = start; | ||||
r->r_end = end; | r->r_end = end; | ||||
r->r_rm = rm; | r->r_rm = rm; | ||||
mtx_lock(rm->rm_mtx); | mtx_lock(rm->rm_mtx); | ||||
/* Skip entries before us. */ | /* Skip entries before us. */ | ||||
TAILQ_FOREACH(s, &rm->rm_list, r_link) { | TAILQ_FOREACH(s, &rm->rm_list, r_link) { | ||||
if (s->r_end == ULONG_MAX) | if (s->r_end == ~0) | ||||
break; | break; | ||||
if (s->r_end + 1 >= r->r_start) | if (s->r_end + 1 >= r->r_start) | ||||
break; | break; | ||||
} | } | ||||
/* If we ran off the end of the list, insert at the tail. */ | /* If we ran off the end of the list, insert at the tail. */ | ||||
if (s == NULL) { | if (s == NULL) { | ||||
TAILQ_INSERT_TAIL(&rm->rm_list, r, r_link); | TAILQ_INSERT_TAIL(&rm->rm_list, r, r_link); | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | |||||
mtx_unlock(&rman_mtx); | mtx_unlock(&rman_mtx); | ||||
mtx_destroy(rm->rm_mtx); | mtx_destroy(rm->rm_mtx); | ||||
free(rm->rm_mtx, M_RMAN); | free(rm->rm_mtx, M_RMAN); | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
rman_first_free_region(struct rman *rm, u_long *start, u_long *end) | rman_first_free_region(struct rman *rm, rman_res_t *start, rman_res_t *end) | ||||
{ | { | ||||
struct resource_i *r; | struct resource_i *r; | ||||
mtx_lock(rm->rm_mtx); | mtx_lock(rm->rm_mtx); | ||||
TAILQ_FOREACH(r, &rm->rm_list, r_link) { | TAILQ_FOREACH(r, &rm->rm_list, r_link) { | ||||
if (!(r->r_flags & RF_ALLOCATED)) { | if (!(r->r_flags & RF_ALLOCATED)) { | ||||
*start = r->r_start; | *start = r->r_start; | ||||
*end = r->r_end; | *end = r->r_end; | ||||
mtx_unlock(rm->rm_mtx); | mtx_unlock(rm->rm_mtx); | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
mtx_unlock(rm->rm_mtx); | mtx_unlock(rm->rm_mtx); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
int | int | ||||
rman_last_free_region(struct rman *rm, u_long *start, u_long *end) | rman_last_free_region(struct rman *rm, rman_res_t *start, rman_res_t *end) | ||||
{ | { | ||||
struct resource_i *r; | struct resource_i *r; | ||||
mtx_lock(rm->rm_mtx); | mtx_lock(rm->rm_mtx); | ||||
TAILQ_FOREACH_REVERSE(r, &rm->rm_list, resource_head, r_link) { | TAILQ_FOREACH_REVERSE(r, &rm->rm_list, resource_head, r_link) { | ||||
if (!(r->r_flags & RF_ALLOCATED)) { | if (!(r->r_flags & RF_ALLOCATED)) { | ||||
*start = r->r_start; | *start = r->r_start; | ||||
*end = r->r_end; | *end = r->r_end; | ||||
mtx_unlock(rm->rm_mtx); | mtx_unlock(rm->rm_mtx); | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
mtx_unlock(rm->rm_mtx); | mtx_unlock(rm->rm_mtx); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
/* Shrink or extend one or both ends of an allocated resource. */ | /* Shrink or extend one or both ends of an allocated resource. */ | ||||
int | int | ||||
rman_adjust_resource(struct resource *rr, u_long start, u_long end) | rman_adjust_resource(struct resource *rr, rman_res_t start, rman_res_t end) | ||||
{ | { | ||||
struct resource_i *r, *s, *t, *new; | struct resource_i *r, *s, *t, *new; | ||||
struct rman *rm; | struct rman *rm; | ||||
/* Not supported for shared resources. */ | /* Not supported for shared resources. */ | ||||
r = rr->__r_i; | r = rr->__r_i; | ||||
if (r->r_flags & RF_SHAREABLE) | if (r->r_flags & RF_SHAREABLE) | ||||
return (EINVAL); | return (EINVAL); | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | |||||
mtx_unlock(rm->rm_mtx); | mtx_unlock(rm->rm_mtx); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
#define SHARE_TYPE(f) (f & (RF_SHAREABLE | RF_PREFETCHABLE)) | #define SHARE_TYPE(f) (f & (RF_SHAREABLE | RF_PREFETCHABLE)) | ||||
struct resource * | struct resource * | ||||
rman_reserve_resource_bound(struct rman *rm, u_long start, u_long end, | rman_reserve_resource_bound(struct rman *rm, rman_res_t start, rman_res_t end, | ||||
u_long count, u_long bound, u_int flags, | rman_res_t count, rman_res_t bound, u_int flags, | ||||
struct device *dev) | struct device *dev) | ||||
{ | { | ||||
u_int new_rflags; | u_int new_rflags; | ||||
struct resource_i *r, *s, *rv; | struct resource_i *r, *s, *rv; | ||||
u_long rstart, rend, amask, bmask; | rman_res_t rstart, rend, amask, bmask; | ||||
rv = NULL; | rv = NULL; | ||||
DPRINTF(("rman_reserve_resource_bound: <%s> request: [%#lx, %#lx], " | DPRINTF(("rman_reserve_resource_bound: <%s> request: [%#jx, %#jx], " | ||||
"length %#lx, flags %u, device %s\n", rm->rm_descr, start, end, | "length %#jx, flags %x, device %s\n", rm->rm_descr, start, end, | ||||
count, flags, | count, flags, | ||||
dev == NULL ? "<null>" : device_get_nameunit(dev))); | dev == NULL ? "<null>" : device_get_nameunit(dev))); | ||||
KASSERT((flags & RF_FIRSTSHARE) == 0, | KASSERT((flags & RF_FIRSTSHARE) == 0, | ||||
("invalid flags %#x", flags)); | ("invalid flags %#x", flags)); | ||||
new_rflags = (flags & ~RF_FIRSTSHARE) | RF_ALLOCATED; | new_rflags = (flags & ~RF_FIRSTSHARE) | RF_ALLOCATED; | ||||
mtx_lock(rm->rm_mtx); | mtx_lock(rm->rm_mtx); | ||||
r = TAILQ_FIRST(&rm->rm_list); | |||||
if (r == NULL) { | |||||
DPRINTF(("NULL list head\n")); | |||||
} else { | |||||
DPRINTF(("rman_reserve_resource_bound: trying %#jx <%#jx,%#jx>\n", | |||||
r->r_end, start, count-1)); | |||||
} | |||||
for (r = TAILQ_FIRST(&rm->rm_list); | for (r = TAILQ_FIRST(&rm->rm_list); | ||||
r && r->r_end < start + count - 1; | r && r->r_end < start + count - 1; | ||||
r = TAILQ_NEXT(r, r_link)) | r = TAILQ_NEXT(r, r_link)) { | ||||
; | ; | ||||
DPRINTF(("rman_reserve_resource_bound: tried %#jx <%#jx,%#jx>\n", | |||||
r->r_end, start, count-1)); | |||||
} | |||||
if (r == NULL) { | if (r == NULL) { | ||||
DPRINTF(("could not find a region\n")); | DPRINTF(("could not find a region\n")); | ||||
goto out; | goto out; | ||||
} | } | ||||
amask = (1ul << RF_ALIGNMENT(flags)) - 1; | amask = (1ull << RF_ALIGNMENT(flags)) - 1; | ||||
KASSERT(start <= ULONG_MAX - amask, | KASSERT(start <= RM_MAX_END - amask, | ||||
jhb: I think this should be RMAN_MAX_END, not BUS_SPACE_MAXADDR. BUS_SPACE_MAXADDR should (IMO)… | |||||
("start (%#lx) + amask (%#lx) would wrap around", start, amask)); | ("start (%#jx) + amask (%#jx) would wrap around", start, amask)); | ||||
/* If bound is 0, bmask will also be 0 */ | /* If bound is 0, bmask will also be 0 */ | ||||
bmask = ~(bound - 1); | bmask = ~(bound - 1); | ||||
/* | /* | ||||
* First try to find an acceptable totally-unshared region. | * First try to find an acceptable totally-unshared region. | ||||
*/ | */ | ||||
for (s = r; s; s = TAILQ_NEXT(s, r_link)) { | for (s = r; s; s = TAILQ_NEXT(s, r_link)) { | ||||
DPRINTF(("considering [%#lx, %#lx]\n", s->r_start, s->r_end)); | DPRINTF(("considering [%#jx, %#jx]\n", s->r_start, s->r_end)); | ||||
/* | /* | ||||
* The resource list is sorted, so there is no point in | * The resource list is sorted, so there is no point in | ||||
* searching further once r_start is too large. | * searching further once r_start is too large. | ||||
*/ | */ | ||||
if (s->r_start > end - (count - 1)) { | if (s->r_start > end - (count - 1)) { | ||||
DPRINTF(("s->r_start (%#lx) + count - 1> end (%#lx)\n", | DPRINTF(("s->r_start (%#jx) + count - 1> end (%#jx)\n", | ||||
s->r_start, end)); | s->r_start, end)); | ||||
break; | break; | ||||
} | } | ||||
if (s->r_start > ULONG_MAX - amask) { | if (s->r_start > RM_MAX_END - amask) { | ||||
Done Inline ActionsHere as well. jhb: Here as well. | |||||
DPRINTF(("s->r_start (%#lx) + amask (%#lx) too large\n", | DPRINTF(("s->r_start (%#jx) + amask (%#jx) too large\n", | ||||
s->r_start, amask)); | s->r_start, amask)); | ||||
break; | break; | ||||
} | } | ||||
if (s->r_flags & RF_ALLOCATED) { | if (s->r_flags & RF_ALLOCATED) { | ||||
DPRINTF(("region is allocated\n")); | DPRINTF(("region is allocated\n")); | ||||
continue; | continue; | ||||
} | } | ||||
rstart = ulmax(s->r_start, start); | rstart = ummax(s->r_start, start); | ||||
/* | /* | ||||
* Try to find a region by adjusting to boundary and alignment | * Try to find a region by adjusting to boundary and alignment | ||||
* until both conditions are satisfied. This is not an optimal | * until both conditions are satisfied. This is not an optimal | ||||
* algorithm, but in most cases it isn't really bad, either. | * algorithm, but in most cases it isn't really bad, either. | ||||
*/ | */ | ||||
do { | do { | ||||
rstart = (rstart + amask) & ~amask; | rstart = (rstart + amask) & ~amask; | ||||
if (((rstart ^ (rstart + count - 1)) & bmask) != 0) | if (((rstart ^ (rstart + count - 1)) & bmask) != 0) | ||||
rstart += bound - (rstart & ~bmask); | rstart += bound - (rstart & ~bmask); | ||||
} while ((rstart & amask) != 0 && rstart < end && | } while ((rstart & amask) != 0 && rstart < end && | ||||
rstart < s->r_end); | rstart < s->r_end); | ||||
rend = ulmin(s->r_end, ulmax(rstart + count - 1, end)); | rend = ummin(s->r_end, ummax(rstart + count - 1, end)); | ||||
if (rstart > rend) { | if (rstart > rend) { | ||||
DPRINTF(("adjusted start exceeds end\n")); | DPRINTF(("adjusted start exceeds end\n")); | ||||
continue; | continue; | ||||
} | } | ||||
DPRINTF(("truncated region: [%#lx, %#lx]; size %#lx (requested %#lx)\n", | DPRINTF(("truncated region: [%#jx, %#jx]; size %#jx (requested %#jx)\n", | ||||
rstart, rend, (rend - rstart + 1), count)); | rstart, rend, (rend - rstart + 1), count)); | ||||
if ((rend - rstart + 1) >= count) { | if ((rend - rstart + 1) >= count) { | ||||
DPRINTF(("candidate region: [%#lx, %#lx], size %#lx\n", | DPRINTF(("candidate region: [%#jx, %#jx], size %#jx\n", | ||||
rstart, rend, (rend - rstart + 1))); | rstart, rend, (rend - rstart + 1))); | ||||
if ((s->r_end - s->r_start + 1) == count) { | if ((s->r_end - s->r_start + 1) == count) { | ||||
DPRINTF(("candidate region is entire chunk\n")); | DPRINTF(("candidate region is entire chunk\n")); | ||||
rv = s; | rv = s; | ||||
rv->r_flags = new_rflags; | rv->r_flags = new_rflags; | ||||
rv->r_dev = dev; | rv->r_dev = dev; | ||||
goto out; | goto out; | ||||
} | } | ||||
Show All 14 Lines | |||||
rv->r_start = rstart; | rv->r_start = rstart; | ||||
rv->r_end = rstart + count - 1; | rv->r_end = rstart + count - 1; | ||||
rv->r_flags = new_rflags; | rv->r_flags = new_rflags; | ||||
rv->r_dev = dev; | rv->r_dev = dev; | ||||
rv->r_rm = rm; | rv->r_rm = rm; | ||||
if (s->r_start < rv->r_start && s->r_end > rv->r_end) { | if (s->r_start < rv->r_start && s->r_end > rv->r_end) { | ||||
DPRINTF(("splitting region in three parts: " | DPRINTF(("splitting region in three parts: " | ||||
"[%#lx, %#lx]; [%#lx, %#lx]; [%#lx, %#lx]\n", | "[%#jx, %#jx]; [%#jx, %#jx]; [%#jx, %#jx]\n", | ||||
s->r_start, rv->r_start - 1, | s->r_start, rv->r_start - 1, | ||||
rv->r_start, rv->r_end, | rv->r_start, rv->r_end, | ||||
rv->r_end + 1, s->r_end)); | rv->r_end + 1, s->r_end)); | ||||
/* | /* | ||||
* We are allocating in the middle. | * We are allocating in the middle. | ||||
*/ | */ | ||||
r = int_alloc_resource(M_NOWAIT); | r = int_alloc_resource(M_NOWAIT); | ||||
if (r == NULL) { | if (r == NULL) { | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
out: | out: | ||||
mtx_unlock(rm->rm_mtx); | mtx_unlock(rm->rm_mtx); | ||||
return (rv == NULL ? NULL : &rv->r_r); | return (rv == NULL ? NULL : &rv->r_r); | ||||
} | } | ||||
struct resource * | struct resource * | ||||
rman_reserve_resource(struct rman *rm, u_long start, u_long end, u_long count, | rman_reserve_resource(struct rman *rm, rman_res_t start, rman_res_t end, | ||||
u_int flags, struct device *dev) | rman_res_t count, u_int flags, struct device *dev) | ||||
{ | { | ||||
return (rman_reserve_resource_bound(rm, start, end, count, 0, flags, | return (rman_reserve_resource_bound(rm, start, end, count, 0, flags, | ||||
dev)); | dev)); | ||||
} | } | ||||
int | int | ||||
rman_activate_resource(struct resource *re) | rman_activate_resource(struct resource *re) | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | |||||
break; | break; | ||||
if (~(1 << i) & size) | if (~(1 << i) & size) | ||||
i++; | i++; | ||||
return(RF_ALIGNMENT_LOG2(i)); | return(RF_ALIGNMENT_LOG2(i)); | ||||
} | } | ||||
void | void | ||||
rman_set_start(struct resource *r, u_long start) | rman_set_start(struct resource *r, rman_res_t start) | ||||
{ | { | ||||
r->__r_i->r_start = start; | r->__r_i->r_start = start; | ||||
} | } | ||||
u_long | rman_res_t | ||||
rman_get_start(struct resource *r) | rman_get_start(struct resource *r) | ||||
{ | { | ||||
return (r->__r_i->r_start); | return (r->__r_i->r_start); | ||||
} | } | ||||
void | void | ||||
rman_set_end(struct resource *r, u_long end) | rman_set_end(struct resource *r, rman_res_t end) | ||||
{ | { | ||||
r->__r_i->r_end = end; | r->__r_i->r_end = end; | ||||
} | } | ||||
u_long | rman_res_t | ||||
rman_get_end(struct resource *r) | rman_get_end(struct resource *r) | ||||
{ | { | ||||
return (r->__r_i->r_end); | return (r->__r_i->r_end); | ||||
} | } | ||||
u_long | rman_res_t | ||||
rman_get_size(struct resource *r) | rman_get_size(struct resource *r) | ||||
{ | { | ||||
return (r->__r_i->r_end - r->__r_i->r_start + 1); | return (r->__r_i->r_end - r->__r_i->r_start + 1); | ||||
} | } | ||||
u_int | u_int | ||||
rman_get_flags(struct resource *r) | rman_get_flags(struct resource *r) | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | |||||
#ifdef DDB | #ifdef DDB | ||||
static void | static void | ||||
dump_rman_header(struct rman *rm) | dump_rman_header(struct rman *rm) | ||||
{ | { | ||||
if (db_pager_quit) | if (db_pager_quit) | ||||
return; | return; | ||||
db_printf("rman %p: %s (0x%lx-0x%lx full range)\n", | db_printf("rman %p: %s (0x%jx-0x%jx full range)\n", | ||||
rm, rm->rm_descr, rm->rm_start, rm->rm_end); | rm, rm->rm_descr, (rman_res_t)rm->rm_start, (rman_res_t)rm->rm_end); | ||||
} | } | ||||
static void | static void | ||||
dump_rman(struct rman *rm) | dump_rman(struct rman *rm) | ||||
{ | { | ||||
struct resource_i *r; | struct resource_i *r; | ||||
const char *devname; | const char *devname; | ||||
if (db_pager_quit) | if (db_pager_quit) | ||||
return; | return; | ||||
TAILQ_FOREACH(r, &rm->rm_list, r_link) { | TAILQ_FOREACH(r, &rm->rm_list, r_link) { | ||||
if (r->r_dev != NULL) { | if (r->r_dev != NULL) { | ||||
devname = device_get_nameunit(r->r_dev); | devname = device_get_nameunit(r->r_dev); | ||||
if (devname == NULL) | if (devname == NULL) | ||||
devname = "nomatch"; | devname = "nomatch"; | ||||
} else | } else | ||||
devname = NULL; | devname = NULL; | ||||
db_printf(" 0x%lx-0x%lx (RID=%d) ", | db_printf(" 0x%jx-0x%jx (RID=%d) ", | ||||
r->r_start, r->r_end, r->r_rid); | r->r_start, r->r_end, r->r_rid); | ||||
if (devname != NULL) | if (devname != NULL) | ||||
db_printf("(%s)\n", devname); | db_printf("(%s)\n", devname); | ||||
else | else | ||||
db_printf("----\n"); | db_printf("----\n"); | ||||
if (db_pager_quit) | if (db_pager_quit) | ||||
return; | return; | ||||
} | } | ||||
Show All 31 Lines |
I think this should be RMAN_MAX_END, not BUS_SPACE_MAXADDR. BUS_SPACE_MAXADDR should (IMO) only be used by consumers to set the bounds on various rmans. The rman code itself should only operate on opaque rman_res_t values.