Commit 14fbf50d authored by Jeff Layton's avatar Jeff Layton Committed by Steve French

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: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent e7ddee90
...@@ -107,9 +107,9 @@ void cifs_dump_mids(struct TCP_Server_Info *server) ...@@ -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 *tmp, *tmp2, *tmp3;
struct list_head *tmp1;
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) ...@@ -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); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &GlobalSMBSessionList) { 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); list_for_each(tmp2, &server->smb_ses_list) {
if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || ses = list_entry(tmp2, struct cifsSesInfo,
(ses->serverNOS == NULL)) { smb_ses_list);
seq_printf(m, "\nentry for %s not fully " if ((ses->serverDomain == NULL) ||
"displayed\n\t", ses->serverName); (ses->serverOS == NULL) ||
} else { (ses->serverNOS == NULL)) {
seq_printf(m, 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->ses_count, ses->serverOS, ses->serverNOS,
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", "Server: %d SecMode: 0x%x Req On Wire: %d",
ses->server->tcpStatus, server->tcpStatus, server->srv_count,
ses->server->srv_count, server->secMode,
ses->server->secMode, atomic_read(&server->inFlight));
atomic_read(&ses->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(&server->inSend),
atomic_read(&ses->server->num_waiters)); 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) { list_for_each(tmp3, &server->pending_mid_q) {
mid_entry = list_entry(tmp1, struct 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) ...@@ -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:");
......
...@@ -1031,24 +1031,24 @@ static int cifs_oplock_thread(void *dummyarg) ...@@ -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) { read_lock(&cifs_tcp_ses_lock);
ses = list_entry(tmp, struct cifsSesInfo, list_for_each(tmp, &cifs_tcp_ses_list) {
cifsSessionList); server = list_entry(tmp, struct TCP_Server_Info,
if (ses->server && atomic_read(&ses->server->inFlight)) tcp_ses_list);
wake_up_all(&ses->server->response_q); 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) ...@@ -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
......
...@@ -195,14 +195,14 @@ struct cifsUidInfo { ...@@ -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; ...@@ -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 */
......
...@@ -102,7 +102,6 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path, ...@@ -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 *);
......
...@@ -799,20 +799,16 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) ...@@ -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) ...@@ -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,
......
This diff is collapsed.
...@@ -75,12 +75,11 @@ sesInfoAlloc(void) ...@@ -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) ...@@ -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 */ , ...@@ -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); read_lock(&cifs_tcp_ses_lock);
list_for_each(temp_item, &GlobalSMBSessionList) { list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList); 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 */ , ...@@ -364,7 +360,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
} }
} }
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment