Skip to content

Commit

Permalink
Merge tag '6.7-rc-smb3-client-fixes-part2' of git://git.samba.org/sfr…
Browse files Browse the repository at this point in the history
…ench/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
  • Loading branch information
torvalds committed Nov 12, 2023
2 parents 3ca112b + fd2bd7c commit 1b907d0
Show file tree
Hide file tree
Showing 16 changed files with 491 additions and 88 deletions.
25 changes: 23 additions & 2 deletions fs/smb/client/cifs_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -544,11 +551,25 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
"\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");
}
Expand Down Expand Up @@ -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) \
Expand Down
6 changes: 6 additions & 0 deletions fs/smb/client/cifs_ioctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)

/*
Expand Down
4 changes: 2 additions & 2 deletions fs/smb/client/cifsfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
16 changes: 13 additions & 3 deletions fs/smb/client/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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) \
Expand Down Expand Up @@ -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;

/*
Expand All @@ -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++) {
Expand All @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions fs/smb/client/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
48 changes: 40 additions & 8 deletions fs/smb/client/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
18 changes: 13 additions & 5 deletions fs/smb/client/dfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,23 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
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;
}

Expand Down
1 change: 1 addition & 0 deletions fs/smb/client/fs_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down
26 changes: 26 additions & 0 deletions fs/smb/client/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
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)
{
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 1b907d0

Please sign in to comment.