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