From a1522e2178b98014ff7efc86ccea4e5283fcda6b Mon Sep 17 00:00:00 2001 From: imp Date: Thu, 23 Aug 2018 05:06:22 +0000 Subject: [PATCH] When trying to match the nomatch event passed to us, attempt to look up the device described by the nomatch event in the device tree. If we find it, then if the device is marked as have already attached to a device once, then ignore the device. This keeps us from reloading the device driver when it has just been manually unloaded. All devies that have had a driver attach to them at least once no longer participate in pnp-based autoloading. Differential Revision: https://reviews.freebsd.org/D16735 --- sbin/devmatch/devmatch.c | 65 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/sbin/devmatch/devmatch.c b/sbin/devmatch/devmatch.c index 9173270520a..bc3a4e1f03a 100644 --- a/sbin/devmatch/devmatch.c +++ b/sbin/devmatch/devmatch.c @@ -65,6 +65,7 @@ static int verbose_flag; static void *hints; static void *hints_end; +static struct devinfo_dev *root; static void * read_hints(const char *fn, size_t *len) @@ -440,10 +441,50 @@ find_unmatched(struct devinfo_dev *dev, void *arg) return (devinfo_foreach_device_child(dev, find_unmatched, arg)); } +struct exact_info +{ + const char *bus; + const char *loc; + struct devinfo_dev *dev; +}; + +/* + * Look for the exact location specified by the nomatch event. The + * loc and pnpinfo run together to get the string we're looking for, + * so we have to synthesize the same thing that subr_bus.c is + * generating in devnomatch/devaddq to do the string comparison. + */ +static int +find_exact_dev(struct devinfo_dev *dev, void *arg) +{ + struct devinfo_dev *parent; + char *loc; + struct exact_info *info; + + info = arg; + do { + if (info->dev != NULL) + break; + if (!(dev->dd_flags & DF_ENABLED)) + break; + parent = devinfo_handle_to_device(dev->dd_parent); + if (strcmp(info->bus, parent->dd_name) != 0) + break; + asprintf(&loc, "%s %s", parent->dd_pnpinfo, + parent->dd_location); + if (strcmp(loc, info->loc) == 0) + info->dev = dev; + free(loc); + } while (0); + + return (devinfo_foreach_device_child(dev, find_exact_dev, arg)); +} + static void find_nomatch(char *nomatch) { - char *bus, *pnpinfo, *tmp; + char *bus, *pnpinfo, *tmp, *busnameunit; + struct exact_info info; /* * Find our bus name. It will include the unit number. We have to search @@ -459,6 +500,9 @@ find_nomatch(char *nomatch) errx(1, "No bus found in nomatch string: '%s'", nomatch); bus = tmp + 4; *tmp = '\0'; + busnameunit = strdup(bus); + if (busnameunit == NULL) + errx(1, "Can't allocate memory for strings"); tmp = bus + strlen(bus) - 1; while (tmp > bus && isdigit(*tmp)) tmp--; @@ -475,6 +519,17 @@ find_nomatch(char *nomatch) errx(1, "Malformed NOMATCH string: '%s'", nomatch); pnpinfo = nomatch + 4; + /* + * See if we can find the devinfo_dev for this device. If we + * can, and it's been attached before, we should filter it out + * so that a kldunload foo doesn't cause an immediate reload. + */ + info.loc = pnpinfo; + info.bus = busnameunit; + info.dev = NULL; + devinfo_foreach_device_child(root, find_exact_dev, (void *)&info); + if (info.dev != NULL && info.dev->dd_flags & DF_ATTACHED_ONCE) + exit(0); search_hints(bus, "", pnpinfo); exit(0); @@ -490,7 +545,6 @@ usage(void) int main(int argc, char **argv) { - struct devinfo_dev *root; int ch; while ((ch = getopt_long(argc, argv, "adh:p:uv", @@ -530,12 +584,13 @@ main(int argc, char **argv) exit(0); } - if (nomatch_str != NULL) - find_nomatch(nomatch_str); if (devinfo_init()) err(1, "devinfo_init"); if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) errx(1, "can't find root device"); - devinfo_foreach_device_child(root, find_unmatched, (void *)0); + if (nomatch_str != NULL) + find_nomatch(nomatch_str); + else + devinfo_foreach_device_child(root, find_unmatched, (void *)0); devinfo_free(); } -- 2.45.0