Commit 7a043feb authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.4-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:
 "Eight, mostly small, smb3 client fixes:

   - important fix for deferred close oops (race with unmount) found
     with xfstest generic/098 to some servers

   - important reconnect fix

   - fix problem with max_credits mount option

   - two multichannel (interface related) fixes

   - one trivial removal of confusing comment

   - two small debugging improvements (to better spot crediting
     problems)"

* tag '6.4-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: add a warning when the in-flight count goes negative
  cifs: fix lease break oops in xfstest generic/098
  cifs: fix max_credits implementation
  cifs: fix sockaddr comparison in iface_cmp
  smb/client: print "Unknown" instead of bogus link speed value
  cifs: print all credit counters in DebugData
  cifs: fix status checks in cifs_tree_connect
  smb: remove obsolete comment
parents b6dad517 e4645cc2
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <uapi/linux/ethtool.h>
#include "cifspdu.h" #include "cifspdu.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "cifsproto.h" #include "cifsproto.h"
...@@ -130,12 +131,14 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) ...@@ -130,12 +131,14 @@ 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;
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 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"
"\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d" "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d"
"\n\t\tIn Send: %d In MaxReq Wait: %d", "\n\t\tIn Send: %d In MaxReq Wait: %d",
i+1, server->conn_id, i+1, server->conn_id,
server->credits, server->credits,
server->echo_credits,
server->oplock_credits,
server->dialect, server->dialect,
server->tcpStatus, server->tcpStatus,
server->reconnect_instance, server->reconnect_instance,
...@@ -146,18 +149,62 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) ...@@ -146,18 +149,62 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
atomic_read(&server->num_waiters)); atomic_read(&server->num_waiters));
} }
static inline const char *smb_speed_to_str(size_t bps)
{
size_t mbps = bps / 1000 / 1000;
switch (mbps) {
case SPEED_10:
return "10Mbps";
case SPEED_100:
return "100Mbps";
case SPEED_1000:
return "1Gbps";
case SPEED_2500:
return "2.5Gbps";
case SPEED_5000:
return "5Gbps";
case SPEED_10000:
return "10Gbps";
case SPEED_14000:
return "14Gbps";
case SPEED_20000:
return "20Gbps";
case SPEED_25000:
return "25Gbps";
case SPEED_40000:
return "40Gbps";
case SPEED_50000:
return "50Gbps";
case SPEED_56000:
return "56Gbps";
case SPEED_100000:
return "100Gbps";
case SPEED_200000:
return "200Gbps";
case SPEED_400000:
return "400Gbps";
case SPEED_800000:
return "800Gbps";
default:
return "Unknown";
}
}
static void static void
cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
{ {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
seq_printf(m, "\tSpeed: %zu bps\n", iface->speed); seq_printf(m, "\tSpeed: %s\n", smb_speed_to_str(iface->speed));
seq_puts(m, "\t\tCapabilities: "); seq_puts(m, "\t\tCapabilities: ");
if (iface->rdma_capable) if (iface->rdma_capable)
seq_puts(m, "rdma "); seq_puts(m, "rdma ");
if (iface->rss_capable) if (iface->rss_capable)
seq_puts(m, "rss "); seq_puts(m, "rss ");
if (!iface->rdma_capable && !iface->rss_capable)
seq_puts(m, "None");
seq_putc(m, '\n'); seq_putc(m, '\n');
if (iface->sockaddr.ss_family == AF_INET) if (iface->sockaddr.ss_family == AF_INET)
seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr); seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
...@@ -350,8 +397,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -350,8 +397,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
atomic_read(&server->smbd_conn->mr_used_count)); atomic_read(&server->smbd_conn->mr_used_count));
skip_rdma: skip_rdma:
#endif #endif
seq_printf(m, "\nNumber of credits: %d Dialect 0x%x", seq_printf(m, "\nNumber of credits: %d,%d,%d Dialect 0x%x",
server->credits, server->dialect); server->credits,
server->echo_credits,
server->oplock_credits,
server->dialect);
if (server->compress_algorithm == SMB3_COMPRESS_LZNT1) if (server->compress_algorithm == SMB3_COMPRESS_LZNT1)
seq_printf(m, " COMPRESS_LZNT1"); seq_printf(m, " COMPRESS_LZNT1");
else if (server->compress_algorithm == SMB3_COMPRESS_LZ77) else if (server->compress_algorithm == SMB3_COMPRESS_LZ77)
......
...@@ -970,43 +970,6 @@ release_iface(struct kref *ref) ...@@ -970,43 +970,6 @@ release_iface(struct kref *ref)
kfree(iface); kfree(iface);
} }
/*
* compare two interfaces a and b
* return 0 if everything matches.
* return 1 if a has higher link speed, or rdma capable, or rss capable
* return -1 otherwise.
*/
static inline int
iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
{
int cmp_ret = 0;
WARN_ON(!a || !b);
if (a->speed == b->speed) {
if (a->rdma_capable == b->rdma_capable) {
if (a->rss_capable == b->rss_capable) {
cmp_ret = memcmp(&a->sockaddr, &b->sockaddr,
sizeof(a->sockaddr));
if (!cmp_ret)
return 0;
else if (cmp_ret > 0)
return 1;
else
return -1;
} else if (a->rss_capable > b->rss_capable)
return 1;
else
return -1;
} else if (a->rdma_capable > b->rdma_capable)
return 1;
else
return -1;
} else if (a->speed > b->speed)
return 1;
else
return -1;
}
struct cifs_chan { struct cifs_chan {
unsigned int in_reconnect : 1; /* if session setup in progress for this channel */ unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
......
...@@ -87,6 +87,7 @@ extern int cifs_handle_standard(struct TCP_Server_Info *server, ...@@ -87,6 +87,7 @@ extern int cifs_handle_standard(struct TCP_Server_Info *server,
struct mid_q_entry *mid); struct mid_q_entry *mid);
extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
extern int smb3_parse_opt(const char *options, const char *key, char **val); extern int smb3_parse_opt(const char *options, const char *key, char **val);
extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);
extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
extern int cifs_call_async(struct TCP_Server_Info *server, extern int cifs_call_async(struct TCP_Server_Info *server,
......
...@@ -1288,6 +1288,56 @@ cifs_demultiplex_thread(void *p) ...@@ -1288,6 +1288,56 @@ cifs_demultiplex_thread(void *p)
module_put_and_kthread_exit(0); module_put_and_kthread_exit(0);
} }
int
cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs)
{
struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
switch (srcaddr->sa_family) {
case AF_UNSPEC:
switch (rhs->sa_family) {
case AF_UNSPEC:
return 0;
case AF_INET:
case AF_INET6:
return 1;
default:
return -1;
}
case AF_INET: {
switch (rhs->sa_family) {
case AF_UNSPEC:
return -1;
case AF_INET:
return memcmp(saddr4, vaddr4,
sizeof(struct sockaddr_in));
case AF_INET6:
return 1;
default:
return -1;
}
}
case AF_INET6: {
switch (rhs->sa_family) {
case AF_UNSPEC:
case AF_INET:
return -1;
case AF_INET6:
return memcmp(saddr6,
vaddr6,
sizeof(struct sockaddr_in6));
default:
return -1;
}
}
default:
return -1; /* don't expect to be here */
}
}
/* /*
* Returns true if srcaddr isn't specified and rhs isn't specified, or * Returns true if srcaddr isn't specified and rhs isn't specified, or
* if srcaddr is specified and matches the IP address of the rhs argument * if srcaddr is specified and matches the IP address of the rhs argument
...@@ -4086,16 +4136,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru ...@@ -4086,16 +4136,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
/* only send once per connect */ /* only send once per connect */
spin_lock(&tcon->tc_lock); spin_lock(&tcon->tc_lock);
if (tcon->status == TID_GOOD) {
spin_unlock(&tcon->tc_lock);
return 0;
}
if (tcon->status != TID_NEW && if (tcon->status != TID_NEW &&
tcon->status != TID_NEED_TCON) { tcon->status != TID_NEED_TCON) {
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
return -EHOSTDOWN; return -EHOSTDOWN;
} }
if (tcon->status == TID_GOOD) {
spin_unlock(&tcon->tc_lock);
return 0;
}
tcon->status = TID_IN_TCON; tcon->status = TID_IN_TCON;
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
......
...@@ -575,16 +575,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru ...@@ -575,16 +575,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
/* only send once per connect */ /* only send once per connect */
spin_lock(&tcon->tc_lock); spin_lock(&tcon->tc_lock);
if (tcon->status == TID_GOOD) {
spin_unlock(&tcon->tc_lock);
return 0;
}
if (tcon->status != TID_NEW && if (tcon->status != TID_NEW &&
tcon->status != TID_NEED_TCON) { tcon->status != TID_NEED_TCON) {
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
return -EHOSTDOWN; return -EHOSTDOWN;
} }
if (tcon->status == TID_GOOD) {
spin_unlock(&tcon->tc_lock);
return 0;
}
tcon->status = TID_IN_TCON; tcon->status = TID_IN_TCON;
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
......
...@@ -4942,9 +4942,13 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4942,9 +4942,13 @@ void cifs_oplock_break(struct work_struct *work)
* disconnected since oplock already released by the server * disconnected since oplock already released by the server
*/ */
if (!oplock_break_cancelled) { if (!oplock_break_cancelled) {
rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, /* check for server null since can race with kill_sb calling tree disconnect */
if (tcon->ses && tcon->ses->server) {
rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid,
volatile_fid, net_fid, cinode); volatile_fid, net_fid, cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc); cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
} else
pr_warn_once("lease break not sent for unmounted share\n");
} }
cifs_done_oplock_break(cinode); cifs_done_oplock_break(cinode);
......
...@@ -34,6 +34,8 @@ static int ...@@ -34,6 +34,8 @@ static int
change_conf(struct TCP_Server_Info *server) change_conf(struct TCP_Server_Info *server)
{ {
server->credits += server->echo_credits + server->oplock_credits; server->credits += server->echo_credits + server->oplock_credits;
if (server->credits > server->max_credits)
server->credits = server->max_credits;
server->oplock_credits = server->echo_credits = 0; server->oplock_credits = server->echo_credits = 0;
switch (server->credits) { switch (server->credits) {
case 0: case 0:
...@@ -91,6 +93,7 @@ smb2_add_credits(struct TCP_Server_Info *server, ...@@ -91,6 +93,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
server->conn_id, server->hostname, *val, server->conn_id, server->hostname, *val,
add, server->in_flight); add, server->in_flight);
} }
WARN_ON_ONCE(server->in_flight == 0);
server->in_flight--; server->in_flight--;
if (server->in_flight == 0 && if (server->in_flight == 0 &&
((optype & CIFS_OP_MASK) != CIFS_NEG_OP) && ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) &&
...@@ -510,6 +513,43 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) ...@@ -510,6 +513,43 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
return rsize; return rsize;
} }
/*
* compare two interfaces a and b
* return 0 if everything matches.
* return 1 if a is rdma capable, or rss capable, or has higher link speed
* return -1 otherwise.
*/
static int
iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
{
int cmp_ret = 0;
WARN_ON(!a || !b);
if (a->rdma_capable == b->rdma_capable) {
if (a->rss_capable == b->rss_capable) {
if (a->speed == b->speed) {
cmp_ret = cifs_ipaddr_cmp((struct sockaddr *) &a->sockaddr,
(struct sockaddr *) &b->sockaddr);
if (!cmp_ret)
return 0;
else if (cmp_ret > 0)
return 1;
else
return -1;
} else if (a->speed > b->speed)
return 1;
else
return -1;
} else if (a->rss_capable > b->rss_capable)
return 1;
else
return -1;
} else if (a->rdma_capable > b->rdma_capable)
return 1;
else
return -1;
}
static int static int
parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
size_t buf_len, struct cifs_ses *ses, bool in_mount) size_t buf_len, struct cifs_ses *ses, bool in_mount)
......
...@@ -1305,7 +1305,12 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) ...@@ -1305,7 +1305,12 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
} }
/* enough to enable echos and oplocks and one max size write */ /* enough to enable echos and oplocks and one max size write */
req->hdr.CreditRequest = cpu_to_le16(130); if (server->credits >= server->max_credits)
req->hdr.CreditRequest = cpu_to_le16(0);
else
req->hdr.CreditRequest = cpu_to_le16(
min_t(int, server->max_credits -
server->credits, 130));
/* only one of SMB2 signing flags may be set in SMB2 request */ /* only one of SMB2 signing flags may be set in SMB2 request */
if (server->sign) if (server->sign)
...@@ -1899,7 +1904,12 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, ...@@ -1899,7 +1904,12 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
rqst.rq_nvec = 2; rqst.rq_nvec = 2;
/* Need 64 for max size write so ask for more in case not there yet */ /* Need 64 for max size write so ask for more in case not there yet */
req->hdr.CreditRequest = cpu_to_le16(64); if (server->credits >= server->max_credits)
req->hdr.CreditRequest = cpu_to_le16(0);
else
req->hdr.CreditRequest = cpu_to_le16(
min_t(int, server->max_credits -
server->credits, 64));
rc = cifs_send_recv(xid, ses, server, rc = cifs_send_recv(xid, ses, server,
&rqst, &resp_buftype, flags, &rsp_iov); &rqst, &resp_buftype, flags, &rsp_iov);
...@@ -4227,6 +4237,7 @@ smb2_async_readv(struct cifs_readdata *rdata) ...@@ -4227,6 +4237,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
unsigned int total_len; unsigned int total_len;
int credit_request;
cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
__func__, rdata->offset, rdata->bytes); __func__, rdata->offset, rdata->bytes);
...@@ -4258,7 +4269,13 @@ smb2_async_readv(struct cifs_readdata *rdata) ...@@ -4258,7 +4269,13 @@ smb2_async_readv(struct cifs_readdata *rdata)
if (rdata->credits.value > 0) { if (rdata->credits.value > 0) {
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
SMB2_MAX_BUFFER_SIZE)); SMB2_MAX_BUFFER_SIZE));
shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
if (server->credits >= server->max_credits)
shdr->CreditRequest = cpu_to_le16(0);
else
shdr->CreditRequest = cpu_to_le16(
min_t(int, server->max_credits -
server->credits, credit_request));
rc = adjust_credits(server, &rdata->credits, rdata->bytes); rc = adjust_credits(server, &rdata->credits, rdata->bytes);
if (rc) if (rc)
...@@ -4468,6 +4485,7 @@ smb2_async_writev(struct cifs_writedata *wdata, ...@@ -4468,6 +4485,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
unsigned int total_len; unsigned int total_len;
struct cifs_io_parms _io_parms; struct cifs_io_parms _io_parms;
struct cifs_io_parms *io_parms = NULL; struct cifs_io_parms *io_parms = NULL;
int credit_request;
if (!wdata->server) if (!wdata->server)
server = wdata->server = cifs_pick_channel(tcon->ses); server = wdata->server = cifs_pick_channel(tcon->ses);
...@@ -4572,7 +4590,13 @@ smb2_async_writev(struct cifs_writedata *wdata, ...@@ -4572,7 +4590,13 @@ smb2_async_writev(struct cifs_writedata *wdata,
if (wdata->credits.value > 0) { if (wdata->credits.value > 0) {
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
SMB2_MAX_BUFFER_SIZE)); SMB2_MAX_BUFFER_SIZE));
shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
if (server->credits >= server->max_credits)
shdr->CreditRequest = cpu_to_le16(0);
else
shdr->CreditRequest = cpu_to_le16(
min_t(int, server->max_credits -
server->credits, credit_request));
rc = adjust_credits(server, &wdata->credits, io_parms->length); rc = adjust_credits(server, &wdata->credits, io_parms->length);
if (rc) if (rc)
......
...@@ -55,7 +55,7 @@ alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) ...@@ -55,7 +55,7 @@ alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
temp->pid = current->pid; temp->pid = current->pid;
temp->command = cpu_to_le16(smb_buffer->Command); temp->command = cpu_to_le16(smb_buffer->Command);
cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
/* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ /* easier to use jiffies */
/* when mid allocated can be before when sent */ /* when mid allocated can be before when sent */
temp->when_alloc = jiffies; temp->when_alloc = jiffies;
temp->server = server; temp->server = server;
......
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