summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/subr_bus.c')
-rw-r--r--sys/kern/subr_bus.c153
1 files changed, 151 insertions, 2 deletions
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index eaec75b..8c2db32 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -2866,6 +2866,7 @@ resource_list_add(struct resource_list *rl, int type, int rid,
rle->type = type;
rle->rid = rid;
rle->res = NULL;
+ rle->flags = 0;
}
if (rle->res)
@@ -2878,6 +2879,31 @@ resource_list_add(struct resource_list *rl, int type, int rid,
}
/**
+ * @brief Determine if a resource entry is busy.
+ *
+ * Returns true if a resource entry is busy meaning that it has an
+ * associated resource that is not an unallocated "reserved" resource.
+ *
+ * @param rl the resource list to search
+ * @param type the resource entry type (e.g. SYS_RES_MEMORY)
+ * @param rid the resource identifier
+ *
+ * @returns Non-zero if the entry is busy, zero otherwise.
+ */
+int
+resource_list_busy(struct resource_list *rl, int type, int rid)
+{
+ struct resource_list_entry *rle;
+
+ rle = resource_list_find(rl, type, rid);
+ if (rle == NULL || rle->res == NULL)
+ return (0);
+ if ((rle->flags & (RLE_RESERVED | RLE_ALLOCATED)) == RLE_RESERVED)
+ return (0);
+ return (1);
+}
+
+/**
* @brief Find a resource entry by type and rid.
*
* @param rl the resource list to search
@@ -2920,6 +2946,66 @@ resource_list_delete(struct resource_list *rl, int type, int rid)
}
/**
+ * @brief Allocate a reserved resource
+ *
+ * This can be used by busses to force the allocation of resources
+ * that are always active in the system even if they are not allocated
+ * by a driver (e.g. PCI BARs). This function is usually called when
+ * adding a new child to the bus. The resource is allocated from the
+ * parent bus when it is reserved. The resource list entry is marked
+ * with RLE_RESERVED to note that it is a reserved resource.
+ *
+ * Subsequent attempts to allocate the resource with
+ * resource_list_alloc() will succeed the first time and will set
+ * RLE_ALLOCATED to note that it has been allocated. When a reserved
+ * resource that has been allocated is released with
+ * resource_list_release() the resource RLE_ALLOCATED is cleared, but
+ * the actual resource remains allocated. The resource can be released to
+ * the parent bus by calling resource_list_unreserve().
+ *
+ * @param rl the resource list to allocate from
+ * @param bus the parent device of @p child
+ * @param child the device for which the resource is being reserved
+ * @param type the type of resource to allocate
+ * @param rid a pointer to the resource identifier
+ * @param start hint at the start of the resource range - pass
+ * @c 0UL for any start address
+ * @param end hint at the end of the resource range - pass
+ * @c ~0UL for any end address
+ * @param count hint at the size of range required - pass @c 1
+ * for any size
+ * @param flags any extra flags to control the resource
+ * allocation - see @c RF_XXX flags in
+ * <sys/rman.h> for details
+ *
+ * @returns the resource which was allocated or @c NULL if no
+ * resource could be allocated
+ */
+struct resource *
+resource_list_reserve(struct resource_list *rl, device_t bus, device_t child,
+ int type, int *rid, u_long start, u_long end, u_long count, u_int flags)
+{
+ struct resource_list_entry *rle = NULL;
+ int passthrough = (device_get_parent(child) != bus);
+ struct resource *r;
+
+ if (passthrough)
+ panic(
+ "resource_list_reserve() should only be called for direct children");
+ if (flags & RF_ACTIVE)
+ panic(
+ "resource_list_reserve() should only reserve inactive resources");
+
+ r = resource_list_alloc(rl, bus, child, type, rid, start, end, count,
+ flags);
+ if (r != NULL) {
+ rle = resource_list_find(rl, type, *rid);
+ rle->flags |= RLE_RESERVED;
+ }
+ return (r);
+}
+
+/**
* @brief Helper function for implementing BUS_ALLOC_RESOURCE()
*
* Implement BUS_ALLOC_RESOURCE() by looking up a resource from the list
@@ -2970,8 +3056,19 @@ resource_list_alloc(struct resource_list *rl, device_t bus, device_t child,
if (!rle)
return (NULL); /* no resource of that type/rid */
- if (rle->res)
+ if (rle->res) {
+ if (rle->flags & RLE_RESERVED) {
+ if (rle->flags & RLE_ALLOCATED)
+ return (NULL);
+ else if ((flags & RF_ACTIVE) &&
+ bus_activate_resource(child, type, *rid,
+ rle->res) != 0)
+ return (NULL);
+ else
+ return (rle->res);
+ }
panic("resource_list_alloc: resource entry is busy");
+ }
if (isdefault) {
start = rle->start;
@@ -3003,7 +3100,7 @@ resource_list_alloc(struct resource_list *rl, device_t bus, device_t child,
* @param rl the resource list which was allocated from
* @param bus the parent device of @p child
* @param child the device which is requesting a release
- * @param type the type of resource to allocate
+ * @param type the type of resource to release
* @param rid the resource identifier
* @param res the resource to release
*
@@ -3030,6 +3127,19 @@ resource_list_release(struct resource_list *rl, device_t bus, device_t child,
panic("resource_list_release: can't find resource");
if (!rle->res)
panic("resource_list_release: resource entry is not busy");
+ if (rle->flags & RLE_RESERVED) {
+ if (rle->flags & RLE_ALLOCATED) {
+ if (rman_get_flags(res) & RF_ACTIVE) {
+ error = bus_deactivate_resource(child, type,
+ rid, res);
+ if (error)
+ return (error);
+ }
+ rle->flags &= ~RLE_ALLOCATED;
+ return (0);
+ }
+ return (EINVAL);
+ }
error = BUS_RELEASE_RESOURCE(device_get_parent(bus), child,
type, rid, res);
@@ -3041,6 +3151,45 @@ resource_list_release(struct resource_list *rl, device_t bus, device_t child,
}
/**
+ * @brief Fully release a reserved resource
+ *
+ * Fully releases a resouce reserved via resource_list_reserve().
+ *
+ * @param rl the resource list which was allocated from
+ * @param bus the parent device of @p child
+ * @param child the device whose reserved resource is being released
+ * @param type the type of resource to release
+ * @param rid the resource identifier
+ * @param res the resource to release
+ *
+ * @retval 0 success
+ * @retval non-zero a standard unix error code indicating what
+ * error condition prevented the operation
+ */
+int
+resource_list_unreserve(struct resource_list *rl, device_t bus, device_t child,
+ int type, int rid)
+{
+ struct resource_list_entry *rle = NULL;
+ int passthrough = (device_get_parent(child) != bus);
+
+ if (passthrough)
+ panic(
+ "resource_list_unreserve() should only be called for direct children");
+
+ rle = resource_list_find(rl, type, rid);
+
+ if (!rle)
+ panic("resource_list_unreserve: can't find resource");
+ if (!(rle->flags & RLE_RESERVED))
+ return (EINVAL);
+ if (rle->flags & RLE_ALLOCATED)
+ return (EBUSY);
+ rle->flags &= ~RLE_RESERVED;
+ return (resource_list_release(rl, bus, child, type, rid, rle->res));
+}
+
+/**
* @brief Print a description of resources in a resource list
*
* Print all resources of a specified type, for use in BUS_PRINT_CHILD().
OpenPOWER on IntegriCloud