Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -6039,6 +6039,113 @@ UID_ROOT, GID_WHEEL, 0600, "devctl2"); } +/* + * For maintaining device 'at' location info to avoid recomputing it + */ +struct device_location_node { + const char *dln_locator; + const char *dln_path; + TAILQ_ENTRY(device_location_node) dln_link; +}; +typedef TAILQ_HEAD(device_location_list, device_location_node) device_location_list_t; + +struct device_location_cache { + device_location_list_t dlc_list; +}; + + +/* + * Location cache for wired devices... Maybe need a better name? + */ +device_location_cache_t * +dev_wired_cache_init(void) +{ + device_location_cache_t *dcp; + + dcp = malloc(sizeof(*dcp), M_BUS, M_WAITOK | M_ZERO); + TAILQ_INIT(&dcp->dlc_list); + return (dcp); +} + +void +dev_wired_cache_fini(device_location_cache_t *dcp) +{ + struct device_location_node *dln, *tdln; + + TAILQ_FOREACH_SAFE(dln, &dcp->dlc_list, dln_link, tdln) { + /* Note: one allocation for both node and strings */ + free(dln, M_BUS); + } + free(dcp, M_BUS); +} + +static struct device_location_node * +dev_wired_cache_lookup(device_location_cache_t *dcp, const char *locator) +{ + struct device_location_node *dln; + + TAILQ_FOREACH(dln, &dcp->dlc_list, dln_link) { + if (strcmp(locator, dln->dln_locator) == 0) + return (dln); + } + return (NULL); +} + +static struct device_location_node * +dev_wired_cache_add(device_location_cache_t *dcp, const char *locator, const char *path) +{ + struct device_location_node *dln; + size_t len; + char *l, *p = NULL; + + len = sizeof(*dln) + strlen(locator) + 1; + if (path != NULL) + len += strlen(path) + 1; + dln = malloc(len, M_BUS, M_WAITOK | M_ZERO); + dln->dln_locator = l = (char *)(dln + 1); + memcpy(l, locator, strlen(locator) + 1); + if (path == NULL) { + dln->dln_path = NULL; + } else { + dln->dln_path = p = l + strlen(locator) + 1; + memcpy(p, path, strlen(path) + 1); + } + TAILQ_INSERT_HEAD(&dcp->dlc_list, dln, dln_link); + KASSERT(len == sizeof(*dln) + strlen(dln->dln_locator) + 1 + + (dln->dln_path ? (strlen(dln->dln_path) + 1) : 0), + ("dev_wired_cache_add: doesn't add up")); + return (dln); +} + +bool +dev_wired_cache_match(device_location_cache_t *dcp, device_t dev, const char *at) +{ + const char *cp, *path; + char locator[32]; + int len; + struct device_location_node *res; + + cp = strchr(at, ':'); + if (cp == NULL) + return (false); + len = cp - at; + if (len > sizeof(locator) - 1) /* Skip too long locator */ + return (false); + memcpy(locator, at, len); + locator[len] = '\0'; + cp++; + /* maybe cache this inside device_t and look that up, but not yet */ + res = dev_wired_cache_lookup(dcp, locator); + if (res == NULL) { + path = device_get_path(dev, locator); + res = dev_wired_cache_add(dcp, locator, path); + free(__DECONST(void *, path), M_BUS); + } + if (res == NULL || res->dln_path == NULL) + return (false); + return (strcmp(res->dln_path, cp) == 0); +} + /* * APIs to manage deprecation and obsolescence. */ Index: sys/sys/bus.h =================================================================== --- sys/sys/bus.h +++ sys/sys/bus.h @@ -832,6 +832,13 @@ device_get_nameunit(device_get_parent(dev)), e)); \ } +struct device_location_cache; +typedef struct device_location_cache device_location_cache_t; +device_location_cache_t *dev_wired_cache_init(void); +void dev_wired_cache_fini(device_location_cache_t *dcp); +bool dev_wired_cache_match(device_location_cache_t *dcp, device_t dev, const char *at); + + /** * Shorthand macros, taking resource argument * Generated with sys/tools/bus_macro.sh