changeset 13595:565bd3085959

2041 panic in nsmb_close Reviewed by: Dan McDonald <danmcd@nexenta.com> Reviewed by: Richard Elling <richard.elling@richardelling.com> Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Garrett D'Amore <garrett@damore.org>
author Gordon Ross <gwr@nexenta.com>
date Sat, 04 Feb 2012 15:55:57 -0500
parents ad255bc59fdf
children 51d528ff7e15
files usr/src/lib/libsmbfs/smb/iod_wk.c usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h
diffstat 3 files changed, 198 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/libsmbfs/smb/iod_wk.c	Fri Feb 03 06:30:25 2012 +0000
+++ b/usr/src/lib/libsmbfs/smb/iod_wk.c	Sat Feb 04 15:55:57 2012 -0500
@@ -20,6 +20,7 @@
  */
 
 /*
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -148,6 +149,13 @@
 				goto out;
 			}
 			vcst = work->wk_out_state;
+			/*
+			 * Go ahead and close the transport now,
+			 * rather than wait until reconnect to
+			 * this server.
+			 */
+			close(ctx->ct_tran_fd);
+			ctx->ct_tran_fd = -1;
 			continue;
 
 		case SMBIOD_ST_DEAD:
--- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c	Fri Feb 03 06:30:25 2012 +0000
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c	Sat Feb 04 15:55:57 2012 -0500
@@ -32,6 +32,7 @@
  * $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $
  */
 /*
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -79,8 +80,6 @@
 static int smb_tcpsndbuf = 0x20000;
 static int smb_tcprcvbuf = 0x20000;
 
-static int  nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
-	uint8_t *rpcodep);
 static int  nb_disconnect(struct nbpcb *nbp);
 
 
@@ -100,6 +99,10 @@
 	int events, fmode, timo, waitflg;
 	int error = 0;
 
+	/* We should be the only reader. */
+	ASSERT(nbp->nbp_flags & NBF_RECVLOCK);
+	/* nbp->nbp_tiptr checked by caller */
+
 	/*
 	 * Get the first message (fragment) if
 	 * we don't already have a left-over.
@@ -224,14 +227,19 @@
  * Send a T_DISCON_REQ (disconnect)
  */
 static int
-nb_snddis(TIUSER *tiptr)
+nb_snddis(struct nbpcb *nbp)
 {
-	cred_t *cr;
+	TIUSER *tiptr = nbp->nbp_tiptr;
+	cred_t *cr = nbp->nbp_cred;
 	mblk_t *mp;
 	struct T_discon_req *dreq;
-	int error, fmode, mlen;
+	int error, mlen;
+
+	ASSERT(MUTEX_HELD(&nbp->nbp_lock));
 
-	cr = ddi_get_cred();
+	if (tiptr == NULL)
+		return (EBADF);
+
 	mlen = sizeof (struct T_discon_req);
 	if (!(mp = allocb_cred_wait(mlen, STR_NOSIG, &error, cr, NOPID)))
 		return (error);
@@ -243,12 +251,12 @@
 	dreq->SEQ_number = -1;
 	mp->b_wptr += sizeof (struct T_discon_req);
 
-	fmode = tiptr->fp->f_flag;
-	if ((error = tli_send(tiptr, mp, fmode)) != 0)
-		return (error);
-
-	fmode = 0; /* need to block */
-	error = get_ok_ack(tiptr, T_DISCON_REQ, fmode);
+	error = tli_send(tiptr, mp, tiptr->fp->f_flag);
+	/*
+	 * There is an OK/ACK response expected, which is
+	 * either handled by our receiver thread, or just
+	 * discarded if we're closing this endpoint.
+	 */
 
 	return (error);
 }
@@ -335,13 +343,13 @@
  * zero-length message, return EAGAIN so the caller knows that
  * something was received.  This avoids false triggering of the
  * "server not responding" state machine.
+ *
+ * Calls to this are serialized at a higher level.
  */
