Commit 1b907d05 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 3ca112b7 fd2bd7c0
...@@ -136,6 +136,11 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) ...@@ -136,6 +136,11 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
{ {
struct TCP_Server_Info *server = chan->server; 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" seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
"\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x" "\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
"\n\t\tTCP status: %d Instance: %d" "\n\t\tTCP status: %d Instance: %d"
...@@ -279,6 +284,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -279,6 +284,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct cifs_server_iface *iface; 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; int c, i, j;
seq_puts(m, seq_puts(m,
...@@ -544,11 +551,25 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -544,11 +551,25 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
"\tLast updated: %lu seconds ago", "\tLast updated: %lu seconds ago",
ses->iface_count, ses->iface_count,
(jiffies - ses->iface_last_update) / HZ); (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; j = 0;
list_for_each_entry(iface, &ses->iface_list, list_for_each_entry(iface, &ses->iface_list,
iface_head) { iface_head) {
seq_printf(m, "\n\t%d)", ++j); seq_printf(m, "\n\t%d)", ++j);
cifs_dump_iface(m, iface); 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)) if (is_ses_using_iface(ses, iface))
seq_puts(m, "\t\t[CONNECTED]\n"); seq_puts(m, "\t\t[CONNECTED]\n");
} }
...@@ -746,14 +767,14 @@ static ssize_t name##_write(struct file *file, const char __user *buffer, \ ...@@ -746,14 +767,14 @@ static ssize_t name##_write(struct file *file, const char __user *buffer, \
size_t count, loff_t *ppos) \ size_t count, loff_t *ppos) \
{ \ { \
int rc; \ int rc; \
rc = kstrtoint_from_user(buffer, count, 10, & name); \ rc = kstrtoint_from_user(buffer, count, 10, &name); \
if (rc) \ if (rc) \
return rc; \ return rc; \
return count; \ return count; \
} \ } \
static int name##_proc_show(struct seq_file *m, void *v) \ 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; \ return 0; \
} \ } \
static int name##_open(struct inode *inode, struct file *file) \ static int name##_open(struct inode *inode, struct file *file) \
......
...@@ -26,6 +26,11 @@ struct smb_mnt_fs_info { ...@@ -26,6 +26,11 @@ struct smb_mnt_fs_info {
__u64 cifs_posix_caps; __u64 cifs_posix_caps;
} __packed; } __packed;
struct smb_mnt_tcon_info {
__u32 tid;
__u64 session_id;
} __packed;
struct smb_snapshot_array { struct smb_snapshot_array {
__u32 number_of_snapshots; __u32 number_of_snapshots;
__u32 number_of_snapshots_returned; __u32 number_of_snapshots_returned;
...@@ -108,6 +113,7 @@ struct smb3_notify_info { ...@@ -108,6 +113,7 @@ struct smb3_notify_info {
#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify) #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_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_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) #define CIFS_IOC_SHUTDOWN _IOR('X', 125, __u32)
/* /*
......
...@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops; ...@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */ #endif /* CONFIG_CIFS_NFSD_EXPORT */
/* when changing internal version - update following two lines at same time */ /* when changing internal version - update following two lines at same time */
#define SMB3_PRODUCT_BUILD 45 #define SMB3_PRODUCT_BUILD 46
#define CIFS_VERSION "2.45" #define CIFS_VERSION "2.46"
#endif /* _CIFSFS_H */ #endif /* _CIFSFS_H */
...@@ -650,6 +650,7 @@ struct TCP_Server_Info { ...@@ -650,6 +650,7 @@ struct TCP_Server_Info {
bool noautotune; /* do not autotune send buf sizes */ bool noautotune; /* do not autotune send buf sizes */
bool nosharesock; bool nosharesock;
bool tcp_nodelay; bool tcp_nodelay;
bool terminate;
unsigned int credits; /* send no more requests at once */ unsigned int credits; /* send no more requests at once */
unsigned int max_credits; /* can override large 32000 default at mnt */ unsigned int max_credits; /* can override large 32000 default at mnt */
unsigned int in_flight; /* number of requests on the wire to server */ unsigned int in_flight; /* number of requests on the wire to server */
...@@ -969,6 +970,8 @@ struct cifs_server_iface { ...@@ -969,6 +970,8 @@ struct cifs_server_iface {
struct list_head iface_head; struct list_head iface_head;
struct kref refcount; struct kref refcount;
size_t speed; size_t speed;
size_t weight_fulfilled;
unsigned int num_channels;
unsigned int rdma_capable : 1; unsigned int rdma_capable : 1;
unsigned int rss_capable : 1; unsigned int rss_capable : 1;
unsigned int is_active : 1; /* unset if non existent */ unsigned int is_active : 1; /* unset if non existent */
...@@ -1050,6 +1053,7 @@ struct cifs_ses { ...@@ -1050,6 +1053,7 @@ struct cifs_ses {
spinlock_t chan_lock; spinlock_t chan_lock;
/* ========= begin: protected by chan_lock ======== */ /* ========= begin: protected by chan_lock ======== */
#define CIFS_MAX_CHANNELS 16 #define CIFS_MAX_CHANNELS 16
#define CIFS_INVAL_CHAN_INDEX (-1)
#define CIFS_ALL_CHANNELS_SET(ses) \ #define CIFS_ALL_CHANNELS_SET(ses) \
((1UL << (ses)->chan_count) - 1) ((1UL << (ses)->chan_count) - 1)
#define CIFS_ALL_CHANS_GOOD(ses) \ #define CIFS_ALL_CHANS_GOOD(ses) \
...@@ -2143,6 +2147,7 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst, ...@@ -2143,6 +2147,7 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
unsigned int len, skip; unsigned int len, skip;
unsigned int nents = 0; unsigned int nents = 0;
unsigned long addr; unsigned long addr;
size_t data_size;
int i, j; int i, j;
/* /*
...@@ -2158,17 +2163,21 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst, ...@@ -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 * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
*/ */
for (i = 0; i < num_rqst; i++) { 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 /* 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. * in the sglist. It's hard to keep track of which is what.
* Instead, we convert to a BVEC-type iterator higher up. * 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; return -EIO;
/* We also don't want to have any extra refs or pins to clean /* We also don't want to have any extra refs or pins to clean
* up in the sglist. * 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; return -EIO;
for (j = 0; j < rqst[i].rq_nvec; j++) { for (j = 0; j < rqst[i].rq_nvec; j++) {
...@@ -2184,6 +2193,7 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst, ...@@ -2184,6 +2193,7 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
} }
skip = 0; skip = 0;
} }
if (data_size)
nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX); nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
} }
nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE); nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
......
...@@ -132,6 +132,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid, ...@@ -132,6 +132,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
struct smb_hdr *in_buf, struct smb_hdr *in_buf,
struct smb_hdr *out_buf, struct smb_hdr *out_buf,
int *bytes_returned); int *bytes_returned);
void void
cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
bool all_channels); bool all_channels);
...@@ -610,13 +611,13 @@ void cifs_free_hash(struct shash_desc **sdesc); ...@@ -610,13 +611,13 @@ void cifs_free_hash(struct shash_desc **sdesc);
struct cifs_chan * struct cifs_chan *
cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server); 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, bool is_server_using_iface(struct TCP_Server_Info *server,
struct cifs_server_iface *iface); struct cifs_server_iface *iface);
bool is_ses_using_iface(struct cifs_ses *ses, 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); void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
unsigned int int
cifs_ses_get_chan_index(struct cifs_ses *ses, cifs_ses_get_chan_index(struct cifs_ses *ses,
struct TCP_Server_Info *server); struct TCP_Server_Info *server);
void void
...@@ -640,6 +641,8 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses, ...@@ -640,6 +641,8 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
bool bool
cifs_chan_is_iface_active(struct cifs_ses *ses, cifs_chan_is_iface_active(struct cifs_ses *ses,
struct TCP_Server_Info *server); struct TCP_Server_Info *server);
void
cifs_disable_secondary_channels(struct cifs_ses *ses);
int int
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server); cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);
int int
......
...@@ -132,6 +132,9 @@ static void smb2_query_server_interfaces(struct work_struct *work) ...@@ -132,6 +132,9 @@ static void smb2_query_server_interfaces(struct work_struct *work)
free_xid(xid); free_xid(xid);
if (rc) { if (rc) {
if (rc == -EOPNOTSUPP)
return;
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
__func__, rc); __func__, rc);
} }
...@@ -173,7 +176,11 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, ...@@ -173,7 +176,11 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
for (i = 0; i < ses->chan_count; i++) { for (i = 0; i < ses->chan_count; i++) {
if (!ses->chans[i].server)
continue;
spin_lock(&ses->chans[i].server->srv_lock); spin_lock(&ses->chans[i].server->srv_lock);
if (ses->chans[i].server->tcpStatus != CifsExiting)
ses->chans[i].server->tcpStatus = CifsNeedReconnect; ses->chans[i].server->tcpStatus = CifsNeedReconnect;
spin_unlock(&ses->chans[i].server->srv_lock); spin_unlock(&ses->chans[i].server->srv_lock);
} }
...@@ -212,6 +219,14 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, ...@@ -212,6 +219,14 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { 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 */ /* check if iface is still active */
if (!cifs_chan_is_iface_active(ses, server)) if (!cifs_chan_is_iface_active(ses, server))
cifs_chan_update_iface(ses, server); cifs_chan_update_iface(ses, server);
...@@ -246,6 +261,8 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, ...@@ -246,6 +261,8 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
spin_lock(&tcon->tc_lock); spin_lock(&tcon->tc_lock);
tcon->status = TID_NEED_RECON; tcon->status = TID_NEED_RECON;
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
cancel_delayed_work(&tcon->query_interfaces);
} }
if (ses->tcon_ipc) { if (ses->tcon_ipc) {
ses->tcon_ipc->need_reconnect = true; ses->tcon_ipc->need_reconnect = true;
...@@ -385,7 +402,13 @@ static int __cifs_reconnect(struct TCP_Server_Info *server, ...@@ -385,7 +402,13 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
spin_unlock(&server->srv_lock); spin_unlock(&server->srv_lock);
cifs_swn_reset_server_dstaddr(server); cifs_swn_reset_server_dstaddr(server);
cifs_server_unlock(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); } while (server->tcpStatus == CifsNeedReconnect);
...@@ -515,7 +538,13 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server) ...@@ -515,7 +538,13 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
spin_unlock(&server->srv_lock); spin_unlock(&server->srv_lock);
cifs_swn_reset_server_dstaddr(server); cifs_swn_reset_server_dstaddr(server);
cifs_server_unlock(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); } while (server->tcpStatus == CifsNeedReconnect);
mutex_lock(&server->refpath_lock); mutex_lock(&server->refpath_lock);
...@@ -1597,16 +1626,19 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) ...@@ -1597,16 +1626,19 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
cancel_delayed_work_sync(&server->echo); cancel_delayed_work_sync(&server->echo);
if (from_reconnect) if (from_reconnect) {
/* /*
* Avoid deadlock here: reconnect work calls * Avoid deadlock here: reconnect work calls
* cifs_put_tcp_session() at its end. Need to be sure * cifs_put_tcp_session() at its end. Need to be sure
* that reconnect work does nothing with server pointer after * that reconnect work does nothing with server pointer after
* that step. * that step.
*/ */
cancel_delayed_work(&server->reconnect); if (cancel_delayed_work(&server->reconnect))
else cifs_put_tcp_session(server, from_reconnect);
cancel_delayed_work_sync(&server->reconnect); } else {
if (cancel_delayed_work_sync(&server->reconnect))
cifs_put_tcp_session(server, from_reconnect);
}
spin_lock(&server->srv_lock); spin_lock(&server->srv_lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
...@@ -3560,7 +3592,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) ...@@ -3560,7 +3592,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
ctx->prepath = NULL; ctx->prepath = NULL;
out: 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); rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
if (rc) if (rc)
goto error; goto error;
......
...@@ -263,15 +263,23 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) ...@@ -263,15 +263,23 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
return rc; 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) static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
{ {
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
int rc; int rc = 0;
if (!ctx->nodfs && ctx->dfs_automount) {
rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL); rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
if (!rc) if (!rc)
cifs_set_port(addr, ctx->port); cifs_set_port(addr, ctx->port);
ctx->dfs_automount = false;
}
return rc; return rc;
} }
......
...@@ -268,6 +268,7 @@ struct smb3_fs_context { ...@@ -268,6 +268,7 @@ struct smb3_fs_context {
bool witness:1; /* use witness protocol */ bool witness:1; /* use witness protocol */
char *leaf_fullpath; char *leaf_fullpath;
struct cifs_ses *dfs_root_ses; struct cifs_ses *dfs_root_ses;
bool dfs_automount:1; /* set for dfs automount only */
}; };
extern const struct fs_parameter_spec smb3_fs_parameters[]; extern const struct fs_parameter_spec smb3_fs_parameters[];
......
...@@ -117,6 +117,20 @@ static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, ...@@ -117,6 +117,20 @@ static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
return rc; 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, static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
void __user *arg) void __user *arg)
{ {
...@@ -129,6 +143,7 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, ...@@ -129,6 +143,7 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
fsinf->version = 1; fsinf->version = 1;
fsinf->protocol_id = tcon->ses->server->vals->protocol_id; fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
fsinf->tcon_flags = tcon->Flags;
fsinf->device_characteristics = fsinf->device_characteristics =
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics); le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); 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) ...@@ -414,6 +429,17 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
tcon = tlink_tcon(pSMBFile->tlink); tcon = tlink_tcon(pSMBFile->tlink);
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
break; 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: case CIFS_ENUMERATE_SNAPSHOTS:
if (pSMBFile == NULL) if (pSMBFile == NULL)
break; break;
......
...@@ -117,6 +117,18 @@ cifs_build_devname(char *nodename, const char *prepath) ...@@ -117,6 +117,18 @@ cifs_build_devname(char *nodename, const char *prepath)
return dev; 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 */ /* Return full path out of a dentry set for automount */
static char *automount_fullpath(struct dentry *dentry, void *page) static char *automount_fullpath(struct dentry *dentry, void *page)
{ {
...@@ -212,8 +224,9 @@ static struct vfsmount *cifs_do_automount(struct path *path) ...@@ -212,8 +224,9 @@ static struct vfsmount *cifs_do_automount(struct path *path)
ctx->source = NULL; ctx->source = NULL;
goto out; goto out;
} }
cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s\n", ctx->dfs_automount = is_dfs_mount(mntpt);
__func__, ctx->source, ctx->UNC, ctx->prepath); 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); mnt = fc_mount(fc);
out: out:
......
This diff is collapsed.
...@@ -756,6 +756,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_ ...@@ -756,6 +756,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
unsigned int ret_data_len = 0; unsigned int ret_data_len = 0;
struct network_interface_info_ioctl_rsp *out_buf = NULL; struct network_interface_info_ioctl_rsp *out_buf = NULL;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct TCP_Server_Info *pserver;
/* do not query too frequently */ /* do not query too frequently */
if (ses->iface_last_update && if (ses->iface_last_update &&
...@@ -780,6 +781,11 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_ ...@@ -780,6 +781,11 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
if (rc) if (rc)
goto out; 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: out:
kfree(out_buf); kfree(out_buf);
return rc; return rc;
......
...@@ -163,6 +163,9 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -163,6 +163,9 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
int rc = 0; int rc = 0;
struct nls_table *nls_codepage = NULL; struct nls_table *nls_codepage = NULL;
struct cifs_ses *ses; 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 * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
...@@ -223,6 +226,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -223,6 +226,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
return -EAGAIN; 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); spin_unlock(&server->srv_lock);
again: again:
...@@ -241,12 +250,24 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -241,12 +250,24 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
tcon->need_reconnect); tcon->need_reconnect);
mutex_lock(&ses->session_mutex); 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 * Recheck after acquire mutex. If another thread is negotiating
* and the server never sends an answer the socket will be closed * and the server never sends an answer the socket will be closed
* and tcpStatus set to reconnect. * and tcpStatus set to reconnect.
*/ */
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsNeedReconnect) { if (server->tcpStatus == CifsNeedReconnect) {
spin_unlock(&server->srv_lock); spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
...@@ -283,6 +304,53 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -283,6 +304,53 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
rc = cifs_negotiate_protocol(0, ses, server); rc = cifs_negotiate_protocol(0, ses, server);
if (!rc) { 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); rc = cifs_setup_session(0, ses, server, nls_codepage);
if ((rc == -EACCES) && !tcon->retry) { if ((rc == -EACCES) && !tcon->retry) {
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
...@@ -307,17 +375,44 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -307,17 +375,44 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
tcon->need_reopen_files = true; tcon->need_reopen_files = true;
rc = cifs_tree_connect(0, tcon, nls_codepage); rc = cifs_tree_connect(0, tcon, nls_codepage);
mutex_unlock(&ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc) { if (rc) {
/* If sess reconnected but tcon didn't, something strange ... */ /* If sess reconnected but tcon didn't, something strange ... */
mutex_unlock(&ses->session_mutex);
cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc);
goto out; 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) 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); atomic_inc(&tconInfoReconnectCount);
out: out:
...@@ -3808,6 +3903,13 @@ void smb2_reconnect_server(struct work_struct *work) ...@@ -3808,6 +3903,13 @@ void smb2_reconnect_server(struct work_struct *work)
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
mutex_lock(&pserver->reconnect_mutex); 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_list);
INIT_LIST_HEAD(&tmp_ses_list); INIT_LIST_HEAD(&tmp_ses_list);
cifs_dbg(FYI, "Reconnecting tcons and channels\n"); cifs_dbg(FYI, "Reconnecting tcons and channels\n");
...@@ -3852,12 +3954,6 @@ void smb2_reconnect_server(struct work_struct *work) ...@@ -3852,12 +3954,6 @@ void smb2_reconnect_server(struct work_struct *work)
} }
spin_unlock(&ses->chan_lock); 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); spin_unlock(&cifs_tcp_ses_lock);
...@@ -3905,13 +4001,17 @@ void smb2_reconnect_server(struct work_struct *work) ...@@ -3905,13 +4001,17 @@ void smb2_reconnect_server(struct work_struct *work)
done: done:
cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
if (resched) if (resched) {
queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
mutex_unlock(&pserver->reconnect_mutex); 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 */ /* now we can safely release srv struct */
if (tcon_exist || ses_exist) cifs_put_tcp_session(server, true);
cifs_put_tcp_session(server, 1);
} }
int int
...@@ -3931,7 +4031,12 @@ SMB2_echo(struct TCP_Server_Info *server) ...@@ -3931,7 +4031,12 @@ SMB2_echo(struct TCP_Server_Info *server)
server->ops->need_neg(server)) { server->ops->need_neg(server)) {
spin_unlock(&server->srv_lock); spin_unlock(&server->srv_lock);
/* No need to send echo on newly established connections */ /* 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; return rc;
} }
spin_unlock(&server->srv_lock); spin_unlock(&server->srv_lock);
......
...@@ -413,7 +413,13 @@ generate_smb3signingkey(struct cifs_ses *ses, ...@@ -413,7 +413,13 @@ generate_smb3signingkey(struct cifs_ses *ses,
ses->ses_status == SES_GOOD); ses->ses_status == SES_GOOD);
chan_index = cifs_ses_get_chan_index(ses, server); 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->chan_lock);
spin_unlock(&ses->ses_lock); spin_unlock(&ses->ses_lock);
......
...@@ -1023,7 +1023,7 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) ...@@ -1023,7 +1023,7 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
for (i = 0; i < ses->chan_count; i++) { for (i = 0; i < ses->chan_count; i++) {
server = ses->chans[i].server; server = ses->chans[i].server;
if (!server) if (!server || server->terminate)
continue; continue;
/* /*
......
...@@ -150,10 +150,13 @@ static int cifs_xattr_set(const struct xattr_handler *handler, ...@@ -150,10 +150,13 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto out; 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, rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
full_path, name, value, (__u16)size, full_path, name, value, (__u16)size,
cifs_sb->local_nls, cifs_sb); cifs_sb->local_nls, cifs_sb);
if (rc == 0)
inode_set_ctime_current(inode);
}
break; break;
case XATTR_CIFS_ACL: case XATTR_CIFS_ACL:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment