svn commit: r259550 - head/sys/dev/nmdm

Gleb Smirnoff glebius at FreeBSD.org
Wed Dec 18 12:53:49 UTC 2013


Author: glebius
Date: Wed Dec 18 12:53:48 2013
New Revision: 259550
URL: http://svnweb.freebsd.org/changeset/base/259550

Log:
  Make nmdm(4) destroy devices when both sides of a pair are disconnected.
  This makes it possible to kldunload nmdm.ko when there are no users of it.
  
  Reviewed by:	kib
  Sponsored by:	Nginx, Inc.

Modified:
  head/sys/dev/nmdm/nmdm.c

Modified: head/sys/dev/nmdm/nmdm.c
==============================================================================
--- head/sys/dev/nmdm/nmdm.c	Wed Dec 18 12:50:43 2013	(r259549)
+++ head/sys/dev/nmdm/nmdm.c	Wed Dec 18 12:53:48 2013	(r259550)
@@ -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;
-
-	atomic_add_int(&nmdm_count, 1);
+	struct nmdmpart *np;
+	struct nmdmpart *onp;
+	struct tty *otp;
+
+	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
 	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 __u
 
 	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 __u
 		}
 	}
 
+	/* 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;


More information about the svn-src-head mailing list