瀏覽代碼

cifs: reinstate sharing of SMB sessions sans races

We do this by abandoning the global list of SMB sessions and instead
moving to a per-server list. This entails adding a new list head to the
TCP_Server_Info struct. The refcounting for the cifsSesInfo is moved to
a non-atomic variable. We have to protect it by a lock anyway, so there's
no benefit to making it an atomic. The list and refcount are protected
by the global cifs_tcp_ses_lock.

The patch also adds a new routines to find and put SMB sessions and
that properly take and put references under the lock.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Jeff Layton 16 年之前
父節點
當前提交
14fbf50d69
共有 7 個文件被更改,包括 175 次插入166 次删除
  1. 27 26
      fs/cifs/cifs_debug.c
  2. 8 9
      fs/cifs/cifsfs.c
  3. 2 4
      fs/cifs/cifsglob.h
  4. 0 1
      fs/cifs/cifsproto.h
  5. 7 15
      fs/cifs/cifssmb.c
  6. 125 101
      fs/cifs/connect.c
  7. 6 10
      fs/cifs/misc.c

+ 27 - 26
fs/cifs/cifs_debug.c

@@ -107,9 +107,9 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
 #ifdef CONFIG_PROC_FS
 #ifdef CONFIG_PROC_FS
 static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 {
 {
-	struct list_head *tmp;
-	struct list_head *tmp1;
+	struct list_head *tmp, *tmp2, *tmp3;
 	struct mid_q_entry *mid_entry;
 	struct mid_q_entry *mid_entry;
+	struct TCP_Server_Info *server;
 	struct cifsSesInfo *ses;
 	struct cifsSesInfo *ses;
 	struct cifsTconInfo *tcon;
 	struct cifsTconInfo *tcon;
 	int i;
 	int i;
@@ -122,43 +122,45 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 	seq_printf(m, "Servers:");
 	seq_printf(m, "Servers:");
 
 
 	i = 0;
 	i = 0;
-	read_lock(&GlobalSMBSeslock);
-	list_for_each(tmp, &GlobalSMBSessionList) {
+	read_lock(&cifs_tcp_ses_lock);
+	list_for_each(tmp, &cifs_tcp_ses_list) {
+		server = list_entry(tmp, struct TCP_Server_Info,
+				    tcp_ses_list);
 		i++;
 		i++;
-		ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
-		if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) ||
-		   (ses->serverNOS == NULL)) {
-			seq_printf(m, "\nentry for %s not fully "
-					"displayed\n\t", ses->serverName);
-		} else {
-			seq_printf(m,
+		list_for_each(tmp2, &server->smb_ses_list) {
+			ses = list_entry(tmp2, struct cifsSesInfo,
+					 smb_ses_list);
+			if ((ses->serverDomain == NULL) ||
+				(ses->serverOS == NULL) ||
+				(ses->serverNOS == NULL)) {
+				seq_printf(m, "\nentry for %s not fully "
+					   "displayed\n\t", ses->serverName);
+			} else {
+				seq_printf(m,
 				    "\n%d) Name: %s  Domain: %s Mounts: %d OS:"
 				    "\n%d) Name: %s  Domain: %s Mounts: %d OS:"
 				    " %s  \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
 				    " %s  \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
 				    " session status: %d\t",
 				    " session status: %d\t",
 				i, ses->serverName, ses->serverDomain,
 				i, ses->serverName, ses->serverDomain,
-				atomic_read(&ses->inUse),
-				ses->serverOS, ses->serverNOS,
+				ses->ses_count, ses->serverOS, ses->serverNOS,
 				ses->capabilities, ses->status);
 				ses->capabilities, ses->status);
-		}
-		if (ses->server) {
+			}
 			seq_printf(m, "TCP status: %d\n\tLocal Users To "
 			seq_printf(m, "TCP status: %d\n\tLocal Users To "
-				    "Server: %d SecMode: 0x%x Req On Wire: %d",
-				ses->server->tcpStatus,
-				ses->server->srv_count,
-				ses->server->secMode,
-				atomic_read(&ses->server->inFlight));
+				   "Server: %d SecMode: 0x%x Req On Wire: %d",
+				   server->tcpStatus, server->srv_count,
+				   server->secMode,
+				   atomic_read(&server->inFlight));
 
 
 #ifdef CONFIG_CIFS_STATS2
 #ifdef CONFIG_CIFS_STATS2
 			seq_printf(m, " In Send: %d In MaxReq Wait: %d",
 			seq_printf(m, " In Send: %d In MaxReq Wait: %d",
-				atomic_read(&ses->server->inSend),
-				atomic_read(&ses->server->num_waiters));
+				atomic_read(&server->inSend),
+				atomic_read(&server->num_waiters));
 #endif
 #endif
 
 
 			seq_puts(m, "\nMIDs:\n");
 			seq_puts(m, "\nMIDs:\n");
 
 
 			spin_lock(&GlobalMid_Lock);
 			spin_lock(&GlobalMid_Lock);
-			list_for_each(tmp1, &ses->server->pending_mid_q) {
-				mid_entry = list_entry(tmp1, struct
+			list_for_each(tmp3, &server->pending_mid_q) {
+				mid_entry = list_entry(tmp3, struct
 					mid_q_entry,
 					mid_q_entry,
 					qhead);
 					qhead);
 				seq_printf(m, "State: %d com: %d pid:"
 				seq_printf(m, "State: %d com: %d pid:"
@@ -171,9 +173,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 			}
 			}
 			spin_unlock(&GlobalMid_Lock);
 			spin_unlock(&GlobalMid_Lock);
 		}
 		}
-
 	}
 	}
-	read_unlock(&GlobalSMBSeslock);
+	read_unlock(&cifs_tcp_ses_lock);
 	seq_putc(m, '\n');
 	seq_putc(m, '\n');
 
 
 	seq_puts(m, "Shares:");
 	seq_puts(m, "Shares:");

+ 8 - 9
fs/cifs/cifsfs.c

@@ -1031,24 +1031,24 @@ static int cifs_oplock_thread(void *dummyarg)
 static int cifs_dnotify_thread(void *dummyarg)
 static int cifs_dnotify_thread(void *dummyarg)
 {
 {
 	struct list_head *tmp;
 	struct list_head *tmp;
-	struct cifsSesInfo *ses;
+	struct TCP_Server_Info *server;
 
 
 	do {
 	do {
 		if (try_to_freeze())
 		if (try_to_freeze())
 			continue;
 			continue;
 		set_current_state(TASK_INTERRUPTIBLE);
 		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(15*HZ);
 		schedule_timeout(15*HZ);
-		read_lock(&GlobalSMBSeslock);
 		/* check if any stuck requests that need
 		/* check if any stuck requests that need
 		   to be woken up and wakeq so the
 		   to be woken up and wakeq so the
 		   thread can wake up and error out */
 		   thread can wake up and error out */
-		list_for_each(tmp, &GlobalSMBSessionList) {
-			ses = list_entry(tmp, struct cifsSesInfo,
-				cifsSessionList);
-			if (ses->server && atomic_read(&ses->server->inFlight))
-				wake_up_all(&ses->server->response_q);
+		read_lock(&cifs_tcp_ses_lock);
+		list_for_each(tmp, &cifs_tcp_ses_list) {
+			server = list_entry(tmp, struct TCP_Server_Info,
+					 tcp_ses_list);
+			if (atomic_read(&server->inFlight))
+				wake_up_all(&server->response_q);
 		}
 		}
-		read_unlock(&GlobalSMBSeslock);
+		read_unlock(&cifs_tcp_ses_lock);
 	} while (!kthread_should_stop());
 	} while (!kthread_should_stop());
 
 
 	return 0;
 	return 0;
@@ -1060,7 +1060,6 @@ init_cifs(void)
 	int rc = 0;
 	int rc = 0;
 	cifs_proc_init();
 	cifs_proc_init();
 	INIT_LIST_HEAD(&cifs_tcp_ses_list);
 	INIT_LIST_HEAD(&cifs_tcp_ses_list);
-	INIT_LIST_HEAD(&GlobalSMBSessionList); /* BB to be removed by jl */
 	INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
 	INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
 	INIT_LIST_HEAD(&GlobalOplock_Q);
 	INIT_LIST_HEAD(&GlobalOplock_Q);
 #ifdef CONFIG_CIFS_EXPERIMENTAL
 #ifdef CONFIG_CIFS_EXPERIMENTAL

+ 2 - 4
fs/cifs/cifsglob.h

@@ -195,14 +195,14 @@ struct cifsUidInfo {
  * Session structure.  One of these for each uid session with a particular host
  * Session structure.  One of these for each uid session with a particular host
  */
  */
 struct cifsSesInfo {
 struct cifsSesInfo {
-	struct list_head cifsSessionList;
+	struct list_head smb_ses_list;
 	struct list_head tcon_list;
 	struct list_head tcon_list;
 	struct semaphore sesSem;
 	struct semaphore sesSem;
 #if 0
 #if 0
 	struct cifsUidInfo *uidInfo;	/* pointer to user info */
 	struct cifsUidInfo *uidInfo;	/* pointer to user info */
 #endif
 #endif
 	struct TCP_Server_Info *server;	/* pointer to server info */
 	struct TCP_Server_Info *server;	/* pointer to server info */
-	atomic_t inUse; /* # of mounts (tree connections) on this ses */
+	int ses_count;		/* reference counter */
 	enum statusEnum status;
 	enum statusEnum status;
 	unsigned overrideSecFlg;  /* if non-zero override global sec flags */
 	unsigned overrideSecFlg;  /* if non-zero override global sec flags */
 	__u16 ipc_tid;		/* special tid for connection to IPC share */
 	__u16 ipc_tid;		/* special tid for connection to IPC share */
@@ -602,8 +602,6 @@ GLOBAL_EXTERN struct list_head		cifs_tcp_ses_list;
 
 
 /* protects cifs_tcp_ses_list and srv_count for each tcp session */
 /* protects cifs_tcp_ses_list and srv_count for each tcp session */
 GLOBAL_EXTERN rwlock_t		cifs_tcp_ses_lock;
 GLOBAL_EXTERN rwlock_t		cifs_tcp_ses_lock;
-
-GLOBAL_EXTERN struct list_head GlobalSMBSessionList; /* BB to be removed by jl*/
 GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */
 GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */
 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;  /* protects list inserts on 3 above */
 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;  /* protects list inserts on 3 above */
 
 

+ 0 - 1
fs/cifs/cifsproto.h

@@ -102,7 +102,6 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path,
 			    const __u16 *pfid);
 			    const __u16 *pfid);
 extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 
 
-extern void cifs_put_tcp_session(struct TCP_Server_Info *server);
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
 			const char *);
 			const char *);
 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);

+ 7 - 15
fs/cifs/cifssmb.c

@@ -799,20 +799,16 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
 	int rc = 0;
 	int rc = 0;
 
 
 	cFYI(1, ("In SMBLogoff for session disconnect"));
 	cFYI(1, ("In SMBLogoff for session disconnect"));
-	if (ses)
-		down(&ses->sesSem);
-	else
-		return -EIO;
-
-	atomic_dec(&ses->inUse);
-	if (atomic_read(&ses->inUse) > 0) {
-		up(&ses->sesSem);
-		return -EBUSY;
-	}
 
 
-	if (ses->server == NULL)
+	/*
+	 * BB: do we need to check validity of ses and server? They should
+	 * always be valid since we have an active reference. If not, that
+	 * should probably be a BUG()
+	 */
+	if (!ses || !ses->server)
 		return -EIO;
 		return -EIO;
 
 
+	down(&ses->sesSem);
 	if (ses->need_reconnect)
 	if (ses->need_reconnect)
 		goto session_already_dead; /* no need to send SMBlogoff if uid
 		goto session_already_dead; /* no need to send SMBlogoff if uid
 					      already closed due to reconnect */
 					      already closed due to reconnect */
@@ -833,10 +829,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
 	pSMB->AndXCommand = 0xFF;
 	pSMB->AndXCommand = 0xFF;
 	rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
 	rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
 session_already_dead:
 session_already_dead:
-	if (ses->server) {
-		cifs_put_tcp_session(ses->server);
-		rc = 0;
-	}
 	up(&ses->sesSem);
 	up(&ses->sesSem);
 
 
 	/* if session dead then we do not need to do ulogoff,
 	/* if session dead then we do not need to do ulogoff,

+ 125 - 101
fs/cifs/connect.c

@@ -144,23 +144,18 @@ cifs_reconnect(struct TCP_Server_Info *server)
 
 
 	/* before reconnecting the tcp session, mark the smb session (uid)
 	/* before reconnecting the tcp session, mark the smb session (uid)
 		and the tid bad so they are not used until reconnected */
 		and the tid bad so they are not used until reconnected */
-	read_lock(&GlobalSMBSeslock);
-	list_for_each(tmp, &GlobalSMBSessionList) {
-		ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
-		if (ses->server) {
-			if (ses->server == server) {
-				ses->need_reconnect = true;
-				ses->ipc_tid = 0;
-			}
-		}
-		/* else tcp and smb sessions need reconnection */
+	read_lock(&cifs_tcp_ses_lock);
+	list_for_each(tmp, &server->smb_ses_list) {
+		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+		ses->need_reconnect = true;
+		ses->ipc_tid = 0;
 	}
 	}
+	read_unlock(&cifs_tcp_ses_lock);
 	list_for_each(tmp, &GlobalTreeConnectionList) {
 	list_for_each(tmp, &GlobalTreeConnectionList) {
 		tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
 		tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
 		if ((tcon->ses) && (tcon->ses->server == server))
 		if ((tcon->ses) && (tcon->ses->server == server))
 			tcon->need_reconnect = true;
 			tcon->need_reconnect = true;
 	}
 	}
-	read_unlock(&GlobalSMBSeslock);
 	/* do not want to be sending data on a socket we are freeing */
 	/* do not want to be sending data on a socket we are freeing */
 	down(&server->tcpSem);
 	down(&server->tcpSem);
 	if (server->ssocket) {
 	if (server->ssocket) {
@@ -696,29 +691,29 @@ multi_t2_fnd:
 	if (smallbuf) /* no sense logging a debug message if NULL */
 	if (smallbuf) /* no sense logging a debug message if NULL */
 		cifs_small_buf_release(smallbuf);
 		cifs_small_buf_release(smallbuf);
 
 
-	read_lock(&GlobalSMBSeslock);
+	/*
+	 * BB: we shouldn't have to do any of this. It shouldn't be
+	 * possible to exit from the thread with active SMB sessions
+	 */
+	read_lock(&cifs_tcp_ses_lock);
 	if (list_empty(&server->pending_mid_q)) {
 	if (list_empty(&server->pending_mid_q)) {
 		/* loop through server session structures attached to this and
 		/* loop through server session structures attached to this and
 		    mark them dead */
 		    mark them dead */
-		list_for_each(tmp, &GlobalSMBSessionList) {
-			ses =
-			    list_entry(tmp, struct cifsSesInfo,
-				       cifsSessionList);
-			if (ses->server == server) {
-				ses->status = CifsExiting;
-				ses->server = NULL;
-			}
+		list_for_each(tmp, &server->smb_ses_list) {
+			ses = list_entry(tmp, struct cifsSesInfo,
+					 smb_ses_list);
+			ses->status = CifsExiting;
+			ses->server = NULL;
 		}
 		}
-		read_unlock(&GlobalSMBSeslock);
+		read_unlock(&cifs_tcp_ses_lock);
 	} else {
 	} else {
 		/* although we can not zero the server struct pointer yet,
 		/* although we can not zero the server struct pointer yet,
 		since there are active requests which may depnd on them,
 		since there are active requests which may depnd on them,
 		mark the corresponding SMB sessions as exiting too */
 		mark the corresponding SMB sessions as exiting too */
-		list_for_each(tmp, &GlobalSMBSessionList) {
+		list_for_each(tmp, &server->smb_ses_list) {
 			ses = list_entry(tmp, struct cifsSesInfo,
 			ses = list_entry(tmp, struct cifsSesInfo,
-					 cifsSessionList);
-			if (ses->server == server)
-				ses->status = CifsExiting;
+					 smb_ses_list);
+			ses->status = CifsExiting;
 		}
 		}
 
 
 		spin_lock(&GlobalMid_Lock);
 		spin_lock(&GlobalMid_Lock);
@@ -733,7 +728,7 @@ multi_t2_fnd:
 			}
 			}
 		}
 		}
 		spin_unlock(&GlobalMid_Lock);
 		spin_unlock(&GlobalMid_Lock);
-		read_unlock(&GlobalSMBSeslock);
+		read_unlock(&cifs_tcp_ses_lock);
 		/* 1/8th of sec is more than enough time for them to exit */
 		/* 1/8th of sec is more than enough time for them to exit */
 		msleep(125);
 		msleep(125);
 	}
 	}
