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

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

cifs: reinstate sharing of tree connections



Use a similar approach to the SMB session sharing. Add a list of tcons
attached to each SMB session. Move the refcount to non-atomic. Protect
all of the above with the cifs_tcp_ses_lock. Add functions to
properly find and put references to the tcons.

Signed-off-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent d82c2df5
Loading
Loading
Loading
Loading
+128 −108
Original line number Diff line number Diff line
@@ -107,12 +107,13 @@ 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, *tmp2, *tmp3;
	struct list_head *tmp1, *tmp2, *tmp3;
	struct mid_q_entry *mid_entry;
	struct TCP_Server_Info *server;
	struct cifsSesInfo *ses;
	struct cifsTconInfo *tcon;
	int i;
	int i, j;
	__u32 dev_type;

	seq_puts(m,
		    "Display Internal CIFS Data Structures for Debugging\n"
@@ -123,8 +124,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)

	i = 0;
	read_lock(&cifs_tcp_ses_lock);
	list_for_each(tmp, &cifs_tcp_ses_list) {
		server = list_entry(tmp, struct TCP_Server_Info,
	list_for_each(tmp1, &cifs_tcp_ses_list) {
		server = list_entry(tmp1, struct TCP_Server_Info,
				    tcp_ses_list);
		i++;
		list_for_each(tmp2, &server->smb_ses_list) {
@@ -133,11 +134,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
			if ((ses->serverDomain == NULL) ||
				(ses->serverOS == NULL) ||
				(ses->serverNOS == NULL)) {
				seq_printf(m, "\nentry for %s not fully "
					   "displayed\n\t", ses->serverName);
				seq_printf(m, "\n%d) entry for %s not fully "
					   "displayed\n\t", i, ses->serverName);
			} else {
				seq_printf(m,
				    "\n%d) Name: %s  Domain: %s Mounts: %d OS:"
				    "\n%d) Name: %s  Domain: %s Uses: %d OS:"
				    " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
				    " session status: %d\t",
				i, ses->serverName, ses->serverDomain,
@@ -156,44 +157,21 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
				atomic_read(&server->num_waiters));
#endif

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

			spin_lock(&GlobalMid_Lock);
			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:"
						" %d tsk: %p mid %d\n",
						mid_entry->midState,
						(int)mid_entry->command,
						mid_entry->pid,
						mid_entry->tsk,
						mid_entry->mid);
			}
			spin_unlock(&GlobalMid_Lock);
		}
	}
	read_unlock(&cifs_tcp_ses_lock);
	seq_putc(m, '\n');

	seq_puts(m, "Shares:");

	i = 0;
	read_lock(&GlobalSMBSeslock);
	list_for_each(tmp, &GlobalTreeConnectionList) {
		__u32 dev_type;
		i++;
		tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
			seq_puts(m, "\n\tShares:");
			j = 0;
			list_for_each(tmp3, &ses->tcon_list) {
				tcon = list_entry(tmp3, struct cifsTconInfo,
						  tcon_list);
				++j;
				dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
		seq_printf(m, "\n%d) %s Uses: %d ", i,
				 tcon->treeName, atomic_read(&tcon->useCount));
				seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
					   tcon->treeName, tcon->tc_count);
				if (tcon->nativeFileSystem) {
					seq_printf(m, "Type: %s ",
						   tcon->nativeFileSystem);
				}
				seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
				 "\nPathComponentMax: %d Status: %d",
					"\nPathComponentMax: %d Status: 0x%d",
					le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
					le32_to_cpu(tcon->fsAttrInfo.Attributes),
					le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
@@ -207,9 +185,27 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)

				if (tcon->need_reconnect)
					seq_puts(m, "\tDISCONNECTED ");
				seq_putc(m, '\n');
			}
	read_unlock(&GlobalSMBSeslock);

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

			spin_lock(&GlobalMid_Lock);
			list_for_each(tmp3, &server->pending_mid_q) {
				mid_entry = list_entry(tmp3, struct mid_q_entry,
					qhead);
				seq_printf(m, "\tState: %d com: %d pid:"
						" %d tsk: %p mid %d\n",
						mid_entry->midState,
						(int)mid_entry->command,
						mid_entry->pid,
						mid_entry->tsk,
						mid_entry->mid);
			}
			spin_unlock(&GlobalMid_Lock);
		}
	}
	read_unlock(&cifs_tcp_ses_lock);
	seq_putc(m, '\n');

	/* BB add code to dump additional info such as TCP session info now */