-/*ARGSUSED*/
 static int
 nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
     uint8_t *rpcodep)
 {
-	TIUSER *tiptr = nbp->nbp_tiptr;
 	mblk_t *m0;
 	uint8_t rpcode;
 	int error;
@@ -349,10 +357,8 @@
 
 	/* We should be the only reader. */
 	ASSERT(nbp->nbp_flags & NBF_RECVLOCK);
-	if ((nbp->nbp_flags & NBF_CONNECTED) == 0)
-		return (ENOTCONN);
 
-	if (tiptr == NULL)
+	if (nbp->nbp_tiptr == NULL)
 		return (EBADF);
 	if (mpp) {
 		if (*mpp) {
@@ -475,6 +481,9 @@
 
 /*
  * SMB transport interface
+ *
+ * This is called only by the thread creating this endpoint,
+ * so we're single-threaded here.
  */
 /*ARGSUSED*/
 static int
@@ -489,13 +498,20 @@
 	nbp->nbp_vc = vcp;
 	nbp->nbp_sndbuf = smb_tcpsndbuf;
 	nbp->nbp_rcvbuf = smb_tcprcvbuf;
+	nbp->nbp_cred = cr;
+	crhold(cr);
 	mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL);
 	vcp->vc_tdata = nbp;
 
 	return (0);
 }
 
-/*ARGSUSED*/
+/*
+ * destroy a transport endpoint
+ *
+ * This is called only by the thread with the last reference
+ * to this endpoint, so we're single-threaded here.
+ */
 static int
 smb_nbst_done(struct smb_vc *vcp)
 {
@@ -518,43 +534,97 @@
 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr);
 	if (nbp->nbp_paddr)
 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr);
+	if (nbp->nbp_cred)
+		crfree(nbp->nbp_cred);
 	mutex_destroy(&nbp->nbp_lock);
 	kmem_free(nbp, sizeof (*nbp));
 	return (0);
 }
 
+/*
+ * Loan a transport file pointer (from user space) to this
+ * IOD endpoint.  There should be no other thread using this
+ * endpoint when we do this, but lock for consistency.
+ */
+static int
+nb_loan_fp(struct nbpcb *nbp, struct file *fp, cred_t *cr)
+{
+	TIUSER *tiptr;
+	int err;
+
+	err = t_kopen(fp, 0, 0, &tiptr, cr);
+	if (err != 0)
+		return (err);
+
+	mutex_enter(&nbp->nbp_lock);
+
+	nbp->nbp_tiptr = tiptr;
+	nbp->nbp_fmode = tiptr->fp->f_flag;
+	nbp->nbp_flags |= NBF_CONNECTED;
+	nbp->nbp_state = NBST_SESSION;
+
+	mutex_exit(&nbp->nbp_lock);
+
+	return (0);
+}
+
+/*
+ * Take back the transport file pointer we previously loaned.
+ * It's possible there may be another thread in here, so let
+ * others get out of the way before we pull the rug out.
+ *
+ * Some notes about the locking here:  The higher-level IOD code
+ * serializes activity such that at most one reader and writer
+ * thread can be active in this code (and possibly both).
+ * Keeping nbp_lock held during the activities of these two
+ * threads would lead to the possibility of nbp_lock being
+ * held by a blocked thread, so this instead sets one of the
+ * flags (NBF_SENDLOCK | NBF_RECVLOCK) when a sender or a
+ * receiver is active (respectively).  Lastly, tear-down is
+ * the only tricky bit (here) where we must wait for any of
+ * these activities to get out of current calls so they will
+ * notice that we've turned off the NBF_CONNECTED flag.
+ */
+static void
+nb_unloan_fp(struct nbpcb *nbp)
+{
+
+	mutex_enter(&nbp->nbp_lock);
+
+	nbp->nbp_flags &= ~NBF_CONNECTED;
+	while (nbp->nbp_flags & (NBF_SENDLOCK | NBF_RECVLOCK)) {
+		nbp->nbp_flags |= NBF_LOCKWAIT;
+		cv_wait(&nbp->nbp_cv, &nbp->nbp_lock);
+	}
+
+	if (nbp->nbp_tiptr != NULL) {
+		(void) t_kclose(nbp->nbp_tiptr, 0);
+		nbp->nbp_tiptr = NULL;
+	}
+	nbp->nbp_state = NBST_CLOSED;
+
+	mutex_exit(&nbp->nbp_lock);
+}
+
 static int
 smb_nbst_loan_fp(struct smb_vc *vcp, struct file *fp, cred_t *cr)
 {
 	struct nbpcb *nbp = vcp->vc_tdata;
-	TIUSER *tiptr;
 	int error = 0;
 
-	mutex_enter(&nbp->nbp_lock);
-
 	/*
 	 * Un-loan the existing one, if any.
 	 */
-	if (nbp->nbp_tiptr != NULL) {
-		(void) t_kclose(nbp->nbp_tiptr, 0);
-		nbp->nbp_tiptr = NULL;
-		nbp->nbp_flags &= ~NBF_CONNECTED;
-		nbp->nbp_state = NBST_CLOSED;
-	}
+	(void) nb_disconnect(nbp);
+	nb_unloan_fp(nbp);
 
 	/*
 	 * Loan the new one passed in.
 	 */
-	if (fp != NULL && 0 == (error =
-	    t_kopen(fp, 0, 0, &tiptr, cr))) {
-		nbp->nbp_tiptr = tiptr;
-		nbp->nbp_fmode = tiptr->fp->f_flag;
-		nbp->nbp_flags |= NBF_CONNECTED;
-		nbp->nbp_state = NBST_SESSION;
+	if (fp != NULL) {
+		error = nb_loan_fp(nbp, fp, cr);
 	}
 
-	mutex_exit(&nbp->nbp_lock);
-
 	return (error);
 }
 
@@ -587,47 +657,41 @@
 static int
 nb_disconnect(struct nbpcb *nbp)
 {
-	TIUSER *tiptr;
-	int save_flags;
 	int err = 0;
 
-	tiptr = nbp->nbp_tiptr;
-	if (tiptr == NULL)
-		return (EBADF);
-
 	mutex_enter(&nbp->nbp_lock);
-	save_flags = nbp->nbp_flags;
-	nbp->nbp_flags &= ~NBF_CONNECTED;
-	if (nbp->nbp_frag) {
-		freemsg(nbp->nbp_frag);
-		nbp->nbp_frag = NULL;
-	}
-	mutex_exit(&nbp->nbp_lock);
+
+	if ((nbp->nbp_flags & NBF_CONNECTED) != 0) {
+		nbp->nbp_flags &= ~NBF_CONNECTED;
 
-	if (save_flags & NBF_CONNECTED)
-		err = nb_snddis(tiptr);
+		if (nbp->nbp_frag != NULL) {
+			freemsg(nbp->nbp_frag);
+			nbp->nbp_frag = NULL;
+		}
 
-	if (nbp->nbp_state != NBST_RETARGET) {
-		nbp->nbp_state = NBST_CLOSED;
+		err = nb_snddis(nbp);
 	}
 
+	mutex_exit(&nbp->nbp_lock);
 	return (err);
 }
 
 /*
- * Always consume the message.
- * (On error too!)
+ * Add the NetBIOS session header and send.
+ *
+ * Calls to this are serialized at a higher level.
  */
-/*ARGSUSED*/
 static int
-smb_nbst_send(struct smb_vc *vcp, mblk_t *m)
+nbssn_send(struct nbpcb *nbp, mblk_t *m)
 {
-	struct nbpcb *nbp = vcp->vc_tdata;
 	ptrdiff_t diff;
 	uint32_t mlen;
 	int error;
 
-	if (nbp == NULL || nbp->nbp_tiptr == NULL) {
+	/* We should be the only sender. */
+	ASSERT(nbp->nbp_flags & NBF_SENDLOCK);
+
+	if (nbp->nbp_tiptr == NULL) {
 		error = EBADF;
 		goto errout;
 	}
@@ -669,7 +733,7 @@
 
 		/* M_PREPEND */
 		m0 = allocb_wait(4, BPRI_LO, STR_NOSIG, &error);
-		if (!m0)
+		if (m0 == NULL)
 			goto errout;
 
 		m0->b_wptr += 4;
@@ -682,31 +746,81 @@
 	return (error);
 
 errout:
-	if (m)
+	if (m != NULL)
 		m_freem(m);
 	return (error);
 }
 
+/*
+ * Always consume the message.
+ * (On error too!)
+ */
+static int
+smb_nbst_send(struct smb_vc *vcp, mblk_t *m)
+{
+	struct nbpcb *nbp = vcp->vc_tdata;
+	int err;
+
+	mutex_enter(&nbp->nbp_lock);
+	if ((nbp->nbp_flags & NBF_CONNECTED) == 0) {
+		err = ENOTCONN;
+		goto out;
+	}
+	if (nbp->nbp_flags & NBF_SENDLOCK) {
+		NBDEBUG("multiple smb_nbst_send!\n");
+		err = EWOULDBLOCK;
+		goto out;
+	}
+	nbp->nbp_flags |= NBF_SENDLOCK;
+	mutex_exit(&nbp->nbp_lock);
+
+	err = nbssn_send(nbp, m);
+	m = NULL; /* nbssn_send always consumes this */
+
+	mutex_enter(&nbp->nbp_lock);
+	nbp->nbp_flags &= ~NBF_SENDLOCK;
+	if (nbp->nbp_flags & NBF_LOCKWAIT) {
+		nbp->nbp_flags &= ~NBF_LOCKWAIT;
+		cv_broadcast(&nbp->nbp_cv);
+	}
+out:
+	mutex_exit(&nbp->nbp_lock);
+	if (m != NULL)
+		m_freem(m);
+	return (err);
+}
+
 static int
 smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp)
 {
 	struct nbpcb *nbp = vcp->vc_tdata;
 	uint8_t rpcode;
-	int error, rplen;
+	int err, rplen;
 
 	mutex_enter(&nbp->nbp_lock);
+	if ((nbp->nbp_flags & NBF_CONNECTED) == 0) {
+		err = ENOTCONN;
+		goto out;
+	}
 	if (nbp->nbp_flags & NBF_RECVLOCK) {
-		NBDEBUG("attempt to reenter session layer!\n");
-		mutex_exit(&nbp->nbp_lock);
-		return (EWOULDBLOCK);
+		NBDEBUG("multiple smb_nbst_recv!\n");
+		err = EWOULDBLOCK;
+		goto out;
 	}
 	nbp->nbp_flags |= NBF_RECVLOCK;
 	mutex_exit(&nbp->nbp_lock);
-	error = nbssn_recv(nbp, mpp, &rplen, &rpcode);
+
+	err = nbssn_recv(nbp, mpp, &rplen, &rpcode);
+
 	mutex_enter(&nbp->nbp_lock);
 	nbp->nbp_flags &= ~NBF_RECVLOCK;
+	if (nbp->nbp_flags & NBF_LOCKWAIT) {
+		nbp->nbp_flags &= ~NBF_LOCKWAIT;
+		cv_broadcast(&nbp->nbp_cv);
+	}
+out:
 	mutex_exit(&nbp->nbp_lock);
-	return (error);
+	return (err);
 }
 
 /*
@@ -718,16 +832,7 @@
 static int
 smb_nbst_poll(struct smb_vc *vcp, int ticks)
 {
-	int error;
-	int events = 0;
-	int waitflg = READWAIT;
-	struct nbpcb *nbp = vcp->vc_tdata;
-
-	error = t_kspoll(nbp->nbp_tiptr, ticks, waitflg, &events);
-	if (!error && !events)
-		error = ETIME;
-
-	return (error);
+	return (ENOTSUP);
 }
 
 static int
--- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h	Fri Feb 03 06:30:25 2012 +0000
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h	Sat Feb 04 15:55:57 2012 -0500
@@ -31,11 +31,12 @@
  *
  * $Id: smb_trantcp.h,v 1.8 2004/08/03 23:50:01 lindak Exp $
  */
+/*
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
+ */
 #ifndef _NETSMB_SMB_TRANTCP_H_
 #define	_NETSMB_SMB_TRANTCP_H_
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 enum nbstate {
 	NBST_CLOSED,
 	NBST_RQSENT,
@@ -51,24 +52,27 @@
 struct nbpcb {
 	struct smb_vc	*nbp_vc;
 	struct tiuser	*nbp_tiptr;	/* KTLI transport handle... */
-	ushort_t	nbp_fmode;
 	mblk_t		*nbp_frag;	/* left-over from last recv */
 
 	struct sockaddr_nb *nbp_laddr;	/* local address */
 	struct sockaddr_nb *nbp_paddr;	/* peer address */
+	void		*nbp_selectid;
+	cred_t		*nbp_cred;
 
 	int		nbp_flags;
 #define	NBF_LOCADDR	0x0001		/* has local addr */
 #define	NBF_CONNECTED	0x0002
 #define	NBF_RECVLOCK	0x0004
-#define	NBF_UPCALLED	0x0010
+#define	NBF_SENDLOCK	0x0008
+#define	NBF_LOCKWAIT	0x0010
 
+	ushort_t	nbp_fmode;
 	enum nbstate	nbp_state;
 	struct timespec	nbp_timo;
 	int		nbp_sndbuf;
 	int		nbp_rcvbuf;
-	void		*nbp_selectid;
 	kmutex_t	nbp_lock;
+	kcondvar_t	nbp_cv;
 };
 typedef struct nbpcb nbpcb_t;