svn commit: r271800 - stable/10/sys/dev/nmdm

Peter Grehan grehan at FreeBSD.org
Thu Sep 18 19:20:09 UTC 2014


Author: grehan
Date: Thu Sep 18 19:20:08 2014
New Revision: 271800
URL: http://svnweb.freebsd.org/changeset/base/271800

Log:
  MFC nmdm driver changes, r259550 and r271350
  
  r259550 (glebius):
   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.
  
  r271350:
   Fix issue with nmdm and leading zeros in device name.
  
   The nmdm code enforces a number between the 'nmdm' and 'A|B' portions
   of the device name. This is then used as a unit number, and sprintf'd
   back into the tty name. If leading zeros were used in the name,
   the created device name is different than the string used for the
   clone-open (e.g. /dev/nmdm0001A will result in /dev/nmdm1A).
  
   Since unit numbers are no longer required with the updated tty
   code, there seems to be no reason to force the string to be a
   number. The fix is to allow an arbitrary string between
   'nmdm' and 'A|B', within the constraints of devfs names. This allows
   all existing user of numeric strings to continue to work, and also
   allows more meaningful names to be used, such as bhyve VM names.
  
  PR:	192281
  Approved by:	re (glebius)

Modified:
  stable/10/sys/dev/nmdm/nmdm.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/nmdm/nmdm.c
==============================================================================
--- stable/10/sys/dev/nmdm/nmdm.c	Thu Sep 18 19:15:40 2014	(r271799)
+++ stable/10/sys/dev/nmdm/nmdm.c	Thu Sep 18 19:20:08 2014	(r271800)
@@ -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,15 +98,83 @@ struct nmdmsoftc {
 
 static int nmdm_count = 0;
 
-static struct nmdmsoftc *
-nmdm_alloc(unsigned long unit)
+static void
+nmdm_close(struct tty *tp)
+{
+	struct nmdmpart *np;
+	struct nmdmpart *onp;
+	struct tty *otp;
+
+	np = tty_softc(tp);
+	onp = np->np_other;
+	otp = onp->np_tty;
+
+	/* If second part is opened, do not destroy ourselves. */
+	if (tty_opened(otp))
+		return;
+
+	/* Shut down self. */
+	tty_rel_gone(tp);
+
+	/* 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);
+}
+
+static void
+nmdm_free(void *softc)
+{
+	struct nmdmpart *np = (struct nmdmpart *)softc;
+	struct nmdmsoftc *ns = np->np_pair;
+
+	callout_drain(&np->np_callout);
+	taskqueue_drain(taskqueue_swi, &np->np_task);
+
+	/*
+	 * 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;
+	char *end;
+	int error;
+	char endc;
 
-	atomic_add_int(&nmdm_count, 1);
+	if (*dev != NULL)
+		return;
+	if (strncmp(name, "nmdm", 4) != 0)
+		return;
+	if (strlen(name) <= strlen("nmdmX"))
+		return;
+
+	/* Device name must be "nmdm%s%c", where %c is 'A' or 'B'. */
+	end = name + strlen(name) - 1;
+	endc = *end;
+	if (endc != 'A' && endc != 'B')
+		return;
 
-	ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK|M_ZERO);
+	ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK | M_ZERO);
 	mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF);
 
 	/* Hook the pairs together. */
@@ -119,43 +191,38 @@ nmdm_alloc(unsigned long unit)
 	/* 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);
+	*end = 'A';
+	error = tty_makedevf(tp, NULL, endc == 'A' ? TTYMK_CLONING : 0,
+	    "%s", name);
+	if (error) {
+		*end = endc;
+		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);
-	tty_makedev(tp, NULL, "nmdm%luB", unit);
-
-	return (ns);
-}
-
-static void
-nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen,
-    struct cdev **dev)
-{
-	unsigned long unit;
-	char *end;
-	struct nmdmsoftc *ns;
-
-	if (*dev != NULL)
-		return;
-	if (strncmp(name, "nmdm", 4) != 0)
-		return;
-
-	/* Device name must be "nmdm%lu%c", where %c is 'A' or 'B'. */
-	name += 4;
-	unit = strtoul(name, &end, 10);
-	if (unit == ULONG_MAX || name == end)
-		return;
-	if ((end[0] != 'A' && end[0] != 'B') || end[1] != '\0')
+	*end = 'B';
+	error = tty_makedevf(tp, NULL, endc == 'B' ? TTYMK_CLONING : 0,
+	    "%s", name);
+	if (error) {
+		*end = endc;
+		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;
+	}
 
-	/* XXX: pass privileges? */
-	ns = nmdm_alloc(unit);
-
-	if (end[0] == 'A')
+	if (endc == 'A')
 		*dev = ns->ns_part1.np_tty->t_dev;
 	else
 		*dev = ns->ns_part2.np_tty->t_dev;
+
+	*end = endc;
+	atomic_add_int(&nmdm_count, 1);
 }
 
 static void
@@ -187,6 +254,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 +274,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-all mailing list