Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -210,6 +210,70 @@ #define print_devclass_list() /* nop */ #endif +/* + * Preferred device list. If there's a tie, and the driver is on this list, + * it will be the one that wins the tie. + */ +static int sysctl_preferred(SYSCTL_HANDLER_ARGS); +static const char **preferred_drivers; +static int npref; +SYSCTL_PROC(_hw_bus, OID_AUTO, preferred, CTLTYPE_STRING | CTLFLAG_RWTUN | + CTLFLAG_MPSAFE, NULL, 0, sysctl_preferred, "A", "list of preferrred drivers"); + +static int +sysctl_preferred(SYSCTL_HANDLER_ARGS) +{ + int error, len, newlen; + struct sbuf *sbuf; + char *buffer, *walk1, *walk2; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + + sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req); + for (int i = 0; i < npref; i++) { + if (i > 0) + sbuf_putc(sbuf, ','); + sbuf_printf(sbuf, "%s", preferred_drivers[i]); + } + + error = sbuf_finish(sbuf); + sbuf_delete(sbuf); + + if (error || req->newptr == NULL) + return (error); + + len = req->newlen - req->newidx; + if (len == 0) + return (0); + + walk1 = buffer = malloc(len, M_BUS, M_ZERO | M_WAITOK); + error = SYSCTL_IN(req, buffer, len); + + newlen = len; + npref = 1; + for (int i = 0; i < strlen(buffer); i++) + if (buffer[i] == ',') + npref++; + free(preferred_drivers, M_BUS); + preferred_drivers = malloc(newlen + npref * sizeof(const char *), + M_BUS, M_ZERO | M_WAITOK); + walk2 = (char *)(uintptr_t)preferred_drivers + npref * sizeof(const char *); + for (int i = 0; i < npref; i++) { + preferred_drivers[i] = walk2; + len = strcspn(walk1, ","); + memcpy(walk2, walk1, len); + walk2[len] = '\0'; + walk1 += len + 1; + walk2 += len + 1; + } + + free(buffer, M_BUS); + return (error); +} + + /* * dev sysctl tree */ @@ -377,7 +441,6 @@ static int devctl_queue_length = DEVCTL_DEFAULT_QUEUE_LEN; SYSCTL_PROC(_hw_bus, OID_AUTO, devctl_queue, CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, 0, sysctl_devctl_queue, "I", "devctl queue length"); - static d_open_t devopen; static d_close_t devclose; static d_read_t devread; @@ -2096,6 +2159,19 @@ return (TAILQ_NEXT(last, link)); } +/** + * @internal + */ +static bool +driver_is_preferred(const char *name) +{ + for (int i = 0; i < npref; i++) + if (strcmp(name, preferred_drivers[i]) == 0) + return (true); + return (false); +} + + /** * @internal */ @@ -2203,7 +2279,8 @@ * best matching driver. Initialise the value * of pri for the first match. */ - if (best == NULL || result > pri) { + if (best == NULL || result > pri || + (result == pri && driver_is_preferred(child->driver->name))) { best = dl; pri = result; continue;