@@ -755,14 +750,13 @@ multi_t2_fnd:
 	if there are any pointing to this (e.g
 	if there are any pointing to this (e.g
 	if a crazy root user tried to kill cifsd
 	if a crazy root user tried to kill cifsd
 	kernel thread explicitly this might happen) */
 	kernel thread explicitly this might happen) */
-	write_lock(&GlobalSMBSeslock);
-	list_for_each(tmp, &GlobalSMBSessionList) {
-		ses = list_entry(tmp, struct cifsSesInfo,
-				cifsSessionList);
-		if (ses->server == server)
-			ses->server = NULL;
+	/* BB: This shouldn't be necessary, see above */
+	read_lock(&cifs_tcp_ses_lock);
+	list_for_each(tmp, &server->smb_ses_list) {
+		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+		ses->server = NULL;
 	}
 	}
-	write_unlock(&GlobalSMBSeslock);
+	read_unlock(&cifs_tcp_ses_lock);
 
 
 	kfree(server->hostname);
 	kfree(server->hostname);
 	task_to_wake = xchg(&server->tsk, NULL);
 	task_to_wake = xchg(&server->tsk, NULL);
@@ -1401,7 +1395,7 @@ cifs_find_tcp_session(struct sockaddr *addr)
 	return NULL;
 	return NULL;
 }
 }
 
 
