diff options
author | Gleb Smirnoff <glebius@FreeBSD.org> | 2013-12-18 12:53:48 +0000 |
---|---|---|
committer | Gleb Smirnoff <glebius@FreeBSD.org> | 2013-12-18 12:53:48 +0000 |
commit | a95ecdf0cfc553f16ab02c4c42d8d56aa8d8e56e (patch) | |
tree | c32e4735bba0ed068a951e9b579781ece76c3a35 | |
parent | e1e585a87ce47b269cf17f20491b59d8f28486db (diff) |
Notes
-rw-r--r-- | sys/dev/nmdm/nmdm.c | 126 |
1 files changed, 99 insertions, 27 deletions
diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c index ceee762d277f..04a0b160c127 100644 --- a/sys/dev/nmdm/nmdm.c +++ b/sys/dev/nmdm/nmdm.c @@ -58,6 +58,8 @@ static tsw_inwakeup_t nmdm_outwakeup; static tsw_outwakeup_t nmdm_inwakeup; static tsw_param_t nmdm_param; static tsw_modem_t nmdm_modem; +static tsw_close_t nmdm_close; +static tsw_free_t nmdm_free; static struct ttydevsw nmdm_class = { .tsw_flags = TF_NOPREFIX, @@ -65,6 +67,8 @@ static struct ttydevsw nmdm_class = { .tsw_outwakeup = nmdm_outwakeup, .tsw_param = nmdm_param, .tsw_modem = nmdm_modem, + .tsw_close = nmdm_close, + .tsw_free = nmdm_free, }; static void nmdm_task_tty(void *, int); @@ -94,47 +98,68 @@ struct nmdmsoftc { static int nmdm_count = 0; -static struct nmdmsoftc * -nmdm_alloc(unsigned long unit) +static void +nmdm_close(struct tty *tp) { - struct nmdmsoftc *ns; - struct tty *tp; + struct nmdmpart *np; + struct nmdmpart *onp; + struct tty *otp; - atomic_add_int(&nmdm_count, 1); + np = tty_softc(tp); + onp = np->np_other; + otp = onp->np_tty; - ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK|M_ZERO); - mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF); + /* If second part is opened, do not destroy ourselves. */ + if (tty_opened(otp)) + return; - /* Hook the pairs together. */ - ns->ns_part1.np_pair = ns; - ns->ns_part1.np_other = &ns->ns_part2; - TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1); - callout_init_mtx(&ns->ns_part1.np_callout, &ns->ns_mtx, 0); + /* Shut down self. */ + tty_rel_gone(tp); - ns->ns_part2.np_pair = ns; - ns->ns_part2.np_other = &ns->ns_part1; - TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); - callout_init_mtx(&ns->ns_part2.np_callout, &ns->ns_mtx, 0); + /* Shut down second part. */ + tty_lock(tp); + onp = np->np_other; + if (onp == NULL) + return; + otp = onp->np_tty; + tty_rel_gone(otp); + tty_lock(tp); +} - /* Create device nodes. */ - tp = ns->ns_part1.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part1, - &ns->ns_mtx); - tty_makedev(tp, NULL, "nmdm%luA", unit); +static void +nmdm_free(void *softc) +{ + struct nmdmpart *np = (struct nmdmpart *)softc; + struct nmdmsoftc *ns = np->np_pair; - tp = ns->ns_part2.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part2, - &ns->ns_mtx); - tty_makedev(tp, NULL, "nmdm%luB", unit); + callout_drain(&np->np_callout); + taskqueue_drain(taskqueue_swi, &np->np_task); - return (ns); + /* + * The function is called on both parts simultaneously. We serialize + * with help of ns_mtx. The first invocation should return and + * delegate freeing of resources to the second. + */ + mtx_lock(&ns->ns_mtx); + if (np->np_other != NULL) { + np->np_other->np_other = NULL; + mtx_unlock(&ns->ns_mtx); + return; + } + mtx_destroy(&ns->ns_mtx); + free(ns, M_NMDM); + atomic_subtract_int(&nmdm_count, 1); } static void nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, struct cdev **dev) { + struct nmdmsoftc *ns; + struct tty *tp; unsigned long unit; char *end; - struct nmdmsoftc *ns; + int error; if (*dev != NULL) return; @@ -149,13 +174,50 @@ nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, if ((end[0] != 'A' && end[0] != 'B') || end[1] != '\0') return; - /* XXX: pass privileges? */ - ns = nmdm_alloc(unit); + ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK | M_ZERO); + mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF); + + /* Hook the pairs together. */ + ns->ns_part1.np_pair = ns; + ns->ns_part1.np_other = &ns->ns_part2; + TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1); + callout_init_mtx(&ns->ns_part1.np_callout, &ns->ns_mtx, 0); + + ns->ns_part2.np_pair = ns; + ns->ns_part2.np_other = &ns->ns_part1; + TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); + callout_init_mtx(&ns->ns_part2.np_callout, &ns->ns_mtx, 0); + + /* Create device nodes. */ + tp = ns->ns_part1.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part1, + &ns->ns_mtx); + error = tty_makedevf(tp, NULL, end[0] == 'A' ? TTYMK_CLONING : 0, + "nmdm%luA", unit); + if (error) { + mtx_destroy(&ns->ns_mtx); + free(ns, M_NMDM); + return; + } + + tp = ns->ns_part2.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part2, + &ns->ns_mtx); + error = tty_makedevf(tp, NULL, end[0] == 'B' ? TTYMK_CLONING : 0, + "nmdm%luB", unit); + if (error) { + mtx_lock(&ns->ns_mtx); + /* see nmdm_free() */ + ns->ns_part1.np_other = NULL; + atomic_add_int(&nmdm_count, 1); + tty_rel_gone(ns->ns_part1.np_tty); + return; + } if (end[0] == 'A') *dev = ns->ns_part1.np_tty->t_dev; else *dev = ns->ns_part2.np_tty->t_dev; + + atomic_add_int(&nmdm_count, 1); } static void @@ -187,6 +249,10 @@ nmdm_task_tty(void *arg, int pending __unused) tp = np->np_tty; tty_lock(tp); + if (tty_gone(tp)) { + tty_unlock(tp); + return; + } otp = np->np_other->np_tty; KASSERT(otp != NULL, ("NULL otp in nmdmstart")); @@ -203,6 +269,12 @@ nmdm_task_tty(void *arg, int pending __unused) } } + /* This may happen when we are in detach process. */ + if (tty_gone(otp)) { + tty_unlock(otp); + return; + } + while (ttydisc_rint_poll(otp) > 0) { if (np->np_rate && !np->np_quota) break; |