# HG changeset patch # User Gordon Ross # Date 1328388957 18000 # Node ID 565bd3085959162ea6dd8d13298ec1c2a1ae3456 # Parent ad255bc59fdfab2f96891742305c3f7ec28c6030 2041 panic in nsmb_close Reviewed by: Dan McDonald Reviewed by: Richard Elling Reviewed by: Robert Mustacchi Approved by: Garrett D'Amore diff -r ad255bc59fdf -r 565bd3085959 usr/src/lib/libsmbfs/smb/iod_wk.c --- 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: diff -r ad255bc59fdf -r 565bd3085959 usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c --- 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 diff -r ad255bc59fdf -r 565bd3085959 usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h --- 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;