-void
+static void
 cifs_put_tcp_session(struct TCP_Server_Info *server)
 cifs_put_tcp_session(struct TCP_Server_Info *server)
 {
 {
 	struct task_struct *task;
 	struct task_struct *task;
@@ -1424,6 +1418,50 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
 		force_sig(SIGKILL, task);
 		force_sig(SIGKILL, task);
 }
 }
 
 
+static struct cifsSesInfo *
+cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
+{
+	struct list_head *tmp;
+	struct cifsSesInfo *ses;
+
+	write_lock(&cifs_tcp_ses_lock);
+	list_for_each(tmp, &server->smb_ses_list) {
+		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+		if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
+			continue;
+
+		++ses->ses_count;
+		write_unlock(&cifs_tcp_ses_lock);
+		return ses;
+	}
+	write_unlock(&cifs_tcp_ses_lock);
+	return NULL;
+}
+
+static void
+cifs_put_smb_ses(struct cifsSesInfo *ses)
+{
+	int xid;
+	struct TCP_Server_Info *server = ses->server;
+
+	write_lock(&cifs_tcp_ses_lock);
+	if (--ses->ses_count > 0) {
+		write_unlock(&cifs_tcp_ses_lock);
+		return;
+	}
+
+	list_del_init(&ses->smb_ses_list);
+	write_unlock(&cifs_tcp_ses_lock);
+
+	if (ses->status == CifsGood) {
+		xid = GetXid();
+		CIFSSMBLogoff(xid, ses);
+		_FreeXid(xid);
+	}
+	sesInfoFree(ses);
+	cifs_put_tcp_session(server);
+}
+
 int
 int
 get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
 get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
 	     const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
 	     const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
