mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
Sixteen smb3/cifs client fixes
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmVP3VMACgkQiiy9cAdy T1HlPAwAoeklucnmjVZJny15qsDHErR9I/CQseMksGHBomAFk2iUjUEL8cjozMMU 3gZuXnYT07Gd95Tk4oytVqEFn4pXl4rdi1gsppr9ewPu0XYZBL0yt9L9rt7g9lm9 yWvwa6skIOjJIeLs+Thzz7MBj3T759TT0O4J4Hl2LQ8QnDPvR9Zh0N01B6boRw7i qG8jcCgJRRHlj6B/e24wGdu7wTUxDxWCXGkyos30VfojdrQwfWJ45Hhn7MiytRfx qeEW2bYdSBZhqpQs6MbpkBz+nUZQf7oxhXbqfxqx8CsjaN7X//qA+mWl47n64t52 h7A73LP8rDe6JJNZRY/LWGNK4vtqEVw2AvvbETqxiteLA61Xp/+3SBt3upepH6eT /EvSXuMmfeHUf/Od2u00ynos4VbFzFelHuzmGatv/s7VeHqRu4ImWHtRhI3BbmjK runuFRNcY8YrGfpu+niXIeYDI0a9zIeCKl75GYbf/Ns53EYYwfKJIrG+BX0i2CUO g72piup1 =xjGh -----END PGP SIGNATURE----- Merge tag '6.7-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6 Pull smb client fixes from Steve French: - ctime caching fix (for setxattr) - encryption fix - DNS resolver mount fix - debugging improvements - multichannel fixes including cases where server stops or starts supporting multichannel after mount - reconnect fix - minor cleanups * tag '6.7-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6: cifs: update internal module version number for cifs.ko cifs: handle when server stops supporting multichannel cifs: handle when server starts supporting multichannel Missing field not being returned in ioctl CIFS_IOC_GET_MNT_INFO smb3: allow dumping session and tcon id to improve stats analysis and debugging smb: client: fix mount when dns_resolver key is not available smb3: fix caching of ctime on setxattr smb3: minor cleanup of session handling code cifs: reconnect work should have reference on server struct cifs: do not pass cifs_sb when trying to add channels cifs: account for primary channel in the interface list cifs: distribute channels across interfaces based on speed cifs: handle cases where a channel is closed smb3: more minor cleanups for session handling routines smb3: minor RDMA cleanup cifs: Fix encryption of cleared, but unset rq_iter data buffers
This commit is contained in:
commit
1b907d0507
@ -136,6 +136,11 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
|
||||
{
|
||||
struct TCP_Server_Info *server = chan->server;
|
||||
|
||||
if (!server) {
|
||||
seq_printf(m, "\n\n\t\tChannel: %d DISABLED", i+1);
|
||||
return;
|
||||
}
|
||||
|
||||
seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
|
||||
"\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
|
||||
"\n\t\tTCP status: %d Instance: %d"
|
||||
@ -279,6 +284,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifs_server_iface *iface;
|
||||
size_t iface_weight = 0, iface_min_speed = 0;
|
||||
struct cifs_server_iface *last_iface = NULL;
|
||||
int c, i, j;
|
||||
|
||||
seq_puts(m,
|
||||
@ -544,11 +551,25 @@ skip_rdma:
|
||||
"\tLast updated: %lu seconds ago",
|
||||
ses->iface_count,
|
||||
(jiffies - ses->iface_last_update) / HZ);
|
||||
|
||||
last_iface = list_last_entry(&ses->iface_list,
|
||||
struct cifs_server_iface,
|
||||
iface_head);
|
||||
iface_min_speed = last_iface->speed;
|
||||
|
||||
j = 0;
|
||||
list_for_each_entry(iface, &ses->iface_list,
|
||||
iface_head) {
|
||||
seq_printf(m, "\n\t%d)", ++j);
|
||||
cifs_dump_iface(m, iface);
|
||||
|
||||
iface_weight = iface->speed / iface_min_speed;
|
||||
seq_printf(m, "\t\tWeight (cur,total): (%zu,%zu)"
|
||||
"\n\t\tAllocated channels: %u\n",
|
||||
iface->weight_fulfilled,
|
||||
iface_weight,
|
||||
iface->num_channels);
|
||||
|
||||
if (is_ses_using_iface(ses, iface))
|
||||
seq_puts(m, "\t\t[CONNECTED]\n");
|
||||
}
|
||||
@ -746,14 +767,14 @@ static ssize_t name##_write(struct file *file, const char __user *buffer, \
|
||||
size_t count, loff_t *ppos) \
|
||||
{ \
|
||||
int rc; \
|
||||
rc = kstrtoint_from_user(buffer, count, 10, & name); \
|
||||
rc = kstrtoint_from_user(buffer, count, 10, &name); \
|
||||
if (rc) \
|
||||
return rc; \
|
||||
return count; \
|
||||
} \
|
||||
static int name##_proc_show(struct seq_file *m, void *v) \
|
||||
{ \
|
||||
seq_printf(m, "%d\n", name ); \
|
||||
seq_printf(m, "%d\n", name); \
|
||||
return 0; \
|
||||
} \
|
||||
static int name##_open(struct inode *inode, struct file *file) \
|
||||
|
@ -26,6 +26,11 @@ struct smb_mnt_fs_info {
|
||||
__u64 cifs_posix_caps;
|
||||
} __packed;
|
||||
|
||||
struct smb_mnt_tcon_info {
|
||||
__u32 tid;
|
||||
__u64 session_id;
|
||||
} __packed;
|
||||
|
||||
struct smb_snapshot_array {
|
||||
__u32 number_of_snapshots;
|
||||
__u32 number_of_snapshots_returned;
|
||||
@ -108,6 +113,7 @@ struct smb3_notify_info {
|
||||
#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
|
||||
#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info)
|
||||
#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info)
|
||||
#define CIFS_IOC_GET_TCON_INFO _IOR(CIFS_IOCTL_MAGIC, 12, struct smb_mnt_tcon_info)
|
||||
#define CIFS_IOC_SHUTDOWN _IOR('X', 125, __u32)
|
||||
|
||||
/*
|
||||
|
@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops;
|
||||
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
||||
|
||||
/* when changing internal version - update following two lines at same time */
|
||||
#define SMB3_PRODUCT_BUILD 45
|
||||
#define CIFS_VERSION "2.45"
|
||||
#define SMB3_PRODUCT_BUILD 46
|
||||
#define CIFS_VERSION "2.46"
|
||||
#endif /* _CIFSFS_H */
|
||||
|
@ -650,6 +650,7 @@ struct TCP_Server_Info {
|
||||
bool noautotune; /* do not autotune send buf sizes */
|
||||
bool nosharesock;
|
||||
bool tcp_nodelay;
|
||||
bool terminate;
|
||||
unsigned int credits; /* send no more requests at once */
|
||||
unsigned int max_credits; /* can override large 32000 default at mnt */
|
||||
unsigned int in_flight; /* number of requests on the wire to server */
|
||||
@ -969,6 +970,8 @@ struct cifs_server_iface {
|
||||
struct list_head iface_head;
|
||||
struct kref refcount;
|
||||
size_t speed;
|
||||
size_t weight_fulfilled;
|
||||
unsigned int num_channels;
|
||||
unsigned int rdma_capable : 1;
|
||||
unsigned int rss_capable : 1;
|
||||
unsigned int is_active : 1; /* unset if non existent */
|
||||
@ -1050,6 +1053,7 @@ struct cifs_ses {
|
||||
spinlock_t chan_lock;
|
||||
/* ========= begin: protected by chan_lock ======== */
|
||||
#define CIFS_MAX_CHANNELS 16
|
||||
#define CIFS_INVAL_CHAN_INDEX (-1)
|
||||
#define CIFS_ALL_CHANNELS_SET(ses) \
|
||||
((1UL << (ses)->chan_count) - 1)
|
||||
#define CIFS_ALL_CHANS_GOOD(ses) \
|
||||
@ -2143,6 +2147,7 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
|
||||
unsigned int len, skip;
|
||||
unsigned int nents = 0;
|
||||
unsigned long addr;
|
||||
size_t data_size;
|
||||
int i, j;
|
||||
|
||||
/*
|
||||
@ -2158,17 +2163,21 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
|
||||
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
|
||||
*/
|
||||
for (i = 0; i < num_rqst; i++) {
|
||||
data_size = iov_iter_count(&rqst[i].rq_iter);
|
||||
|
||||
/* We really don't want a mixture of pinned and unpinned pages
|
||||
* in the sglist. It's hard to keep track of which is what.
|
||||
* Instead, we convert to a BVEC-type iterator higher up.
|
||||
*/
|
||||
if (WARN_ON_ONCE(user_backed_iter(&rqst[i].rq_iter)))
|
||||
if (data_size &&
|
||||
WARN_ON_ONCE(user_backed_iter(&rqst[i].rq_iter)))
|
||||
return -EIO;
|
||||
|
||||
/* We also don't want to have any extra refs or pins to clean
|
||||
* up in the sglist.
|
||||
*/
|
||||
if (WARN_ON_ONCE(iov_iter_extract_will_pin(&rqst[i].rq_iter)))
|
||||
if (data_size &&
|
||||
WARN_ON_ONCE(iov_iter_extract_will_pin(&rqst[i].rq_iter)))
|
||||
return -EIO;
|
||||
|
||||
for (j = 0; j < rqst[i].rq_nvec; j++) {
|
||||
@ -2184,7 +2193,8 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
|
||||
}
|
||||
skip = 0;
|
||||
}
|
||||
nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
|
||||
if (data_size)
|
||||
nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
|
||||
}
|
||||
nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
|
||||
return nents;
|
||||
|
@ -132,6 +132,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
|
||||
struct smb_hdr *in_buf,
|
||||
struct smb_hdr *out_buf,
|
||||
int *bytes_returned);
|
||||
|
||||
void
|
||||
cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
|
||||
bool all_channels);
|
||||
@ -610,13 +611,13 @@ void cifs_free_hash(struct shash_desc **sdesc);
|
||||
|
||||
struct cifs_chan *
|
||||
cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server);
|
||||
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
|
||||
int cifs_try_adding_channels(struct cifs_ses *ses);
|
||||
bool is_server_using_iface(struct TCP_Server_Info *server,
|
||||
struct cifs_server_iface *iface);
|
||||
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
|
||||
void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
|
||||
|
||||
unsigned int
|
||||
int
|
||||
cifs_ses_get_chan_index(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
@ -640,6 +641,8 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
|
||||
bool
|
||||
cifs_chan_is_iface_active(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
cifs_disable_secondary_channels(struct cifs_ses *ses);
|
||||
int
|
||||
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);
|
||||
int
|
||||
|
@ -132,6 +132,9 @@ static void smb2_query_server_interfaces(struct work_struct *work)
|
||||
free_xid(xid);
|
||||
|
||||
if (rc) {
|
||||
if (rc == -EOPNOTSUPP)
|
||||
return;
|
||||
|
||||
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
@ -173,8 +176,12 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
|
||||
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
spin_lock(&ses->chan_lock);
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
if (!ses->chans[i].server)
|
||||
continue;
|
||||
|
||||
spin_lock(&ses->chans[i].server->srv_lock);
|
||||
ses->chans[i].server->tcpStatus = CifsNeedReconnect;
|
||||
if (ses->chans[i].server->tcpStatus != CifsExiting)
|
||||
ses->chans[i].server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&ses->chans[i].server->srv_lock);
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
@ -212,6 +219,14 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
/*
|
||||
* if channel has been marked for termination, nothing to do
|
||||
* for the channel. in fact, we cannot find the channel for the
|
||||
* server. So safe to exit here
|
||||
*/
|
||||
if (server->terminate)
|
||||
break;
|
||||
|
||||
/* check if iface is still active */
|
||||
if (!cifs_chan_is_iface_active(ses, server))
|
||||
cifs_chan_update_iface(ses, server);
|
||||
@ -246,6 +261,8 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
|
||||
spin_lock(&tcon->tc_lock);
|
||||
tcon->status = TID_NEED_RECON;
|
||||
spin_unlock(&tcon->tc_lock);
|
||||
|
||||
cancel_delayed_work(&tcon->query_interfaces);
|
||||
}
|
||||
if (ses->tcon_ipc) {
|
||||
ses->tcon_ipc->need_reconnect = true;
|
||||
@ -385,7 +402,13 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
|
||||
spin_unlock(&server->srv_lock);
|
||||
cifs_swn_reset_server_dstaddr(server);
|
||||
cifs_server_unlock(server);
|
||||
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
|
||||
|
||||
/* increase ref count which reconnect work will drop */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->srv_count++;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
if (mod_delayed_work(cifsiod_wq, &server->reconnect, 0))
|
||||
cifs_put_tcp_session(server, false);
|
||||
}
|
||||
} while (server->tcpStatus == CifsNeedReconnect);
|
||||
|
||||
@ -515,7 +538,13 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
|
||||
spin_unlock(&server->srv_lock);
|
||||
cifs_swn_reset_server_dstaddr(server);
|
||||
cifs_server_unlock(server);
|
||||
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
|
||||
|
||||
/* increase ref count which reconnect work will drop */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->srv_count++;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
if (mod_delayed_work(cifsiod_wq, &server->reconnect, 0))
|
||||
cifs_put_tcp_session(server, false);
|
||||
} while (server->tcpStatus == CifsNeedReconnect);
|
||||
|
||||
mutex_lock(&server->refpath_lock);
|
||||
@ -1597,16 +1626,19 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
|
||||
|
||||
cancel_delayed_work_sync(&server->echo);
|
||||
|
||||
if (from_reconnect)
|
||||
if (from_reconnect) {
|
||||
/*
|
||||
* Avoid deadlock here: reconnect work calls
|
||||
* cifs_put_tcp_session() at its end. Need to be sure
|
||||
* that reconnect work does nothing with server pointer after
|
||||
* that step.
|
||||
*/
|
||||
cancel_delayed_work(&server->reconnect);
|
||||
else
|
||||
cancel_delayed_work_sync(&server->reconnect);
|
||||
if (cancel_delayed_work(&server->reconnect))
|
||||
cifs_put_tcp_session(server, from_reconnect);
|
||||
} else {
|
||||
if (cancel_delayed_work_sync(&server->reconnect))
|
||||
cifs_put_tcp_session(server, from_reconnect);
|
||||
}
|
||||
|
||||
spin_lock(&server->srv_lock);
|
||||
server->tcpStatus = CifsExiting;
|
||||
@ -3560,7 +3592,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
||||
ctx->prepath = NULL;
|
||||
|
||||
out:
|
||||
cifs_try_adding_channels(cifs_sb, mnt_ctx.ses);
|
||||
cifs_try_adding_channels(mnt_ctx.ses);
|
||||
rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
@ -263,15 +263,23 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Resolve UNC hostname in @ctx->source and set ip addr in @ctx->dstaddr */
|
||||
/*
|
||||
* If @ctx->dfs_automount, then update @ctx->dstaddr earlier with the DFS root
|
||||
* server from where we'll start following any referrals. Otherwise rely on the
|
||||
* value provided by mount(2) as the user might not have dns_resolver key set up
|
||||
* and therefore failing to upcall to resolve UNC hostname under @ctx->source.
|
||||
*/
|
||||
static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
|
||||
{
|
||||
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
|
||||
if (!rc)
|
||||
cifs_set_port(addr, ctx->port);
|
||||
if (!ctx->nodfs && ctx->dfs_automount) {
|
||||
rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
|
||||
if (!rc)
|
||||
cifs_set_port(addr, ctx->port);
|
||||
ctx->dfs_automount = false;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -268,6 +268,7 @@ struct smb3_fs_context {
|
||||
bool witness:1; /* use witness protocol */
|
||||
char *leaf_fullpath;
|
||||
struct cifs_ses *dfs_root_ses;
|
||||
bool dfs_automount:1; /* set for dfs automount only */
|
||||
};
|
||||
|
||||
extern const struct fs_parameter_spec smb3_fs_parameters[];
|
||||
|
@ -117,6 +117,20 @@ out_drop_write:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long smb_mnt_get_tcon_info(struct cifs_tcon *tcon, void __user *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct smb_mnt_tcon_info tcon_inf;
|
||||
|
||||
tcon_inf.tid = tcon->tid;
|
||||
tcon_inf.session_id = tcon->ses->Suid;
|
||||
|
||||
if (copy_to_user(arg, &tcon_inf, sizeof(struct smb_mnt_tcon_info)))
|
||||
rc = -EFAULT;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
|
||||
void __user *arg)
|
||||
{
|
||||
@ -129,6 +143,7 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
fsinf->version = 1;
|
||||
fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
|
||||
fsinf->tcon_flags = tcon->Flags;
|
||||
fsinf->device_characteristics =
|
||||
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
|
||||
fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
|
||||
@ -414,6 +429,17 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
tcon = tlink_tcon(pSMBFile->tlink);
|
||||
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
|
||||
break;
|
||||
case CIFS_IOC_GET_TCON_INFO:
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink)) {
|
||||
rc = PTR_ERR(tlink);
|
||||
break;
|
||||
}
|
||||
tcon = tlink_tcon(tlink);
|
||||
rc = smb_mnt_get_tcon_info(tcon, (void __user *)arg);
|
||||
cifs_put_tlink(tlink);
|
||||
break;
|
||||
case CIFS_ENUMERATE_SNAPSHOTS:
|
||||
if (pSMBFile == NULL)
|
||||
break;
|
||||
|
@ -117,6 +117,18 @@ cifs_build_devname(char *nodename, const char *prepath)
|
||||
return dev;
|
||||
}
|
||||
|
||||
static bool is_dfs_mount(struct dentry *dentry)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
|
||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
bool ret;
|
||||
|
||||
spin_lock(&tcon->tc_lock);
|
||||
ret = !!tcon->origin_fullpath;
|
||||
spin_unlock(&tcon->tc_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return full path out of a dentry set for automount */
|
||||
static char *automount_fullpath(struct dentry *dentry, void *page)
|
||||
{
|
||||
@ -212,8 +224,9 @@ static struct vfsmount *cifs_do_automount(struct path *path)
|
||||
ctx->source = NULL;
|
||||
goto out;
|
||||
}
|
||||
cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s\n",
|
||||
__func__, ctx->source, ctx->UNC, ctx->prepath);
|
||||
ctx->dfs_automount = is_dfs_mount(mntpt);
|
||||
cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n",
|
||||
__func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_automount);
|
||||
|
||||
mnt = fc_mount(fc);
|
||||
out:
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "fs_context.h"
|
||||
|
||||
static int
|
||||
cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
cifs_ses_add_channel(struct cifs_ses *ses,
|
||||
struct cifs_server_iface *iface);
|
||||
|
||||
bool
|
||||
@ -69,7 +69,7 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
|
||||
/* channel helper functions. assumed that chan_lock is held by caller. */
|
||||
|
||||
unsigned int
|
||||
int
|
||||
cifs_ses_get_chan_index(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
@ -85,14 +85,17 @@ cifs_ses_get_chan_index(struct cifs_ses *ses,
|
||||
cifs_dbg(VFS, "unable to get chan index for server: 0x%llx",
|
||||
server->conn_id);
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
return CIFS_INVAL_CHAN_INDEX;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_chan_set_in_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX)
|
||||
return;
|
||||
|
||||
ses->chans[chan_index].in_reconnect = true;
|
||||
}
|
||||
@ -103,6 +106,9 @@ cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX)
|
||||
return;
|
||||
|
||||
ses->chans[chan_index].in_reconnect = false;
|
||||
}
|
||||
|
||||
@ -112,6 +118,9 @@ cifs_chan_in_reconnect(struct cifs_ses *ses,
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX)
|
||||
return true; /* err on the safer side */
|
||||
|
||||
return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
|
||||
}
|
||||
|
||||
@ -121,6 +130,9 @@ cifs_chan_set_need_reconnect(struct cifs_ses *ses,
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX)
|
||||
return;
|
||||
|
||||
set_bit(chan_index, &ses->chans_need_reconnect);
|
||||
cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
|
||||
chan_index, ses->chans_need_reconnect);
|
||||
@ -132,6 +144,9 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX)
|
||||
return;
|
||||
|
||||
clear_bit(chan_index, &ses->chans_need_reconnect);
|
||||
cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
|
||||
chan_index, ses->chans_need_reconnect);
|
||||
@ -143,6 +158,9 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX)
|
||||
return true; /* err on the safer side */
|
||||
|
||||
return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
|
||||
}
|
||||
|
||||
@ -152,19 +170,24 @@ cifs_chan_is_iface_active(struct cifs_ses *ses,
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX)
|
||||
return true; /* err on the safer side */
|
||||
|
||||
return ses->chans[chan_index].iface &&
|
||||
ses->chans[chan_index].iface->is_active;
|
||||
}
|
||||
|
||||
/* returns number of channels added */
|
||||
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
int cifs_try_adding_channels(struct cifs_ses *ses)
|
||||
{
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
int old_chan_count, new_chan_count;
|
||||
int left;
|
||||
int rc = 0;
|
||||
int tries = 0;
|
||||
size_t iface_weight = 0, iface_min_speed = 0;
|
||||
struct cifs_server_iface *iface = NULL, *niface = NULL;
|
||||
struct cifs_server_iface *last_iface = NULL;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
|
||||
@ -192,21 +215,11 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/*
|
||||
* Keep connecting to same, fastest, iface for all channels as
|
||||
* long as its RSS. Try next fastest one if not RSS or channel
|
||||
* creation fails.
|
||||
*/
|
||||
spin_lock(&ses->iface_lock);
|
||||
iface = list_first_entry(&ses->iface_list, struct cifs_server_iface,
|
||||
iface_head);
|
||||
spin_unlock(&ses->iface_lock);
|
||||
|
||||
while (left > 0) {
|
||||
|
||||
tries++;
|
||||
if (tries > 3*ses->chan_max) {
|
||||
cifs_dbg(FYI, "too many channel open attempts (%d channels left to open)\n",
|
||||
cifs_dbg(VFS, "too many channel open attempts (%d channels left to open)\n",
|
||||
left);
|
||||
break;
|
||||
}
|
||||
@ -214,23 +227,41 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
spin_lock(&ses->iface_lock);
|
||||
if (!ses->iface_count) {
|
||||
spin_unlock(&ses->iface_lock);
|
||||
cifs_dbg(VFS, "server %s does not advertise interfaces\n",
|
||||
ses->server->hostname);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!iface)
|
||||
iface = list_first_entry(&ses->iface_list, struct cifs_server_iface,
|
||||
iface_head);
|
||||
last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
|
||||
iface_head);
|
||||
iface_min_speed = last_iface->speed;
|
||||
|
||||
list_for_each_entry_safe_from(iface, niface, &ses->iface_list,
|
||||
iface_head) {
|
||||
/* do not mix rdma and non-rdma interfaces */
|
||||
if (iface->rdma_capable != ses->server->rdma)
|
||||
continue;
|
||||
|
||||
/* skip ifaces that are unusable */
|
||||
if (!iface->is_active ||
|
||||
(is_ses_using_iface(ses, iface) &&
|
||||
!iface->rss_capable)) {
|
||||
!iface->rss_capable))
|
||||
continue;
|
||||
|
||||
/* check if we already allocated enough channels */
|
||||
iface_weight = iface->speed / iface_min_speed;
|
||||
|
||||
if (iface->weight_fulfilled >= iface_weight)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* take ref before unlock */
|
||||
kref_get(&iface->refcount);
|
||||
|
||||
spin_unlock(&ses->iface_lock);
|
||||
rc = cifs_ses_add_channel(cifs_sb, ses, iface);
|
||||
rc = cifs_ses_add_channel(ses, iface);
|
||||
spin_lock(&ses->iface_lock);
|
||||
|
||||
if (rc) {
|
||||
@ -241,10 +272,21 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
continue;
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "successfully opened new channel on iface:%pIS\n",
|
||||
iface->num_channels++;
|
||||
iface->weight_fulfilled++;
|
||||
cifs_dbg(VFS, "successfully opened new channel on iface:%pIS\n",
|
||||
&iface->sockaddr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* reached end of list. reset weight_fulfilled and start over */
|
||||
if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
|
||||
list_for_each_entry(iface, &ses->iface_list, iface_head)
|
||||
iface->weight_fulfilled = 0;
|
||||
spin_unlock(&ses->iface_lock);
|
||||
iface = NULL;
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&ses->iface_lock);
|
||||
|
||||
left--;
|
||||
@ -254,6 +296,60 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
return new_chan_count - old_chan_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* called when multichannel is disabled by the server.
|
||||
* this always gets called from smb2_reconnect
|
||||
* and cannot get called in parallel threads.
|
||||
*/
|
||||
void
|
||||
cifs_disable_secondary_channels(struct cifs_ses *ses)
|
||||
{
|
||||
int i, chan_count;
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifs_server_iface *iface;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_count = ses->chan_count;
|
||||
if (chan_count == 1)
|
||||
goto done;
|
||||
|
||||
ses->chan_count = 1;
|
||||
|
||||
/* for all secondary channels reset the need reconnect bit */
|
||||
ses->chans_need_reconnect &= 1;
|
||||
|
||||
for (i = 1; i < chan_count; i++) {
|
||||
iface = ses->chans[i].iface;
|
||||
server = ses->chans[i].server;
|
||||
|
||||
if (iface) {
|
||||
spin_lock(&ses->iface_lock);
|
||||
kref_put(&iface->refcount, release_iface);
|
||||
ses->chans[i].iface = NULL;
|
||||
iface->num_channels--;
|
||||
if (iface->weight_fulfilled)
|
||||
iface->weight_fulfilled--;
|
||||
spin_unlock(&ses->iface_lock);
|
||||
}
|
||||
|
||||
spin_unlock(&ses->chan_lock);
|
||||
if (server && !server->terminate) {
|
||||
server->terminate = true;
|
||||
cifs_signal_cifsd_for_reconnect(server, false);
|
||||
}
|
||||
spin_lock(&ses->chan_lock);
|
||||
|
||||
if (server) {
|
||||
ses->chans[i].server = NULL;
|
||||
cifs_put_tcp_session(server, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
done:
|
||||
spin_unlock(&ses->chan_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* update the iface for the channel if necessary.
|
||||
* will return 0 when iface is updated, 1 if removed, 2 otherwise
|
||||
@ -263,13 +359,16 @@ int
|
||||
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index;
|
||||
size_t iface_weight = 0, iface_min_speed = 0;
|
||||
struct cifs_server_iface *iface = NULL;
|
||||
struct cifs_server_iface *old_iface = NULL;
|
||||
struct cifs_server_iface *last_iface = NULL;
|
||||
struct sockaddr_storage ss;
|
||||
int rc = 0;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (!chan_index) {
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
@ -283,14 +382,49 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
spin_lock(&server->srv_lock);
|
||||
ss = server->dstaddr;
|
||||
spin_unlock(&server->srv_lock);
|
||||
|
||||
spin_lock(&ses->iface_lock);
|
||||
if (!ses->iface_count) {
|
||||
spin_unlock(&ses->iface_lock);
|
||||
cifs_dbg(VFS, "server %s does not advertise interfaces\n", ses->server->hostname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
|
||||
iface_head);
|
||||
iface_min_speed = last_iface->speed;
|
||||
|
||||
/* then look for a new one */
|
||||
list_for_each_entry(iface, &ses->iface_list, iface_head) {
|
||||
if (!chan_index) {
|
||||
/* if we're trying to get the updated iface for primary channel */
|
||||
if (!cifs_match_ipaddr((struct sockaddr *) &ss,
|
||||
(struct sockaddr *) &iface->sockaddr))
|
||||
continue;
|
||||
|
||||
kref_get(&iface->refcount);
|
||||
break;
|
||||
}
|
||||
|
||||
/* do not mix rdma and non-rdma interfaces */
|
||||
if (iface->rdma_capable != server->rdma)
|
||||
continue;
|
||||
|
||||
if (!iface->is_active ||
|
||||
(is_ses_using_iface(ses, iface) &&
|
||||
!iface->rss_capable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check if we already allocated enough channels */
|
||||
iface_weight = iface->speed / iface_min_speed;
|
||||
|
||||
if (iface->weight_fulfilled >= iface_weight)
|
||||
continue;
|
||||
|
||||
kref_get(&iface->refcount);
|
||||
break;
|
||||
}
|
||||
@ -301,16 +435,41 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
cifs_dbg(FYI, "unable to find a suitable iface\n");
|
||||
}
|
||||
|
||||
if (!chan_index && !iface) {
|
||||
cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
|
||||
&ss);
|
||||
spin_unlock(&ses->iface_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* now drop the ref to the current iface */
|
||||
if (old_iface && iface) {
|
||||
cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n",
|
||||
&old_iface->sockaddr,
|
||||
&iface->sockaddr);
|
||||
|
||||
old_iface->num_channels--;
|
||||
if (old_iface->weight_fulfilled)
|
||||
old_iface->weight_fulfilled--;
|
||||
iface->num_channels++;
|
||||
iface->weight_fulfilled++;
|
||||
|
||||
kref_put(&old_iface->refcount, release_iface);
|
||||
} else if (old_iface) {
|
||||
cifs_dbg(FYI, "releasing ref to iface: %pIS\n",
|
||||
&old_iface->sockaddr);
|
||||
|
||||
old_iface->num_channels--;
|
||||
if (old_iface->weight_fulfilled)
|
||||
old_iface->weight_fulfilled--;
|
||||
|
||||
kref_put(&old_iface->refcount, release_iface);
|
||||
} else if (!chan_index) {
|
||||
/* special case: update interface for primary channel */
|
||||
cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
|
||||
&iface->sockaddr);
|
||||
iface->num_channels++;
|
||||
iface->weight_fulfilled++;
|
||||
} else {
|
||||
WARN_ON(!iface);
|
||||
cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
|
||||
@ -319,6 +478,11 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ses->chans[chan_index].iface = iface;
|
||||
|
||||
/* No iface is found. if secondary chan, drop connection */
|
||||
@ -354,7 +518,7 @@ cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
cifs_ses_add_channel(struct cifs_ses *ses,
|
||||
struct cifs_server_iface *iface)
|
||||
{
|
||||
struct TCP_Server_Info *chan_server;
|
||||
@ -433,7 +597,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
* This will be used for encoding/decoding user/domain/pw
|
||||
* during sess setup auth.
|
||||
*/
|
||||
ctx->local_nls = cifs_sb->local_nls;
|
||||
ctx->local_nls = ses->local_nls;
|
||||
|
||||
/* Use RDMA if possible */
|
||||
ctx->rdma = iface->rdma_capable;
|
||||
@ -479,20 +643,16 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
|
||||
rc = cifs_negotiate_protocol(xid, ses, chan->server);
|
||||
if (!rc)
|
||||
rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls);
|
||||
rc = cifs_setup_session(xid, ses, chan->server, ses->local_nls);
|
||||
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
out:
|
||||
if (rc && chan->server) {
|
||||
/*
|
||||
* we should avoid race with these delayed works before we
|
||||
* remove this channel
|
||||
*/
|
||||
cancel_delayed_work_sync(&chan->server->echo);
|
||||
cancel_delayed_work_sync(&chan->server->reconnect);
|
||||
cifs_put_tcp_session(chan->server, 0);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
|
||||
/* we rely on all bits beyond chan_count to be clear */
|
||||
cifs_chan_clear_need_reconnect(ses, chan->server);
|
||||
ses->chan_count--;
|
||||
@ -502,8 +662,6 @@ out:
|
||||
*/
|
||||
WARN_ON(ses->chan_count < 1);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
cifs_put_tcp_session(chan->server, 0);
|
||||
}
|
||||
|
||||
kfree(ctx->UNC);
|
||||
@ -535,8 +693,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
|
||||
|
||||
/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
|
||||
|
||||
/* BB verify whether signing required on neg or just on auth frame
|
||||
(and NTLM case) */
|
||||
/* BB verify whether signing required on neg or just auth frame (and NTLM case) */
|
||||
|
||||
capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
|
||||
CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
|
||||
@ -593,8 +750,10 @@ static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
|
||||
|
||||
/* copy domain */
|
||||
if (ses->domainName == NULL) {
|
||||
/* Sending null domain better than using a bogus domain name (as
|
||||
we did briefly in 2.6.18) since server will use its default */
|
||||
/*
|
||||
* Sending null domain better than using a bogus domain name (as
|
||||
* we did briefly in 2.6.18) since server will use its default
|
||||
*/
|
||||
*bcc_ptr = 0;
|
||||
*(bcc_ptr+1) = 0;
|
||||
bytes_ret = 0;
|
||||
@ -613,8 +772,7 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
|
||||
char *bcc_ptr = *pbcc_area;
|
||||
int bytes_ret = 0;
|
||||
|
||||
/* BB FIXME add check that strings total less
|
||||
than 335 or will need to send them as arrays */
|
||||
/* BB FIXME add check that strings less than 335 or will need to send as arrays */
|
||||
|
||||
/* copy user */
|
||||
if (ses->user_name == NULL) {
|
||||
@ -659,8 +817,7 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
|
||||
if (WARN_ON_ONCE(len < 0))
|
||||
len = CIFS_MAX_DOMAINNAME_LEN - 1;
|
||||
bcc_ptr += len;
|
||||
} /* else we will send a null domain name
|
||||
so the server will default to its own domain */
|
||||
} /* else we send a null domain name so server will default to its own domain */
|
||||
*bcc_ptr = 0;
|
||||
bcc_ptr++;
|
||||
|
||||
@ -756,11 +913,14 @@ static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
|
||||
if (len > bleft)
|
||||
return;
|
||||
|
||||
/* No domain field in LANMAN case. Domain is
|
||||
returned by old servers in the SMB negprot response */
|
||||
/* BB For newer servers which do not support Unicode,
|
||||
but thus do return domain here we could add parsing
|
||||
for it later, but it is not very important */
|
||||
/*
|
||||
* No domain field in LANMAN case. Domain is
|
||||
* returned by old servers in the SMB negprot response
|
||||
*
|
||||
* BB For newer servers which do not support Unicode,
|
||||
* but thus do return domain here, we could add parsing
|
||||
* for it later, but it is not very important
|
||||
*/
|
||||
cifs_dbg(FYI, "ascii: bytes left %d\n", bleft);
|
||||
}
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
@ -816,9 +976,12 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
||||
ses->ntlmssp->server_flags = server_flags;
|
||||
|
||||
memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE);
|
||||
/* In particular we can examine sign flags */
|
||||
/* BB spec says that if AvId field of MsvAvTimestamp is populated then
|
||||
we must set the MIC field of the AUTHENTICATE_MESSAGE */
|
||||
/*
|
||||
* In particular we can examine sign flags
|
||||
*
|
||||
* BB spec says that if AvId field of MsvAvTimestamp is populated then
|
||||
* we must set the MIC field of the AUTHENTICATE_MESSAGE
|
||||
*/
|
||||
|
||||
tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset);
|
||||
tilen = le16_to_cpu(pblob->TargetInfoArray.Length);
|
||||
|
@ -756,6 +756,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
|
||||
unsigned int ret_data_len = 0;
|
||||
struct network_interface_info_ioctl_rsp *out_buf = NULL;
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
struct TCP_Server_Info *pserver;
|
||||
|
||||
/* do not query too frequently */
|
||||
if (ses->iface_last_update &&
|
||||
@ -780,6 +781,11 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* check if iface is still active */
|
||||
pserver = ses->chans[0].server;
|
||||
if (pserver && !cifs_chan_is_iface_active(ses, pserver))
|
||||
cifs_chan_update_iface(ses, pserver);
|
||||
|
||||
out:
|
||||
kfree(out_buf);
|
||||
return rc;
|
||||
|
@ -163,6 +163,9 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
int rc = 0;
|
||||
struct nls_table *nls_codepage = NULL;
|
||||
struct cifs_ses *ses;
|
||||
int xid;
|
||||
struct TCP_Server_Info *pserver;
|
||||
unsigned int chan_index;
|
||||
|
||||
/*
|
||||
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
|
||||
@ -223,6 +226,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
/* if server is marked for termination, cifsd will cleanup */
|
||||
if (server->terminate) {
|
||||
spin_unlock(&server->srv_lock);
|
||||
return -EHOSTDOWN;
|
||||
}
|
||||
spin_unlock(&server->srv_lock);
|
||||
|
||||
again:
|
||||
@ -241,12 +250,24 @@ again:
|
||||
tcon->need_reconnect);
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
/*
|
||||
* if this is called by delayed work, and the channel has been disabled
|
||||
* in parallel, the delayed work can continue to execute in parallel
|
||||
* there's a chance that this channel may not exist anymore
|
||||
*/
|
||||
spin_lock(&server->srv_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&server->srv_lock);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
rc = -EHOSTDOWN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recheck after acquire mutex. If another thread is negotiating
|
||||
* and the server never sends an answer the socket will be closed
|
||||
* and tcpStatus set to reconnect.
|
||||
*/
|
||||
spin_lock(&server->srv_lock);
|
||||
if (server->tcpStatus == CifsNeedReconnect) {
|
||||
spin_unlock(&server->srv_lock);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
@ -283,6 +304,53 @@ again:
|
||||
|
||||
rc = cifs_negotiate_protocol(0, ses, server);
|
||||
if (!rc) {
|
||||
/*
|
||||
* if server stopped supporting multichannel
|
||||
* and the first channel reconnected, disable all the others.
|
||||
*/
|
||||
if (ses->chan_count > 1 &&
|
||||
!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
|
||||
if (SERVER_IS_CHAN(server)) {
|
||||
cifs_dbg(VFS, "server %s does not support " \
|
||||
"multichannel anymore. skipping secondary channel\n",
|
||||
ses->server->hostname);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
goto skip_terminate;
|
||||
}
|
||||
|
||||
ses->chans[chan_index].server = NULL;
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/*
|
||||
* the above reference of server by channel
|
||||
* needs to be dropped without holding chan_lock
|
||||
* as cifs_put_tcp_session takes a higher lock
|
||||
* i.e. cifs_tcp_ses_lock
|
||||
*/
|
||||
cifs_put_tcp_session(server, 1);
|
||||
|
||||
server->terminate = true;
|
||||
cifs_signal_cifsd_for_reconnect(server, false);
|
||||
|
||||
/* mark primary server as needing reconnect */
|
||||
pserver = server->primary_server;
|
||||
cifs_signal_cifsd_for_reconnect(pserver, false);
|
||||
|
||||
skip_terminate:
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
rc = -EHOSTDOWN;
|
||||
goto out;
|
||||
} else {
|
||||
cifs_server_dbg(VFS, "does not support " \
|
||||
"multichannel anymore. disabling all other channels\n");
|
||||
cifs_disable_secondary_channels(ses);
|
||||
}
|
||||
}
|
||||
|
||||
rc = cifs_setup_session(0, ses, server, nls_codepage);
|
||||
if ((rc == -EACCES) && !tcon->retry) {
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
@ -307,17 +375,44 @@ skip_sess_setup:
|
||||
tcon->need_reopen_files = true;
|
||||
|
||||
rc = cifs_tree_connect(0, tcon, nls_codepage);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
|
||||
if (rc) {
|
||||
/* If sess reconnected but tcon didn't, something strange ... */
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!rc &&
|
||||
(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
/*
|
||||
* query server network interfaces, in case they change
|
||||
*/
|
||||
xid = get_xid();
|
||||
rc = SMB3_request_interfaces(xid, tcon, false);
|
||||
free_xid(xid);
|
||||
|
||||
if (rc)
|
||||
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
|
||||
__func__, rc);
|
||||
|
||||
if (ses->chan_max > ses->chan_count &&
|
||||
!SERVER_IS_CHAN(server)) {
|
||||
if (ses->chan_count == 1)
|
||||
cifs_server_dbg(VFS, "supports multichannel now\n");
|
||||
|
||||
cifs_try_adding_channels(ses);
|
||||
}
|
||||
} else {
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
}
|
||||
|
||||
if (smb2_command != SMB2_INTERNAL_CMD)
|
||||
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
|
||||
if (mod_delayed_work(cifsiod_wq, &server->reconnect, 0))
|
||||
cifs_put_tcp_session(server, false);
|
||||
|
||||
atomic_inc(&tconInfoReconnectCount);
|
||||
out:
|
||||
@ -3808,6 +3903,13 @@ void smb2_reconnect_server(struct work_struct *work)
|
||||
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
|
||||
mutex_lock(&pserver->reconnect_mutex);
|
||||
|
||||
/* if the server is marked for termination, drop the ref count here */
|
||||
if (server->terminate) {
|
||||
cifs_put_tcp_session(server, true);
|
||||
mutex_unlock(&pserver->reconnect_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&tmp_list);
|
||||
INIT_LIST_HEAD(&tmp_ses_list);
|
||||
cifs_dbg(FYI, "Reconnecting tcons and channels\n");
|
||||
@ -3852,12 +3954,6 @@ void smb2_reconnect_server(struct work_struct *work)
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
}
|
||||
/*
|
||||
* Get the reference to server struct to be sure that the last call of
|
||||
* cifs_put_tcon() in the loop below won't release the server pointer.
|
||||
*/
|
||||
if (tcon_exist || ses_exist)
|
||||
server->srv_count++;
|
||||
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
@ -3905,13 +4001,17 @@ void smb2_reconnect_server(struct work_struct *work)
|
||||
|
||||
done:
|
||||
cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
|
||||
if (resched)
|
||||
if (resched) {
|
||||
queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
|
||||
mutex_unlock(&pserver->reconnect_mutex);
|
||||
|
||||
/* no need to put tcp session as we're retrying */
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&pserver->reconnect_mutex);
|
||||
|
||||
/* now we can safely release srv struct */
|
||||
if (tcon_exist || ses_exist)
|
||||
cifs_put_tcp_session(server, 1);
|
||||
cifs_put_tcp_session(server, true);
|
||||
}
|
||||
|
||||
int
|
||||
@ -3931,7 +4031,12 @@ SMB2_echo(struct TCP_Server_Info *server)
|
||||
server->ops->need_neg(server)) {
|
||||
spin_unlock(&server->srv_lock);
|
||||
/* No need to send echo on newly established connections */
|
||||
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->srv_count++;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
if (mod_delayed_work(cifsiod_wq, &server->reconnect, 0))
|
||||
cifs_put_tcp_session(server, false);
|
||||
|
||||
return rc;
|
||||
}
|
||||
spin_unlock(&server->srv_lock);
|
||||
|
@ -413,7 +413,13 @@ generate_smb3signingkey(struct cifs_ses *ses,
|
||||
ses->ses_status == SES_GOOD);
|
||||
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
/* TODO: introduce ref counting for channels when the can be freed */
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
spin_unlock(&ses->ses_lock);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock(&ses->chan_lock);
|
||||
spin_unlock(&ses->ses_lock);
|
||||
|
||||
|
@ -1023,7 +1023,7 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
|
||||
spin_lock(&ses->chan_lock);
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
server = ses->chans[i].server;
|
||||
if (!server)
|
||||
if (!server || server->terminate)
|
||||
continue;
|
||||
|
||||
/*
|
||||
|
@ -150,10 +150,13 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
|
||||
goto out;
|
||||
|
||||
if (pTcon->ses->server->ops->set_EA)
|
||||
if (pTcon->ses->server->ops->set_EA) {
|
||||
rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
|
||||
full_path, name, value, (__u16)size,
|
||||
cifs_sb->local_nls, cifs_sb);
|
||||
if (rc == 0)
|
||||
inode_set_ctime_current(inode);
|
||||
}
|
||||
break;
|
||||
|
||||
case XATTR_CIFS_ACL:
|
||||
|
Loading…
Reference in New Issue
Block a user