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: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
parent
d82c2df54e
commit
f1987b44f6
@ -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)
|
|||||||
|
|
||||||
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)
|
|||||||
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,14 +157,44 @@ 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;
|
||||||
|
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\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: 0x%d",
|
||||||
|
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
|
||||||
|
le32_to_cpu(tcon->fsAttrInfo.Attributes),
|
||||||
|
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
|
||||||
|
tcon->tidStatus);
|
||||||
|
if (dev_type == FILE_DEVICE_DISK)
|
||||||
|
seq_puts(m, " type: DISK ");
|
||||||
|
else if (dev_type == FILE_DEVICE_CD_ROM)
|
||||||
|
seq_puts(m, " type: CDROM ");
|
||||||
|
else
|
||||||
|
seq_printf(m, " type: %d ", dev_type);
|
||||||
|
|
||||||
|
if (tcon->need_reconnect)
|
||||||
|
seq_puts(m, "\tDISCONNECTED ");
|
||||||
|
seq_putc(m, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
seq_puts(m, "\n\tMIDs:\n");
|
||||||
|
|
||||||
spin_lock(&GlobalMid_Lock);
|
spin_lock(&GlobalMid_Lock);
|
||||||
list_for_each(tmp3, &server->pending_mid_q) {
|
list_for_each(tmp3, &server->pending_mid_q) {
|
||||||
mid_entry = list_entry(tmp3, 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, "\tState: %d com: %d pid:"
|
||||||
" %d tsk: %p mid %d\n",
|
" %d tsk: %p mid %d\n",
|
||||||
mid_entry->midState,
|
mid_entry->midState,
|
||||||
(int)mid_entry->command,
|
(int)mid_entry->command,
|
||||||
@ -177,41 +208,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
|||||||
read_unlock(&cifs_tcp_ses_lock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
seq_putc(m, '\n');
|
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);
|
|
||||||
seq_printf(m, "\n%d) %s Uses: %d ", i,
|
|
||||||
tcon->treeName, atomic_read(&tcon->useCount));
|
|
||||||
if (tcon->nativeFileSystem) {
|
|
||||||
seq_printf(m, "Type: %s ",
|
|
||||||
tcon->nativeFileSystem);
|
|
||||||
}
|
|
||||||
seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
|
|
||||||
"\nPathComponentMax: %d Status: %d",
|
|
||||||
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
|
|
||||||
le32_to_cpu(tcon->fsAttrInfo.Attributes),
|
|
||||||
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
|
|
||||||
tcon->tidStatus);
|
|
||||||
if (dev_type == FILE_DEVICE_DISK)
|
|
||||||
seq_puts(m, " type: DISK ");
|
|
||||||
else if (dev_type == FILE_DEVICE_CD_ROM)
|
|
||||||
seq_puts(m, " type: CDROM ");
|
|
||||||
else
|
|
||||||
seq_printf(m, " type: %d ", dev_type);
|
|
||||||
|
|
||||||
if (tcon->need_reconnect)
|
|
||||||
seq_puts(m, "\tDISCONNECTED ");
|
|
||||||
}
|
|
||||||
read_unlock(&GlobalSMBSeslock);
|
|
||||||
|
|
||||||
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 */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -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,33 +241,42 @@ 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,
|
||||||
atomic_set(&tcon->num_smbs_sent, 0);
|
tcp_ses_list);
|
||||||
atomic_set(&tcon->num_writes, 0);
|
list_for_each(tmp2, &server->smb_session_list) {
|
||||||
atomic_set(&tcon->num_reads, 0);
|
ses = list_entry(tmp2, struct cifsSesInfo,
|
||||||
atomic_set(&tcon->num_oplock_brks, 0);
|
smb_session_list);
|
||||||
atomic_set(&tcon->num_opens, 0);
|
list_for_each(tmp3, &ses->tcon_list) {
|
||||||
atomic_set(&tcon->num_closes, 0);
|
tcon = list_entry(tmp3,
|
||||||
atomic_set(&tcon->num_deletes, 0);
|
struct cifsTconInfo,
|
||||||
atomic_set(&tcon->num_mkdirs, 0);
|
tcon_list);
|
||||||
atomic_set(&tcon->num_rmdirs, 0);
|
atomic_set(&tcon->num_smbs_sent, 0);
|
||||||
atomic_set(&tcon->num_renames, 0);
|
atomic_set(&tcon->num_writes, 0);
|
||||||
atomic_set(&tcon->num_t2renames, 0);
|
atomic_set(&tcon->num_reads, 0);
|
||||||
atomic_set(&tcon->num_ffirst, 0);
|
atomic_set(&tcon->num_oplock_brks, 0);
|
||||||
atomic_set(&tcon->num_fnext, 0);
|
atomic_set(&tcon->num_opens, 0);
|
||||||
atomic_set(&tcon->num_fclose, 0);
|
atomic_set(&tcon->num_closes, 0);
|
||||||
atomic_set(&tcon->num_hardlinks, 0);
|
atomic_set(&tcon->num_deletes, 0);
|
||||||
atomic_set(&tcon->num_symlinks, 0);
|
atomic_set(&tcon->num_mkdirs, 0);
|
||||||
atomic_set(&tcon->num_locks, 0);
|
atomic_set(&tcon->num_rmdirs, 0);
|
||||||
|
atomic_set(&tcon->num_renames, 0);
|
||||||
|
atomic_set(&tcon->num_t2renames, 0);
|
||||||
|
atomic_set(&tcon->num_ffirst, 0);
|
||||||
|
atomic_set(&tcon->num_fnext, 0);
|
||||||
|
atomic_set(&tcon->num_fclose, 0);
|
||||||
|
atomic_set(&tcon->num_hardlinks, 0);
|
||||||
|
atomic_set(&tcon->num_symlinks, 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,
|
|||||||
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,44 +316,55 @@ 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) {
|
||||||
i++;
|
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||||
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
|
tcp_ses_list);
|
||||||
seq_printf(m, "\n%d) %s", i, tcon->treeName);
|
list_for_each(tmp2, &server->smb_ses_list) {
|
||||||
if (tcon->need_reconnect)
|
ses = list_entry(tmp2, struct cifsSesInfo,
|
||||||
seq_puts(m, "\tDISCONNECTED ");
|
smb_ses_list);
|
||||||
seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
|
list_for_each(tmp3, &ses->tcon_list) {
|
||||||
atomic_read(&tcon->num_smbs_sent),
|
tcon = list_entry(tmp3,
|
||||||
atomic_read(&tcon->num_oplock_brks));
|
struct cifsTconInfo,
|
||||||
seq_printf(m, "\nReads: %d Bytes: %lld",
|
tcon_list);
|
||||||
atomic_read(&tcon->num_reads),
|
i++;
|
||||||
(long long)(tcon->bytes_read));
|
seq_printf(m, "\n%d) %s", i, tcon->treeName);
|
||||||
seq_printf(m, "\nWrites: %d Bytes: %lld",
|
if (tcon->need_reconnect)
|
||||||
atomic_read(&tcon->num_writes),
|
seq_puts(m, "\tDISCONNECTED ");
|
||||||
(long long)(tcon->bytes_written));
|
seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
|
||||||
seq_printf(m,
|
atomic_read(&tcon->num_smbs_sent),
|
||||||
"\nLocks: %d HardLinks: %d Symlinks: %d",
|
atomic_read(&tcon->num_oplock_brks));
|
||||||
atomic_read(&tcon->num_locks),
|
seq_printf(m, "\nReads: %d Bytes: %lld",
|
||||||
atomic_read(&tcon->num_hardlinks),
|
atomic_read(&tcon->num_reads),
|
||||||
atomic_read(&tcon->num_symlinks));
|
(long long)(tcon->bytes_read));
|
||||||
|
seq_printf(m, "\nWrites: %d Bytes: %lld",
|
||||||
seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d",
|
atomic_read(&tcon->num_writes),
|
||||||
atomic_read(&tcon->num_opens),
|
(long long)(tcon->bytes_written));
|
||||||
atomic_read(&tcon->num_closes),
|
seq_printf(m, "\nLocks: %d HardLinks: %d "
|
||||||
atomic_read(&tcon->num_deletes));
|
"Symlinks: %d",
|
||||||
seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
|
atomic_read(&tcon->num_locks),
|
||||||
atomic_read(&tcon->num_mkdirs),
|
atomic_read(&tcon->num_hardlinks),
|
||||||
atomic_read(&tcon->num_rmdirs));
|
atomic_read(&tcon->num_symlinks));
|
||||||
seq_printf(m, "\nRenames: %d T2 Renames %d",
|
seq_printf(m, "\nOpens: %d Closes: %d"
|
||||||
atomic_read(&tcon->num_renames),
|
"Deletes: %d",
|
||||||
atomic_read(&tcon->num_t2renames));
|
atomic_read(&tcon->num_opens),
|
||||||
seq_printf(m, "\nFindFirst: %d FNext %d FClose %d",
|
atomic_read(&tcon->num_closes),
|
||||||
atomic_read(&tcon->num_ffirst),
|
atomic_read(&tcon->num_deletes));
|
||||||
atomic_read(&tcon->num_fnext),
|
seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
|
||||||
atomic_read(&tcon->num_fclose));
|
atomic_read(&tcon->num_mkdirs),
|
||||||
|
atomic_read(&tcon->num_rmdirs));
|
||||||
|
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",
|
||||||
|
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');
|
seq_putc(m, '\n');
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -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)
|
|||||||
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 {
|
|||||||
* 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 */
|
|||||||
*/
|
*/
|
||||||
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,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
|
|||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
cFYI(1, ("In tree disconnect"));
|
cFYI(1, ("In tree disconnect"));
|
||||||
|
|
||||||
|
/* BB: do we need to check this? These should never be NULL. */
|
||||||
|
if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If last user of the connection and
|
* No need to return error on this operation if tid invalidated and
|
||||||
* connection alive - disconnect it
|
* closed on server already e.g. due to tcp session crashing. Also,
|
||||||
* If this is the last connection on the server session disconnect it
|
* the tcon is no longer on the list, so no need to take lock before
|
||||||
* (and inside session disconnect we should check if tcp socket needs
|
* checking this.
|
||||||
* to be freed and kernel thread woken up).
|
|
||||||
*/
|
*/
|
||||||
if (tcon)
|
if (tcon->need_reconnect)
|
||||||
down(&tcon->tconSem);
|
|
||||||
else
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
atomic_dec(&tcon->useCount);
|
|
||||||
if (atomic_read(&tcon->useCount) > 0) {
|
|
||||||
up(&tcon->tconSem);
|
|
||||||
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 */
|
|
||||||
if (tcon->need_reconnect) {
|
|
||||||
up(&tcon->tconSem);
|
|
||||||
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)
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
||||||
|
@ -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)
|
|||||||
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) {
|
||||||
|
tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
|
||||||
|
tcon->need_reconnect = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
read_unlock(&cifs_tcp_ses_lock);
|
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))
|
|
||||||
tcon->need_reconnect = true;
|
|
||||||
}
|
|
||||||
/* 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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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:
|
|||||||
/* 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
|
|||||||
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)
|
|||||||
cifs_sb->prepath = NULL;
|
cifs_sb->prepath = NULL;
|
||||||
kfree(tmp);
|
kfree(tmp);
|
||||||
|
|
||||||
FreeXid(xid);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
|||||||
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)
|
|||||||
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
|
|||||||
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)
|
|||||||
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 = false;
|
||||||
pCifsInode->clientCanCacheRead
|
pCifsInode->oplockPending = true;
|
||||||
= false;
|
AllocOplockQEntry(netfile->pInode,
|
||||||
pCifsInode->oplockPending = true;
|
netfile->netfid, tcon);
|
||||||
AllocOplockQEntry(netfile->pInode,
|
cFYI(1, ("about to wake up oplock thread"));
|
||||||
netfile->netfid,
|
if (oplockThread)
|
||||||
tcon);
|
wake_up_process(oplockThread);
|
||||||
cFYI(1,
|
|
||||||
("about to wake up oplock thread"));
|
return true;
|
||||||
if (oplockThread)
|
|
||||||
wake_up_process(oplockThread);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user