@@ -1958,7 +1996,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 	struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
 	struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
 	struct smb_vol volume_info;
 	struct smb_vol volume_info;
 	struct cifsSesInfo *pSesInfo = NULL;
 	struct cifsSesInfo *pSesInfo = NULL;
-	struct cifsSesInfo *existingCifsSes = NULL;
 	struct cifsTconInfo *tcon = NULL;
 	struct cifsTconInfo *tcon = NULL;
 	struct TCP_Server_Info *srvTcp = NULL;
 	struct TCP_Server_Info *srvTcp = NULL;
 
 
@@ -2112,6 +2149,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 				volume_info.target_rfc1001_name, 16);
 				volume_info.target_rfc1001_name, 16);
 			srvTcp->sequence_number = 0;
 			srvTcp->sequence_number = 0;
 			INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
 			INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
+			INIT_LIST_HEAD(&srvTcp->smb_ses_list);
 			++srvTcp->srv_count;
 			++srvTcp->srv_count;
 			write_lock(&cifs_tcp_ses_lock);
 			write_lock(&cifs_tcp_ses_lock);
 			list_add(&srvTcp->tcp_ses_list,
 			list_add(&srvTcp->tcp_ses_list,
@@ -2120,10 +2158,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 		}
 		}
 	}
 	}
 
 