@@ -235,7 +231,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
{
	char c;
	int rc;
	struct list_head *tmp;
	struct list_head *tmp1, *tmp2, *tmp3;
	struct TCP_Server_Info *server;
	struct cifsSesInfo *ses;
	struct cifsTconInfo *tcon;

	rc = get_user(c, buffer);
@@ -243,14 +241,21 @@ static ssize_t cifs_stats_proc_write(struct file *file,
		return rc;

	if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
		read_lock(&GlobalSMBSeslock);
#ifdef CONFIG_CIFS_STATS2
		atomic_set(&totBufAllocCount, 0);
		atomic_set(&totSmBufAllocCount, 0);
#endif /* CONFIG_CIFS_STATS2 */
		list_for_each(tmp, &GlobalTreeConnectionList) {
			tcon = list_entry(tmp, struct cifsTconInfo,
					cifsConnectionList);
		read_lock(&cifs_tcp_ses_lock);
		list_for_each(tmp1, &cifs_tcp_ses_list) {
			server = list_entry(tmp1, struct TCP_Server_Info,
					    tcp_ses_list);
			list_for_each(tmp2, &server->smb_session_list) {
				ses = list_entry(tmp2, struct cifsSesInfo,
						 smb_session_list);
				list_for_each(tmp3, &ses->tcon_list) {
					tcon = list_entry(tmp3,
							  struct cifsTconInfo,
							  tcon_list);
					atomic_set(&tcon->num_smbs_sent, 0);
					atomic_set(&tcon->num_writes, 0);
					atomic_set(&tcon->num_reads, 0);
@@ -269,7 +274,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
					atomic_set(&tcon->num_symlinks, 0);
					atomic_set(&tcon->num_locks, 0);
				}
		read_unlock(&GlobalSMBSeslock);
			}
		}
		read_unlock(&cifs_tcp_ses_lock);
	}

	return count;
@@ -278,7 +285,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
static int cifs_stats_proc_show(struct seq_file *m, void *v)
{
	int i;
	struct list_head *tmp;
	struct list_head *tmp1, *tmp2, *tmp3;
	struct TCP_Server_Info *server;
	struct cifsSesInfo *ses;
	struct cifsTconInfo *tcon;

	seq_printf(m,
@@ -307,10 +316,18 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
		GlobalCurrentXid, GlobalMaxActiveXid);

	i = 0;
	read_lock(&GlobalSMBSeslock);
	list_for_each(tmp, &GlobalTreeConnectionList) {
	read_lock(&cifs_tcp_ses_lock);
	list_for_each(tmp1, &cifs_tcp_ses_list) {
		server = list_entry(tmp1, struct TCP_Server_Info,
				    tcp_ses_list);
		list_for_each(tmp2, &server->smb_ses_list) {
			ses = list_entry(tmp2, struct cifsSesInfo,
					 smb_ses_list);
			list_for_each(tmp3, &ses->tcon_list) {
				tcon = list_entry(tmp3,
						  struct cifsTconInfo,
						  tcon_list);
				i++;
		tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
				seq_printf(m, "\n%d) %s", i, tcon->treeName);
				if (tcon->need_reconnect)
					seq_puts(m, "\tDISCONNECTED ");
@@ -323,13 +340,13 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
				seq_printf(m, "\nWrites: %d Bytes: %lld",
					atomic_read(&tcon->num_writes),
					(long long)(tcon->bytes_written));
		seq_printf(m,
			"\nLocks: %d HardLinks: %d Symlinks: %d",
				seq_printf(m, "\nLocks: %d HardLinks: %d "
					      "Symlinks: %d",
					atomic_read(&tcon->num_locks),
					atomic_read(&tcon->num_hardlinks),
					atomic_read(&tcon->num_symlinks));

		seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d",
				seq_printf(m, "\nOpens: %d Closes: %d"
					      "Deletes: %d",
					atomic_read(&tcon->num_opens),
					atomic_read(&tcon->num_closes),
					atomic_read(&tcon->num_deletes));
@@ -339,12 +356,15 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
				seq_printf(m, "\nRenames: %d T2 Renames %d",
					atomic_read(&tcon->num_renames),
					atomic_read(&tcon->num_t2renames));
		seq_printf(m, "\nFindFirst: %d FNext %d FClose %d",
				seq_printf(m, "\nFindFirst: %d FNext %d "
					      "FClose %d",
					atomic_read(&tcon->num_ffirst),
					atomic_read(&tcon->num_fnext),
					atomic_read(&tcon->num_fclose));
			}
	read_unlock(&GlobalSMBSeslock);
		}
	}
	read_unlock(&cifs_tcp_ses_lock);

	seq_putc(m, '\n');
	return 0;
+4 −4
Original line number Diff line number Diff line
@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb)
	tcon = cifs_sb->tcon;
	if (tcon == NULL)
		return;
	down(&tcon->tconSem);
	if (atomic_read(&tcon->useCount) == 1)

	read_lock(&cifs_tcp_ses_lock);
	if (tcon->tc_count == 1)
		tcon->tidStatus = CifsExiting;
	up(&tcon->tconSem);
	read_unlock(&cifs_tcp_ses_lock);

	/* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
	/* cancel_notify_requests(tcon); */
@@ -1060,7 +1061,6 @@ init_cifs(void)
	int rc = 0;
	cifs_proc_init();
	INIT_LIST_HEAD(&cifs_tcp_ses_list);
	INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
	INIT_LIST_HEAD(&GlobalOplock_Q);
#ifdef CONFIG_CIFS_EXPERIMENTAL
	INIT_LIST_HEAD(&GlobalDnotifyReqList);
+8 −5
Original line number Diff line number Diff line
@@ -233,16 +233,15 @@ struct cifsSesInfo {
 * session
 */
struct cifsTconInfo {
	struct list_head cifsConnectionList;
	struct list_head tcon_list;
	int tc_count;
	struct list_head openFileList;
	struct semaphore tconSem;
	struct cifsSesInfo *ses;	/* pointer to session associated with */
	char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
	char *nativeFileSystem;
	__u16 tid;		/* The 2 byte tree id */
	__u16 Flags;		/* optional support bits */
	enum statusEnum tidStatus;
	atomic_t useCount;	/* how many explicit/implicit mounts to share */
#ifdef CONFIG_CIFS_STATS
	atomic_t num_smbs_sent;
	atomic_t num_writes;
@@ -600,9 +599,13 @@ require use of the stronger protocol */
 */
GLOBAL_EXTERN struct list_head		cifs_tcp_ses_list;

/* protects cifs_tcp_ses_list and srv_count for each tcp session */
/*
 * This lock protects the cifs_tcp_ses_list, the list of smb sessions per
 * tcp session, and the list of tcon's per smb session. It also protects
 * the reference counters for the server, smb session, and tcon. Finally,
 * changes to the tcon->tidStatus should be done while holding this lock.
 */
GLOBAL_EXTERN rwlock_t		cifs_tcp_ses_lock;
GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */
GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;  /* protects list inserts on 3 above */

GLOBAL_EXTERN struct list_head GlobalOplock_Q;
+12 −31
Original line number Diff line number Diff line
@@ -742,48 +742,29 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
	int rc = 0;

	cFYI(1, ("In tree disconnect"));
	/*
	 *  If last user of the connection and
	 *  connection alive - disconnect it
	 *  If this is the last connection on the server session disconnect it
	 *  (and inside session disconnect we should check if tcp socket needs
	 *  to be freed and kernel thread woken up).
	 */
	if (tcon)
		down(&tcon->tconSem);
	else
		return -EIO;

	atomic_dec(&tcon->useCount);
	if (atomic_read(&tcon->useCount) > 0) {
		up(&tcon->tconSem);
		return -EBUSY;
	}
	/* BB: do we need to check this? These should never be NULL. */
	if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
		return -EIO;

	/* No need to return error on this operation if tid invalidated and
	closed on server already e.g. due to tcp session crashing */
	if (tcon->need_reconnect) {
		up(&tcon->tconSem);
	/*
	 * No need to return error on this operation if tid invalidated and
	 * closed on server already e.g. due to tcp session crashing. Also,
	 * the tcon is no longer on the list, so no need to take lock before
	 * checking this.
	 */
	if (tcon->need_reconnect)
		return 0;
	}

	if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
		up(&tcon->tconSem);
		return -EIO;
	}
	rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
			    (void **)&smb_buffer);
	if (rc) {
		up(&tcon->tconSem);
	if (rc)
		return rc;
	}

	rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
	if (rc)
		cFYI(1, ("Tree disconnect failed %d", rc));

	up(&tcon->tconSem);

	/* No need to return error on this operation if tid invalidated and
	   closed on server already e.g. due to tcp session crashing */
	if (rc == -EAGAIN)
+62 −31
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ static int
cifs_reconnect(struct TCP_Server_Info *server)
{
	int rc = 0;
	struct list_head *tmp;
	struct list_head *tmp, *tmp2;
	struct cifsSesInfo *ses;
	struct cifsTconInfo *tcon;
	struct mid_q_entry *mid_entry;
@@ -149,13 +149,12 @@ cifs_reconnect(struct TCP_Server_Info *server)
		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
		ses->need_reconnect = true;
		ses->ipc_tid = 0;
	}
	read_unlock(&cifs_tcp_ses_lock);
	list_for_each(tmp, &GlobalTreeConnectionList) {
		tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
		if ((tcon->ses) && (tcon->ses->server == server))
		list_for_each(tmp2, &ses->tcon_list) {
			tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
			tcon->need_reconnect = true;
		}
	}
	read_unlock(&cifs_tcp_ses_lock);
	/* do not want to be sending data on a socket we are freeing */
	down(&server->tcpSem);
	if (server->ssocket) {
@@ -1462,6 +1461,52 @@ cifs_put_smb_ses(struct cifsSesInfo *ses)
	cifs_put_tcp_session(server);
}

static struct cifsTconInfo *
cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
{
	struct list_head *tmp;
	struct cifsTconInfo *tcon;

	write_lock(&cifs_tcp_ses_lock);
	list_for_each(tmp, &ses->tcon_list) {
		tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
		if (tcon->tidStatus == CifsExiting)
			continue;
		if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
			continue;

		++tcon->tc_count;
		write_unlock(&cifs_tcp_ses_lock);
		return tcon;
	}
	write_unlock(&cifs_tcp_ses_lock);
	return NULL;
}

static void
cifs_put_tcon(struct cifsTconInfo *tcon)
{
	int xid;
	struct cifsSesInfo *ses = tcon->ses;

	write_lock(&cifs_tcp_ses_lock);
	if (--tcon->tc_count > 0) {
		write_unlock(&cifs_tcp_ses_lock);
		return;
	}

	list_del_init(&tcon->tcon_list);
	write_unlock(&cifs_tcp_ses_lock);

	xid = GetXid();
	CIFSSMBTDis(xid, tcon);
	_FreeXid(xid);

	DeleteTconOplockQEntries(tcon);
	tconInfoFree(tcon);
	cifs_put_smb_ses(ses);
}

int
get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
	     const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
@@ -2220,11 +2265,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
	if (!rc) {
		setup_cifs_sb(&volume_info, cifs_sb);

		tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
		if (tcon) {
			cFYI(1, ("Found match on UNC path"));
			if (tcon->seal != volume_info.seal)
				cERROR(1, ("transport encryption setting "
					   "conflicts with existing tid"));
			/* existing tcon already has a reference */
			cifs_put_smb_ses(pSesInfo);
		} else {
			tcon = tconInfoAlloc();
			if (tcon == NULL) {
@@ -2257,6 +2302,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
			if (rc)
				goto mount_fail_check;
			tcon->seal = volume_info.seal;
			tcon->ses = pSesInfo;
			write_lock(&cifs_tcp_ses_lock);
			list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
			write_unlock(&cifs_tcp_ses_lock);
		}

		/* we can have only one retry value for a connection
@@ -2283,18 +2332,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
		/* If find_unc succeeded then rc == 0 so we can not end */
		/* up accidently freeing someone elses tcon struct */
		if (tcon)
			tconInfoFree(tcon);

		/* should also end up putting our tcp session ref if needed */
		if (pSesInfo)
			cifs_put_tcon(tcon);
		else if (pSesInfo)
			cifs_put_smb_ses(pSesInfo);
		else
			cifs_put_tcp_session(srvTcp);
		goto out;
	}
	atomic_inc(&tcon->useCount);
	cifs_sb->tcon = tcon;
	tcon->ses = pSesInfo;

	/* do not care if following two calls succeed - informational */
	if (!tcon->ipc) {
@@ -3565,23 +3610,10 @@ int
cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
{
	int rc = 0;
	int xid;
	struct cifsSesInfo *ses = NULL;
	char *tmp;

	xid = GetXid();

	if (cifs_sb->tcon) {
		ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
		rc = CIFSSMBTDis(xid, cifs_sb->tcon);
		if (rc == -EBUSY) {
			FreeXid(xid);
			return 0;
		}
		DeleteTconOplockQEntries(cifs_sb->tcon);
		tconInfoFree(cifs_sb->tcon);
		cifs_put_smb_ses(ses);
	}
	if (cifs_sb->tcon)
		cifs_put_tcon(cifs_sb->tcon);

	cifs_sb->tcon = NULL;
	tmp = cifs_sb->prepath;
@@ -3589,7 +3621,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
	cifs_sb->prepath = NULL;
	kfree(tmp);

	FreeXid(xid);
	return rc;
}

Loading