summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man9/Makefile5
-rw-r--r--share/man/man9/rman.9127
-rw-r--r--sys/kern/subr_rman.c158
-rw-r--r--sys/sys/rman.h3
4 files changed, 288 insertions, 5 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index bf2ceb3..93814c6 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -967,9 +967,11 @@ MLINKS+=refcount.9 refcount_acquire.9 \
MLINKS+=resource_int_value.9 resource_long_value.9 \
resource_int_value.9 resource_string_value.9
MLINKS+=rman.9 rman_activate_resource.9 \
+ rman.9 rman_adjust_resource.9 \
rman.9 rman_await_resource.9 \
rman.9 rman_deactivate_resource.9 \
rman.9 rman_fini.9 \
+ rman.9 rman_first_free_region.9 \
rman.9 rman_get_bushandle.9 \
rman.9 rman_get_bustag.9 \
rman.9 rman_get_device.9 \
@@ -980,6 +982,9 @@ MLINKS+=rman.9 rman_activate_resource.9 \
rman.9 rman_get_start.9 \
rman.9 rman_get_virtual.9 \
rman.9 rman_init.9 \
+ rman.9 rman_init_from_resource.9 \
+ rman.9 rman_is_region_manager.9 \
+ rman.9 rman_last_free_region.9 \
rman.9 rman_make_alignment_flags.9 \
rman.9 rman_manage_region.9 \
rman.9 rman_release_resource.9 \
diff --git a/share/man/man9/rman.9 b/share/man/man9/rman.9
index 113b608..7f8d889 100644
--- a/share/man/man9/rman.9
+++ b/share/man/man9/rman.9
@@ -25,17 +25,22 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 29, 2007
+.Dd April 29, 2011
.Dt RMAN 9
.Os
.Sh NAME
.Nm rman ,
.Nm rman_activate_resource ,
+.Nm rman_adjust_resource ,
.Nm rman_await_resource ,
.Nm rman_deactivate_resource ,
.Nm rman_fini ,
.Nm rman_init ,
+.Nm rman_init_from_resource ,
+.Nm rman_is_region_manager ,
.Nm rman_manage_region ,
+.Nm rman_first_free_region ,
+.Nm rman_last_free_region ,
.Nm rman_release_resource ,
.Nm rman_reserve_resource ,
.Nm rman_reserve_resource_bound ,
@@ -60,6 +65,8 @@
.Ft int
.Fn rman_activate_resource "struct resource *r"
.Ft int
+.Fn rman_adjust_resource "struct resource *r" "u_long start" "u_long end"
+.Ft int
.Fn rman_await_resource "struct resource *r" "int pri2" "int timo"
.Ft int
.Fn rman_deactivate_resource "struct resource *r"
@@ -68,8 +75,16 @@
.Ft int
.Fn rman_init "struct rman *rm"
.Ft int
+.Fn rman_init_from_resource "struct rman *rm" "struct resource *r"
+.Ft int
+.Fn rman_is_region_manager "struct resource *r" "struct rman *rm"
+.Ft int
.Fn rman_manage_region "struct rman *rm" "u_long start" "u_long end"
.Ft int
+.Fn rman_first_free_region "struct rman *rm" "u_long *start" "u_long *end"
+.Ft int
+.Fn rman_last_free_region "struct rman *rm" "u_long *start" "u_long *end"
+.Ft int
.Fn rman_release_resource "struct resource *r"
.Ft "struct resource *"
.Fo rman_reserve_resource
@@ -155,6 +170,14 @@ shall be set to
The field
.Va rm_descr
shall be set to a string that describes the resource to be managed.
+The
+.Va rm_start
+and
+.Va rm_end
+fields may be set to limit the range of acceptable resource addresses.
+If these fields are not set,
+.Fn rman_init
+will initialize them to allow the entire range of resource addresses.
It also initializes any mutexes associated with the structure.
If
.Fn rman_init
@@ -194,12 +217,49 @@ If successful,
will return 0.
If the region overlaps with an existing region, it will return
.Er EBUSY .
+If any part of the region falls outside of the valid address range for
+.Fa rm ,
+it will return
+.Er EINVAL .
.Er ENOMEM
-will be return when
+will be returned when
.Fn rman_manage_region
failed to allocate memory for the region.
.Pp
The
+.Fn rman_init_from_resource
+function is a wrapper routine to create a resource manager backed by an
+existing resource.
+It initializes
+.Fa rm
+using
+.Fn rman_init
+and then adds a region to
+.Fa rm
+corresponding to the address range allocated to
+.Fa r
+via
+.Fn rman_manage_region .
+.Pp
+The
+.Fn rman_first_free_region
+and
+.Fn rman_last_free_region
+functions can be used to query a resource manager for its first
+.Pq or last
+unallocated region.
+If
+.Fa rm
+contains no free region,
+these functions will return
+.Er ENOENT .
+Otherwise,
+.Fa *start
+and
+.Fa *end
+are set to the bounds of the free region and zero is returned.
+.Pp
+The
.Fn rman_reserve_resource_bound
function is where the bulk of the
.Nm
@@ -212,8 +272,9 @@ The caller can specify the
.Fa start
and
.Fa end
-of an acceptable range, as well as
-alignment, and the code will attempt to find a free segment which fits.
+of an acceptable range,
+as well as a boundary restriction and required aligment,
+and the code will attempt to find a free segment which fits.
The
.Fa start
argument is the lowest acceptable starting value of the resource.
@@ -225,6 +286,19 @@ Therefore,
must be \[<=]
.Fa end
for any allocation to happen.
+The aligment requirement
+.Pq if any
+is specified in
+.Fa flags .
+The
+.Fa bound
+argument may be set to specify a boundary restriction such that an
+allocated region may cross an address that is a multiple of the
+boundary.
+The
+.Fa bound
+argument must be a power of two.
+It may be set to zero to specify no boundary restriction.
The default behavior is to allocate an exclusive segment, unless the
.Dv RF_SHAREABLE
or
@@ -240,7 +314,7 @@ function is used to reserve resources within a previously established region.
It is a simplified interface to
.Fn rman_reserve_resource_bound
which passes 0 for the
-.Fa flags
+.Fa bound
argument.
.Pp
The
@@ -251,6 +325,49 @@ This should be used when calling
.Fn rman_reserve_resource_bound .
.Pp
The
+.Fn rman_is_region_manager
+function returns true if the allocated resource
+.Fa r
+was allocated from
+.Fa rm .
+Otherwise,
+it returns false.
+.Pp
+The
+.Fn rman_adjust_resource
+function is used to adjust the reserved address range of an allocated resource
+to reserve
+.Fa start
+through
+.Fa end .
+It can be used to grow or shrink one or both ends of the resource range.
+The current implementation does not support entirely relocating the resource
+and will fail with
+.Er EINVAL
+if the new resource range does not overlap the old resource range.
+If either end of the resource range grows and the new resource range would
+conflict with another allocated resource,
+the function will fail with
+.Er EBUSY .
+The
+.Fn rman_adjust_resource
+function does not support adjusting the resource range for shared resources
+and will fail such attempts with
+.Er EINVAL .
+Upon success,
+the resource
+.Fa r
+will have a start address of
+.Fa start
+and an end address of
+.Fa end
+and the function will return zero.
+Note that none of the constraints of the original allocation request such
+as alignment or boundary restrictions are checked by
+.Fn rman_adjust_resource .
+It is the caller's responsibility to enforce any such requirements.
+.Pp
+The
.Fn rman_release_resource
function releases the reserved resource
.Fa r .
diff --git a/sys/kern/subr_rman.c b/sys/kern/subr_rman.c
index 4352278..3014b19 100644
--- a/sys/kern/subr_rman.c
+++ b/sys/kern/subr_rman.c
@@ -272,6 +272,164 @@ rman_fini(struct rman *rm)
return 0;
}
+int
+rman_first_free_region(struct rman *rm, u_long *start, u_long *end)
+{
+ struct resource_i *r;
+
+ mtx_lock(rm->rm_mtx);
+ TAILQ_FOREACH(r, &rm->rm_list, r_link) {
+ if (!(r->r_flags & RF_ALLOCATED)) {
+ *start = r->r_start;
+ *end = r->r_end;
+ mtx_unlock(rm->rm_mtx);
+ return (0);
+ }
+ }
+ mtx_unlock(rm->rm_mtx);
+ return (ENOENT);
+}
+
+int
+rman_last_free_region(struct rman *rm, u_long *start, u_long *end)
+{
+ struct resource_i *r;
+
+ mtx_lock(rm->rm_mtx);
+ TAILQ_FOREACH_REVERSE(r, &rm->rm_list, resource_head, r_link) {
+ if (!(r->r_flags & RF_ALLOCATED)) {
+ *start = r->r_start;
+ *end = r->r_end;
+ mtx_unlock(rm->rm_mtx);
+ return (0);
+ }
+ }
+ mtx_unlock(rm->rm_mtx);
+ return (ENOENT);
+}
+
+/* Shrink or extend one or both ends of an allocated resource. */
+int
+rman_adjust_resource(struct resource *rr, u_long start, u_long end)
+{
+ struct resource_i *r, *s, *t, *new;
+ struct rman *rm;
+
+ /* Not supported for shared resources. */
+ r = rr->__r_i;
+ if (r->r_flags & (RF_TIMESHARE | RF_SHAREABLE))
+ return (EINVAL);
+
+ /*
+ * This does not support wholesale moving of a resource. At
+ * least part of the desired new range must overlap with the
+ * existing resource.
+ */
+ if (end < r->r_start || r->r_end < start)
+ return (EINVAL);
+
+ /*
+ * Find the two resource regions immediately adjacent to the
+ * allocated resource.
+ */
+ rm = r->r_rm;
+ mtx_lock(rm->rm_mtx);
+#ifdef INVARIANTS
+ TAILQ_FOREACH(s, &rm->rm_list, r_link) {
+ if (s == r)
+ break;
+ }
+ if (s == NULL)
+ panic("resource not in list");
+#endif
+ s = TAILQ_PREV(r, resource_head, r_link);
+ t = TAILQ_NEXT(r, r_link);
+ KASSERT(s == NULL || s->r_end + 1 == r->r_start,
+ ("prev resource mismatch"));
+ KASSERT(t == NULL || r->r_end + 1 == t->r_start,
+ ("next resource mismatch"));
+
+ /*
+ * See if the changes are permitted. Shrinking is always allowed,
+ * but growing requires sufficient room in the adjacent region.
+ */
+ if (start < r->r_start && (s == NULL || (s->r_flags & RF_ALLOCATED) ||
+ s->r_start > start)) {
+ mtx_unlock(rm->rm_mtx);
+ return (EBUSY);
+ }
+ if (end > r->r_end && (t == NULL || (t->r_flags & RF_ALLOCATED) ||
+ t->r_end < end)) {
+ mtx_unlock(rm->rm_mtx);
+ return (EBUSY);
+ }
+
+ /*
+ * While holding the lock, grow either end of the resource as
+ * needed and shrink either end if the shrinking does not require
+ * allocating a new resource. We can safely drop the lock and then
+ * insert a new range to handle the shrinking case afterwards.
+ */
+ if (start < r->r_start ||
+ (start > r->r_start && s != NULL && !(s->r_flags & RF_ALLOCATED))) {
+ KASSERT(s->r_flags == 0, ("prev is busy"));
+ r->r_start = start;
+ if (s->r_start == start) {
+ TAILQ_REMOVE(&rm->rm_list, s, r_link);
+ free(s, M_RMAN);
+ } else
+ s->r_end = start - 1;
+ }
+ if (end > r->r_end ||
+ (end < r->r_end && t != NULL && !(t->r_flags & RF_ALLOCATED))) {
+ KASSERT(t->r_flags == 0, ("next is busy"));
+ r->r_end = end;
+ if (t->r_end == end) {
+ TAILQ_REMOVE(&rm->rm_list, t, r_link);
+ free(t, M_RMAN);
+ } else
+ t->r_start = end + 1;
+ }
+ mtx_unlock(rm->rm_mtx);
+
+ /*
+ * Handle the shrinking cases that require allocating a new
+ * resource to hold the newly-free region. We have to recheck
+ * if we still need this new region after acquiring the lock.
+ */
+ if (start > r->r_start) {
+ new = int_alloc_resource(M_WAITOK);
+ new->r_start = r->r_start;
+ new->r_end = start - 1;
+ new->r_rm = rm;
+ mtx_lock(rm->rm_mtx);
+ r->r_start = start;
+ s = TAILQ_PREV(r, resource_head, r_link);
+ if (s != NULL && !(s->r_flags & RF_ALLOCATED)) {
+ s->r_end = start - 1;
+ free(new, M_RMAN);
+ } else
+ TAILQ_INSERT_BEFORE(r, new, r_link);
+ mtx_unlock(rm->rm_mtx);
+ }
+ if (end < r->r_end) {
+ new = int_alloc_resource(M_WAITOK);
+ new->r_start = end + 1;
+ new->r_end = r->r_end;
+ new->r_rm = rm;
+ mtx_lock(rm->rm_mtx);
+ r->r_end = end;
+ t = TAILQ_NEXT(r, r_link);
+ if (t != NULL && !(t->r_flags & RF_ALLOCATED)) {
+ t->r_start = end + 1;
+ free(new, M_RMAN);
+ } else
+ TAILQ_INSERT_AFTER(&rm->rm_list, r, new, r_link);
+ mtx_unlock(rm->rm_mtx);
+ }
+ return (0);
+}
+
struct resource *
rman_reserve_resource_bound(struct rman *rm, u_long start, u_long end,
u_long count, u_long bound, u_int flags,
diff --git a/sys/sys/rman.h b/sys/sys/rman.h
index ba06cc0..b34ef37 100644
--- a/sys/sys/rman.h
+++ b/sys/sys/rman.h
@@ -116,7 +116,9 @@ struct rman {
TAILQ_HEAD(rman_head, rman);
int rman_activate_resource(struct resource *r);
+int rman_adjust_resource(struct resource *r, u_long start, u_long end);
int rman_await_resource(struct resource *r, int pri, int timo);
+int rman_first_free_region(struct rman *rm, u_long *start, u_long *end);
bus_space_handle_t rman_get_bushandle(struct resource *);
bus_space_tag_t rman_get_bustag(struct resource *);
u_long rman_get_end(struct resource *);
@@ -130,6 +132,7 @@ int rman_deactivate_resource(struct resource *r);
int rman_fini(struct rman *rm);
int rman_init(struct rman *rm);
int rman_init_from_resource(struct rman *rm, struct resource *r);
+int rman_last_free_region(struct rman *rm, u_long *start, u_long *end);
uint32_t rman_make_alignment_flags(uint32_t size);
int rman_manage_region(struct rman *rm, u_long start, u_long end);
int rman_is_region_manager(struct resource *r, struct rman *rm);
OpenPOWER on IntegriCloud