-	if (existingCifsSes) {
-		pSesInfo = existingCifsSes;
+	pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
+	if (pSesInfo) {
 		cFYI(1, ("Existing smb sess found (status=%d)",
 		cFYI(1, ("Existing smb sess found (status=%d)",
 			pSesInfo->status));
 			pSesInfo->status));
+		/*
+		 * The existing SMB session already has a reference to srvTcp,
+		 * so we can put back the extra one we got before
+		 */
+		cifs_put_tcp_session(srvTcp);
+
 		down(&pSesInfo->sesSem);
 		down(&pSesInfo->sesSem);
 		if (pSesInfo->need_reconnect) {
 		if (pSesInfo->need_reconnect) {
 			cFYI(1, ("Session needs reconnect"));
 			cFYI(1, ("Session needs reconnect"));
@@ -2134,41 +2178,44 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 	} else if (!rc) {
 	} else if (!rc) {
 		cFYI(1, ("Existing smb sess not found"));
 		cFYI(1, ("Existing smb sess not found"));
 		pSesInfo = sesInfoAlloc();
 		pSesInfo = sesInfoAlloc();
-		if (pSesInfo == NULL)
+		if (pSesInfo == NULL) {
 			rc = -ENOMEM;
 			rc = -ENOMEM;
-		else {
-			pSesInfo->server = srvTcp;
-			sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
-				NIPQUAD(sin_server->sin_addr.s_addr));
+			goto mount_fail_check;
 		}
 		}
 
 
-		if (!rc) {
-			/* volume_info.password freed at unmount */
-			if (volume_info.password) {
-				pSesInfo->password = volume_info.password;
-				/* set to NULL to prevent freeing on exit */
-				volume_info.password = NULL;
-			}
-			if (volume_info.username)
-				strncpy(pSesInfo->userName,
-					volume_info.username,
-					MAX_USERNAME_SIZE);
-			if (volume_info.domainname) {
-				int len = strlen(volume_info.domainname);
-				pSesInfo->domainName =
-					kmalloc(len + 1, GFP_KERNEL);
-				if (pSesInfo->domainName)
-					strcpy(pSesInfo->domainName,
-						volume_info.domainname);
-			}
-			pSesInfo->linux_uid = volume_info.linux_uid;
-			pSesInfo->overrideSecFlg = volume_info.secFlg;
-			down(&pSesInfo->sesSem);
-			/* BB FIXME need to pass vol->secFlgs BB */
-			rc = cifs_setup_session(xid, pSesInfo,
-						cifs_sb->local_nls);
-			up(&pSesInfo->sesSem);
-		}
+		/* new SMB session uses our srvTcp ref */
+		pSesInfo->server = srvTcp;
+		sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
+			NIPQUAD(sin_server->sin_addr.s_addr));
+
+		write_lock(&cifs_tcp_ses_lock);
+		list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
+		write_unlock(&cifs_tcp_ses_lock);
+
+		/* volume_info.password freed at unmount */
+		if (volume_info.password) {
+			pSesInfo->password = volume_info.password;
+			/* set to NULL to prevent freeing on exit */
+			volume_info.password = NULL;
+		}
+		if (volume_info.username)
+			strncpy(pSesInfo->userName, volume_info.username,
+				MAX_USERNAME_SIZE);
+		if (volume_info.domainname) {
+			int len = strlen(volume_info.domainname);
+			pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
+			if (pSesInfo->domainName)
+				strcpy(pSesInfo->domainName,
+					volume_info.domainname);
+		}
+		pSesInfo->linux_uid = volume_info.linux_uid;
+		pSesInfo->overrideSecFlg = volume_info.secFlg;
+		down(&pSesInfo->sesSem);
+
+		/* BB FIXME need to pass vol->secFlgs BB */
+		rc = cifs_setup_session(xid, pSesInfo,
+					cifs_sb->local_nls);
+		up(&pSesInfo->sesSem);
 	}
 	}
 
 
 	/* search for existing tcon to this server share */
 	/* search for existing tcon to this server share */
