mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
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: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
parent
e7ddee9037
commit
14fbf50d69
@ -107,9 +107,9 @@ 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;
|
struct list_head *tmp, *tmp2, *tmp3;
|
||||||
struct list_head *tmp1;
|
|
||||||
struct mid_q_entry *mid_entry;
|
struct mid_q_entry *mid_entry;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
struct cifsSesInfo *ses;
|
struct cifsSesInfo *ses;
|
||||||
struct cifsTconInfo *tcon;
|
struct cifsTconInfo *tcon;
|
||||||
int i;
|
int i;
|
||||||
@ -122,43 +122,45 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
|||||||
seq_printf(m, "Servers:");
|
seq_printf(m, "Servers:");
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
read_lock(&GlobalSMBSeslock);
|
read_lock(&cifs_tcp_ses_lock);
|
||||||
list_for_each(tmp, &GlobalSMBSessionList) {
|
list_for_each(tmp, &cifs_tcp_ses_list) {
|
||||||
|
server = list_entry(tmp, struct TCP_Server_Info,
|
||||||
|
tcp_ses_list);
|
||||||
i++;
|
i++;
|
||||||
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
|
list_for_each(tmp2, &server->smb_ses_list) {
|
||||||
if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) ||
|
ses = list_entry(tmp2, struct cifsSesInfo,
|
||||||
(ses->serverNOS == NULL)) {
|
smb_ses_list);
|
||||||
seq_printf(m, "\nentry for %s not fully "
|
if ((ses->serverDomain == NULL) ||
|
||||||
"displayed\n\t", ses->serverName);
|
(ses->serverOS == NULL) ||
|
||||||
} else {
|
(ses->serverNOS == NULL)) {
|
||||||
seq_printf(m,
|
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:"
|
"\n%d) Name: %s Domain: %s Mounts: %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,
|
||||||
atomic_read(&ses->inUse),
|
ses->ses_count, ses->serverOS, ses->serverNOS,
|
||||||
ses->serverOS, ses->serverNOS,
|
|
||||||
ses->capabilities, ses->status);
|
ses->capabilities, ses->status);
|
||||||
}
|
}
|
||||||
if (ses->server) {
|
|
||||||
seq_printf(m, "TCP status: %d\n\tLocal Users To "
|
seq_printf(m, "TCP status: %d\n\tLocal Users To "
|
||||||
"Server: %d SecMode: 0x%x Req On Wire: %d",
|
"Server: %d SecMode: 0x%x Req On Wire: %d",
|
||||||
ses->server->tcpStatus,
|
server->tcpStatus, server->srv_count,
|
||||||
ses->server->srv_count,
|
server->secMode,
|
||||||
ses->server->secMode,
|
atomic_read(&server->inFlight));
|
||||||
atomic_read(&ses->server->inFlight));
|
|
||||||
|
|
||||||
#ifdef CONFIG_CIFS_STATS2
|
#ifdef CONFIG_CIFS_STATS2
|
||||||
seq_printf(m, " In Send: %d In MaxReq Wait: %d",
|
seq_printf(m, " In Send: %d In MaxReq Wait: %d",
|
||||||
atomic_read(&ses->server->inSend),
|
atomic_read(&server->inSend),
|
||||||
atomic_read(&ses->server->num_waiters));
|
atomic_read(&server->num_waiters));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
seq_puts(m, "\nMIDs:\n");
|
seq_puts(m, "\nMIDs:\n");
|
||||||
|
|
||||||
spin_lock(&GlobalMid_Lock);
|
spin_lock(&GlobalMid_Lock);
|
||||||
list_for_each(tmp1, &ses->server->pending_mid_q) {
|
list_for_each(tmp3, &server->pending_mid_q) {
|
||||||
mid_entry = list_entry(tmp1, 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, "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);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
seq_putc(m, '\n');
|
seq_putc(m, '\n');
|
||||||
|
|
||||||
seq_puts(m, "Shares:");
|
seq_puts(m, "Shares:");
|
||||||
|
@ -1031,24 +1031,24 @@ static int cifs_oplock_thread(void *dummyarg)
|
|||||||
static int cifs_dnotify_thread(void *dummyarg)
|
static int cifs_dnotify_thread(void *dummyarg)
|
||||||
{
|
{
|
||||||
struct list_head *tmp;
|
struct list_head *tmp;
|
||||||
struct cifsSesInfo *ses;
|
struct TCP_Server_Info *server;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (try_to_freeze())
|
if (try_to_freeze())
|
||||||
continue;
|
continue;
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
schedule_timeout(15*HZ);
|
schedule_timeout(15*HZ);
|
||||||
read_lock(&GlobalSMBSeslock);
|
|
||||||
/* check if any stuck requests that need
|
/* check if any stuck requests that need
|
||||||
to be woken up and wakeq so the
|
to be woken up and wakeq so the
|
||||||
thread can wake up and error out */
|
thread can wake up and error out */
|
||||||
list_for_each(tmp, &GlobalSMBSessionList) {
|
read_lock(&cifs_tcp_ses_lock);
|
||||||
ses = list_entry(tmp, struct cifsSesInfo,
|
list_for_each(tmp, &cifs_tcp_ses_list) {
|
||||||
cifsSessionList);
|
server = list_entry(tmp, struct TCP_Server_Info,
|
||||||
if (ses->server && atomic_read(&ses->server->inFlight))
|
tcp_ses_list);
|
||||||
wake_up_all(&ses->server->response_q);
|
if (atomic_read(&server->inFlight))
|
||||||
|
wake_up_all(&server->response_q);
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
} while (!kthread_should_stop());
|
} while (!kthread_should_stop());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1060,7 +1060,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(&GlobalSMBSessionList); /* BB to be removed by jl */
|
|
||||||
INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
|
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
|
||||||
|
@ -195,14 +195,14 @@ struct cifsUidInfo {
|
|||||||
* Session structure. One of these for each uid session with a particular host
|
* Session structure. One of these for each uid session with a particular host
|
||||||
*/
|
*/
|
||||||
struct cifsSesInfo {
|
struct cifsSesInfo {
|
||||||
struct list_head cifsSessionList;
|
struct list_head smb_ses_list;
|
||||||
struct list_head tcon_list;
|
struct list_head tcon_list;
|
||||||
struct semaphore sesSem;
|
struct semaphore sesSem;
|
||||||
#if 0
|
#if 0
|
||||||
struct cifsUidInfo *uidInfo; /* pointer to user info */
|
struct cifsUidInfo *uidInfo; /* pointer to user info */
|
||||||
#endif
|
#endif
|
||||||
struct TCP_Server_Info *server; /* pointer to server info */
|
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;
|
enum statusEnum status;
|
||||||
unsigned overrideSecFlg; /* if non-zero override global sec flags */
|
unsigned overrideSecFlg; /* if non-zero override global sec flags */
|
||||||
__u16 ipc_tid; /* special tid for connection to IPC share */
|
__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 */
|
/* protects cifs_tcp_ses_list and srv_count for each tcp session */
|
||||||
GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
|
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 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 */
|
||||||
|
|
||||||
|
@ -102,7 +102,6 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path,
|
|||||||
const __u16 *pfid);
|
const __u16 *pfid);
|
||||||
extern int mode_to_acl(struct inode *inode, const char *path, __u64);
|
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 *,
|
extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
|
||||||
const char *);
|
const char *);
|
||||||
extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
|
extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
|
||||||
|
@ -799,20 +799,16 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
|
|||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
cFYI(1, ("In SMBLogoff for session disconnect"));
|
cFYI(1, ("In SMBLogoff for session disconnect"));
|
||||||
if (ses)
|
|
||||||
down(&ses->sesSem);
|
/*
|
||||||
else
|
* BB: do we need to check validity of ses and server? They should
|
||||||
return -EIO;
|
* always be valid since we have an active reference. If not, that
|
||||||
|
* should probably be a BUG()
|
||||||
atomic_dec(&ses->inUse);
|
*/
|
||||||
if (atomic_read(&ses->inUse) > 0) {
|
if (!ses || !ses->server)
|
||||||
up(&ses->sesSem);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ses->server == NULL)
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
|
down(&ses->sesSem);
|
||||||
if (ses->need_reconnect)
|
if (ses->need_reconnect)
|
||||||
goto session_already_dead; /* no need to send SMBlogoff if uid
|
goto session_already_dead; /* no need to send SMBlogoff if uid
|
||||||
already closed due to reconnect */
|
already closed due to reconnect */
|
||||||
@ -833,10 +829,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
|
|||||||
pSMB->AndXCommand = 0xFF;
|
pSMB->AndXCommand = 0xFF;
|
||||||
rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
|
rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
|
||||||
session_already_dead:
|
session_already_dead:
|
||||||
if (ses->server) {
|
|
||||||
cifs_put_tcp_session(ses->server);
|
|
||||||
rc = 0;
|
|
||||||
}
|
|
||||||
up(&ses->sesSem);
|
up(&ses->sesSem);
|
||||||
|
|
||||||
/* if session dead then we do not need to do ulogoff,
|
/* if session dead then we do not need to do ulogoff,
|
||||||
|
@ -144,23 +144,18 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||||||
|
|
||||||
/* before reconnecting the tcp session, mark the smb session (uid)
|
/* before reconnecting the tcp session, mark the smb session (uid)
|
||||||
and the tid bad so they are not used until reconnected */
|
and the tid bad so they are not used until reconnected */
|
||||||
read_lock(&GlobalSMBSeslock);
|
read_lock(&cifs_tcp_ses_lock);
|
||||||
list_for_each(tmp, &GlobalSMBSessionList) {
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
|
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
|
||||||
if (ses->server) {
|
ses->need_reconnect = true;
|
||||||
if (ses->server == server) {
|
ses->ipc_tid = 0;
|
||||||
ses->need_reconnect = true;
|
|
||||||
ses->ipc_tid = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* else tcp and smb sessions need reconnection */
|
|
||||||
}
|
}
|
||||||
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
list_for_each(tmp, &GlobalTreeConnectionList) {
|
list_for_each(tmp, &GlobalTreeConnectionList) {
|
||||||
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
|
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
|
||||||
if ((tcon->ses) && (tcon->ses->server == server))
|
if ((tcon->ses) && (tcon->ses->server == server))
|
||||||
tcon->need_reconnect = true;
|
tcon->need_reconnect = true;
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
|
||||||
/* 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) {
|
||||||
@ -696,29 +691,29 @@ multi_t2_fnd:
|
|||||||
if (smallbuf) /* no sense logging a debug message if NULL */
|
if (smallbuf) /* no sense logging a debug message if NULL */
|
||||||
cifs_small_buf_release(smallbuf);
|
cifs_small_buf_release(smallbuf);
|
||||||
|
|
||||||
read_lock(&GlobalSMBSeslock);
|
/*
|
||||||
|
* BB: we shouldn't have to do any of this. It shouldn't be
|
||||||
|
* possible to exit from the thread with active SMB sessions
|
||||||
|
*/
|
||||||
|
read_lock(&cifs_tcp_ses_lock);
|
||||||
if (list_empty(&server->pending_mid_q)) {
|
if (list_empty(&server->pending_mid_q)) {
|
||||||
/* loop through server session structures attached to this and
|
/* loop through server session structures attached to this and
|
||||||
mark them dead */
|
mark them dead */
|
||||||
list_for_each(tmp, &GlobalSMBSessionList) {
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
ses =
|
ses = list_entry(tmp, struct cifsSesInfo,
|
||||||
list_entry(tmp, struct cifsSesInfo,
|
smb_ses_list);
|
||||||
cifsSessionList);
|
ses->status = CifsExiting;
|
||||||
if (ses->server == server) {
|
ses->server = NULL;
|
||||||
ses->status = CifsExiting;
|
|
||||||
ses->server = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
} else {
|
} else {
|
||||||
/* although we can not zero the server struct pointer yet,
|
/* although we can not zero the server struct pointer yet,
|
||||||
since there are active requests which may depnd on them,
|
since there are active requests which may depnd on them,
|
||||||
mark the corresponding SMB sessions as exiting too */
|
mark the corresponding SMB sessions as exiting too */
|
||||||
list_for_each(tmp, &GlobalSMBSessionList) {
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
ses = list_entry(tmp, struct cifsSesInfo,
|
ses = list_entry(tmp, struct cifsSesInfo,
|
||||||
cifsSessionList);
|
smb_ses_list);
|
||||||
if (ses->server == server)
|
ses->status = CifsExiting;
|
||||||
ses->status = CifsExiting;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&GlobalMid_Lock);
|
spin_lock(&GlobalMid_Lock);
|
||||||
@ -733,7 +728,7 @@ multi_t2_fnd:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
/* 1/8th of sec is more than enough time for them to exit */
|
/* 1/8th of sec is more than enough time for them to exit */
|
||||||
msleep(125);
|
msleep(125);
|
||||||
}
|
}
|
||||||
@ -755,14 +750,13 @@ multi_t2_fnd:
|
|||||||
if there are any pointing to this (e.g
|
if there are any pointing to this (e.g
|
||||||
if a crazy root user tried to kill cifsd
|
if a crazy root user tried to kill cifsd
|
||||||
kernel thread explicitly this might happen) */
|
kernel thread explicitly this might happen) */
|
||||||
write_lock(&GlobalSMBSeslock);
|
/* BB: This shouldn't be necessary, see above */
|
||||||
list_for_each(tmp, &GlobalSMBSessionList) {
|
read_lock(&cifs_tcp_ses_lock);
|
||||||
ses = list_entry(tmp, struct cifsSesInfo,
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
cifsSessionList);
|
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
|
||||||
if (ses->server == server)
|
ses->server = NULL;
|
||||||
ses->server = NULL;
|
|
||||||
}
|
}
|
||||||
write_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
|
|
||||||
kfree(server->hostname);
|
kfree(server->hostname);
|
||||||
task_to_wake = xchg(&server->tsk, NULL);
|
task_to_wake = xchg(&server->tsk, NULL);
|
||||||
@ -1401,7 +1395,7 @@ cifs_find_tcp_session(struct sockaddr *addr)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
cifs_put_tcp_session(struct TCP_Server_Info *server)
|
cifs_put_tcp_session(struct TCP_Server_Info *server)
|
||||||
{
|
{
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
@ -1424,6 +1418,50 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
|
|||||||
force_sig(SIGKILL, task);
|
force_sig(SIGKILL, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct cifsSesInfo *
|
||||||
|
cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
|
||||||
|
{
|
||||||
|
struct list_head *tmp;
|
||||||
|
struct cifsSesInfo *ses;
|
||||||
|
|
||||||
|
write_lock(&cifs_tcp_ses_lock);
|
||||||
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
|
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
|
||||||
|
if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
++ses->ses_count;
|
||||||
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return ses;
|
||||||
|
}
|
||||||
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cifs_put_smb_ses(struct cifsSesInfo *ses)
|
||||||
|
{
|
||||||
|
int xid;
|
||||||
|
struct TCP_Server_Info *server = ses->server;
|
||||||
|
|
||||||
|
write_lock(&cifs_tcp_ses_lock);
|
||||||
|
if (--ses->ses_count > 0) {
|
||||||
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del_init(&ses->smb_ses_list);
|
||||||
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
|
|
||||||
|
if (ses->status == CifsGood) {
|
||||||
|
xid = GetXid();
|
||||||
|
CIFSSMBLogoff(xid, ses);
|
||||||
|
_FreeXid(xid);
|
||||||
|
}
|
||||||
|
sesInfoFree(ses);
|
||||||
|
cifs_put_tcp_session(server);
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
@ -1958,7 +1996,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||||||
struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
|
struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
|
||||||
struct smb_vol volume_info;
|
struct smb_vol volume_info;
|
||||||
struct cifsSesInfo *pSesInfo = NULL;
|
struct cifsSesInfo *pSesInfo = NULL;
|
||||||
struct cifsSesInfo *existingCifsSes = NULL;
|
|
||||||
struct cifsTconInfo *tcon = NULL;
|
struct cifsTconInfo *tcon = NULL;
|
||||||
struct TCP_Server_Info *srvTcp = NULL;
|
struct TCP_Server_Info *srvTcp = NULL;
|
||||||
|
|
||||||
@ -2112,6 +2149,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||||||
volume_info.target_rfc1001_name, 16);
|
volume_info.target_rfc1001_name, 16);
|
||||||
srvTcp->sequence_number = 0;
|
srvTcp->sequence_number = 0;
|
||||||
INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
|
INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
|
||||||
|
INIT_LIST_HEAD(&srvTcp->smb_ses_list);
|
||||||
++srvTcp->srv_count;
|
++srvTcp->srv_count;
|
||||||
write_lock(&cifs_tcp_ses_lock);
|
write_lock(&cifs_tcp_ses_lock);
|
||||||
list_add(&srvTcp->tcp_ses_list,
|
list_add(&srvTcp->tcp_ses_list,
|
||||||
@ -2120,10 +2158,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingCifsSes) {
|
pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
|
||||||
pSesInfo = existingCifsSes;
|
if (pSesInfo) {
|
||||||
cFYI(1, ("Existing smb sess found (status=%d)",
|
cFYI(1, ("Existing smb sess found (status=%d)",
|
||||||
pSesInfo->status));
|
pSesInfo->status));
|
||||||
|
/*
|
||||||
|
* The existing SMB session already has a reference to srvTcp,
|
||||||
|
* so we can put back the extra one we got before
|
||||||
|
*/
|
||||||
|
cifs_put_tcp_session(srvTcp);
|
||||||
|
|
||||||
down(&pSesInfo->sesSem);
|
down(&pSesInfo->sesSem);
|
||||||
if (pSesInfo->need_reconnect) {
|
if (pSesInfo->need_reconnect) {
|
||||||
cFYI(1, ("Session needs reconnect"));
|
cFYI(1, ("Session needs reconnect"));
|
||||||
@ -2134,41 +2178,44 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||||||
} else if (!rc) {
|
} else if (!rc) {
|
||||||
cFYI(1, ("Existing smb sess not found"));
|
cFYI(1, ("Existing smb sess not found"));
|
||||||
pSesInfo = sesInfoAlloc();
|
pSesInfo = sesInfoAlloc();
|
||||||
if (pSesInfo == NULL)
|
if (pSesInfo == NULL) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
else {
|
goto mount_fail_check;
|
||||||
pSesInfo->server = srvTcp;
|
|
||||||
sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
|
|
||||||
NIPQUAD(sin_server->sin_addr.s_addr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rc) {
|
/* new SMB session uses our srvTcp ref */
|
||||||
/* volume_info.password freed at unmount */
|
pSesInfo->server = srvTcp;
|
||||||
if (volume_info.password) {
|
sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
|
||||||
pSesInfo->password = volume_info.password;
|
NIPQUAD(sin_server->sin_addr.s_addr));
|
||||||
/* set to NULL to prevent freeing on exit */
|
|
||||||
volume_info.password = NULL;
|
write_lock(&cifs_tcp_ses_lock);
|
||||||
}
|
list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
|
||||||
if (volume_info.username)
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
strncpy(pSesInfo->userName,
|
|
||||||
volume_info.username,
|
/* volume_info.password freed at unmount */
|
||||||
MAX_USERNAME_SIZE);
|
if (volume_info.password) {
|
||||||
if (volume_info.domainname) {
|
pSesInfo->password = volume_info.password;
|
||||||
int len = strlen(volume_info.domainname);
|
/* set to NULL to prevent freeing on exit */
|
||||||
pSesInfo->domainName =
|
volume_info.password = NULL;
|
||||||
kmalloc(len + 1, GFP_KERNEL);
|
|
||||||
if (pSesInfo->domainName)
|
|
||||||
strcpy(pSesInfo->domainName,
|
|
||||||
volume_info.domainname);
|
|
||||||
}
|
|
||||||
pSesInfo->linux_uid = volume_info.linux_uid;
|
|
||||||
pSesInfo->overrideSecFlg = volume_info.secFlg;
|
|
||||||
down(&pSesInfo->sesSem);
|
|
||||||
/* BB FIXME need to pass vol->secFlgs BB */
|
|
||||||
rc = cifs_setup_session(xid, pSesInfo,
|
|
||||||
cifs_sb->local_nls);
|
|
||||||
up(&pSesInfo->sesSem);
|
|
||||||
}
|
}
|
||||||
|
if (volume_info.username)
|
||||||
|
strncpy(pSesInfo->userName, volume_info.username,
|
||||||
|
MAX_USERNAME_SIZE);
|
||||||
|
if (volume_info.domainname) {
|
||||||
|
int len = strlen(volume_info.domainname);
|
||||||
|
pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
|
||||||
|
if (pSesInfo->domainName)
|
||||||
|
strcpy(pSesInfo->domainName,
|
||||||
|
volume_info.domainname);
|
||||||
|
}
|
||||||
|
pSesInfo->linux_uid = volume_info.linux_uid;
|
||||||
|
pSesInfo->overrideSecFlg = volume_info.secFlg;
|
||||||
|
down(&pSesInfo->sesSem);
|
||||||
|
|
||||||
|
/* BB FIXME need to pass vol->secFlgs BB */
|
||||||
|
rc = cifs_setup_session(xid, pSesInfo,
|
||||||
|
cifs_sb->local_nls);
|
||||||
|
up(&pSesInfo->sesSem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* search for existing tcon to this server share */
|
/* search for existing tcon to this server share */
|
||||||
@ -2209,11 +2256,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||||||
tcon->Flags));
|
tcon->Flags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!rc) {
|
if (rc)
|
||||||
atomic_inc(&pSesInfo->inUse);
|
|
||||||
tcon->seal = volume_info.seal;
|
|
||||||
} else
|
|
||||||
goto mount_fail_check;
|
goto mount_fail_check;
|
||||||
|
tcon->seal = volume_info.seal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we can have only one retry value for a connection
|
/* we can have only one retry value for a connection
|
||||||
@ -2234,29 +2279,19 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||||||
/* BB FIXME fix time_gran to be larger for LANMAN sessions */
|
/* BB FIXME fix time_gran to be larger for LANMAN sessions */
|
||||||
sb->s_time_gran = 100;
|
sb->s_time_gran = 100;
|
||||||
|
|
||||||
/* on error free sesinfo and tcon struct if needed */
|
|
||||||
mount_fail_check:
|
mount_fail_check:
|
||||||
|
/* on error free sesinfo and tcon struct if needed */
|
||||||
if (rc) {
|
if (rc) {
|
||||||
/* 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);
|
tconInfoFree(tcon);
|
||||||
|
|
||||||
if (existingCifsSes == NULL) {
|
/* should also end up putting our tcp session ref if needed */
|
||||||
if (pSesInfo) {
|
if (pSesInfo)
|
||||||
if ((pSesInfo->server) &&
|
cifs_put_smb_ses(pSesInfo);
|
||||||
(pSesInfo->status == CifsGood))
|
else
|
||||||
CIFSSMBLogoff(xid, pSesInfo);
|
cifs_put_tcp_session(srvTcp);
|
||||||
else {
|
|
||||||
cFYI(1, ("No session or bad tcon"));
|
|
||||||
if (pSesInfo->server)
|
|
||||||
cifs_put_tcp_session(
|
|
||||||
pSesInfo->server);
|
|
||||||
}
|
|
||||||
sesInfoFree(pSesInfo);
|
|
||||||
/* pSesInfo = NULL; */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
atomic_inc(&tcon->useCount);
|
atomic_inc(&tcon->useCount);
|
||||||
cifs_sb->tcon = tcon;
|
cifs_sb->tcon = tcon;
|
||||||
@ -3551,16 +3586,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
|||||||
}
|
}
|
||||||
DeleteTconOplockQEntries(cifs_sb->tcon);
|
DeleteTconOplockQEntries(cifs_sb->tcon);
|
||||||
tconInfoFree(cifs_sb->tcon);
|
tconInfoFree(cifs_sb->tcon);
|
||||||
if ((ses) && (ses->server)) {
|
cifs_put_smb_ses(ses);
|
||||||
/* save off task so we do not refer to ses later */
|
|
||||||
cFYI(1, ("About to do SMBLogoff "));
|
|
||||||
rc = CIFSSMBLogoff(xid, ses);
|
|
||||||
if (rc == -EBUSY) {
|
|
||||||
FreeXid(xid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
cFYI(1, ("No session or bad tcon"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cifs_sb->tcon = NULL;
|
cifs_sb->tcon = NULL;
|
||||||
@ -3568,8 +3594,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
|||||||
cifs_sb->prepathlen = 0;
|
cifs_sb->prepathlen = 0;
|
||||||
cifs_sb->prepath = NULL;
|
cifs_sb->prepath = NULL;
|
||||||
kfree(tmp);
|
kfree(tmp);
|
||||||
if (ses)
|
|
||||||
sesInfoFree(ses);
|
|
||||||
|
|
||||||
FreeXid(xid);
|
FreeXid(xid);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -75,12 +75,11 @@ sesInfoAlloc(void)
|
|||||||
|
|
||||||
ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
|
ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
|
||||||
if (ret_buf) {
|
if (ret_buf) {
|
||||||
write_lock(&GlobalSMBSeslock);
|
|
||||||
atomic_inc(&sesInfoAllocCount);
|
atomic_inc(&sesInfoAllocCount);
|
||||||
ret_buf->status = CifsNew;
|
ret_buf->status = CifsNew;
|
||||||
list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList);
|
++ret_buf->ses_count;
|
||||||
|
INIT_LIST_HEAD(&ret_buf->smb_ses_list);
|
||||||
init_MUTEX(&ret_buf->sesSem);
|
init_MUTEX(&ret_buf->sesSem);
|
||||||
write_unlock(&GlobalSMBSeslock);
|
|
||||||
}
|
}
|
||||||
return ret_buf;
|
return ret_buf;
|
||||||
}
|
}
|
||||||
@ -93,10 +92,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_lock(&GlobalSMBSeslock);
|
|
||||||
atomic_dec(&sesInfoAllocCount);
|
atomic_dec(&sesInfoAllocCount);
|
||||||
list_del(&buf_to_free->cifsSessionList);
|
|
||||||
write_unlock(&GlobalSMBSeslock);
|
|
||||||
kfree(buf_to_free->serverOS);
|
kfree(buf_to_free->serverOS);
|
||||||
kfree(buf_to_free->serverDomain);
|
kfree(buf_to_free->serverDomain);
|
||||||
kfree(buf_to_free->serverNOS);
|
kfree(buf_to_free->serverNOS);
|
||||||
@ -350,9 +346,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
|||||||
if (current->fsuid != treeCon->ses->linux_uid) {
|
if (current->fsuid != treeCon->ses->linux_uid) {
|
||||||
cFYI(1, ("Multiuser mode and UID "
|
cFYI(1, ("Multiuser mode and UID "
|
||||||
"did not match tcon uid"));
|
"did not match tcon uid"));
|
||||||
read_lock(&GlobalSMBSeslock);
|
read_lock(&cifs_tcp_ses_lock);
|
||||||
list_for_each(temp_item, &GlobalSMBSessionList) {
|
list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
|
||||||
ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
|
ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
|
||||||
if (ses->linux_uid == current->fsuid) {
|
if (ses->linux_uid == current->fsuid) {
|
||||||
if (ses->server == treeCon->ses->server) {
|
if (ses->server == treeCon->ses->server) {
|
||||||
cFYI(1, ("found matching uid substitute right smb_uid"));
|
cFYI(1, ("found matching uid substitute right smb_uid"));
|
||||||
@ -364,7 +360,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user