Skip to content

Commit

Permalink
cifs: reinstate sharing of SMB sessions sans races
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jeff Layton authored and Steve French committed Nov 14, 2008
1 parent e7ddee9 commit 14fbf50
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 166 deletions.
53 changes: 27 additions & 26 deletions fs/cifs/cifs_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
#ifdef CONFIG_PROC_FS
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 TCP_Server_Info *server;
struct cifsSesInfo *ses;
struct cifsTconInfo *tcon;
int i;
Expand All @@ -122,43 +122,45 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "Servers:");

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++;
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:"
" %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
" session status: %d\t",
i, ses->serverName, ses->serverDomain,
atomic_read(&ses->inUse),
ses->serverOS, ses->serverNOS,
ses->ses_count, ses->serverOS, ses->serverNOS,
ses->capabilities, ses->status);
}
if (ses->server) {
}
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
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

seq_puts(m, "\nMIDs:\n");

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,
qhead);
seq_printf(m, "State: %d com: %d pid:"
Expand All @@ -171,9 +173,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
}
spin_unlock(&GlobalMid_Lock);
}

}
read_unlock(&GlobalSMBSeslock);
read_unlock(&cifs_tcp_ses_lock);
seq_putc(m, '\n');

seq_puts(m, "Shares:");
Expand Down
17 changes: 8 additions & 9 deletions fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1031,24 +1031,24 @@ static int cifs_oplock_thread(void *dummyarg)
static int cifs_dnotify_thread(void *dummyarg)
{
struct list_head *tmp;
struct cifsSesInfo *ses;
struct TCP_Server_Info *server;

do {
if (try_to_freeze())
continue;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(15*HZ);
read_lock(&GlobalSMBSeslock);
/* check if any stuck requests that need
to be woken up and wakeq so the
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());

return 0;
Expand All @@ -1060,7 +1060,6 @@ init_cifs(void)
int rc = 0;
cifs_proc_init();
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(&GlobalOplock_Q);
#ifdef CONFIG_CIFS_EXPERIMENTAL
Expand Down
6 changes: 2 additions & 4 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,14 @@ struct cifsUidInfo {
* Session structure. One of these for each uid session with a particular host
*/
struct cifsSesInfo {
struct list_head cifsSessionList;
struct list_head smb_ses_list;
struct list_head tcon_list;
struct semaphore sesSem;
#if 0
struct cifsUidInfo *uidInfo; /* pointer to user info */
#endif
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;
unsigned overrideSecFlg; /* if non-zero override global sec flags */
__u16 ipc_tid; /* special tid for connection to IPC share */
Expand Down Expand Up @@ -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 */
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 rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */

Expand Down
1 change: 0 additions & 1 deletion fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path,
const __u16 *pfid);
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 *,
const char *);
extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
Expand Down
22 changes: 7 additions & 15 deletions fs/cifs/cifssmb.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,20 +799,16 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
int rc = 0;

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;

down(&ses->sesSem);
if (ses->need_reconnect)
goto session_already_dead; /* no need to send SMBlogoff if uid
already closed due to reconnect */
Expand All @@ -833,10 +829,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
pSMB->AndXCommand = 0xFF;
rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
session_already_dead:
if (ses->server) {
cifs_put_tcp_session(ses->server);
rc = 0;
}
up(&ses->sesSem);

/* if session dead then we do not need to do ulogoff,
Expand Down
Loading

0 comments on commit 14fbf50

Please sign in to comment.