summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorgrehan <grehan@FreeBSD.org>2014-09-18 19:20:08 +0000
committergrehan <grehan@FreeBSD.org>2014-09-18 19:20:08 +0000
commit98e396e70c506053493336e4b4d4258832b1e3f5 (patch)
treecd12cca0cb1719d2320d53982e44d0608c55043a /sys/dev
parentaf46b1fe8b01bcc5ce46b7a5f4fe229b3ba26b5b (diff)
downloadFreeBSD-src-98e396e70c506053493336e4b4d4258832b1e3f5.zip
FreeBSD-src-98e396e70c506053493336e4b4d4258832b1e3f5.tar.gz
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)
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/nmdm/nmdm.c143
1 files changed, 110 insertions, 33 deletions
diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c
index ceee762..dbe24cb 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,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 __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 +274,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;
OpenPOWER on IntegriCloud