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>

authored by Jeff Layton and committed by Steve French 14fbf50d e7ddee90

+175 -166
+27 -26
fs/cifs/cifs_debug.c
··· 107 #ifdef CONFIG_PROC_FS 108 static int cifs_debug_data_proc_show(struct seq_file *m, void *v) 109 { 110 - struct list_head *tmp; 111 - struct list_head *tmp1; 112 struct mid_q_entry *mid_entry; 113 struct cifsSesInfo *ses; 114 struct cifsTconInfo *tcon; 115 int i; ··· 122 seq_printf(m, "Servers:"); 123 124 i = 0; 125 - read_lock(&GlobalSMBSeslock); 126 - list_for_each(tmp, &GlobalSMBSessionList) { 127 i++; 128 - ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); 129 - if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || 130 - (ses->serverNOS == NULL)) { 131 - seq_printf(m, "\nentry for %s not fully " 132 - "displayed\n\t", ses->serverName); 133 - } else { 134 - seq_printf(m, 135 "\n%d) Name: %s Domain: %s Mounts: %d OS:" 136 " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB" 137 " session status: %d\t", 138 i, ses->serverName, ses->serverDomain, 139 - atomic_read(&ses->inUse), 140 - ses->serverOS, ses->serverNOS, 141 ses->capabilities, ses->status); 142 - } 143 - if (ses->server) { 144 seq_printf(m, "TCP status: %d\n\tLocal Users To " 145 - "Server: %d SecMode: 0x%x Req On Wire: %d", 146 - ses->server->tcpStatus, 147 - ses->server->srv_count, 148 - ses->server->secMode, 149 - atomic_read(&ses->server->inFlight)); 150 151 #ifdef CONFIG_CIFS_STATS2 152 seq_printf(m, " In Send: %d In MaxReq Wait: %d", 153 - atomic_read(&ses->server->inSend), 154 - atomic_read(&ses->server->num_waiters)); 155 #endif 156 157 seq_puts(m, "\nMIDs:\n"); 158 159 spin_lock(&GlobalMid_Lock); 160 - list_for_each(tmp1, &ses->server->pending_mid_q) { 161 - mid_entry = list_entry(tmp1, struct 162 mid_q_entry, 163 qhead); 164 seq_printf(m, "State: %d com: %d pid:" ··· 173 } 174 spin_unlock(&GlobalMid_Lock); 175 } 176 - 177 } 178 - read_unlock(&GlobalSMBSeslock); 179 seq_putc(m, '\n'); 180 181 seq_puts(m, "Shares:");
··· 107 #ifdef CONFIG_PROC_FS 108 static int cifs_debug_data_proc_show(struct seq_file *m, void *v) 109 { 110 + struct list_head *tmp, *tmp2, *tmp3; 111 struct mid_q_entry *mid_entry; 112 + struct TCP_Server_Info *server; 113 struct cifsSesInfo *ses; 114 struct cifsTconInfo *tcon; 115 int i; ··· 122 seq_printf(m, "Servers:"); 123 124 i = 0; 125 + read_lock(&cifs_tcp_ses_lock); 126 + list_for_each(tmp, &cifs_tcp_ses_list) { 127 + server = list_entry(tmp, struct TCP_Server_Info, 128 + tcp_ses_list); 129 i++; 130 + list_for_each(tmp2, &server->smb_ses_list) { 131 + ses = list_entry(tmp2, struct cifsSesInfo, 132 + smb_ses_list); 133 + if ((ses->serverDomain == NULL) || 134 + (ses->serverOS == NULL) || 135 + (ses->serverNOS == NULL)) { 136 + seq_printf(m, "\nentry for %s not fully " 137 + "displayed\n\t", ses->serverName); 138 + } else { 139 + seq_printf(m, 140 "\n%d) Name: %s Domain: %s Mounts: %d OS:" 141 " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB" 142 " session status: %d\t", 143 i, ses->serverName, ses->serverDomain, 144 + ses->ses_count, ses->serverOS, ses->serverNOS, 145 ses->capabilities, ses->status); 146 + } 147 seq_printf(m, "TCP status: %d\n\tLocal Users To " 148 + "Server: %d SecMode: 0x%x Req On Wire: %d", 149 + server->tcpStatus, server->srv_count, 150 + server->secMode, 151 + atomic_read(&server->inFlight)); 152 153 #ifdef CONFIG_CIFS_STATS2 154 seq_printf(m, " In Send: %d In MaxReq Wait: %d", 155 + atomic_read(&server->inSend), 156 + atomic_read(&server->num_waiters)); 157 #endif 158 159 seq_puts(m, "\nMIDs:\n"); 160 161 spin_lock(&GlobalMid_Lock); 162 + list_for_each(tmp3, &server->pending_mid_q) { 163 + mid_entry = list_entry(tmp3, struct 164 mid_q_entry, 165 qhead); 166 seq_printf(m, "State: %d com: %d pid:" ··· 171 } 172 spin_unlock(&GlobalMid_Lock); 173 } 174 } 175 + read_unlock(&cifs_tcp_ses_lock); 176 seq_putc(m, '\n'); 177 178 seq_puts(m, "Shares:");
+8 -9
fs/cifs/cifsfs.c
··· 1031 static int cifs_dnotify_thread(void *dummyarg) 1032 { 1033 struct list_head *tmp; 1034 - struct cifsSesInfo *ses; 1035 1036 do { 1037 if (try_to_freeze()) 1038 continue; 1039 set_current_state(TASK_INTERRUPTIBLE); 1040 schedule_timeout(15*HZ); 1041 - read_lock(&GlobalSMBSeslock); 1042 /* check if any stuck requests that need 1043 to be woken up and wakeq so the 1044 thread can wake up and error out */ 1045 - list_for_each(tmp, &GlobalSMBSessionList) { 1046 - ses = list_entry(tmp, struct cifsSesInfo, 1047 - cifsSessionList); 1048 - if (ses->server && atomic_read(&ses->server->inFlight)) 1049 - wake_up_all(&ses->server->response_q); 1050 } 1051 - read_unlock(&GlobalSMBSeslock); 1052 } while (!kthread_should_stop()); 1053 1054 return 0; ··· 1060 int rc = 0; 1061 cifs_proc_init(); 1062 INIT_LIST_HEAD(&cifs_tcp_ses_list); 1063 - INIT_LIST_HEAD(&GlobalSMBSessionList); /* BB to be removed by jl */ 1064 INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */ 1065 INIT_LIST_HEAD(&GlobalOplock_Q); 1066 #ifdef CONFIG_CIFS_EXPERIMENTAL
··· 1031 static int cifs_dnotify_thread(void *dummyarg) 1032 { 1033 struct list_head *tmp; 1034 + struct TCP_Server_Info *server; 1035 1036 do { 1037 if (try_to_freeze()) 1038 continue; 1039 set_current_state(TASK_INTERRUPTIBLE); 1040 schedule_timeout(15*HZ); 1041 /* check if any stuck requests that need 1042 to be woken up and wakeq so the 1043 thread can wake up and error out */ 1044 + read_lock(&cifs_tcp_ses_lock); 1045 + list_for_each(tmp, &cifs_tcp_ses_list) { 1046 + server = list_entry(tmp, struct TCP_Server_Info, 1047 + tcp_ses_list); 1048 + if (atomic_read(&server->inFlight)) 1049 + wake_up_all(&server->response_q); 1050 } 1051 + read_unlock(&cifs_tcp_ses_lock); 1052 } while (!kthread_should_stop()); 1053 1054 return 0; ··· 1060 int rc = 0; 1061 cifs_proc_init(); 1062 INIT_LIST_HEAD(&cifs_tcp_ses_list); 1063 INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */ 1064 INIT_LIST_HEAD(&GlobalOplock_Q); 1065 #ifdef CONFIG_CIFS_EXPERIMENTAL
+2 -4
fs/cifs/cifsglob.h
··· 195 * Session structure. One of these for each uid session with a particular host 196 */ 197 struct cifsSesInfo { 198 - struct list_head cifsSessionList; 199 struct list_head tcon_list; 200 struct semaphore sesSem; 201 #if 0 202 struct cifsUidInfo *uidInfo; /* pointer to user info */ 203 #endif 204 struct TCP_Server_Info *server; /* pointer to server info */ 205 - atomic_t inUse; /* # of mounts (tree connections) on this ses */ 206 enum statusEnum status; 207 unsigned overrideSecFlg; /* if non-zero override global sec flags */ 208 __u16 ipc_tid; /* special tid for connection to IPC share */ ··· 602 603 /* protects cifs_tcp_ses_list and srv_count for each tcp session */ 604 GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; 605 - 606 - GLOBAL_EXTERN struct list_head GlobalSMBSessionList; /* BB to be removed by jl*/ 607 GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */ 608 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ 609
··· 195 * Session structure. One of these for each uid session with a particular host 196 */ 197 struct cifsSesInfo { 198 + struct list_head smb_ses_list; 199 struct list_head tcon_list; 200 struct semaphore sesSem; 201 #if 0 202 struct cifsUidInfo *uidInfo; /* pointer to user info */ 203 #endif 204 struct TCP_Server_Info *server; /* pointer to server info */ 205 + int ses_count; /* reference counter */ 206 enum statusEnum status; 207 unsigned overrideSecFlg; /* if non-zero override global sec flags */ 208 __u16 ipc_tid; /* special tid for connection to IPC share */ ··· 602 603 /* protects cifs_tcp_ses_list and srv_count for each tcp session */ 604 GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; 605 GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */ 606 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ 607
-1
fs/cifs/cifsproto.h
··· 102 const __u16 *pfid); 103 extern int mode_to_acl(struct inode *inode, const char *path, __u64); 104 105 - extern void cifs_put_tcp_session(struct TCP_Server_Info *server); 106 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, 107 const char *); 108 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
··· 102 const __u16 *pfid); 103 extern int mode_to_acl(struct inode *inode, const char *path, __u64); 104 105 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, 106 const char *); 107 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
+8 -16
fs/cifs/cifssmb.c
··· 799 int rc = 0; 800 801 cFYI(1, ("In SMBLogoff for session disconnect")); 802 - if (ses) 803 - down(&ses->sesSem); 804 - else 805 return -EIO; 806 807 - atomic_dec(&ses->inUse); 808 - if (atomic_read(&ses->inUse) > 0) { 809 - up(&ses->sesSem); 810 - return -EBUSY; 811 - } 812 - 813 - if (ses->server == NULL) 814 - return -EIO; 815 - 816 if (ses->need_reconnect) 817 goto session_already_dead; /* no need to send SMBlogoff if uid 818 already closed due to reconnect */ ··· 829 pSMB->AndXCommand = 0xFF; 830 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); 831 session_already_dead: 832 - if (ses->server) { 833 - cifs_put_tcp_session(ses->server); 834 - rc = 0; 835 - } 836 up(&ses->sesSem); 837 838 /* if session dead then we do not need to do ulogoff,
··· 799 int rc = 0; 800 801 cFYI(1, ("In SMBLogoff for session disconnect")); 802 + 803 + /* 804 + * BB: do we need to check validity of ses and server? They should 805 + * always be valid since we have an active reference. If not, that 806 + * should probably be a BUG() 807 + */ 808 + if (!ses || !ses->server) 809 return -EIO; 810 811 + down(&ses->sesSem); 812 if (ses->need_reconnect) 813 goto session_already_dead; /* no need to send SMBlogoff if uid 814 already closed due to reconnect */ ··· 833 pSMB->AndXCommand = 0xFF; 834 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); 835 session_already_dead: 836 up(&ses->sesSem); 837 838 /* if session dead then we do not need to do ulogoff,
+124 -100
fs/cifs/connect.c
··· 144 145 /* before reconnecting the tcp session, mark the smb session (uid) 146 and the tid bad so they are not used until reconnected */ 147 - read_lock(&GlobalSMBSeslock); 148 - list_for_each(tmp, &GlobalSMBSessionList) { 149 - ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); 150 - if (ses->server) { 151 - if (ses->server == server) { 152 - ses->need_reconnect = true; 153 - ses->ipc_tid = 0; 154 - } 155 - } 156 - /* else tcp and smb sessions need reconnection */ 157 } 158 list_for_each(tmp, &GlobalTreeConnectionList) { 159 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); 160 if ((tcon->ses) && (tcon->ses->server == server)) 161 tcon->need_reconnect = true; 162 } 163 - read_unlock(&GlobalSMBSeslock); 164 /* do not want to be sending data on a socket we are freeing */ 165 down(&server->tcpSem); 166 if (server->ssocket) { ··· 691 if (smallbuf) /* no sense logging a debug message if NULL */ 692 cifs_small_buf_release(smallbuf); 693 694 - read_lock(&GlobalSMBSeslock); 695 if (list_empty(&server->pending_mid_q)) { 696 /* loop through server session structures attached to this and 697 mark them dead */ 698 - list_for_each(tmp, &GlobalSMBSessionList) { 699 - ses = 700 - list_entry(tmp, struct cifsSesInfo, 701 - cifsSessionList); 702 - if (ses->server == server) { 703 - ses->status = CifsExiting; 704 - ses->server = NULL; 705 - } 706 } 707 - read_unlock(&GlobalSMBSeslock); 708 } else { 709 /* although we can not zero the server struct pointer yet, 710 since there are active requests which may depnd on them, 711 mark the corresponding SMB sessions as exiting too */ 712 - list_for_each(tmp, &GlobalSMBSessionList) { 713 ses = list_entry(tmp, struct cifsSesInfo, 714 - cifsSessionList); 715 - if (ses->server == server) 716 - ses->status = CifsExiting; 717 } 718 719 spin_lock(&GlobalMid_Lock); ··· 728 } 729 } 730 spin_unlock(&GlobalMid_Lock); 731 - read_unlock(&GlobalSMBSeslock); 732 /* 1/8th of sec is more than enough time for them to exit */ 733 msleep(125); 734 } ··· 750 if there are any pointing to this (e.g 751 if a crazy root user tried to kill cifsd 752 kernel thread explicitly this might happen) */ 753 - write_lock(&GlobalSMBSeslock); 754 - list_for_each(tmp, &GlobalSMBSessionList) { 755 - ses = list_entry(tmp, struct cifsSesInfo, 756 - cifsSessionList); 757 - if (ses->server == server) 758 - ses->server = NULL; 759 } 760 - write_unlock(&GlobalSMBSeslock); 761 762 kfree(server->hostname); 763 task_to_wake = xchg(&server->tsk, NULL); ··· 1395 return NULL; 1396 } 1397 1398 - void 1399 cifs_put_tcp_session(struct TCP_Server_Info *server) 1400 { 1401 struct task_struct *task; ··· 1416 task = xchg(&server->tsk, NULL); 1417 if (task) 1418 force_sig(SIGKILL, task); 1419 } 1420 1421 int ··· 1996 struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; 1997 struct smb_vol volume_info; 1998 struct cifsSesInfo *pSesInfo = NULL; 1999 - struct cifsSesInfo *existingCifsSes = NULL; 2000 struct cifsTconInfo *tcon = NULL; 2001 struct TCP_Server_Info *srvTcp = NULL; 2002 ··· 2149 volume_info.target_rfc1001_name, 16); 2150 srvTcp->sequence_number = 0; 2151 INIT_LIST_HEAD(&srvTcp->tcp_ses_list); 2152 ++srvTcp->srv_count; 2153 write_lock(&cifs_tcp_ses_lock); 2154 list_add(&srvTcp->tcp_ses_list, ··· 2158 } 2159 } 2160 2161 - if (existingCifsSes) { 2162 - pSesInfo = existingCifsSes; 2163 cFYI(1, ("Existing smb sess found (status=%d)", 2164 pSesInfo->status)); 2165 down(&pSesInfo->sesSem); 2166 if (pSesInfo->need_reconnect) { 2167 cFYI(1, ("Session needs reconnect")); ··· 2178 } else if (!rc) { 2179 cFYI(1, ("Existing smb sess not found")); 2180 pSesInfo = sesInfoAlloc(); 2181 - if (pSesInfo == NULL) 2182 rc = -ENOMEM; 2183 - else { 2184 - pSesInfo->server = srvTcp; 2185 - sprintf(pSesInfo->serverName, "%u.%u.%u.%u", 2186 - NIPQUAD(sin_server->sin_addr.s_addr)); 2187 } 2188 2189 - if (!rc) { 2190 - /* volume_info.password freed at unmount */ 2191 - if (volume_info.password) { 2192 - pSesInfo->password = volume_info.password; 2193 - /* set to NULL to prevent freeing on exit */ 2194 - volume_info.password = NULL; 2195 - } 2196 - if (volume_info.username) 2197 - strncpy(pSesInfo->userName, 2198 - volume_info.username, 2199 - MAX_USERNAME_SIZE); 2200 - if (volume_info.domainname) { 2201 - int len = strlen(volume_info.domainname); 2202 - pSesInfo->domainName = 2203 - kmalloc(len + 1, GFP_KERNEL); 2204 - if (pSesInfo->domainName) 2205 - strcpy(pSesInfo->domainName, 2206 - volume_info.domainname); 2207 - } 2208 - pSesInfo->linux_uid = volume_info.linux_uid; 2209 - pSesInfo->overrideSecFlg = volume_info.secFlg; 2210 - down(&pSesInfo->sesSem); 2211 - /* BB FIXME need to pass vol->secFlgs BB */ 2212 - rc = cifs_setup_session(xid, pSesInfo, 2213 - cifs_sb->local_nls); 2214 - up(&pSesInfo->sesSem); 2215 } 2216 } 2217 2218 /* search for existing tcon to this server share */ ··· 2256 tcon->Flags)); 2257 } 2258 } 2259 - if (!rc) { 2260 - atomic_inc(&pSesInfo->inUse); 2261 - tcon->seal = volume_info.seal; 2262 - } else 2263 goto mount_fail_check; 2264 } 2265 2266 /* we can have only one retry value for a connection ··· 2279 /* BB FIXME fix time_gran to be larger for LANMAN sessions */ 2280 sb->s_time_gran = 100; 2281 2282 - /* on error free sesinfo and tcon struct if needed */ 2283 mount_fail_check: 2284 if (rc) { 2285 /* If find_unc succeeded then rc == 0 so we can not end */ 2286 /* up accidently freeing someone elses tcon struct */ 2287 if (tcon) 2288 tconInfoFree(tcon); 2289 2290 - if (existingCifsSes == NULL) { 2291 - if (pSesInfo) { 2292 - if ((pSesInfo->server) && 2293 - (pSesInfo->status == CifsGood)) 2294 - CIFSSMBLogoff(xid, pSesInfo); 2295 - else { 2296 - cFYI(1, ("No session or bad tcon")); 2297 - if (pSesInfo->server) 2298 - cifs_put_tcp_session( 2299 - pSesInfo->server); 2300 - } 2301 - sesInfoFree(pSesInfo); 2302 - /* pSesInfo = NULL; */ 2303 - } 2304 - } 2305 } else { 2306 atomic_inc(&tcon->useCount); 2307 cifs_sb->tcon = tcon; ··· 3586 } 3587 DeleteTconOplockQEntries(cifs_sb->tcon); 3588 tconInfoFree(cifs_sb->tcon); 3589 - if ((ses) && (ses->server)) { 3590 - /* save off task so we do not refer to ses later */ 3591 - cFYI(1, ("About to do SMBLogoff ")); 3592 - rc = CIFSSMBLogoff(xid, ses); 3593 - if (rc == -EBUSY) { 3594 - FreeXid(xid); 3595 - return 0; 3596 - } 3597 - } else 3598 - cFYI(1, ("No session or bad tcon")); 3599 } 3600 3601 cifs_sb->tcon = NULL; ··· 3594 cifs_sb->prepathlen = 0; 3595 cifs_sb->prepath = NULL; 3596 kfree(tmp); 3597 - if (ses) 3598 - sesInfoFree(ses); 3599 3600 FreeXid(xid); 3601 return rc;
··· 144 145 /* before reconnecting the tcp session, mark the smb session (uid) 146 and the tid bad so they are not used until reconnected */ 147 + read_lock(&cifs_tcp_ses_lock); 148 + list_for_each(tmp, &server->smb_ses_list) { 149 + ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); 150 + ses->need_reconnect = true; 151 + ses->ipc_tid = 0; 152 } 153 + read_unlock(&cifs_tcp_ses_lock); 154 list_for_each(tmp, &GlobalTreeConnectionList) { 155 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); 156 if ((tcon->ses) && (tcon->ses->server == server)) 157 tcon->need_reconnect = true; 158 } 159 /* do not want to be sending data on a socket we are freeing */ 160 down(&server->tcpSem); 161 if (server->ssocket) { ··· 696 if (smallbuf) /* no sense logging a debug message if NULL */ 697 cifs_small_buf_release(smallbuf); 698 699 + /* 700 + * BB: we shouldn't have to do any of this. It shouldn't be 701 + * possible to exit from the thread with active SMB sessions 702 + */ 703 + read_lock(&cifs_tcp_ses_lock); 704 if (list_empty(&server->pending_mid_q)) { 705 /* loop through server session structures attached to this and 706 mark them dead */ 707 + list_for_each(tmp, &server->smb_ses_list) { 708 + ses = list_entry(tmp, struct cifsSesInfo, 709 + smb_ses_list); 710 + ses->status = CifsExiting; 711 + ses->server = NULL; 712 } 713 + read_unlock(&cifs_tcp_ses_lock); 714 } else { 715 /* although we can not zero the server struct pointer yet, 716 since there are active requests which may depnd on them, 717 mark the corresponding SMB sessions as exiting too */ 718 + list_for_each(tmp, &server->smb_ses_list) { 719 ses = list_entry(tmp, struct cifsSesInfo, 720 + smb_ses_list); 721 + ses->status = CifsExiting; 722 } 723 724 spin_lock(&GlobalMid_Lock); ··· 733 } 734 } 735 spin_unlock(&GlobalMid_Lock); 736 + read_unlock(&cifs_tcp_ses_lock); 737 /* 1/8th of sec is more than enough time for them to exit */ 738 msleep(125); 739 } ··· 755 if there are any pointing to this (e.g 756 if a crazy root user tried to kill cifsd 757 kernel thread explicitly this might happen) */ 758 + /* BB: This shouldn't be necessary, see above */ 759 + read_lock(&cifs_tcp_ses_lock); 760 + list_for_each(tmp, &server->smb_ses_list) { 761 + ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); 762 + ses->server = NULL; 763 } 764 + read_unlock(&cifs_tcp_ses_lock); 765 766 kfree(server->hostname); 767 task_to_wake = xchg(&server->tsk, NULL); ··· 1401 return NULL; 1402 } 1403 1404 + static void 1405 cifs_put_tcp_session(struct TCP_Server_Info *server) 1406 { 1407 struct task_struct *task; ··· 1422 task = xchg(&server->tsk, NULL); 1423 if (task) 1424 force_sig(SIGKILL, task); 1425 + } 1426 + 1427 + static struct cifsSesInfo * 1428 + cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) 1429 + { 1430 + struct list_head *tmp; 1431 + struct cifsSesInfo *ses; 1432 + 1433 + write_lock(&cifs_tcp_ses_lock); 1434 + list_for_each(tmp, &server->smb_ses_list) { 1435 + ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); 1436 + if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) 1437 + continue; 1438 + 1439 + ++ses->ses_count; 1440 + write_unlock(&cifs_tcp_ses_lock); 1441 + return ses; 1442 + } 1443 + write_unlock(&cifs_tcp_ses_lock); 1444 + return NULL; 1445 + } 1446 + 1447 + static void 1448 + cifs_put_smb_ses(struct cifsSesInfo *ses) 1449 + { 1450 + int xid; 1451 + struct TCP_Server_Info *server = ses->server; 1452 + 1453 + write_lock(&cifs_tcp_ses_lock); 1454 + if (--ses->ses_count > 0) { 1455 + write_unlock(&cifs_tcp_ses_lock); 1456 + return; 1457 + } 1458 + 1459 + list_del_init(&ses->smb_ses_list); 1460 + write_unlock(&cifs_tcp_ses_lock); 1461 + 1462 + if (ses->status == CifsGood) { 1463 + xid = GetXid(); 1464 + CIFSSMBLogoff(xid, ses); 1465 + _FreeXid(xid); 1466 + } 1467 + sesInfoFree(ses); 1468 + cifs_put_tcp_session(server); 1469 } 1470 1471 int ··· 1958 struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; 1959 struct smb_vol volume_info; 1960 struct cifsSesInfo *pSesInfo = NULL; 1961 struct cifsTconInfo *tcon = NULL; 1962 struct TCP_Server_Info *srvTcp = NULL; 1963 ··· 2112 volume_info.target_rfc1001_name, 16); 2113 srvTcp->sequence_number = 0; 2114 INIT_LIST_HEAD(&srvTcp->tcp_ses_list); 2115 + INIT_LIST_HEAD(&srvTcp->smb_ses_list); 2116 ++srvTcp->srv_count; 2117 write_lock(&cifs_tcp_ses_lock); 2118 list_add(&srvTcp->tcp_ses_list, ··· 2120 } 2121 } 2122 2123 + pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username); 2124 + if (pSesInfo) { 2125 cFYI(1, ("Existing smb sess found (status=%d)", 2126 pSesInfo->status)); 2127 + /* 2128 + * The existing SMB session already has a reference to srvTcp, 2129 + * so we can put back the extra one we got before 2130 + */ 2131 + cifs_put_tcp_session(srvTcp); 2132 + 2133 down(&pSesInfo->sesSem); 2134 if (pSesInfo->need_reconnect) { 2135 cFYI(1, ("Session needs reconnect")); ··· 2134 } else if (!rc) { 2135 cFYI(1, ("Existing smb sess not found")); 2136 pSesInfo = sesInfoAlloc(); 2137 + if (pSesInfo == NULL) { 2138 rc = -ENOMEM; 2139 + goto mount_fail_check; 2140 } 2141 2142 + /* new SMB session uses our srvTcp ref */ 2143 + pSesInfo->server = srvTcp; 2144 + sprintf(pSesInfo->serverName, "%u.%u.%u.%u", 2145 + NIPQUAD(sin_server->sin_addr.s_addr)); 2146 + 2147 + write_lock(&cifs_tcp_ses_lock); 2148 + list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); 2149 + write_unlock(&cifs_tcp_ses_lock); 2150 + 2151 + /* volume_info.password freed at unmount */ 2152 + if (volume_info.password) { 2153 + pSesInfo->password = volume_info.password; 2154 + /* set to NULL to prevent freeing on exit */ 2155 + volume_info.password = NULL; 2156 } 2157 + if (volume_info.username) 2158 + strncpy(pSesInfo->userName, volume_info.username, 2159 + MAX_USERNAME_SIZE); 2160 + if (volume_info.domainname) { 2161 + int len = strlen(volume_info.domainname); 2162 + pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL); 2163 + if (pSesInfo->domainName) 2164 + strcpy(pSesInfo->domainName, 2165 + volume_info.domainname); 2166 + } 2167 + pSesInfo->linux_uid = volume_info.linux_uid; 2168 + pSesInfo->overrideSecFlg = volume_info.secFlg; 2169 + down(&pSesInfo->sesSem); 2170 + 2171 + /* BB FIXME need to pass vol->secFlgs BB */ 2172 + rc = cifs_setup_session(xid, pSesInfo, 2173 + cifs_sb->local_nls); 2174 + up(&pSesInfo->sesSem); 2175 } 2176 2177 /* search for existing tcon to this server share */ ··· 2209 tcon->Flags)); 2210 } 2211 } 2212 + if (rc) 2213 goto mount_fail_check; 2214 + tcon->seal = volume_info.seal; 2215 } 2216 2217 /* we can have only one retry value for a connection ··· 2234 /* BB FIXME fix time_gran to be larger for LANMAN sessions */ 2235 sb->s_time_gran = 100; 2236 2237 mount_fail_check: 2238 + /* on error free sesinfo and tcon struct if needed */ 2239 if (rc) { 2240 /* If find_unc succeeded then rc == 0 so we can not end */ 2241 /* up accidently freeing someone elses tcon struct */ 2242 if (tcon) 2243 tconInfoFree(tcon); 2244 2245 + /* should also end up putting our tcp session ref if needed */ 2246 + if (pSesInfo) 2247 + cifs_put_smb_ses(pSesInfo); 2248 + else 2249 + cifs_put_tcp_session(srvTcp); 2250 } else { 2251 atomic_inc(&tcon->useCount); 2252 cifs_sb->tcon = tcon; ··· 3551 } 3552 DeleteTconOplockQEntries(cifs_sb->tcon); 3553 tconInfoFree(cifs_sb->tcon); 3554 + cifs_put_smb_ses(ses); 3555 } 3556 3557 cifs_sb->tcon = NULL; ··· 3568 cifs_sb->prepathlen = 0; 3569 cifs_sb->prepath = NULL; 3570 kfree(tmp); 3571 3572 FreeXid(xid); 3573 return rc;
+6 -10
fs/cifs/misc.c
··· 75 76 ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); 77 if (ret_buf) { 78 - write_lock(&GlobalSMBSeslock); 79 atomic_inc(&sesInfoAllocCount); 80 ret_buf->status = CifsNew; 81 - list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList); 82 init_MUTEX(&ret_buf->sesSem); 83 - write_unlock(&GlobalSMBSeslock); 84 } 85 return ret_buf; 86 } ··· 92 return; 93 } 94 95 - write_lock(&GlobalSMBSeslock); 96 atomic_dec(&sesInfoAllocCount); 97 - list_del(&buf_to_free->cifsSessionList); 98 - write_unlock(&GlobalSMBSeslock); 99 kfree(buf_to_free->serverOS); 100 kfree(buf_to_free->serverDomain); 101 kfree(buf_to_free->serverNOS); ··· 346 if (current->fsuid != treeCon->ses->linux_uid) { 347 cFYI(1, ("Multiuser mode and UID " 348 "did not match tcon uid")); 349 - read_lock(&GlobalSMBSeslock); 350 - list_for_each(temp_item, &GlobalSMBSessionList) { 351 - ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList); 352 if (ses->linux_uid == current->fsuid) { 353 if (ses->server == treeCon->ses->server) { 354 cFYI(1, ("found matching uid substitute right smb_uid")); ··· 360 } 361 } 362 } 363 - read_unlock(&GlobalSMBSeslock); 364 } 365 } 366 }
··· 75 76 ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); 77 if (ret_buf) { 78 atomic_inc(&sesInfoAllocCount); 79 ret_buf->status = CifsNew; 80 + ++ret_buf->ses_count; 81 + INIT_LIST_HEAD(&ret_buf->smb_ses_list); 82 init_MUTEX(&ret_buf->sesSem); 83 } 84 return ret_buf; 85 } ··· 93 return; 94 } 95 96 atomic_dec(&sesInfoAllocCount); 97 kfree(buf_to_free->serverOS); 98 kfree(buf_to_free->serverDomain); 99 kfree(buf_to_free->serverNOS); ··· 350 if (current->fsuid != treeCon->ses->linux_uid) { 351 cFYI(1, ("Multiuser mode and UID " 352 "did not match tcon uid")); 353 + read_lock(&cifs_tcp_ses_lock); 354 + list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { 355 + ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list); 356 if (ses->linux_uid == current->fsuid) { 357 if (ses->server == treeCon->ses->server) { 358 cFYI(1, ("found matching uid substitute right smb_uid")); ··· 364 } 365 } 366 } 367 + read_unlock(&cifs_tcp_ses_lock); 368 } 369 } 370 }