Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+27 −26
Original line number Diff line number Diff line
@@ -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;
@@ -122,11 +122,16 @@ 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) ||
		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);
@@ -136,29 +141,26 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
				    " %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->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:"
@@ -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:");
+8 −9
Original line number Diff line number Diff line
@@ -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_unlock(&GlobalSMBSeslock);
		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(&cifs_tcp_ses_lock);
	} while (!kthread_should_stop());

	return 0;
@@ -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
+2 −4
Original line number Diff line number Diff line
@@ -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 */
@@ -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 */

+0 −1
Original line number Diff line number Diff line
@@ -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 *);
+7 −15
Original line number Diff line number Diff line
@@ -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 */
@@ -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,
Loading