@@ -2209,11 +2256,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 						tcon->Flags));
 						tcon->Flags));
 				}
 				}
 			}
 			}
-			if (!rc) {
-				atomic_inc(&pSesInfo->inUse);
-				tcon->seal = volume_info.seal;
-			} else
+			if (rc)
 				goto mount_fail_check;
 				goto mount_fail_check;
+			tcon->seal = volume_info.seal;
 		}
 		}
 
 
 		/* we can have only one retry value for a connection
 		/* we can have only one retry value for a connection
@@ -2234,29 +2279,19 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 	/* BB FIXME fix time_gran to be larger for LANMAN sessions */
 	/* BB FIXME fix time_gran to be larger for LANMAN sessions */
 	sb->s_time_gran = 100;
 	sb->s_time_gran = 100;
 
 
-/* on error free sesinfo and tcon struct if needed */
 mount_fail_check:
 mount_fail_check:
+	/* on error free sesinfo and tcon struct if needed */
 	if (rc) {
 	if (rc) {
 		/* If find_unc succeeded then rc == 0 so we can not end */
 		/* If find_unc succeeded then rc == 0 so we can not end */
 		/* up accidently freeing someone elses tcon struct */
 		/* up accidently freeing someone elses tcon struct */
 		if (tcon)
 		if (tcon)
 			tconInfoFree(tcon);
 			tconInfoFree(tcon);
 
 
-		if (existingCifsSes == NULL) {
-			if (pSesInfo) {
-				if ((pSesInfo->server) &&
-				    (pSesInfo->status == CifsGood))
-					CIFSSMBLogoff(xid, pSesInfo);
-				else {
-					cFYI(1, ("No session or bad tcon"));
-					if (pSesInfo->server)
-						cifs_put_tcp_session(
-							pSesInfo->server);
-				}
-				sesInfoFree(pSesInfo);
-				/* pSesInfo = NULL; */
-			}
-		}
+		/* should also end up putting our tcp session ref if needed */
+		if (pSesInfo)
+			cifs_put_smb_ses(pSesInfo);
+		else
+			cifs_put_tcp_session(srvTcp);
 	} else {
 	} else {
 		atomic_inc(&tcon->useCount);
 		atomic_inc(&tcon->useCount);
 		cifs_sb->tcon = tcon;
 		cifs_sb->tcon = tcon;
@@ -3551,16 +3586,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
 		}
 		}
 		DeleteTconOplockQEntries(cifs_sb->tcon);
 		DeleteTconOplockQEntries(cifs_sb->tcon);
 		tconInfoFree(cifs_sb->tcon);
 		tconInfoFree(cifs_sb->tcon);
