summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGleb Smirnoff <glebius@FreeBSD.org>2013-12-18 12:53:48 +0000
committerGleb Smirnoff <glebius@FreeBSD.org>2013-12-18 12:53:48 +0000
commita95ecdf0cfc553f16ab02c4c42d8d56aa8d8e56e (patch)
treec32e4735bba0ed068a951e9b579781ece76c3a35
parente1e585a87ce47b269cf17f20491b59d8f28486db (diff)
Notes
-rw-r--r--sys/dev/nmdm/nmdm.c126
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;