bhnd(4): Fix resource allocation issues exposed by chipc PMU support.
- Delete all chipc children on attachment failure. - Added missing bhnd_nexus bhnd_bus_deactivate_resource implementation. - Drop a CHIPC_UNLOCK() accidentally left behind after lifting synchronization into the chipc region refcounting API. - Fix re-allocation of chipc resources. Previously, the resource ID was reset to -1 on release, preventing later re-allocation. Approved by: re (gjb), adrian (mentor) Differential Revision: https://reviews.freebsd.org/D6849
This commit is contained in:
parent
2d40bd13de
commit
4bf2fbdedf
@ -106,10 +106,26 @@ bhnd_nexus_activate_resource(device_t dev, device_t child, int type, int rid,
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bhnd_nexus_deactivate_resource(device_t dev, device_t child,
|
||||
int type, int rid, struct bhnd_resource *r)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Always direct */
|
||||
KASSERT(r->direct, ("indirect resource delegated to bhnd_nexus\n"));
|
||||
|
||||
if ((error = bus_deactivate_resource(child, type, rid, r->res)))
|
||||
return (error);
|
||||
|
||||
r->direct = false;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t bhnd_nexus_methods[] = {
|
||||
/* bhnd interface */
|
||||
DEVMETHOD(bhnd_bus_activate_resource, bhnd_nexus_activate_resource),
|
||||
DEVMETHOD(bhnd_bus_deactivate_resource, bhnd_nexus_deactivate_resource),
|
||||
DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_nexus_is_hw_disabled),
|
||||
DEVMETHOD(bhnd_bus_get_attach_type, bhnd_nexus_get_attach_type),
|
||||
|
||||
|
@ -282,6 +282,8 @@ chipc_attach(device_t dev)
|
||||
return (0);
|
||||
|
||||
failed:
|
||||
device_delete_children(sc->dev);
|
||||
|
||||
if (sc->core_region != NULL) {
|
||||
chipc_release_region(sc, sc->core_region,
|
||||
RF_ALLOCATED|RF_ACTIVE);
|
||||
@ -878,10 +880,8 @@ chipc_alloc_resource(device_t dev, device_t child, int type,
|
||||
}
|
||||
|
||||
/* Try to retain a region reference */
|
||||
if ((error = chipc_retain_region(sc, cr, RF_ALLOCATED))) {
|
||||
CHIPC_UNLOCK(sc);
|
||||
if ((error = chipc_retain_region(sc, cr, RF_ALLOCATED)))
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Make our rman reservation */
|
||||
rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
|
||||
|
@ -84,11 +84,13 @@ struct chipc_region {
|
||||
bhnd_addr_t cr_addr; /**< region base address */
|
||||
bhnd_addr_t cr_end; /**< region end address */
|
||||
bhnd_size_t cr_count; /**< region count */
|
||||
int cr_rid; /**< rid, or -1 if no rid
|
||||
* is allocated by the bus for
|
||||
* this region */
|
||||
int cr_rid; /**< rid to use when performing
|
||||
resource allocation, or -1
|
||||
if region has no assigned
|
||||
resource ID */
|
||||
|
||||
struct bhnd_resource *cr_res; /**< bus resource, or NULL */
|
||||
int cr_res_rid; /**< cr_res RID, if any. */
|
||||
u_int cr_refs; /**< RF_ALLOCATED refcount */
|
||||
u_int cr_act_refs; /**< RF_ACTIVE refcount */
|
||||
|
||||
|
@ -150,9 +150,10 @@ chipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type,
|
||||
|
||||
cr->cr_end = cr->cr_addr + cr->cr_count - 1;
|
||||
|
||||
/* Note that not all regions have an assigned rid, in which case
|
||||
* this will return -1 */
|
||||
/* Fetch default resource ID for this region. Not all regions have an
|
||||
* assigned rid, in which case this will return -1 */
|
||||
cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region);
|
||||
|
||||
return (cr);
|
||||
|
||||
failed:
|
||||
@ -177,7 +178,7 @@ chipc_free_region(struct chipc_softc *sc, struct chipc_region *cr)
|
||||
cr->cr_region_num, cr->cr_refs));
|
||||
|
||||
if (cr->cr_res != NULL) {
|
||||
bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_rid,
|
||||
bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_res_rid,
|
||||
cr->cr_res);
|
||||
}
|
||||
|
||||
@ -264,10 +265,16 @@ chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags)
|
||||
KASSERT(cr->cr_res == NULL,
|
||||
("non-NULL resource has refcount"));
|
||||
|
||||
cr->cr_res = bhnd_alloc_resource(sc->dev,
|
||||
SYS_RES_MEMORY, &cr->cr_rid, cr->cr_addr,
|
||||
cr->cr_end, cr->cr_count, 0);
|
||||
/* Fetch initial resource ID */
|
||||
if ((cr->cr_res_rid = cr->cr_rid) == -1) {
|
||||
CHIPC_UNLOCK(sc);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/* Allocate resource */
|
||||
cr->cr_res = bhnd_alloc_resource(sc->dev,
|
||||
SYS_RES_MEMORY, &cr->cr_res_rid, cr->cr_addr,
|
||||
cr->cr_end, cr->cr_count, 0);
|
||||
if (cr->cr_res == NULL) {
|
||||
CHIPC_UNLOCK(sc);
|
||||
return (ENXIO);
|
||||
@ -287,7 +294,7 @@ chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags)
|
||||
/* If this is the first reference, activate the resource */
|
||||
if (cr->cr_act_refs == 0) {
|
||||
error = bhnd_activate_resource(sc->dev, SYS_RES_MEMORY,
|
||||
cr->cr_rid, cr->cr_res);
|
||||
cr->cr_res_rid, cr->cr_res);
|
||||
if (error) {
|
||||
/* Drop any allocation reference acquired
|
||||
* above */
|
||||
@ -324,6 +331,8 @@ chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
|
||||
CHIPC_LOCK(sc);
|
||||
error = 0;
|
||||
|
||||
KASSERT(cr->cr_res != NULL, ("release on NULL region resource"));
|
||||
|
||||
if (flags & RF_ACTIVE) {
|
||||
KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released"));
|
||||
KASSERT(cr->cr_act_refs <= cr->cr_refs,
|
||||
@ -332,7 +341,7 @@ chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
|
||||
/* If this is the last reference, deactivate the resource */
|
||||
if (cr->cr_act_refs == 1) {
|
||||
error = bhnd_deactivate_resource(sc->dev,
|
||||
SYS_RES_MEMORY, cr->cr_rid, cr->cr_res);
|
||||
SYS_RES_MEMORY, cr->cr_res_rid, cr->cr_res);
|
||||
if (error)
|
||||
goto done;
|
||||
}
|
||||
@ -343,16 +352,14 @@ chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
|
||||
|
||||
if (flags & RF_ALLOCATED) {
|
||||
KASSERT(cr->cr_refs > 0, ("overrelease of refs"));
|
||||
|
||||
/* If this is the last reference, release the resource */
|
||||
if (cr->cr_refs == 1) {
|
||||
error = bhnd_release_resource(sc->dev,
|
||||
SYS_RES_MEMORY, cr->cr_rid, cr->cr_res);
|
||||
error = bhnd_release_resource(sc->dev, SYS_RES_MEMORY,
|
||||
cr->cr_res_rid, cr->cr_res);
|
||||
if (error)
|
||||
goto done;
|
||||
|
||||
cr->cr_res = NULL;
|
||||
cr->cr_rid = -1;
|
||||
}
|
||||
|
||||
/* Drop our allocation refcount */
|
||||
|
Loading…
Reference in New Issue
Block a user