-		if ((ses) && (ses->server)) {
-			/* save off task so we do not refer to ses later */
-			cFYI(1, ("About to do SMBLogoff "));
-			rc = CIFSSMBLogoff(xid, ses);
-			if (rc == -EBUSY) {
-				FreeXid(xid);
-				return 0;
-			}
-		} else
-			cFYI(1, ("No session or bad tcon"));
+		cifs_put_smb_ses(ses);
 	}
 	}
 
 
 	cifs_sb->tcon = NULL;
 	cifs_sb->tcon = NULL;
@@ -3568,8 +3594,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
 	cifs_sb->prepathlen = 0;
 	cifs_sb->prepathlen = 0;
 	cifs_sb->prepath = NULL;
 	cifs_sb->prepath = NULL;
 	kfree(tmp);
 	kfree(tmp);
-	if (ses)
-		sesInfoFree(ses);
 
 
 	FreeXid(xid);
 	FreeXid(xid);
 	return rc;
 	return rc;

+ 6 - 10
fs/cifs/misc.c

@@ -75,12 +75,11 @@ sesInfoAlloc(void)
 
 
 	ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
 	ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
 	if (ret_buf) {
 	if (ret_buf) {
-		write_lock(&GlobalSMBSeslock);
 		atomic_inc(&sesInfoAllocCount);
 		atomic_inc(&sesInfoAllocCount);
 		ret_buf->status = CifsNew;
 		ret_buf->status = CifsNew;
-		list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList);
+		++ret_buf->ses_count;
+		INIT_LIST_HEAD(&ret_buf->smb_ses_list);
 		init_MUTEX(&ret_buf->sesSem);
 		init_MUTEX(&ret_buf->sesSem);
-		write_unlock(&GlobalSMBSeslock);
 	}
 	}
 	return ret_buf;
 	return ret_buf;
 }
 }
