Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -405,27 +405,30 @@ .d_name = "devctl", }; +#define DEVCTL_BUFFER (1024 - sizeof(void *)) struct dev_event_info { - char *dei_data; STAILQ_ENTRY(dev_event_info) dei_link; + char dei_data[DEVCTL_BUFFER]; }; STAILQ_HEAD(devq, dev_event_info); static struct dev_softc { - int inuse; - int nonblock; - int queued; - int async; - struct mtx mtx; - struct cv cv; - struct selinfo sel; - struct devq devq; - struct sigio *sigio; + int inuse; + int nonblock; + int queued; + int async; + struct mtx mtx; + struct cv cv; + struct selinfo sel; + struct devq devq; + struct sigio *sigio; + uma_zone_t zone; } devsoftc; + static void filt_devctl_detach(struct knote *kn); static int filt_devctl_read(struct knote *kn, long hint); @@ -446,6 +449,9 @@ cv_init(&devsoftc.cv, "dev cv"); STAILQ_INIT(&devsoftc.devq); knlist_init_mtx(&devsoftc.sel.si_note, &devsoftc.mtx); + devsoftc.zone = uma_zcreate("DEVCTL", sizeof(struct dev_event_info), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + uma_prealloc(devsoftc.zone, devctl_queue_length); devctl2_init(); } @@ -510,8 +516,7 @@ devsoftc.queued--; mtx_unlock(&devsoftc.mtx); rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); + uma_zfree(devsoftc.zone, n1); return (rv); } @@ -600,42 +605,53 @@ return (devsoftc.inuse == 1); } -/** - * @brief Queue data to be read from the devctl device - * - * Generic interface to queue data to the devctl device. It is - * assumed that @p data is properly formatted. It is further assumed - * that @p data is allocated using the M_BUS malloc type. - */ -static void -devctl_queue_data_f(char *data, int flags) +static struct dev_event_info * +devctl_alloc_dei() { - struct dev_event_info *n1 = NULL, *n2 = NULL; + struct dev_event_info *dei = NULL; - if (strlen(data) == 0) - goto out; + mtx_lock(&devsoftc.mtx); if (devctl_queue_length == 0) goto out; - n1 = malloc(sizeof(*n1), M_BUS, flags); - if (n1 == NULL) - goto out; - n1->dei_data = data; - mtx_lock(&devsoftc.mtx); - if (devctl_queue_length == 0) { - mtx_unlock(&devsoftc.mtx); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); - return; - } - /* Leave at least one spot in the queue... */ - while (devsoftc.queued > devctl_queue_length - 1) { - n2 = STAILQ_FIRST(&devsoftc.devq); + if (devctl_queue_length == devsoftc.queued) { + dei = STAILQ_FIRST(&devsoftc.devq); STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); - free(n2->dei_data, M_BUS); - free(n2, M_BUS); devsoftc.queued--; + } else { + /* dei can't be NULL -- we know we have at least one in the zone */ + dei = uma_zalloc(devsoftc.zone, M_NOWAIT); + MPASS(dei != NULL); } - STAILQ_INSERT_TAIL(&devsoftc.devq, n1, dei_link); + *dei->dei_data = '\0'; +out: + mtx_unlock(&devsoftc.mtx); + return (dei); +} + +static struct dev_event_info * +devctl_alloc_dei_sb(struct sbuf *sb) +{ + struct dev_event_info *dei; + + dei = devctl_alloc_dei(); + if (dei != NULL) + sbuf_new(sb, dei->dei_data, DEVCTL_BUFFER, SBUF_FIXEDLEN); + return (dei); +} + + +static void +devctl_free_dei(struct dev_event_info *dei) +{ + uma_zfree(devsoftc.zone, dei); +} + +static void +devctl_queue(struct dev_event_info *dei) +{ +// printf("%s", dei->dei_data); + mtx_lock(&devsoftc.mtx); + STAILQ_INSERT_TAIL(&devsoftc.devq, dei, dei_link); devsoftc.queued++; cv_broadcast(&devsoftc.cv); KNOTE_LOCKED(&devsoftc.sel.si_note, 0); @@ -643,20 +659,6 @@ selwakeup(&devsoftc.sel); if (devsoftc.async && devsoftc.sigio != NULL) pgsigio(&devsoftc.sigio, SIGIO, 0); - return; -out: - /* - * We have to free data on all error paths since the caller - * assumes it will be free'd when this item is dequeued. - */ - free(data, M_BUS); - return; -} - -static void -devctl_queue_data(char *data) -{ - devctl_queue_data_f(data, M_NOWAIT); } /** @@ -664,34 +666,35 @@ */ void devctl_notify_f(const char *system, const char *subsystem, const char *type, - const char *data, int flags) -{ - int len = 0; - char *msg; - - if (system == NULL) - return; /* BOGUS! Must specify system. */ - if (subsystem == NULL) - return; /* BOGUS! Must specify subsystem. */ - if (type == NULL) - return; /* BOGUS! Must specify type. */ - len += strlen(" system=") + strlen(system); - len += strlen(" subsystem=") + strlen(subsystem); - len += strlen(" type=") + strlen(type); - /* add in the data message plus newline. */ - if (data != NULL) - len += strlen(data); - len += 3; /* '!', '\n', and NUL */ - msg = malloc(len, M_BUS, flags); - if (msg == NULL) - return; /* Drop it on the floor */ - if (data != NULL) - snprintf(msg, len, "!system=%s subsystem=%s type=%s %s\n", - system, subsystem, type, data); - else - snprintf(msg, len, "!system=%s subsystem=%s type=%s\n", - system, subsystem, type); - devctl_queue_data_f(msg, flags); + const char *data, int flags __unused) +{ + struct dev_event_info *dei; + struct sbuf sb; + + if (system == NULL || subsystem == NULL || type == NULL) + return; + dei = devctl_alloc_dei_sb(&sb); + if (dei == NULL) + return; + sbuf_cpy(&sb, "!system="); + sbuf_cat(&sb, system); + sbuf_cat(&sb, " subsystem="); + sbuf_cat(&sb, subsystem); + sbuf_cat(&sb, " type="); + sbuf_cat(&sb, type); + if (data != NULL) { + sbuf_putc(&sb, ' '); + sbuf_cat(&sb, data); + } + sbuf_putc(&sb, '\n'); + sbuf_finish(&sb); + if (sbuf_error(&sb)) + goto bad; + devctl_queue(dei); + return; +bad: + devctl_free_dei(dei); + return; } void @@ -713,54 +716,63 @@ * object of that event, plus the plug and play info and location info * for that event. This is likely most useful for devices, but less * useful for other consumers of this interface. Those should use - * the devctl_queue_data() interface instead. + * the devctl_notify() interface instead. */ +/* Note: we reach inside of sbuf because we may not be able to alloc here */ +#define SBUF_FREESPACE(s) ((s)->s_size - ((s)->s_len + 1)) /* XXX copied from subr_sbuf.c */ static void devaddq(const char *type, const char *what, device_t dev) { - char *data = NULL; - char *loc = NULL; - char *pnp = NULL; + struct dev_event_info *dei; const char *parstr; + char *p; + struct sbuf sb; + size_t freespace; - if (!devctl_queue_length)/* Rare race, but lost races safely discard */ + dei = devctl_alloc_dei_sb(&sb); + if (dei == NULL) return; - data = malloc(1024, M_BUS, M_NOWAIT); - if (data == NULL) - goto bad; - - /* get the bus specific location of this device */ - loc = malloc(1024, M_BUS, M_NOWAIT); - if (loc == NULL) + sbuf_cpy(&sb, type); + sbuf_cat(&sb, what); + sbuf_cat(&sb, " at "); + + /* Add in the location */ + p = sb.s_buf + sbuf_len(&sb); + *p = '\0'; /* sbuf buffer isn't NUL terminated until sbuf_finish() */ + freespace = SBUF_FREESPACE(&sb); + if (freespace <= 0) goto bad; - *loc = '\0'; - bus_child_location_str(dev, loc, 1024); - - /* Get the bus specific pnp info of this device */ - pnp = malloc(1024, M_BUS, M_NOWAIT); - if (pnp == NULL) + bus_child_location_str(dev, p, freespace); + sbuf_setpos(&sb, sbuf_len(&sb) + strlen(p)); + + /* Add in pnpinfo */ + p = sb.s_buf + sbuf_len(&sb); + *p = '\0'; + freespace = SBUF_FREESPACE(&sb); + if (freespace <= 0) goto bad; - *pnp = '\0'; - bus_child_pnpinfo_str(dev, pnp, 1024); + bus_child_pnpinfo_str(dev, p, freespace); + sbuf_setpos(&sb, sbuf_len(&sb) + strlen(p)); + sbuf_cat(&sb, " on "); /* Get the parent of this device, or / if high enough in the tree. */ if (device_get_parent(dev) == NULL) parstr = "."; /* Or '/' ? */ else parstr = device_get_nameunit(device_get_parent(dev)); - /* String it all together. */ - snprintf(data, 1024, "%s%s at %s %s on %s\n", type, what, loc, pnp, - parstr); - free(loc, M_BUS); - free(pnp, M_BUS); - devctl_queue_data(data); + sbuf_cat(&sb, parstr); + sbuf_cat(&sb, "\n"); + sbuf_finish(&sb); + if (sbuf_error(&sb) != 0) + goto bad; + devctl_queue(dei); return; bad: - free(pnp, M_BUS); - free(loc, M_BUS); - free(data, M_BUS); + devctl_free_dei(dei); return; } +#undef CAT + /* * A device was added to the tree. We are called just after it successfully @@ -814,13 +826,14 @@ while (!STAILQ_EMPTY(&devsoftc.devq)) { n1 = STAILQ_FIRST(&devsoftc.devq); STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); + uma_zfree(devsoftc.zone, n1); } devsoftc.queued = 0; devctl_queue_length = 0; + /* XXX destroy the zone? */ } else { devctl_queue_length = DEVCTL_DEFAULT_QUEUE_LEN; + /* XXX init the zone? */ } if (mtx_initialized(&devsoftc.mtx)) mtx_unlock(&devsoftc.mtx); @@ -845,8 +858,7 @@ while (devsoftc.queued > devctl_queue_length) { n1 = STAILQ_FIRST(&devsoftc.devq); STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); + uma_zfree(devsoftc.zone, n1); devsoftc.queued--; } if (mtx_initialized(&devsoftc.mtx))