Index: subr_bus.c =================================================================== --- subr_bus.c (revision 242913) +++ subr_bus.c (working copy) @@ -364,16 +364,13 @@ 0, sysctl_devctl_queue, "I", "devctl queue length"); static d_open_t devopen; -static d_close_t devclose; static d_read_t devread; static d_ioctl_t devioctl; static d_poll_t devpoll; static struct cdevsw dev_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_open = devopen, - .d_close = devclose, .d_read = devread, .d_ioctl = devioctl, .d_poll = devpoll, @@ -384,54 +381,93 @@ { char *dei_data; TAILQ_ENTRY(dev_event_info) dei_link; + int refcount; }; TAILQ_HEAD(devq, dev_event_info); -static struct dev_softc +static MALLOC_DEFINE(M_DEVCTL, "devctl", "devctl device"); + +struct dev_softc { - int inuse; int nonblock; - int queued; + struct selinfo sel; + struct proc *async_proc; + TAILQ_ENTRY(dev_softc) next; + struct dev_event_info *curinfo; +}; + + +TAILQ_HEAD(devctlq, dev_softc); + +static struct global_devctl { struct mtx mtx; struct cv cv; - struct selinfo sel; + struct devctlq devctlq; struct devq devq; - struct proc *async_proc; -} devsoftc; + int queued; +} global_devctl; static struct cdev *devctl_dev; static void +devdtor(void *data) +{ + struct dev_softc *devsoftc = data; + struct dev_event_info *n1; + struct dev_event_info *n2; + + mtx_lock(&global_devctl.mtx); + TAILQ_REMOVE(&global_devctl.devctlq, devsoftc, next); + + n1 = devsoftc->curinfo; + while (n1) { + n2 = TAILQ_NEXT(n1, dei_link); + if (n1->refcount > 1) { + n1->refcount--; + TAILQ_REMOVE(&global_devctl.devq, n1, dei_link); + free(n1->dei_data, M_BUS); + free(n1, M_BUS); + } + + n1 = n2; + } + mtx_unlock(&global_devctl.mtx); + seldrain(&devsoftc->sel); + free(devsoftc, M_DEVCTL); + +} + +static void devinit(void) { devctl_dev = make_dev_credf(MAKEDEV_ETERNAL, &dev_cdevsw, 0, NULL, UID_ROOT, GID_WHEEL, 0600, "devctl"); - mtx_init(&devsoftc.mtx, "dev mtx", "devd", MTX_DEF); - cv_init(&devsoftc.cv, "dev cv"); - TAILQ_INIT(&devsoftc.devq); + mtx_init(&global_devctl.mtx, "devctl mtx", "devctl", MTX_DEF); + cv_init(&global_devctl.cv, "dev cv"); + + TAILQ_INIT(&global_devctl.devctlq); + TAILQ_INIT(&global_devctl.devq); } static int devopen(struct cdev *dev, int oflags, int devtype, struct thread *td) { - if (devsoftc.inuse) - return (EBUSY); - /* move to init */ - devsoftc.inuse = 1; - devsoftc.nonblock = 0; - devsoftc.async_proc = NULL; - return (0); -} + struct dev_softc *devsoftc; + struct dev_event_info *n1; -static int -devclose(struct cdev *dev, int fflag, int devtype, struct thread *td) -{ - devsoftc.inuse = 0; - mtx_lock(&devsoftc.mtx); - cv_broadcast(&devsoftc.cv); - mtx_unlock(&devsoftc.mtx); - devsoftc.async_proc = NULL; + devsoftc = malloc(sizeof(struct dev_softc), M_DEVCTL, M_WAITOK|M_ZERO); + devsoftc->nonblock = 0; + devsoftc->async_proc = NULL; + + mtx_lock(&global_devctl.mtx); + TAILQ_INSERT_TAIL(&global_devctl.devctlq, devsoftc, next); + TAILQ_FOREACH(n1, &global_devctl.devq, dei_link) + n1->refcount++; + devsoftc->curinfo = TAILQ_FIRST(&global_devctl.devq); + mtx_unlock(&global_devctl.mtx); + devfs_set_cdevpriv(devsoftc, devdtor); + return (0); } @@ -447,49 +483,71 @@ devread(struct cdev *dev, struct uio *uio, int ioflag) { struct dev_event_info *n1; + struct dev_softc *devsoftc; int rv; - - mtx_lock(&devsoftc.mtx); - while (TAILQ_EMPTY(&devsoftc.devq)) { - if (devsoftc.nonblock) { - mtx_unlock(&devsoftc.mtx); + int error; + int should_free; + + should_free = 0; + error = devfs_get_cdevpriv((void **)&devsoftc); + if (error != 0) + return (error); + mtx_lock(&global_devctl.mtx); + n1 = devsoftc->curinfo; + while (!n1) { + if (devsoftc->nonblock) { + mtx_unlock(&global_devctl.mtx); return (EAGAIN); } - rv = cv_wait_sig(&devsoftc.cv, &devsoftc.mtx); + rv = cv_wait_sig(&global_devctl.cv, &global_devctl.mtx); if (rv) { /* * Need to translate ERESTART to EINTR here? -- jake */ - mtx_unlock(&devsoftc.mtx); + mtx_unlock(&global_devctl.mtx); return (rv); } + n1 = devsoftc->curinfo; } - n1 = TAILQ_FIRST(&devsoftc.devq); - TAILQ_REMOVE(&devsoftc.devq, n1, dei_link); - devsoftc.queued--; - mtx_unlock(&devsoftc.mtx); + devsoftc->curinfo = TAILQ_NEXT(n1, dei_link); + if (n1->refcount > 1) { + n1->refcount--; + TAILQ_REMOVE(&global_devctl.devq, n1, dei_link); + global_devctl.queued--; + should_free = 1; + } + mtx_unlock(&global_devctl.mtx); rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); + if (should_free) { + free(n1->dei_data, M_BUS); + free(n1, M_BUS); + } return (rv); } static int devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { + struct dev_softc *devsoftc; + int error; + + error = devfs_get_cdevpriv((void **)&devsoftc); + if (error != 0) + return (error); + switch (cmd) { case FIONBIO: if (*(int*)data) - devsoftc.nonblock = 1; + devsoftc->nonblock = 1; else - devsoftc.nonblock = 0; + devsoftc->nonblock = 0; return (0); case FIOASYNC: if (*(int*)data) - devsoftc.async_proc = td->td_proc; + devsoftc->async_proc = td->td_proc; else - devsoftc.async_proc = NULL; + devsoftc->async_proc = NULL; return (0); /* (un)Support for other fcntl() calls. */ @@ -507,16 +565,22 @@ static int devpoll(struct cdev *dev, int events, struct thread *td) { + struct dev_softc *devsoftc; + int error; int revents = 0; - mtx_lock(&devsoftc.mtx); + error = devfs_get_cdevpriv((void **)&devsoftc); + if (error != 0) + return (error); + + mtx_lock(&global_devctl.mtx); if (events & (POLLIN | POLLRDNORM)) { - if (!TAILQ_EMPTY(&devsoftc.devq)) + if (devsoftc->curinfo != NULL) revents = events & (POLLIN | POLLRDNORM); else - selrecord(td, &devsoftc.sel); + selrecord(td, &devsoftc->sel); } - mtx_unlock(&devsoftc.mtx); + mtx_unlock(&global_devctl.mtx); return (revents); } @@ -527,7 +591,7 @@ boolean_t devctl_process_running(void) { - return (devsoftc.inuse == 1); + return !TAILQ_EMPTY(&global_devctl.devctlq); } /** @@ -542,6 +606,7 @@ { struct dev_event_info *n1 = NULL, *n2 = NULL; struct proc *p; + struct dev_softc *devsoftc; if (strlen(data) == 0) goto out; @@ -551,32 +616,45 @@ if (n1 == NULL) goto out; n1->dei_data = data; - mtx_lock(&devsoftc.mtx); + n1->refcount = 0; + mtx_lock(&global_devctl.mtx); if (devctl_queue_length == 0) { - mtx_unlock(&devsoftc.mtx); + mtx_unlock(&global_devctl.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 = TAILQ_FIRST(&devsoftc.devq); - TAILQ_REMOVE(&devsoftc.devq, n2, dei_link); + while (global_devctl.queued > devctl_queue_length - 1) { + n2 = TAILQ_FIRST(&global_devctl.devq); + TAILQ_FOREACH(devsoftc, &global_devctl.devctlq, next) { + if (devsoftc->curinfo == n2) + devsoftc->curinfo = TAILQ_NEXT(n2, dei_link); + } + TAILQ_REMOVE(&global_devctl.devq, n2, dei_link); free(n2->dei_data, M_BUS); free(n2, M_BUS); - devsoftc.queued--; + global_devctl.queued--; } - TAILQ_INSERT_TAIL(&devsoftc.devq, n1, dei_link); - devsoftc.queued++; - cv_broadcast(&devsoftc.cv); - mtx_unlock(&devsoftc.mtx); - selwakeup(&devsoftc.sel); - p = devsoftc.async_proc; - if (p != NULL) { - PROC_LOCK(p); - kern_psignal(p, SIGIO); - PROC_UNLOCK(p); + TAILQ_INSERT_TAIL(&global_devctl.devq, n1, dei_link); + TAILQ_FOREACH(devsoftc, &global_devctl.devctlq, next) { + if (devsoftc->curinfo == NULL) { + devsoftc->curinfo = TAILQ_FIRST(&global_devctl.devq); + if (devsoftc->curinfo != n1) + devsoftc->curinfo->refcount++; + } + n1->refcount++; + selwakeup(&devsoftc->sel); + p = devsoftc->async_proc; + if (p != NULL) { + PROC_LOCK(p); + kern_psignal(p, SIGIO); + PROC_UNLOCK(p); + } } + global_devctl.queued++; + cv_broadcast(&global_devctl.cv); + mtx_unlock(&global_devctl.mtx); return; out: /* @@ -738,26 +816,30 @@ sysctl_devctl_disable(SYSCTL_HANDLER_ARGS) { struct dev_event_info *n1; + struct dev_softc *devsoftc; int dis, error; dis = devctl_queue_length == 0; error = sysctl_handle_int(oidp, &dis, 0, req); if (error || !req->newptr) return (error); - mtx_lock(&devsoftc.mtx); + mtx_lock(&global_devctl.mtx); if (dis) { - while (!TAILQ_EMPTY(&devsoftc.devq)) { - n1 = TAILQ_FIRST(&devsoftc.devq); - TAILQ_REMOVE(&devsoftc.devq, n1, dei_link); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); + while (!TAILQ_EMPTY(&global_devctl.devq)) { + n1 = TAILQ_FIRST(&global_devctl.devq); + TAILQ_REMOVE(&global_devctl.devq, n1, dei_link); + free(n1->dei_data, M_BUS); + free(n1, M_BUS); + } + TAILQ_FOREACH(devsoftc, &global_devctl.devctlq, next) { + devsoftc->curinfo = NULL; + global_devctl.queued = 0; + devctl_queue_length = 0; } - devsoftc.queued = 0; - devctl_queue_length = 0; } else { devctl_queue_length = DEVCTL_DEFAULT_QUEUE_LEN; } - mtx_unlock(&devsoftc.mtx); + mtx_unlock(&global_devctl.mtx); return (0); } @@ -765,6 +847,7 @@ sysctl_devctl_queue(SYSCTL_HANDLER_ARGS) { struct dev_event_info *n1; + struct dev_softc *devsoftc; int q, error; q = devctl_queue_length; @@ -773,16 +856,24 @@ return (error); if (q < 0) return (EINVAL); - mtx_lock(&devsoftc.mtx); + mtx_lock(&global_devctl.mtx); devctl_queue_length = q; - while (devsoftc.queued > devctl_queue_length) { - n1 = TAILQ_FIRST(&devsoftc.devq); - TAILQ_REMOVE(&devsoftc.devq, n1, dei_link); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); - devsoftc.queued--; + TAILQ_FOREACH(devsoftc, &global_devctl.devctlq, next) { + while (global_devctl.queued > devctl_queue_length) { + n1 = TAILQ_FIRST(&global_devctl.devq); + TAILQ_FOREACH(devsoftc, &global_devctl.devctlq, next) { + if (devsoftc->curinfo == n1) + devsoftc->curinfo = TAILQ_NEXT(n1, dei_link); + } + + TAILQ_REMOVE(&global_devctl.devq, n1, dei_link); + free(n1->dei_data, M_BUS); + free(n1, M_BUS); + global_devctl.queued--; + } } - mtx_unlock(&devsoftc.mtx); + mtx_unlock(&global_devctl.mtx); + return (0); }