@@ -93,10 +92,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
 		return;
 		return;
 	}
 	}
 
 
-	write_lock(&GlobalSMBSeslock);
 	atomic_dec(&sesInfoAllocCount);
 	atomic_dec(&sesInfoAllocCount);
-	list_del(&buf_to_free->cifsSessionList);
-	write_unlock(&GlobalSMBSeslock);
 	kfree(buf_to_free->serverOS);
 	kfree(buf_to_free->serverOS);
 	kfree(buf_to_free->serverDomain);
 	kfree(buf_to_free->serverDomain);
 	kfree(buf_to_free->serverNOS);
 	kfree(buf_to_free->serverNOS);
@@ -350,9 +346,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
 				if (current->fsuid != treeCon->ses->linux_uid) {
 				if (current->fsuid != treeCon->ses->linux_uid) {
 					cFYI(1, ("Multiuser mode and UID "
 					cFYI(1, ("Multiuser mode and UID "
 						 "did not match tcon uid"));
 						 "did not match tcon uid"));
-					read_lock(&GlobalSMBSeslock);
-					list_for_each(temp_item, &GlobalSMBSessionList) {
-						ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
+					read_lock(&cifs_tcp_ses_lock);
+					list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
+						ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
 						if (ses->linux_uid == current->fsuid) {
 						if (ses->linux_uid == current->fsuid) {
 							if (ses->server == treeCon->ses->server) {
 							if (ses->server == treeCon->ses->server) {
 								cFYI(1, ("found matching uid substitute right smb_uid"));
 								cFYI(1, ("found matching uid substitute right smb_uid"));
@@ -364,7 +360,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
 							}
 							}
 						}
 						}
 					}
 					}
-					read_unlock(&GlobalSMBSeslock);
+					read_unlock(&cifs_tcp_ses_lock);
 				}
 				}
 			}
 			}
 		}
 		}