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

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
...@@ -107,12 +107,13 @@ void cifs_dump_mids(struct TCP_Server_Info *server) ...@@ -107,12 +107,13 @@ 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, *tmp2, *tmp3; struct list_head *tmp1, *tmp2, *tmp3;
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifsSesInfo *ses; struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
int i; int i, j;
__u32 dev_type;
seq_puts(m, seq_puts(m,
"Display Internal CIFS Data Structures for Debugging\n" "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) ...@@ -123,8 +124,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
i = 0; i = 0;
read_lock(&cifs_tcp_ses_lock); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); tcp_ses_list);
i++; i++;
list_for_each(tmp2, &server->smb_ses_list) { list_for_each(tmp2, &server->smb_ses_list) {
...@@ -133,12 +134,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -133,12 +134,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
if ((ses->serverDomain == NULL) || if ((ses->serverDomain == NULL) ||
(ses->serverOS == NULL) || (ses->serverOS == NULL) ||
(ses->serverNOS == NULL)) { (ses->serverNOS == NULL)) {
seq_printf(m, "\nentry for %s not fully " seq_printf(m, "\n%d) entry for %s not fully "
"displayed\n\t", ses->serverName); "displayed\n\t", i, ses->serverName);
} else { } else {
seq_printf(m, 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" " %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,
ses->ses_count, ses->serverOS, ses->serverNOS, ses->ses_count, ses->serverOS, ses->serverNOS,
...@@ -156,44 +157,21 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -156,44 +157,21 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
atomic_read(&server->num_waiters)); atomic_read(&server->num_waiters));
#endif #endif
seq_puts(m, "\nMIDs:\n"); seq_puts(m, "\n\tShares:");
j = 0;
spin_lock(&GlobalMid_Lock); list_for_each(tmp3, &ses->tcon_list) {
list_for_each(tmp3, &server->pending_mid_q) { tcon = list_entry(tmp3, struct cifsTconInfo,
mid_entry = list_entry(tmp3, struct tcon_list);
mid_q_entry, ++j;
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);
dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
seq_printf(m, "\n%d) %s Uses: %d ", i, seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
tcon->treeName, atomic_read(&tcon->useCount)); tcon->treeName, tcon->tc_count);
if (tcon->nativeFileSystem) { if (tcon->nativeFileSystem) {
seq_printf(m, "Type: %s ", seq_printf(m, "Type: %s ",
tcon->nativeFileSystem); tcon->nativeFileSystem);
} }
seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" 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->fsDevInfo.DeviceCharacteristics),
le32_to_cpu(tcon->fsAttrInfo.Attributes), le32_to_cpu(tcon->fsAttrInfo.Attributes),
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
...@@ -207,9 +185,27 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -207,9 +185,27 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
if (tcon->need_reconnect) if (tcon->need_reconnect)
seq_puts(m, "\tDISCONNECTED "); 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'); seq_putc(m, '\n');
/* BB add code to dump additional info such as TCP session info now */ /* 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, ...@@ -235,7 +231,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
{ {
char c; char c;
int rc; int rc;
struct list_head *tmp; struct list_head *tmp1, *tmp2, *tmp3;
struct TCP_Server_Info *server;
struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
rc = get_user(c, buffer); rc = get_user(c, buffer);
...@@ -243,14 +241,21 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -243,14 +241,21 @@ static ssize_t cifs_stats_proc_write(struct file *file,
return rc; return rc;
if (c == '1' || c == 'y' || c == 'Y' || c == '0') { if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
read_lock(&GlobalSMBSeslock);
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
atomic_set(&totBufAllocCount, 0); atomic_set(&totBufAllocCount, 0);
atomic_set(&totSmBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0);
#endif /* CONFIG_CIFS_STATS2 */ #endif /* CONFIG_CIFS_STATS2 */
list_for_each(tmp, &GlobalTreeConnectionList) { read_lock(&cifs_tcp_ses_lock);
tcon = list_entry(tmp, struct cifsTconInfo, list_for_each(tmp1, &cifs_tcp_ses_list) {
cifsConnectionList); 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_smbs_sent, 0);
atomic_set(&tcon->num_writes, 0); atomic_set(&tcon->num_writes, 0);
atomic_set(&tcon->num_reads, 0); atomic_set(&tcon->num_reads, 0);
...@@ -269,7 +274,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -269,7 +274,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
atomic_set(&tcon->num_symlinks, 0); atomic_set(&tcon->num_symlinks, 0);
atomic_set(&tcon->num_locks, 0); atomic_set(&tcon->num_locks, 0);
} }
read_unlock(&GlobalSMBSeslock); }
}
read_unlock(&cifs_tcp_ses_lock);
} }
return count; return count;
...@@ -278,7 +285,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -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) static int cifs_stats_proc_show(struct seq_file *m, void *v)
{ {
int i; int i;
struct list_head *tmp; struct list_head *tmp1, *tmp2, *tmp3;
struct TCP_Server_Info *server;
struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
seq_printf(m, seq_printf(m,
...@@ -307,10 +316,18 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) ...@@ -307,10 +316,18 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
GlobalCurrentXid, GlobalMaxActiveXid); GlobalCurrentXid, GlobalMaxActiveXid);
i = 0; i = 0;
read_lock(&GlobalSMBSeslock); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &GlobalTreeConnectionList) { 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++; i++;
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
seq_printf(m, "\n%d) %s", i, tcon->treeName); seq_printf(m, "\n%d) %s", i, tcon->treeName);
if (tcon->need_reconnect) if (tcon->need_reconnect)
seq_puts(m, "\tDISCONNECTED "); seq_puts(m, "\tDISCONNECTED ");
...@@ -323,13 +340,13 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) ...@@ -323,13 +340,13 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\nWrites: %d Bytes: %lld", seq_printf(m, "\nWrites: %d Bytes: %lld",
atomic_read(&tcon->num_writes), atomic_read(&tcon->num_writes),
(long long)(tcon->bytes_written)); (long long)(tcon->bytes_written));
seq_printf(m, seq_printf(m, "\nLocks: %d HardLinks: %d "
"\nLocks: %d HardLinks: %d Symlinks: %d", "Symlinks: %d",
atomic_read(&tcon->num_locks), atomic_read(&tcon->num_locks),
atomic_read(&tcon->num_hardlinks), atomic_read(&tcon->num_hardlinks),
atomic_read(&tcon->num_symlinks)); atomic_read(&tcon->num_symlinks));
seq_printf(m, "\nOpens: %d Closes: %d"
seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", "Deletes: %d",
atomic_read(&tcon->num_opens), atomic_read(&tcon->num_opens),
atomic_read(&tcon->num_closes), atomic_read(&tcon->num_closes),
atomic_read(&tcon->num_deletes)); atomic_read(&tcon->num_deletes));
...@@ -339,12 +356,15 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) ...@@ -339,12 +356,15 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\nRenames: %d T2 Renames %d", seq_printf(m, "\nRenames: %d T2 Renames %d",
atomic_read(&tcon->num_renames), atomic_read(&tcon->num_renames),
atomic_read(&tcon->num_t2renames)); 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_ffirst),
atomic_read(&tcon->num_fnext), atomic_read(&tcon->num_fnext),
atomic_read(&tcon->num_fclose)); atomic_read(&tcon->num_fclose));
} }
read_unlock(&GlobalSMBSeslock); }
}
read_unlock(&cifs_tcp_ses_lock);
seq_putc(m, '\n'); seq_putc(m, '\n');
return 0; return 0;
......
...@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb) ...@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb)
tcon = cifs_sb->tcon; tcon = cifs_sb->tcon;
if (tcon == NULL) if (tcon == NULL)
return; return;
down(&tcon->tconSem);
if (atomic_read(&tcon->useCount) == 1) read_lock(&cifs_tcp_ses_lock);
if (tcon->tc_count == 1)
tcon->tidStatus = CifsExiting; tcon->tidStatus = CifsExiting;
up(&tcon->tconSem); read_unlock(&cifs_tcp_ses_lock);
/* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
/* cancel_notify_requests(tcon); */ /* cancel_notify_requests(tcon); */
...@@ -1060,7 +1061,6 @@ init_cifs(void) ...@@ -1060,7 +1061,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(&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
INIT_LIST_HEAD(&GlobalDnotifyReqList); INIT_LIST_HEAD(&GlobalDnotifyReqList);
......
...@@ -233,16 +233,15 @@ struct cifsSesInfo { ...@@ -233,16 +233,15 @@ struct cifsSesInfo {
* session * session
*/ */
struct cifsTconInfo { struct cifsTconInfo {
struct list_head cifsConnectionList; struct list_head tcon_list;
int tc_count;
struct list_head openFileList; struct list_head openFileList;
struct semaphore tconSem;
struct cifsSesInfo *ses; /* pointer to session associated with */ struct cifsSesInfo *ses; /* pointer to session associated with */
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
char *nativeFileSystem; char *nativeFileSystem;
__u16 tid; /* The 2 byte tree id */ __u16 tid; /* The 2 byte tree id */
__u16 Flags; /* optional support bits */ __u16 Flags; /* optional support bits */
enum statusEnum tidStatus; enum statusEnum tidStatus;
atomic_t useCount; /* how many explicit/implicit mounts to share */
#ifdef CONFIG_CIFS_STATS #ifdef CONFIG_CIFS_STATS
atomic_t num_smbs_sent; atomic_t num_smbs_sent;
atomic_t num_writes; atomic_t num_writes;
...@@ -600,9 +599,13 @@ require use of the stronger protocol */ ...@@ -600,9 +599,13 @@ require use of the stronger protocol */
*/ */
GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; 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 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 rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
GLOBAL_EXTERN struct list_head GlobalOplock_Q; GLOBAL_EXTERN struct list_head GlobalOplock_Q;
......
...@@ -742,48 +742,29 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) ...@@ -742,48 +742,29 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
int rc = 0; int rc = 0;
cFYI(1, ("In tree disconnect")); 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); /* BB: do we need to check this? These should never be NULL. */
if (atomic_read(&tcon->useCount) > 0) { if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
up(&tcon->tconSem); return -EIO;
return -EBUSY;
}
/* No need to return error on this operation if tid invalidated and /*
closed on server already e.g. due to tcp session crashing */ * No need to return error on this operation if tid invalidated and
if (tcon->need_reconnect) { * closed on server already e.g. due to tcp session crashing. Also,
up(&tcon->tconSem); * the tcon is no longer on the list, so no need to take lock before
* checking this.
*/
if (tcon->need_reconnect)
return 0; 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, rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
(void **)&smb_buffer); (void **)&smb_buffer);
if (rc) { if (rc)
up(&tcon->tconSem);
return rc; return rc;
}
rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
if (rc) if (rc)
cFYI(1, ("Tree disconnect failed %d", rc)); cFYI(1, ("Tree disconnect failed %d", rc));
up(&tcon->tconSem);
/* No need to return error on this operation if tid invalidated and /* No need to return error on this operation if tid invalidated and
closed on server already e.g. due to tcp session crashing */ closed on server already e.g. due to tcp session crashing */
if (rc == -EAGAIN) if (rc == -EAGAIN)
......
...@@ -124,7 +124,7 @@ static int ...@@ -124,7 +124,7 @@ static int
cifs_reconnect(struct TCP_Server_Info *server) cifs_reconnect(struct TCP_Server_Info *server)
{ {
int rc = 0; int rc = 0;
struct list_head *tmp; struct list_head *tmp, *tmp2;
struct cifsSesInfo *ses; struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
...@@ -149,13 +149,12 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -149,13 +149,12 @@ cifs_reconnect(struct TCP_Server_Info *server)
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
ses->need_reconnect = true; ses->need_reconnect = true;
ses->ipc_tid = 0; ses->ipc_tid = 0;
} list_for_each(tmp2, &ses->tcon_list) {
read_unlock(&cifs_tcp_ses_lock); tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
list_for_each(tmp, &GlobalTreeConnectionList) {
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
if ((tcon->ses) && (tcon->ses->server == server))
tcon->need_reconnect = true; tcon->need_reconnect = true;
} }
}
read_unlock(&cifs_tcp_ses_lock);
/* do not want to be sending data on a socket we are freeing */ /* do not want to be sending data on a socket we are freeing */
down(&server->tcpSem); down(&server->tcpSem);
if (server->ssocket) { if (server->ssocket) {
...@@ -1462,6 +1461,52 @@ cifs_put_smb_ses(struct cifsSesInfo *ses) ...@@ -1462,6 +1461,52 @@ cifs_put_smb_ses(struct cifsSesInfo *ses)
cifs_put_tcp_session(server); 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 int
get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
const struct nls_table *nls_codepage, unsigned int *pnum_referrals, 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, ...@@ -2220,11 +2265,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
if (!rc) { if (!rc) {
setup_cifs_sb(&volume_info, cifs_sb); setup_cifs_sb(&volume_info, cifs_sb);
tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
if (tcon) { if (tcon) {
cFYI(1, ("Found match on UNC path")); cFYI(1, ("Found match on UNC path"));
if (tcon->seal != volume_info.seal) /* existing tcon already has a reference */
cERROR(1, ("transport encryption setting " cifs_put_smb_ses(pSesInfo);
"conflicts with existing tid"));
} else { } else {
tcon = tconInfoAlloc(); tcon = tconInfoAlloc();
if (tcon == NULL) { if (tcon == NULL) {
...@@ -2257,6 +2302,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2257,6 +2302,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
if (rc) if (rc)
goto mount_fail_check; goto mount_fail_check;
tcon->seal = volume_info.seal; 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 /* we can have only one retry value for a connection
...@@ -2283,18 +2332,14 @@ mount_fail_check: ...@@ -2283,18 +2332,14 @@ mount_fail_check:
/* If find_unc succeeded then rc == 0 so we can not end */ /* If find_unc succeeded then rc == 0 so we can not end */
/* up accidently freeing someone elses tcon struct */ /* up accidently freeing someone elses tcon struct */
if (tcon) if (tcon)
tconInfoFree(tcon); cifs_put_tcon(tcon);
else if (pSesInfo)
/* should also end up putting our tcp session ref if needed */
if (pSesInfo)
cifs_put_smb_ses(pSesInfo); cifs_put_smb_ses(pSesInfo);
else else
cifs_put_tcp_session(srvTcp); cifs_put_tcp_session(srvTcp);
goto out; goto out;
} }
atomic_inc(&tcon->useCount);
cifs_sb->tcon = tcon; cifs_sb->tcon = tcon;
tcon->ses = pSesInfo;
/* do not care if following two calls succeed - informational */ /* do not care if following two calls succeed - informational */
if (!tcon->ipc) { if (!tcon->ipc) {
...@@ -3565,23 +3610,10 @@ int ...@@ -3565,23 +3610,10 @@ int
cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
{ {
int rc = 0; int rc = 0;
int xid;
struct cifsSesInfo *ses = NULL;
char *tmp; char *tmp;
xid = GetXid(); if (cifs_sb->tcon)
cifs_put_tcon(cifs_sb->tcon);
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);
}
cifs_sb->tcon = NULL; cifs_sb->tcon = NULL;
tmp = cifs_sb->prepath; tmp = cifs_sb->prepath;
...@@ -3589,7 +3621,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) ...@@ -3589,7 +3621,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
cifs_sb->prepath = NULL; cifs_sb->prepath = NULL;
kfree(tmp); kfree(tmp);
FreeXid(xid);
return rc; return rc;
} }
......
...@@ -79,6 +79,7 @@ sesInfoAlloc(void) ...@@ -79,6 +79,7 @@ sesInfoAlloc(void)
ret_buf->status = CifsNew; ret_buf->status = CifsNew;
++ret_buf->ses_count; ++ret_buf->ses_count;
INIT_LIST_HEAD(&ret_buf->smb_ses_list); INIT_LIST_HEAD(&ret_buf->smb_ses_list);
INIT_LIST_HEAD(&ret_buf->tcon_list);
init_MUTEX(&ret_buf->sesSem); init_MUTEX(&ret_buf->sesSem);
} }
return ret_buf; return ret_buf;
...@@ -107,17 +108,14 @@ tconInfoAlloc(void) ...@@ -107,17 +108,14 @@ tconInfoAlloc(void)
struct cifsTconInfo *ret_buf; struct cifsTconInfo *ret_buf;
ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL);
if (ret_buf) { if (ret_buf) {
write_lock(&GlobalSMBSeslock);
atomic_inc(&tconInfoAllocCount); atomic_inc(&tconInfoAllocCount);
list_add(&ret_buf->cifsConnectionList,
&GlobalTreeConnectionList);
ret_buf->tidStatus = CifsNew; ret_buf->tidStatus = CifsNew;
++ret_buf->tc_count;
INIT_LIST_HEAD(&ret_buf->openFileList); INIT_LIST_HEAD(&ret_buf->openFileList);
init_MUTEX(&ret_buf->tconSem); INIT_LIST_HEAD(&ret_buf->tcon_list);
#ifdef CONFIG_CIFS_STATS #ifdef CONFIG_CIFS_STATS
spin_lock_init(&ret_buf->stat_lock); spin_lock_init(&ret_buf->stat_lock);
#endif #endif
write_unlock(&GlobalSMBSeslock);
} }
return ret_buf; return ret_buf;
} }
...@@ -129,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to_free) ...@@ -129,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
cFYI(1, ("Null buffer passed to tconInfoFree")); cFYI(1, ("Null buffer passed to tconInfoFree"));
return; return;
} }
write_lock(&GlobalSMBSeslock);
atomic_dec(&tconInfoAllocCount); atomic_dec(&tconInfoAllocCount);
list_del(&buf_to_free->cifsConnectionList);
write_unlock(&GlobalSMBSeslock);
kfree(buf_to_free->nativeFileSystem); kfree(buf_to_free->nativeFileSystem);
kfree(buf_to_free); kfree(buf_to_free);
} }
...@@ -493,9 +488,10 @@ bool ...@@ -493,9 +488,10 @@ bool
is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
{ {
struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
struct list_head *tmp; struct list_head *tmp, *tmp1, *tmp2;
struct list_head *tmp1; struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct cifsInodeInfo *pCifsInode;
struct cifsFileInfo *netfile; struct cifsFileInfo *netfile;
cFYI(1, ("Checking for oplock break or dnotify response")); cFYI(1, ("Checking for oplock break or dnotify response"));
...@@ -550,42 +546,42 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -550,42 +546,42 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
return false; return false;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
read_lock(&GlobalSMBSeslock); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &GlobalTreeConnectionList) { list_for_each(tmp, &srv->smb_ses_list) {
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) { list_for_each(tmp1, &ses->tcon_list) {
tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list);
if (tcon->tid != buf->Tid)
continue;
cifs_stats_inc(&tcon->num_oplock_brks); cifs_stats_inc(&tcon->num_oplock_brks);
list_for_each(tmp1, &tcon->openFileList) { list_for_each(tmp2, &tcon->openFileList) {
netfile = list_entry(tmp1, struct cifsFileInfo, netfile = list_entry(tmp2, struct cifsFileInfo,
tlist); tlist);
if (pSMB->Fid == netfile->netfid) { if (pSMB->Fid != netfile->netfid)
struct cifsInodeInfo *pCifsInode; continue;
read_unlock(&GlobalSMBSeslock);
cFYI(1, read_unlock(&cifs_tcp_ses_lock);
("file id match, oplock break")); cFYI(1, ("file id match, oplock break"));
pCifsInode = pCifsInode = CIFS_I(netfile->pInode);
CIFS_I(netfile->pInode);
pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheAll = false;
if (pSMB->OplockLevel == 0) if (pSMB->OplockLevel == 0)
pCifsInode->clientCanCacheRead pCifsInode->clientCanCacheRead = false;
= false;
pCifsInode->oplockPending = true; pCifsInode->oplockPending = true;
AllocOplockQEntry(netfile->pInode, AllocOplockQEntry(netfile->pInode,
netfile->netfid, netfile->netfid, tcon);
tcon); cFYI(1, ("about to wake up oplock thread"));
cFYI(1,
("about to wake up oplock thread"));
if (oplockThread) if (oplockThread)
wake_up_process(oplockThread); wake_up_process(oplockThread);
return true; return true;
} }
} read_unlock(&cifs_tcp_ses_lock);
read_unlock(&GlobalSMBSeslock);
cFYI(1, ("No matching file for oplock break")); cFYI(1, ("No matching file for oplock break"));
return true; return true;
} }
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
cFYI(1, ("Can not process oplock break for non-existent connection")); cFYI(1, ("Can not process oplock break for non-existent connection"));
return true; return true;
} }
......
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