Commit e519a8c2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.1-rc-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb3 updates from Steve French:

 - smb3/cifs fixes including for large i/o error cases

 - fixes for three xfstests

 - improved crediting (smb3 flow control)

 - improved tracing

* tag '5.1-rc-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: (44 commits)
  fs: cifs: Kconfig: pedantic formatting
  smb3: request more credits on normal (non-large read/write) ops
  CIFS: Mask off signals when sending SMB packets
  CIFS: Return -EAGAIN instead of -ENOTSOCK
  CIFS: Only send SMB2_NEGOTIATE command on new TCP connections
  CIFS: Fix read after write for files with read caching
  smb3: for kerberos mounts display the credential uid used
  cifs: use correct format characters
  smb3: add dynamic trace point for query_info_enter/done
  smb3: add dynamic trace point for smb3_cmd_enter
  smb3: improve dynamic tracing of open and posix mkdir
  smb3: add missing read completion trace point
  smb3: Add tracepoints for read, write and query_dir enter
  smb3: add tracepoints for query dir
  smb3: Update POSIX negotiate context with POSIX ctxt GUID
  cifs: update internal module version number
  CIFS: Try to acquire credits at once for compound requests
  CIFS: Return error code when getting file handle for writeback
  CIFS: Move open file handling to writepages
  CIFS: Move unlocking pages from wdata_send_pages()
  ...
parents d1cae948 50cfad78
......@@ -117,25 +117,25 @@ config CIFS_UPCALL
secure Kerberos authentication is required). If unsure, say Y.
config CIFS_XATTR
bool "CIFS extended attributes"
depends on CIFS
help
Extended attributes are name:value pairs associated with inodes by
the kernel or by users (see the attr(5) manual page for details).
CIFS maps the name of extended attributes beginning with the user
namespace prefix to SMB/CIFS EAs. EAs are stored on Windows
servers without the user namespace prefix, but their names are
seen by Linux cifs clients prefaced by the user namespace prefix.
The system namespace (used by some filesystems to store ACLs) is
not supported at this time.
If unsure, say Y.
bool "CIFS extended attributes"
depends on CIFS
help
Extended attributes are name:value pairs associated with inodes by
the kernel or by users (see the attr(5) manual page for details).
CIFS maps the name of extended attributes beginning with the user
namespace prefix to SMB/CIFS EAs. EAs are stored on Windows
servers without the user namespace prefix, but their names are
seen by Linux cifs clients prefaced by the user namespace prefix.
The system namespace (used by some filesystems to store ACLs) is
not supported at this time.
If unsure, say Y.
config CIFS_POSIX
bool "CIFS POSIX Extensions"
depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR
help
Enabling this option will cause the cifs client to attempt to
bool "CIFS POSIX Extensions"
depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR
help
Enabling this option will cause the cifs client to attempt to
negotiate a newer dialect with servers, such as Samba 3.0.5
or later, that optionally can handle more POSIX like (rather
than Windows like) file behavior. It also enables
......@@ -144,61 +144,62 @@ config CIFS_POSIX
CIFS POSIX ACL support. If unsure, say N.
config CIFS_ACL
bool "Provide CIFS ACL support"
depends on CIFS_XATTR && KEYS
help
Allows fetching CIFS/NTFS ACL from the server. The DACL blob
is handed over to the application/caller. See the man
page for getcifsacl for more information. If unsure, say Y.
bool "Provide CIFS ACL support"
depends on CIFS_XATTR && KEYS
help
Allows fetching CIFS/NTFS ACL from the server. The DACL blob
is handed over to the application/caller. See the man
page for getcifsacl for more information. If unsure, say Y.
config CIFS_DEBUG
bool "Enable CIFS debugging routines"
default y
depends on CIFS
help
Enabling this option adds helpful debugging messages to
the cifs code which increases the size of the cifs module.
If unsure, say Y.
Enabling this option adds helpful debugging messages to
the cifs code which increases the size of the cifs module.
If unsure, say Y.
config CIFS_DEBUG2
bool "Enable additional CIFS debugging routines"
depends on CIFS_DEBUG
help
Enabling this option adds a few more debugging routines
to the cifs code which slightly increases the size of
the cifs module and can cause additional logging of debug
messages in some error paths, slowing performance. This
option can be turned off unless you are debugging
cifs problems. If unsure, say N.
Enabling this option adds a few more debugging routines
to the cifs code which slightly increases the size of
the cifs module and can cause additional logging of debug
messages in some error paths, slowing performance. This
option can be turned off unless you are debugging
cifs problems. If unsure, say N.
config CIFS_DEBUG_DUMP_KEYS
bool "Dump encryption keys for offline decryption (Unsafe)"
depends on CIFS_DEBUG
help
Enabling this will dump the encryption and decryption keys
used to communicate on an encrypted share connection on the
console. This allows Wireshark to decrypt and dissect
encrypted network captures. Enable this carefully.
If unsure, say N.
Enabling this will dump the encryption and decryption keys
used to communicate on an encrypted share connection on the
console. This allows Wireshark to decrypt and dissect
encrypted network captures. Enable this carefully.
If unsure, say N.
config CIFS_DFS_UPCALL
bool "DFS feature support"
depends on CIFS && KEYS
select DNS_RESOLVER
help
Distributed File System (DFS) support is used to access shares
transparently in an enterprise name space, even if the share
moves to a different server. This feature also enables
an upcall mechanism for CIFS which contacts userspace helper
utilities to provide server name resolution (host names to
IP addresses) which is needed in order to reconnect to
servers if their addresses change or for implicit mounts of
DFS junction points. If unsure, say Y.
bool "DFS feature support"
depends on CIFS && KEYS
select DNS_RESOLVER
help
Distributed File System (DFS) support is used to access shares
transparently in an enterprise name space, even if the share
moves to a different server. This feature also enables
an upcall mechanism for CIFS which contacts userspace helper
utilities to provide server name resolution (host names to
IP addresses) which is needed in order to reconnect to
servers if their addresses change or for implicit mounts of
DFS junction points. If unsure, say Y.
config CIFS_NFSD_EXPORT
bool "Allow nfsd to export CIFS file system"
depends on CIFS && BROKEN
help
Allows NFS server to export a CIFS mounted share (nfsd over cifs)
bool "Allow nfsd to export CIFS file system"
depends on CIFS && BROKEN
help
Allows NFS server to export a CIFS mounted share (nfsd over cifs)
config CIFS_SMB_DIRECT
bool "SMB Direct support (Experimental)"
......@@ -209,10 +210,9 @@ config CIFS_SMB_DIRECT
say N.
config CIFS_FSCACHE
bool "Provide CIFS client caching support"
depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
help
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
to be cached locally on disk through the general filesystem cache
manager. If unsure, say N.
bool "Provide CIFS client caching support"
depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
help
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
to be cached locally on disk through the general filesystem cache
manager. If unsure, say N.
......@@ -285,9 +285,9 @@ static void dump_referral(const struct dfs_info3_param *ref)
{
cifs_dbg(FYI, "DFS: ref path: %s\n", ref->path_name);
cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_name);
cifs_dbg(FYI, "DFS: fl: %hd, srv_type: %hd\n",
cifs_dbg(FYI, "DFS: fl: %d, srv_type: %d\n",
ref->flags, ref->server_type);
cifs_dbg(FYI, "DFS: ref_flags: %hd, path_consumed: %hd\n",
cifs_dbg(FYI, "DFS: ref_flags: %d, path_consumed: %d\n",
ref->ref_flag, ref->path_consumed);
}
......
......@@ -58,6 +58,7 @@ struct cifs_sb_info {
spinlock_t tlink_tree_lock;
struct tcon_link *master_tlink;
struct nls_table *local_nls;
unsigned int bsize;
unsigned int rsize;
unsigned int wsize;
unsigned long actimeo; /* attribute cache timeout (jiffies) */
......
......@@ -381,7 +381,7 @@ cifs_show_security(struct seq_file *s, struct cifs_ses *ses)
seq_puts(s, "ntlm");
break;
case Kerberos:
seq_puts(s, "krb5");
seq_printf(s, "krb5,cruid=%u", from_kuid_munged(&init_user_ns,ses->cred_uid));
break;
case RawNTLMSSP:
seq_puts(s, "ntlmssp");
......@@ -554,6 +554,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_printf(s, ",rsize=%u", cifs_sb->rsize);
seq_printf(s, ",wsize=%u", cifs_sb->wsize);
seq_printf(s, ",bsize=%u", cifs_sb->bsize);
seq_printf(s, ",echo_interval=%lu",
tcon->ses->server->echo_interval / HZ);
if (tcon->snapshot_time)
......
......@@ -150,5 +150,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
#define CIFS_VERSION "2.17"
#define CIFS_VERSION "2.18"
#endif /* _CIFSFS_H */
......@@ -216,6 +216,7 @@ struct cifs_io_parms;
struct cifs_search_info;
struct cifsInodeInfo;
struct cifs_open_parms;
struct cifs_credits;
struct smb_version_operations {
int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
......@@ -230,12 +231,15 @@ struct smb_version_operations {
/* check response: verify signature, map error */
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
bool);
void (*add_credits)(struct TCP_Server_Info *, const unsigned int,
const int);
void (*add_credits)(struct TCP_Server_Info *server,
const struct cifs_credits *credits,
const int optype);
void (*set_credits)(struct TCP_Server_Info *, const int);
int * (*get_credits_field)(struct TCP_Server_Info *, const int);
unsigned int (*get_credits)(struct mid_q_entry *);
__u64 (*get_next_mid)(struct TCP_Server_Info *);
void (*revert_current_mid)(struct TCP_Server_Info *server,
const unsigned int val);
/* data offset from read response message */
unsigned int (*read_data_offset)(char *);
/*
......@@ -383,8 +387,8 @@ struct smb_version_operations {
struct cifs_fid *);
/* calculate a size of SMB message */
unsigned int (*calc_smb_size)(void *buf, struct TCP_Server_Info *ptcpi);
/* check for STATUS_PENDING and process it in a positive case */
bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
/* check for STATUS_PENDING and process the response if yes */
bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server);
/* check for STATUS_NETWORK_SESSION_EXPIRED */
bool (*is_session_expired)(char *);
/* send oplock break response */
......@@ -452,7 +456,11 @@ struct smb_version_operations {
unsigned int (*wp_retry_size)(struct inode *);
/* get mtu credits */
int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int,
unsigned int *, unsigned int *);
unsigned int *, struct cifs_credits *);
/* adjust previously taken mtu credits to request size */
int (*adjust_credits)(struct TCP_Server_Info *server,
struct cifs_credits *credits,
const unsigned int payload_size);
/* check if we need to issue closedir */
bool (*dir_needs_close)(struct cifsFileInfo *);
long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
......@@ -557,6 +565,7 @@ struct smb_vol {
bool resilient:1; /* noresilient not required since not fored for CA */
bool domainauto:1;
bool rdma:1;
unsigned int bsize;
unsigned int rsize;
unsigned int wsize;
bool sockopt_tcp_nodelay:1;
......@@ -710,6 +719,11 @@ struct TCP_Server_Info {
int nr_targets;
};
struct cifs_credits {
unsigned int value;
unsigned int instance;
};
static inline unsigned int
in_flight(struct TCP_Server_Info *server)
{
......@@ -731,18 +745,18 @@ has_credits(struct TCP_Server_Info *server, int *credits)
}
static inline void
add_credits(struct TCP_Server_Info *server, const unsigned int add,
add_credits(struct TCP_Server_Info *server, const struct cifs_credits *credits,
const int optype)
{
server->ops->add_credits(server, add, optype);
server->ops->add_credits(server, credits, optype);
}
static inline void
add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add,
const int optype)
add_credits_and_wake_if(struct TCP_Server_Info *server,
const struct cifs_credits *credits, const int optype)
{
if (add) {
server->ops->add_credits(server, add, optype);
if (credits->value) {
server->ops->add_credits(server, credits, optype);
wake_up(&server->request_q);
}
}
......@@ -753,6 +767,14 @@ set_credits(struct TCP_Server_Info *server, const int val)
server->ops->set_credits(server, val);
}
static inline int
adjust_credits(struct TCP_Server_Info *server, struct cifs_credits *credits,
const unsigned int payload_size)
{
return server->ops->adjust_credits ?
server->ops->adjust_credits(server, credits, payload_size) : 0;
}
static inline __le64
get_next_mid64(struct TCP_Server_Info *server)
{
......@@ -770,6 +792,22 @@ get_next_mid(struct TCP_Server_Info *server)
return cpu_to_le16(mid);
}
static inline void
revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
{
if (server->ops->revert_current_mid)
server->ops->revert_current_mid(server, val);
}
static inline void
revert_current_mid_from_hdr(struct TCP_Server_Info *server,
const struct smb2_sync_hdr *shdr)
{
unsigned int num = le16_to_cpu(shdr->CreditCharge);
return revert_current_mid(server, num > 0 ? num : 1);
}
static inline __u16
get_mid(const struct smb_hdr *smb)
{
......@@ -1234,7 +1272,7 @@ struct cifs_readdata {
unsigned int pagesz;
unsigned int page_offset;
unsigned int tailsz;
unsigned int credits;
struct cifs_credits credits;
unsigned int nr_pages;
struct page **pages;
};
......@@ -1260,7 +1298,7 @@ struct cifs_writedata {
unsigned int pagesz;
unsigned int page_offset;
unsigned int tailsz;
unsigned int credits;
struct cifs_credits credits;
unsigned int nr_pages;
struct page **pages;
};
......@@ -1422,6 +1460,7 @@ struct mid_q_entry {
struct kref refcount;
struct TCP_Server_Info *server; /* server corresponding to this mid */
__u64 mid; /* multiplex id */
__u16 credits; /* number of credits consumed by this mid */
__u32 pid; /* process id */
__u32 sequence_number; /* for CIFS signing */
unsigned long when_alloc; /* when mid was created */
......
......@@ -93,7 +93,8 @@ extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
extern int cifs_call_async(struct TCP_Server_Info *server,
struct smb_rqst *rqst,
mid_receive_t *receive, mid_callback_t *callback,
mid_handle_t *handle, void *cbdata, const int flags);
mid_handle_t *handle, void *cbdata, const int flags,
const struct cifs_credits *exist_credits);
extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
struct smb_rqst *rqst, int *resp_buf_type,
const int flags, struct kvec *resp_iov);
......@@ -115,7 +116,7 @@ extern int cifs_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error);
extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
unsigned int size, unsigned int *num,
unsigned int *credits);
struct cifs_credits *credits);
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */, const int flags,
......@@ -133,6 +134,9 @@ extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written);
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
bool fsuid_only,
struct cifsFileInfo **ret_file);
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server);
extern int decode_negTokenInit(unsigned char *security_blob, int length,
......
......@@ -139,8 +139,8 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
return -ENOMEM;
if (tcon->ipc) {
snprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
tcon->ses->server->hostname);
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
tcon->ses->server->hostname);
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
goto out;
}
......@@ -172,7 +172,7 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
continue;
}
snprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
if (!rc)
......@@ -822,9 +822,10 @@ static void
cifs_echo_callback(struct mid_q_entry *mid)
{
struct TCP_Server_Info *server = mid->callback_data;
struct cifs_credits credits = { .value = 1, .instance = 0 };
DeleteMidQEntry(mid);
add_credits(server, 1, CIFS_ECHO_OP);
add_credits(server, &credits, CIFS_ECHO_OP);
}
int
......@@ -859,7 +860,7 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
iov[1].iov_base = (char *)smb + 4;
rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL,
server, CIFS_ASYNC_OP | CIFS_ECHO_OP);
server, CIFS_ASYNC_OP | CIFS_ECHO_OP, NULL);
if (rc)
cifs_dbg(FYI, "Echo request failed: %d\n", rc);
......@@ -1605,16 +1606,17 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
}
if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, 0)) {
server->ops->is_status_pending(buf, server)) {
cifs_discard_remaining_data(server);
return -1;
}
/* set up first two iov for signature check and to get credits */
rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = 4;
rdata->iov[1].iov_base = buf + 4;
rdata->iov[1].iov_len = server->total_read - 4;
rdata->iov[0].iov_len = server->vals->header_preamble_size;
rdata->iov[1].iov_base = buf + server->vals->header_preamble_size;
rdata->iov[1].iov_len =
server->total_read - server->vals->header_preamble_size;
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
......@@ -1713,6 +1715,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
.rq_npages = rdata->nr_pages,
.rq_pagesz = rdata->pagesz,
.rq_tailsz = rdata->tailsz };
struct cifs_credits credits = { .value = 1, .instance = 0 };
cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
__func__, mid->mid, mid->mid_state, rdata->result,
......@@ -1750,7 +1753,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
queue_work(cifsiod_wq, &rdata->work);
DeleteMidQEntry(mid);
add_credits(server, 1, 0);
add_credits(server, &credits, 0);
}
/* cifs_async_readv - send an async write, and set up mid to handle result */
......@@ -1809,7 +1812,7 @@ cifs_async_readv(struct cifs_readdata *rdata)
kref_get(&rdata->refcount);
rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
cifs_readv_callback, NULL, rdata, 0);
cifs_readv_callback, NULL, rdata, 0, NULL);
if (rc == 0)
cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
......@@ -2123,14 +2126,18 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
wdata2->tailsz = tailsz;
wdata2->bytes = cur_len;
wdata2->cfile = find_writable_file(CIFS_I(inode), false);
rc = cifs_get_writable_file(CIFS_I(inode), false,
&wdata2->cfile);
if (!wdata2->cfile) {
cifs_dbg(VFS, "No writable handles for inode\n");
rc = -EBADF;
break;
cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n",
rc);
if (!is_retryable_error(rc))
rc = -EBADF;
} else {
wdata2->pid = wdata2->cfile->pid;
rc = server->ops->async_writev(wdata2,
cifs_writedata_release);
}
wdata2->pid = wdata2->cfile->pid;
rc = server->ops->async_writev(wdata2, cifs_writedata_release);
for (j = 0; j < nr_pages; j++) {
unlock_page(wdata2->pages[j]);
......@@ -2145,6 +2152,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
kref_put(&wdata2->refcount, cifs_writedata_release);
if (is_retryable_error(rc))
continue;
i += nr_pages;
break;
}
......@@ -2152,6 +2160,13 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
i += nr_pages;
} while (i < wdata->nr_pages);
/* cleanup remaining pages from the original wdata */
for (; i < wdata->nr_pages; i++) {
SetPageError(wdata->pages[i]);
end_page_writeback(wdata->pages[i]);
put_page(wdata->pages[i]);
}
if (rc != 0 && !is_retryable_error(rc))
mapping_set_error(inode->i_mapping, rc);
kref_put(&wdata->refcount, cifs_writedata_release);
......@@ -2226,6 +2241,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
unsigned int written;
WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
struct cifs_credits credits = { .value = 1, .instance = 0 };
switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED:
......@@ -2261,7 +2277,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
queue_work(cifsiod_wq, &wdata->work);
DeleteMidQEntry(mid);
add_credits(tcon->ses->server, 1, 0);
add_credits(tcon->ses->server, &credits, 0);
}
/* cifs_async_writev - send an async write, and set up mid to handle result */
......@@ -2339,7 +2355,7 @@ cifs_async_writev(struct cifs_writedata *wdata,
kref_get(&wdata->refcount);
rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
cifs_writev_callback, NULL, wdata, 0);
cifs_writev_callback, NULL, wdata, 0, NULL);
if (rc == 0)
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
......
......@@ -102,7 +102,7 @@ enum {
Opt_backupuid, Opt_backupgid, Opt_uid,
Opt_cruid, Opt_gid, Opt_file_mode,
Opt_dirmode, Opt_port,
Opt_rsize, Opt_wsize, Opt_actimeo,
Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
Opt_echo_interval, Opt_max_credits,
Opt_snapshot,
......@@ -204,6 +204,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_dirmode, "dirmode=%s" },
{ Opt_dirmode, "dir_mode=%s" },
{ Opt_port, "port=%s" },
{ Opt_blocksize, "bsize=%s" },
{ Opt_rsize, "rsize=%s" },
{ Opt_wsize, "wsize=%s" },
{ Opt_actimeo, "actimeo=%s" },
......@@ -348,7 +349,7 @@ static int reconn_set_ipaddr(struct TCP_Server_Info *server)
cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
return -ENOMEM;
}
snprintf(unc, len, "\\\\%s", server->hostname);
scnprintf(unc, len, "\\\\%s", server->hostname);
rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
kfree(unc);
......@@ -592,6 +593,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
msleep(3000);
} else {
atomic_inc(&tcpSesReconnectCount);
set_credits(server, 1);
spin_lock(&GlobalMid_Lock);
if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedNegotiate;
......@@ -1053,7 +1055,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
}
if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, length))
server->ops->is_status_pending(buf, server))
return -1;
if (!mid)
......@@ -1063,6 +1065,26 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return 0;
}
static void
smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
{
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
/*
* SMB1 does not use credits.
*/
if (server->vals->header_preamble_size)
return;
if (shdr->CreditRequest) {
spin_lock(&server->req_lock);
server->credits += le16_to_cpu(shdr->CreditRequest);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
}
}
static int
cifs_demultiplex_thread(void *p)
{
......@@ -1192,6 +1214,7 @@ cifs_demultiplex_thread(void *p)
} else if (server->ops->is_oplock_break &&
server->ops->is_oplock_break(bufs[i],
server)) {
smb2_add_credits_from_hdr(bufs[i], server);
cifs_dbg(FYI, "Received oplock break\n");
} else {
cifs_dbg(VFS, "No task to wake, unknown frame "
......@@ -1203,6 +1226,7 @@ cifs_demultiplex_thread(void *p)
if (server->ops->dump_detail)
server->ops->dump_detail(bufs[i],
server);
smb2_add_credits_from_hdr(bufs[i], server);
cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */
}
......@@ -1486,6 +1510,11 @@ cifs_parse_devname(const char *devname, struct smb_vol *vol)
const char *delims = "/\\";
size_t len;
if (unlikely(!devname || !*devname)) {
cifs_dbg(VFS, "Device name not specified.\n");
return -EINVAL;
}
/* make sure we have a valid UNC double delimiter prefix */
len = strspn(devname, delims);
if (len != 2)
......@@ -1571,7 +1600,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->cred_uid = current_uid();
vol->linux_uid = current_uid();
vol->linux_gid = current_gid();
vol->bsize = 1024 * 1024; /* can improve cp performance significantly */
/*
* default to SFM style remapping of seven reserved characters
* unless user overrides it or we negotiate CIFS POSIX where
......@@ -1944,6 +1973,26 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
}
port = (unsigned short)option;
break;
case Opt_blocksize:
if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "%s: Invalid blocksize value\n",
__func__);
goto cifs_parse_mount_err;
}
/*
* inode blocksize realistically should never need to be
* less than 16K or greater than 16M and default is 1MB.
* Note that small inode block sizes (e.g. 64K) can lead
* to very poor performance of common tools like cp and scp
*/
if ((option < CIFS_MAX_MSGSIZE) ||
(option > (4 * SMB3_DEFAULT_IOSIZE))) {
cifs_dbg(VFS, "%s: Invalid blocksize\n",
__func__);
goto cifs_parse_mount_err;
}
vol->bsize = option;
break;
case Opt_rsize:
if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "%s: Invalid rsize value\n",
......@@ -2609,7 +2658,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
tcp_ses->session_estab = false;
tcp_ses->sequence_number = 0;
tcp_ses->reconnect_instance = 0;
tcp_ses->reconnect_instance = 1;
tcp_ses->lstrp = jiffies;
spin_lock_init(&tcp_ses->req_lock);
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
......@@ -2770,7 +2819,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info)
if (tcon == NULL)
return -ENOMEM;
snprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname);
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname);
/* cannot fail */
nls_codepage = load_nls_default();
......@@ -3839,6 +3888,7 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
spin_lock_init(&cifs_sb->tlink_tree_lock);
cifs_sb->tlink_tree = RB_ROOT;
cifs_sb->bsize = pvolume_info->bsize;
/*
* Temporarily set r/wsize for matching superblock. If we end up using
* new sb then client will later negotiate it downward if needed.
......@@ -4198,7 +4248,7 @@ static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it,
new_unc = kmalloc(len, GFP_KERNEL);
if (!new_unc)
return -ENOMEM;
snprintf(new_unc, len, "\\%s", tgt);
scnprintf(new_unc, len, "\\%s", tgt);
kfree(vol->UNC);
vol->UNC = new_unc;
......@@ -4902,8 +4952,6 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
if (!server->ops->need_neg(server))
return 0;
set_credits(server, 1);
rc = server->ops->negotiate(xid, ses);
if (rc == 0) {
spin_lock(&GlobalMid_Lock);
......
......@@ -1842,24 +1842,30 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
return NULL;
}
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
bool fsuid_only)
/* Return -EBADF if no handle is found and general rc otherwise */
int
cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only,
struct cifsFileInfo **ret_file)
{
struct cifsFileInfo *open_file, *inv_file = NULL;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
bool any_available = false;
int rc;
int rc = -EBADF;
unsigned int refind = 0;
/* Having a null inode here (because mapping->host was set to zero by
the VFS or MM) should not happen but we had reports of on oops (due to
it being zero) during stress testcases so we need to check for it */
*ret_file = NULL;
/*
* Having a null inode here (because mapping->host was set to zero by
* the VFS or MM) should not happen but we had reports of on oops (due
* to it being zero) during stress testcases so we need to check for it
*/
if (cifs_inode == NULL) {
cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n");
dump_stack();
return NULL;
return rc;
}
cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
......@@ -1873,7 +1879,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
refind_writable:
if (refind > MAX_REOPEN_ATT) {
spin_unlock(&tcon->open_file_lock);
return NULL;
return rc;
}
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (!any_available && open_file->pid != current->tgid)
......@@ -1885,7 +1891,8 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
/* found a good writable file */
cifsFileInfo_get(open_file);
spin_unlock(&tcon->open_file_lock);
return open_file;
*ret_file = open_file;
return 0;
} else {
if (!inv_file)
inv_file = open_file;
......@@ -1907,22 +1914,35 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
if (inv_file) {
rc = cifs_reopen_file(inv_file, false);
if (!rc)
return inv_file;
else {
spin_lock(&tcon->open_file_lock);
list_move_tail(&inv_file->flist,
&cifs_inode->openFileList);
spin_unlock(&tcon->open_file_lock);
cifsFileInfo_put(inv_file);
++refind;
inv_file = NULL;
spin_lock(&tcon->open_file_lock);
goto refind_writable;
if (!rc) {
*ret_file = inv_file;
return 0;
}
spin_lock(&tcon->open_file_lock);
list_move_tail(&inv_file->flist, &cifs_inode->openFileList);
spin_unlock(&tcon->open_file_lock);
cifsFileInfo_put(inv_file);
++refind;
inv_file = NULL;
spin_lock(&tcon->open_file_lock);
goto refind_writable;
}
return NULL;
return rc;
}
struct cifsFileInfo *
find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only)
{
struct cifsFileInfo *cfile;
int rc;
rc = cifs_get_writable_file(cifs_inode, fsuid_only, &cfile);
if (rc)
cifs_dbg(FYI, "couldn't find writable handle rc=%d", rc);
return cfile;
}
static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
......@@ -1959,8 +1979,8 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
if (mapping->host->i_size - offset < (loff_t)to)
to = (unsigned)(mapping->host->i_size - offset);
open_file = find_writable_file(CIFS_I(mapping->host), false);
if (open_file) {
rc = cifs_get_writable_file(CIFS_I(mapping->host), false, &open_file);
if (!rc) {
bytes_written = cifs_write(open_file, open_file->pid,
write_data, to - from, &offset);
cifsFileInfo_put(open_file);
......@@ -1970,9 +1990,12 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
rc = 0;
else if (bytes_written < 0)
rc = bytes_written;
else
rc = -EFAULT;
} else {
cifs_dbg(FYI, "No writeable filehandles for inode\n");
rc = -EIO;
cifs_dbg(FYI, "No writable handle for write page rc=%d\n", rc);
if (!is_retryable_error(rc))
rc = -EIO;
}
kunmap(page);
......@@ -2079,9 +2102,9 @@ static int
wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
struct address_space *mapping, struct writeback_control *wbc)
{
int rc = 0;
struct TCP_Server_Info *server;
unsigned int i;
int rc;
struct TCP_Server_Info *server =
tlink_tcon(wdata->cfile->tlink)->ses->server;
wdata->sync_mode = wbc->sync_mode;
wdata->nr_pages = nr_pages;
......@@ -2091,21 +2114,16 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
page_offset(wdata->pages[nr_pages - 1]),
(loff_t)PAGE_SIZE);
wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz;
wdata->pid = wdata->cfile->pid;
if (wdata->cfile != NULL)
cifsFileInfo_put(wdata->cfile);
wdata->cfile = find_writable_file(CIFS_I(mapping->host), false);
if (!wdata->cfile) {
cifs_dbg(VFS, "No writable handles for inode\n");
rc = -EBADF;
} else {
wdata->pid = wdata->cfile->pid;
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
rc = server->ops->async_writev(wdata, cifs_writedata_release);
}
rc = adjust_credits(server, &wdata->credits, wdata->bytes);
if (rc)
return rc;
for (i = 0; i < nr_pages; ++i)
unlock_page(wdata->pages[i]);
if (wdata->cfile->invalidHandle)
rc = -EAGAIN;
else
rc = server->ops->async_writev(wdata, cifs_writedata_release);
return rc;
}
......@@ -2113,11 +2131,13 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
static int cifs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb);
struct inode *inode = mapping->host;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct TCP_Server_Info *server;
bool done = false, scanned = false, range_whole = false;
pgoff_t end, index;
struct cifs_writedata *wdata;
struct cifsFileInfo *cfile = NULL;
int rc = 0;
int saved_rc = 0;
unsigned int xid;
......@@ -2143,11 +2163,23 @@ static int cifs_writepages(struct address_space *mapping,
server = cifs_sb_master_tcon(cifs_sb)->ses->server;
retry:
while (!done && index <= end) {
unsigned int i, nr_pages, found_pages, wsize, credits;
unsigned int i, nr_pages, found_pages, wsize;
pgoff_t next = 0, tofind, saved_index = index;
struct cifs_credits credits_on_stack;
struct cifs_credits *credits = &credits_on_stack;
int get_file_rc = 0;
if (cfile)
cifsFileInfo_put(cfile);
rc = cifs_get_writable_file(CIFS_I(inode), false, &cfile);
/* in case of an error store it to return later */
if (rc)
get_file_rc = rc;
rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
&wsize, &credits);
&wsize, credits);
if (rc != 0) {
done = true;
break;
......@@ -2180,13 +2212,26 @@ static int cifs_writepages(struct address_space *mapping,
continue;
}
wdata->credits = credits;
wdata->credits = credits_on_stack;
wdata->cfile = cfile;
cfile = NULL;
rc = wdata_send_pages(wdata, nr_pages, mapping, wbc);
if (!wdata->cfile) {
cifs_dbg(VFS, "No writable handle in writepages rc=%d\n",
get_file_rc);
if (is_retryable_error(get_file_rc))
rc = get_file_rc;
else
rc = -EBADF;
} else
rc = wdata_send_pages(wdata, nr_pages, mapping, wbc);
for (i = 0; i < nr_pages; ++i)
unlock_page(wdata->pages[i]);
/* send failure -- clean up the mess */
if (rc != 0) {
add_credits_and_wake_if(server, wdata->credits, 0);
add_credits_and_wake_if(server, &wdata->credits, 0);
for (i = 0; i < nr_pages; ++i) {
if (is_retryable_error(rc))
redirty_page_for_writepage(wbc,
......@@ -2238,6 +2283,8 @@ static int cifs_writepages(struct address_space *mapping,
if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
mapping->writeback_index = index;
if (cfile)
cifsFileInfo_put(cfile);
free_xid(xid);
return rc;
}
......@@ -2567,7 +2614,8 @@ static int
cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
struct cifs_aio_ctx *ctx)
{
unsigned int wsize, credits;
unsigned int wsize;
struct cifs_credits credits;
int rc;
struct TCP_Server_Info *server =
tlink_tcon(wdata->cfile->tlink)->ses->server;
......@@ -2577,18 +2625,19 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
* Note: we are attempting to resend the whole wdata not in segments
*/
do {
rc = server->ops->wait_mtu_credits(
server, wdata->bytes, &wsize, &credits);
rc = server->ops->wait_mtu_credits(server, wdata->bytes, &wsize,
&credits);
if (rc)
goto out;
if (wsize < wdata->bytes) {
add_credits_and_wake_if(server, credits, 0);
add_credits_and_wake_if(server, &credits, 0);
msleep(1000);
}
} while (wsize < wdata->bytes);
wdata->credits = credits;
rc = -EAGAIN;
while (rc == -EAGAIN) {
rc = 0;
......@@ -2604,7 +2653,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
return 0;
}
add_credits_and_wake_if(server, wdata->credits, 0);
add_credits_and_wake_if(server, &wdata->credits, 0);
out:
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
......@@ -2627,6 +2676,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
struct TCP_Server_Info *server;
struct page **pagevec;
size_t start;
unsigned int xid;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
pid = open_file->pid;
......@@ -2634,12 +2684,23 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
pid = current->tgid;
server = tlink_tcon(open_file->tlink)->ses->server;
xid = get_xid();
do {
unsigned int wsize, credits;
unsigned int wsize;
struct cifs_credits credits_on_stack;
struct cifs_credits *credits = &credits_on_stack;
if (open_file->invalidHandle) {
rc = cifs_reopen_file(open_file, false);
if (rc == -EAGAIN)
continue;
else if (rc)
break;
}
rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
&wsize, &credits);
&wsize, credits);
if (rc)
break;
......@@ -2731,16 +2792,22 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
wdata->pid = pid;
wdata->bytes = cur_len;
wdata->pagesz = PAGE_SIZE;
wdata->credits = credits;
wdata->credits = credits_on_stack;
wdata->ctx = ctx;
kref_get(&ctx->refcount);
if (!wdata->cfile->invalidHandle ||
!(rc = cifs_reopen_file(wdata->cfile, false)))
rc = server->ops->async_writev(wdata,
rc = adjust_credits(server, &wdata->credits, wdata->bytes);
if (!rc) {
if (wdata->cfile->invalidHandle)
rc = -EAGAIN;
else
rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release);
}
if (rc) {
add_credits_and_wake_if(server, wdata->credits, 0);
add_credits_and_wake_if(server, &wdata->credits, 0);
kref_put(&wdata->refcount,
cifs_uncached_writedata_release);
if (rc == -EAGAIN) {
......@@ -2756,6 +2823,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
len -= cur_len;
} while (len > 0);
free_xid(xid);
return rc;
}
......@@ -3028,14 +3096,16 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
* these pages but not on the region from pos to ppos+len-1.
*/
written = cifs_user_writev(iocb, from);
if (written > 0 && CIFS_CACHE_READ(cinode)) {
if (CIFS_CACHE_READ(cinode)) {
/*
* Windows 7 server can delay breaking level2 oplock if a write
* request comes - break it on the client to prevent reading
* an old data.
* We have read level caching and we have just sent a write
* request to the server thus making data in the cache stale.
* Zap the cache and set oplock/lease level to NONE to avoid
* reading stale data from the cache. All subsequent read
* operations will read new data from the server.
*/
cifs_zap_mapping(inode);
cifs_dbg(FYI, "Set no oplock for inode=%p after a write operation\n",
cifs_dbg(FYI, "Set Oplock/Lease to NONE for inode=%p after write\n",
inode);
cinode->oplock = 0;
}
......@@ -3260,7 +3330,8 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
struct list_head *rdata_list,
struct cifs_aio_ctx *ctx)
{
unsigned int rsize, credits;
unsigned int rsize;
struct cifs_credits credits;
int rc;
struct TCP_Server_Info *server =
tlink_tcon(rdata->cfile->tlink)->ses->server;
......@@ -3277,11 +3348,12 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
goto out;
if (rsize < rdata->bytes) {
add_credits_and_wake_if(server, credits, 0);
add_credits_and_wake_if(server, &credits, 0);
msleep(1000);
}
} while (rsize < rdata->bytes);
rdata->credits = credits;
rc = -EAGAIN;
while (rc == -EAGAIN) {
rc = 0;
......@@ -3297,7 +3369,7 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
return 0;
}
add_credits_and_wake_if(server, rdata->credits, 0);
add_credits_and_wake_if(server, &rdata->credits, 0);
out:
kref_put(&rdata->refcount,
cifs_uncached_readdata_release);
......@@ -3311,7 +3383,9 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
struct cifs_aio_ctx *ctx)
{
struct cifs_readdata *rdata;
unsigned int npages, rsize, credits;
unsigned int npages, rsize;
struct cifs_credits credits_on_stack;
struct cifs_credits *credits = &credits_on_stack;
size_t cur_len;
int rc;
pid_t pid;
......@@ -3331,8 +3405,16 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
iov_iter_advance(&direct_iov, offset - ctx->pos);
do {
if (open_file->invalidHandle) {
rc = cifs_reopen_file(open_file, true);
if (rc == -EAGAIN)
continue;
else if (rc)
break;
}
rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
&rsize, &credits);
&rsize, credits);
if (rc)
break;
......@@ -3406,15 +3488,21 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
rdata->pagesz = PAGE_SIZE;
rdata->read_into_pages = cifs_uncached_read_into_pages;
rdata->copy_into_pages = cifs_uncached_copy_into_pages;
rdata->credits = credits;
rdata->credits = credits_on_stack;
rdata->ctx = ctx;
kref_get(&ctx->refcount);
if (!rdata->cfile->invalidHandle ||
!(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata);
rc = adjust_credits(server, &rdata->credits, rdata->bytes);
if (!rc) {
if (rdata->cfile->invalidHandle)
rc = -EAGAIN;
else
rc = server->ops->async_readv(rdata);
}
if (rc) {
add_credits_and_wake_if(server, rdata->credits, 0);
add_credits_and_wake_if(server, &rdata->credits, 0);
kref_put(&rdata->refcount,
cifs_uncached_readdata_release);
if (rc == -EAGAIN) {
......@@ -3533,8 +3621,6 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx)
ctx->total_len = ctx->len - iov_iter_count(to);
}
cifs_stats_bytes_read(tcon, ctx->total_len);
/* mask nodata case */
if (rc == -ENODATA)
rc = 0;
......@@ -4095,10 +4181,19 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
loff_t offset;
struct page *page, *tpage;
struct cifs_readdata *rdata;
unsigned credits;
struct cifs_credits credits_on_stack;
struct cifs_credits *credits = &credits_on_stack;
if (open_file->invalidHandle) {
rc = cifs_reopen_file(open_file, true);
if (rc == -EAGAIN)
continue;
else if (rc)
break;
}
rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
&rsize, &credits);
&rsize, credits);
if (rc)
break;
......@@ -4144,18 +4239,24 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
rdata->tailsz = PAGE_SIZE;
rdata->read_into_pages = cifs_readpages_read_into_pages;
rdata->copy_into_pages = cifs_readpages_copy_into_pages;
rdata->credits = credits;
rdata->credits = credits_on_stack;
list_for_each_entry_safe(page, tpage, &tmplist, lru) {
list_del(&page->lru);
rdata->pages[rdata->nr_pages++] = page;
}
if (!rdata->cfile->invalidHandle ||
!(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata);
rc = adjust_credits(server, &rdata->credits, rdata->bytes);
if (!rc) {
if (rdata->cfile->invalidHandle)
rc = -EAGAIN;
else
rc = server->ops->async_readv(rdata);
}
if (rc) {
add_credits_and_wake_if(server, rdata->credits, 0);
add_credits_and_wake_if(server, &rdata->credits, 0);
for (i = 0; i < rdata->nr_pages; i++) {
page = rdata->pages[i];
lru_cache_add_file(page);
......
......@@ -2080,7 +2080,7 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
return rc;
generic_fillattr(inode, stat);
stat->blksize = CIFS_MAX_MSGSIZE;
stat->blksize = cifs_sb->bsize;
stat->ino = CIFS_I(inode)->uniqueid;
/* old CIFS Unix Extensions doesn't return create time */
......
......@@ -103,9 +103,9 @@ parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
return rc;
}
snprintf(md5_str2, sizeof(md5_str2),
CIFS_MF_SYMLINK_MD5_FORMAT,
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
scnprintf(md5_str2, sizeof(md5_str2),
CIFS_MF_SYMLINK_MD5_FORMAT,
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
if (strncmp(md5_str1, md5_str2, 17) != 0)
return -EINVAL;
......@@ -142,10 +142,10 @@ format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
return rc;
}
snprintf(buf, buf_len,
CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
link_len,
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
scnprintf(buf, buf_len,
CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
link_len,
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
memcpy(buf + ofs, link_str, link_len);
......
......@@ -117,11 +117,11 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
}
static void
cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add,
const int optype)
cifs_add_credits(struct TCP_Server_Info *server,
const struct cifs_credits *credits, const int optype)
{
spin_lock(&server->req_lock);
server->credits += add;
server->credits += credits->value;
server->in_flight--;
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
......@@ -308,7 +308,7 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
remaining = tgt_total_cnt - total_in_tgt;
if (remaining < 0) {
cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%hu\n",
cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n",
tgt_total_cnt, total_in_tgt);
return -EPROTO;
}
......
......@@ -517,7 +517,6 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
__u8 lease_state;
struct list_head *tmp;
struct cifsFileInfo *cfile;
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_pending_open *open;
struct cifsInodeInfo *cinode;
int ack_req = le32_to_cpu(rsp->Flags &
......@@ -537,13 +536,25 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
le32_to_cpu(rsp->NewLeaseState));
server->ops->set_oplock_level(cinode, lease_state, 0, NULL);
if (ack_req)
cfile->oplock_break_cancelled = false;
else
cfile->oplock_break_cancelled = true;
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
/*
* Set or clear flags depending on the lease state being READ.
* HANDLE caching flag should be added when the client starts
* to defer closing remote file handles with HANDLE leases.
*/
if (lease_state & SMB2_LEASE_READ_CACHING_HE)
set_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
else
clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
queue_work(cifsoplockd_wq, &cfile->oplock_break);
kfree(lw);
return true;
......@@ -648,13 +659,6 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK)
return false;
if (rsp->sync_hdr.CreditRequest) {
spin_lock(&server->req_lock);
server->credits += le16_to_cpu(rsp->sync_hdr.CreditRequest);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
}
if (rsp->StructureSize !=
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
if (le16_to_cpu(rsp->StructureSize) == 44)
......
......@@ -67,10 +67,13 @@ change_conf(struct TCP_Server_Info *server)
}
static void
smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
const int optype)
smb2_add_credits(struct TCP_Server_Info *server,
const struct cifs_credits *credits, const int optype)
{
int *val, rc = -1;
unsigned int add = credits->value;
unsigned int instance = credits->instance;
bool reconnect_detected = false;
spin_lock(&server->req_lock);
val = server->ops->get_credits_field(server, optype);
......@@ -79,8 +82,11 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0))
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
server->hostname, *val);
if ((instance == 0) || (instance == server->reconnect_instance))
*val += add;
else
reconnect_detected = true;
*val += add;
if (*val > 65000) {
*val = 65000; /* Don't get near 64K credits, avoid srv bugs */
printk_once(KERN_WARNING "server overflowed SMB3 credits\n");
......@@ -102,7 +108,12 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
if (server->tcpStatus == CifsNeedReconnect)
if (reconnect_detected)
cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
add, instance);
if (server->tcpStatus == CifsNeedReconnect
|| server->tcpStatus == CifsExiting)
return;
switch (rc) {
......@@ -163,7 +174,7 @@ smb2_get_credits(struct mid_q_entry *mid)
static int
smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
unsigned int *num, unsigned int *credits)
unsigned int *num, struct cifs_credits *credits)
{
int rc = 0;
unsigned int scredits;
......@@ -189,7 +200,8 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
/* can deadlock with reopen */
if (scredits <= 8) {
*num = SMB2_MAX_BUFFER_SIZE;
*credits = 0;
credits->value = 0;
credits->instance = 0;
break;
}
......@@ -198,8 +210,10 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
*num = min_t(unsigned int, size,
scredits * SMB2_MAX_BUFFER_SIZE);
*credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
server->credits -= *credits;
credits->value =
DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
credits->instance = server->reconnect_instance;
server->credits -= credits->value;
server->in_flight++;
break;
}
......@@ -208,6 +222,38 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
return rc;
}
static int
smb2_adjust_credits(struct TCP_Server_Info *server,
struct cifs_credits *credits,
const unsigned int payload_size)
{
int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE);
if (!credits->value || credits->value == new_val)
return 0;
if (credits->value < new_val) {
WARN_ONCE(1, "request has less credits (%d) than required (%d)",
credits->value, new_val);
return -ENOTSUPP;
}
spin_lock(&server->req_lock);
if (server->reconnect_instance != credits->instance) {
spin_unlock(&server->req_lock);
cifs_dbg(VFS, "trying to return %d credits to old session\n",
credits->value - new_val);
return -EAGAIN;
}
server->credits += credits->value - new_val;
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
credits->value = new_val;
return 0;
}
static __u64
smb2_get_next_mid(struct TCP_Server_Info *server)
{
......@@ -219,6 +265,15 @@ smb2_get_next_mid(struct TCP_Server_Info *server)
return mid;
}
static void
smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
{
spin_lock(&GlobalMid_Lock);
if (server->CurrentMid >= val)
server->CurrentMid -= val;
spin_unlock(&GlobalMid_Lock);
}
static struct mid_q_entry *
smb2_find_mid(struct TCP_Server_Info *server, char *buf)
{
......@@ -940,6 +995,16 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
memset(rsp_iov, 0, sizeof(rsp_iov));
if (ses->server->ops->query_all_EAs) {
if (!ea_value) {
rc = ses->server->ops->query_all_EAs(xid, tcon, path,
ea_name, NULL, 0,
cifs_sb);
if (rc == -ENODATA)
goto sea_exit;
}
}
/* Open */
memset(&open_iov, 0, sizeof(open_iov));
rqst[0].rq_iov = open_iov;
......@@ -1753,14 +1818,14 @@ smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
* the number of credits and return true. Otherwise - return false.
*/
static bool
smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
{
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
if (shdr->Status != STATUS_PENDING)
return false;
if (!length) {
if (shdr->CreditRequest) {
spin_lock(&server->req_lock);
server->credits += le16_to_cpu(shdr->CreditRequest);
spin_unlock(&server->req_lock);
......@@ -2594,6 +2659,15 @@ smb2_downgrade_oplock(struct TCP_Server_Info *server,
server->ops->set_oplock_level(cinode, 0, 0, NULL);
}
static void
smb21_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, bool set_level2)
{
server->ops->set_oplock_level(cinode,
set_level2 ? SMB2_LEASE_READ_CACHING_HE :
0, 0, NULL);
}
static void
smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int epoch, bool *purge_cache)
......@@ -3210,15 +3284,15 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
}
if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, 0))
server->ops->is_status_pending(buf, server))
return -1;
/* set up first two iov to get credits */
rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = 4;
rdata->iov[1].iov_base = buf + 4;
rdata->iov[0].iov_len = 0;
rdata->iov[1].iov_base = buf;
rdata->iov[1].iov_len =
min_t(unsigned int, buf_len, server->vals->read_rsp_size) - 4;
min_t(unsigned int, buf_len, server->vals->read_rsp_size);
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
......@@ -3541,6 +3615,7 @@ struct smb_version_operations smb20_operations = {
.get_credits = smb2_get_credits,
.wait_mtu_credits = cifs_wait_mtu_credits,
.get_next_mid = smb2_get_next_mid,
.revert_current_mid = smb2_revert_current_mid,
.read_data_offset = smb2_read_data_offset,
.read_data_length = smb2_read_data_length,
.map_error = map_smb2_to_linux_error,
......@@ -3635,7 +3710,9 @@ struct smb_version_operations smb21_operations = {
.get_credits_field = smb2_get_credits_field,
.get_credits = smb2_get_credits,
.wait_mtu_credits = smb2_wait_mtu_credits,
.adjust_credits = smb2_adjust_credits,
.get_next_mid = smb2_get_next_mid,
.revert_current_mid = smb2_revert_current_mid,
.read_data_offset = smb2_read_data_offset,
.read_data_length = smb2_read_data_length,
.map_error = map_smb2_to_linux_error,
......@@ -3646,7 +3723,7 @@ struct smb_version_operations smb21_operations = {
.print_stats = smb2_print_stats,
.is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock,
.downgrade_oplock = smb21_downgrade_oplock,
.need_neg = smb2_need_neg,
.negotiate = smb2_negotiate,
.negotiate_wsize = smb2_negotiate_wsize,
......@@ -3731,7 +3808,9 @@ struct smb_version_operations smb30_operations = {
.get_credits_field = smb2_get_credits_field,
.get_credits = smb2_get_credits,
.wait_mtu_credits = smb2_wait_mtu_credits,
.adjust_credits = smb2_adjust_credits,
.get_next_mid = smb2_get_next_mid,
.revert_current_mid = smb2_revert_current_mid,
.read_data_offset = smb2_read_data_offset,
.read_data_length = smb2_read_data_length,
.map_error = map_smb2_to_linux_error,
......@@ -3743,7 +3822,7 @@ struct smb_version_operations smb30_operations = {
.dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock,
.downgrade_oplock = smb21_downgrade_oplock,
.need_neg = smb2_need_neg,
.negotiate = smb2_negotiate,
.negotiate_wsize = smb3_negotiate_wsize,
......@@ -3836,7 +3915,9 @@ struct smb_version_operations smb311_operations = {
.get_credits_field = smb2_get_credits_field,
.get_credits = smb2_get_credits,
.wait_mtu_credits = smb2_wait_mtu_credits,
.adjust_credits = smb2_adjust_credits,
.get_next_mid = smb2_get_next_mid,
.revert_current_mid = smb2_revert_current_mid,
.read_data_offset = smb2_read_data_offset,
.read_data_length = smb2_read_data_length,
.map_error = map_smb2_to_linux_error,
......@@ -3848,7 +3929,7 @@ struct smb_version_operations smb311_operations = {
.dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock,
.downgrade_oplock = smb21_downgrade_oplock,
.need_neg = smb2_need_neg,
.negotiate = smb2_negotiate,
.negotiate_wsize = smb3_negotiate_wsize,
......
......@@ -107,13 +107,13 @@ smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
struct TCP_Server_Info *server = tcon->ses->server;
spin_lock(&server->req_lock);
/* Request up to 2 credits but don't go over the limit. */
/* Request up to 10 credits but don't go over the limit. */
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, 2));
server->credits, 10));
spin_unlock(&server->req_lock);
} else {
shdr->CreditRequest = cpu_to_le16(2);
......@@ -173,8 +173,8 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
return -ENOMEM;
if (tcon->ipc) {
snprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
tcon->ses->server->hostname);
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
tcon->ses->server->hostname);
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
goto out;
}
......@@ -206,7 +206,7 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
continue;
}
snprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
if (!rc)
......@@ -490,6 +490,23 @@ build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt)
{
pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE;
pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
pneg_ctxt->Name[0] = 0x93;
pneg_ctxt->Name[1] = 0xAD;
pneg_ctxt->Name[2] = 0x25;
pneg_ctxt->Name[3] = 0x50;
pneg_ctxt->Name[4] = 0x9C;
pneg_ctxt->Name[5] = 0xB4;
pneg_ctxt->Name[6] = 0x11;
pneg_ctxt->Name[7] = 0xE7;
pneg_ctxt->Name[8] = 0xB4;
pneg_ctxt->Name[9] = 0x23;
pneg_ctxt->Name[10] = 0x83;
pneg_ctxt->Name[11] = 0xDE;
pneg_ctxt->Name[12] = 0x96;
pneg_ctxt->Name[13] = 0x8B;
pneg_ctxt->Name[14] = 0xCD;
pneg_ctxt->Name[15] = 0x7C;
}
static void
......@@ -986,8 +1003,14 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
(char *)pneg_inbuf, inbuflen, (char **)&pneg_rsp, &rsplen);
if (rc != 0) {
if (rc == -EOPNOTSUPP) {
/*
* Old Windows versions or Netapp SMB server can return
* not supported error. Client should accept it.
*/
cifs_dbg(VFS, "Server does not support validate negotiate\n");
return 0;
} else if (rc != 0) {
cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
rc = -EIO;
goto out_free_inbuf;
......@@ -1614,6 +1637,9 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
rqst.rq_iov = iov;
rqst.rq_nvec = 2;
/* Need 64 for max size write so ask for more in case not there yet */
req->sync_hdr.CreditRequest = cpu_to_le16(64);
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
......@@ -2170,6 +2196,8 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
rqst.rq_iov = iov;
rqst.rq_nvec = n_iov;
trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, CREATE_NOT_FILE,
FILE_WRITE_ATTRIBUTES);
/* resource #4: response buffer */
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
if (rc) {
......@@ -2388,6 +2416,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
if (rc)
goto creat_exit;
trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid,
oparms->create_options, oparms->desired_access);
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
&rsp_iov);
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
......@@ -2837,6 +2868,9 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
if (rc)
goto qinf_exit;
trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid,
ses->Suid, info_class, (__u32)info_type);
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
......@@ -2847,6 +2881,9 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
goto qinf_exit;
}
trace_smb3_query_info_done(xid, persistent_fid, tcon->tid,
ses->Suid, info_class, (__u32)info_type);
if (dlen) {
*dlen = le32_to_cpu(rsp->OutputBufferLength);
if (!*data) {
......@@ -2924,14 +2961,16 @@ smb2_echo_callback(struct mid_q_entry *mid)
{
struct TCP_Server_Info *server = mid->callback_data;
struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf;
unsigned int credits_received = 0;
struct cifs_credits credits = { .value = 0, .instance = 0 };
if (mid->mid_state == MID_RESPONSE_RECEIVED
|| mid->mid_state == MID_RESPONSE_MALFORMED)
credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
|| mid->mid_state == MID_RESPONSE_MALFORMED) {
credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest);
credits.instance = server->reconnect_instance;
}
DeleteMidQEntry(mid);
add_credits(server, credits_received, CIFS_ECHO_OP);
add_credits(server, &credits, CIFS_ECHO_OP);
}
void smb2_reconnect_server(struct work_struct *work)
......@@ -3023,7 +3062,7 @@ SMB2_echo(struct TCP_Server_Info *server)
iov[0].iov_base = (char *)req;
rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
server, CIFS_ECHO_OP);
server, CIFS_ECHO_OP, NULL);
if (rc)
cifs_dbg(FYI, "Echo request failed: %d\n", rc);
......@@ -3114,6 +3153,11 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
req->MinimumCount = 0;
req->Length = cpu_to_le32(io_parms->length);
req->Offset = cpu_to_le64(io_parms->offset);
trace_smb3_read_enter(0 /* xid */,
io_parms->persistent_fid,
io_parms->tcon->tid, io_parms->tcon->ses->Suid,
io_parms->offset, io_parms->length);
#ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If we want to do a RDMA write, fill in and append
......@@ -3184,7 +3228,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
struct TCP_Server_Info *server = tcon->ses->server;
struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)rdata->iov[0].iov_base;
unsigned int credits_received = 0;
struct cifs_credits credits = { .value = 0, .instance = 0 };
struct smb_rqst rqst = { .rq_iov = rdata->iov,
.rq_nvec = 2,
.rq_pages = rdata->pages,
......@@ -3199,7 +3243,8 @@ smb2_readv_callback(struct mid_q_entry *mid)
switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED:
credits_received = le16_to_cpu(shdr->CreditRequest);
credits.value = le16_to_cpu(shdr->CreditRequest);
credits.instance = server->reconnect_instance;
/* result already set, check signature */
if (server->sign && !mid->decrypted) {
int rc;
......@@ -3224,11 +3269,11 @@ smb2_readv_callback(struct mid_q_entry *mid)
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
case MID_RESPONSE_MALFORMED:
credits_received = le16_to_cpu(shdr->CreditRequest);
credits.value = le16_to_cpu(shdr->CreditRequest);
credits.instance = server->reconnect_instance;
/* fall through */
default:
if (rdata->result != -ENODATA)
rdata->result = -EIO;
rdata->result = -EIO;
}
#ifdef CONFIG_CIFS_SMB_DIRECT
/*
......@@ -3255,7 +3300,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
queue_work(cifsiod_wq, &rdata->work);
DeleteMidQEntry(mid);
add_credits(server, credits_received, 0);
add_credits(server, &credits, 0);
}
/* smb2_async_readv - send an async read, and set up mid to handle result */
......@@ -3285,17 +3330,8 @@ smb2_async_readv(struct cifs_readdata *rdata)
rc = smb2_new_read_req(
(void **) &buf, &total_len, &io_parms, rdata, 0, 0);
if (rc) {
if (rc == -EAGAIN && rdata->credits) {
/* credits was reset by reconnect */
rdata->credits = 0;
/* reduce in_flight value since we won't send the req */
spin_lock(&server->req_lock);
server->in_flight--;
spin_unlock(&server->req_lock);
}
if (rc)
return rc;
}
if (smb3_encryption_required(io_parms.tcon))
flags |= CIFS_TRANSFORM_REQ;
......@@ -3305,24 +3341,24 @@ smb2_async_readv(struct cifs_readdata *rdata)
shdr = (struct smb2_sync_hdr *)buf;
if (rdata->credits) {
if (rdata->credits.value > 0) {
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
SMB2_MAX_BUFFER_SIZE));
shdr->CreditRequest =
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
spin_lock(&server->req_lock);
server->credits += rdata->credits -
le16_to_cpu(shdr->CreditCharge);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
rdata->credits = le16_to_cpu(shdr->CreditCharge);
rc = adjust_credits(server, &rdata->credits, rdata->bytes);
if (rc)
goto async_readv_out;
flags |= CIFS_HAS_CREDITS;
}
kref_get(&rdata->refcount);
rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
cifs_readv_receive, smb2_readv_callback,
smb3_handle_read_data, rdata, flags);
smb3_handle_read_data, rdata, flags,
&rdata->credits);
if (rc) {
kref_put(&rdata->refcount, cifs_readdata_release);
cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
......@@ -3332,6 +3368,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
io_parms.offset, io_parms.length, rc);
}
async_readv_out:
cifs_small_buf_release(buf);
return rc;
}
......@@ -3378,7 +3415,10 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
io_parms->tcon->tid, ses->Suid,
io_parms->offset, io_parms->length,
rc);
}
} else
trace_smb3_read_done(xid, req->PersistentFileId,
io_parms->tcon->tid, ses->Suid,
io_parms->offset, 0);
free_rsp_buf(resp_buftype, rsp_iov.iov_base);
return rc == -ENODATA ? 0 : rc;
} else
......@@ -3417,14 +3457,16 @@ smb2_writev_callback(struct mid_q_entry *mid)
{
struct cifs_writedata *wdata = mid->callback_data;
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
unsigned int written;
struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
unsigned int credits_received = 0;
struct cifs_credits credits = { .value = 0, .instance = 0 };
switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED:
credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
wdata->result = smb2_check_receive(mid, tcon->ses->server, 0);
credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest);
credits.instance = server->reconnect_instance;
wdata->result = smb2_check_receive(mid, server, 0);
if (wdata->result != 0)
break;
......@@ -3448,7 +3490,8 @@ smb2_writev_callback(struct mid_q_entry *mid)
wdata->result = -EAGAIN;
break;
case MID_RESPONSE_MALFORMED:
credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest);
credits.instance = server->reconnect_instance;
/* fall through */
default:
wdata->result = -EIO;
......@@ -3481,7 +3524,7 @@ smb2_writev_callback(struct mid_q_entry *mid)
queue_work(cifsiod_wq, &wdata->work);
DeleteMidQEntry(mid);
add_credits(tcon->ses->server, credits_received, 0);
add_credits(server, &credits, 0);
}
/* smb2_async_writev - send an async write, and set up mid to handle result */
......@@ -3499,17 +3542,8 @@ smb2_async_writev(struct cifs_writedata *wdata,
unsigned int total_len;
rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len);
if (rc) {
if (rc == -EAGAIN && wdata->credits) {
/* credits was reset by reconnect */
wdata->credits = 0;
/* reduce in_flight value since we won't send the req */
spin_lock(&server->req_lock);
server->in_flight--;
spin_unlock(&server->req_lock);
}
goto async_writev_out;
}
if (rc)
return rc;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
......@@ -3526,6 +3560,9 @@ smb2_async_writev(struct cifs_writedata *wdata,
req->DataOffset = cpu_to_le16(
offsetof(struct smb2_write_req, Buffer));
req->RemainingBytes = 0;
trace_smb3_write_enter(0 /* xid */, wdata->cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, wdata->offset, wdata->bytes);
#ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If we want to do a server RDMA read, fill in and append
......@@ -3595,23 +3632,22 @@ smb2_async_writev(struct cifs_writedata *wdata,
req->Length = cpu_to_le32(wdata->bytes);
#endif
if (wdata->credits) {
if (wdata->credits.value > 0) {
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
SMB2_MAX_BUFFER_SIZE));
shdr->CreditRequest =
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
spin_lock(&server->req_lock);
server->credits += wdata->credits -
le16_to_cpu(shdr->CreditCharge);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
wdata->credits = le16_to_cpu(shdr->CreditCharge);
rc = adjust_credits(server, &wdata->credits, wdata->bytes);
if (rc)
goto async_writev_out;
flags |= CIFS_HAS_CREDITS;
}
kref_get(&wdata->refcount);
rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL,
wdata, flags);
wdata, flags, &wdata->credits);
if (rc) {
trace_smb3_write_err(0 /* no xid */, req->PersistentFileId,
......@@ -3674,6 +3710,10 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
offsetof(struct smb2_write_req, Buffer));
req->RemainingBytes = 0;
trace_smb3_write_enter(xid, io_parms->persistent_fid,
io_parms->tcon->tid, io_parms->tcon->ses->Suid,
io_parms->offset, io_parms->length);
iov[0].iov_base = (char *)req;
/* 1 for Buffer */
iov[0].iov_len = total_len - 1;
......@@ -3836,6 +3876,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
rqst.rq_iov = iov;
rqst.rq_nvec = 2;
trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid,
tcon->ses->Suid, index, output_size);
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
......@@ -3843,18 +3886,26 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
if (rc) {
if (rc == -ENODATA &&
rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) {
trace_smb3_query_dir_done(xid, persistent_fid,
tcon->tid, tcon->ses->Suid, index, 0);
srch_inf->endOfSearch = true;
rc = 0;
} else
} else {
trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid,
tcon->ses->Suid, index, 0, rc);
cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
}
goto qdir_exit;
}
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
info_buf_size);
if (rc)
if (rc) {
trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid,
tcon->ses->Suid, index, 0, rc);
goto qdir_exit;
}
srch_inf->unicode = true;
......@@ -3882,6 +3933,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
else
cifs_dbg(VFS, "illegal search buffer type\n");
trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid,
tcon->ses->Suid, index, srch_inf->entries_in_buffer);
return rc;
qdir_exit:
......
......@@ -288,12 +288,12 @@ struct smb2_encryption_neg_context {
__le16 Ciphers[1]; /* Ciphers[0] since only one used now */
} __packed;
#define POSIX_CTXT_DATA_LEN 8
#define POSIX_CTXT_DATA_LEN 16
struct smb2_posix_neg_context {
__le16 ContextType; /* 0x100 */
__le16 DataLength;
__le32 Reserved;
__le64 Reserved1; /* In case needed for future (eg version or caps) */
__u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */
} __packed;
struct smb2_negotiate_rsp {
......
......@@ -576,6 +576,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
struct TCP_Server_Info *server)
{
struct mid_q_entry *temp;
unsigned int credits = le16_to_cpu(shdr->CreditCharge);
if (server == NULL) {
cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n");
......@@ -586,6 +587,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
memset(temp, 0, sizeof(struct mid_q_entry));
kref_init(&temp->refcount);
temp->mid = le64_to_cpu(shdr->MessageId);
temp->credits = credits > 0 ? credits : 1;
temp->pid = current->pid;
temp->command = shdr->Command; /* Always LE */
temp->when_alloc = jiffies;
......@@ -600,6 +602,8 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
atomic_inc(&midCount);
temp->mid_state = MID_REQUEST_ALLOCATED;
trace_smb3_cmd_enter(shdr->TreeId, shdr->SessionId,
le16_to_cpu(shdr->Command), temp->mid);
return temp;
}
......@@ -615,6 +619,10 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
return -EAGAIN;
}
if (ses->server->tcpStatus == CifsNeedNegotiate &&
shdr->Command != SMB2_NEGOTIATE)
return -EAGAIN;
if (ses->status == CifsNew) {
if ((shdr->Command != SMB2_SESSION_SETUP) &&
(shdr->Command != SMB2_NEGOTIATE))
......@@ -634,6 +642,7 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
spin_lock(&GlobalMid_Lock);
list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
spin_unlock(&GlobalMid_Lock);
return 0;
}
......@@ -674,13 +683,18 @@ smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
smb2_seq_num_into_buf(ses->server, shdr);
rc = smb2_get_mid_entry(ses, shdr, &mid);
if (rc)
if (rc) {
revert_current_mid_from_hdr(ses->server, shdr);
return ERR_PTR(rc);
}
rc = smb2_sign_rqst(rqst, ses->server);
if (rc) {
revert_current_mid_from_hdr(ses->server, shdr);
cifs_delete_mid(mid);
return ERR_PTR(rc);
}
return mid;
}
......@@ -692,14 +706,21 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
if (server->tcpStatus == CifsNeedNegotiate &&
shdr->Command != SMB2_NEGOTIATE)
return ERR_PTR(-EAGAIN);
smb2_seq_num_into_buf(server, shdr);
mid = smb2_mid_entry_alloc(shdr, server);
if (mid == NULL)
if (mid == NULL) {
revert_current_mid_from_hdr(server, shdr);
return ERR_PTR(-ENOMEM);
}
rc = smb2_sign_rqst(rqst, server);
if (rc) {
revert_current_mid_from_hdr(server, shdr);
DeleteMidQEntry(mid);
return ERR_PTR(rc);
}
......
......@@ -1550,7 +1550,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
char name[MAX_NAME_LEN];
int rc;
snprintf(name, MAX_NAME_LEN, "smbd_request_%p", info);
scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info);
info->request_cache =
kmem_cache_create(
name,
......@@ -1566,7 +1566,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
if (!info->request_mempool)
goto out1;
snprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);
scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);
info->response_cache =
kmem_cache_create(
name,
......@@ -1582,7 +1582,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
if (!info->response_mempool)
goto out3;
snprintf(name, MAX_NAME_LEN, "smbd_%p", info);
scnprintf(name, MAX_NAME_LEN, "smbd_%p", info);
info->workqueue = create_workqueue(name);
if (!info->workqueue)
goto out4;
......
......@@ -58,6 +58,7 @@ DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \
DEFINE_SMB3_RW_ERR_EVENT(write_err);
DEFINE_SMB3_RW_ERR_EVENT(read_err);
DEFINE_SMB3_RW_ERR_EVENT(query_dir_err);
/* For logging successful read or write */
......@@ -100,8 +101,12 @@ DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \
__u32 len), \
TP_ARGS(xid, fid, tid, sesid, offset, len))
DEFINE_SMB3_RW_DONE_EVENT(write_enter);
DEFINE_SMB3_RW_DONE_EVENT(read_enter);
DEFINE_SMB3_RW_DONE_EVENT(query_dir_enter);
DEFINE_SMB3_RW_DONE_EVENT(write_done);
DEFINE_SMB3_RW_DONE_EVENT(read_done);
DEFINE_SMB3_RW_DONE_EVENT(query_dir_done);
/*
* For handle based calls other than read and write, and get/set info
......@@ -148,6 +153,48 @@ DEFINE_SMB3_FD_ERR_EVENT(close_err);
/*
* For handle based query/set info calls
*/
DECLARE_EVENT_CLASS(smb3_inf_enter_class,
TP_PROTO(unsigned int xid,
__u64 fid,
__u32 tid,
__u64 sesid,
__u8 infclass,
__u32 type),
TP_ARGS(xid, fid, tid, sesid, infclass, type),
TP_STRUCT__entry(
__field(unsigned int, xid)
__field(__u64, fid)
__field(__u32, tid)
__field(__u64, sesid)
__field(__u8, infclass)
__field(__u32, type)
),
TP_fast_assign(
__entry->xid = xid;
__entry->fid = fid;
__entry->tid = tid;
__entry->sesid = sesid;
__entry->infclass = infclass;
__entry->type = type;
),
TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x",
__entry->xid, __entry->sesid, __entry->tid, __entry->fid,
__entry->infclass, __entry->type)
)
#define DEFINE_SMB3_INF_ENTER_EVENT(name) \
DEFINE_EVENT(smb3_inf_enter_class, smb3_##name, \
TP_PROTO(unsigned int xid, \
__u64 fid, \
__u32 tid, \
__u64 sesid, \
__u8 infclass, \
__u32 type), \
TP_ARGS(xid, fid, tid, sesid, infclass, type))
DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter);
DEFINE_SMB3_INF_ENTER_EVENT(query_info_done);
DECLARE_EVENT_CLASS(smb3_inf_err_class,
TP_PROTO(unsigned int xid,
__u64 fid,
......@@ -270,6 +317,7 @@ DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \
__u64 mid), \
TP_ARGS(tid, sesid, cmd, mid))
DEFINE_SMB3_CMD_DONE_EVENT(cmd_enter);
DEFINE_SMB3_CMD_DONE_EVENT(cmd_done);
DEFINE_SMB3_CMD_DONE_EVENT(ses_expired);
......@@ -406,8 +454,47 @@ DEFINE_SMB3_TCON_EVENT(tcon);
/*
* For smb2/smb3 open call
* For smb2/smb3 open (including create and mkdir) calls
*/
DECLARE_EVENT_CLASS(smb3_open_enter_class,
TP_PROTO(unsigned int xid,
__u32 tid,
__u64 sesid,
int create_options,
int desired_access),
TP_ARGS(xid, tid, sesid, create_options, desired_access),
TP_STRUCT__entry(
__field(unsigned int, xid)
__field(__u32, tid)
__field(__u64, sesid)
__field(int, create_options)
__field(int, desired_access)
),
TP_fast_assign(
__entry->xid = xid;
__entry->tid = tid;
__entry->sesid = sesid;
__entry->create_options = create_options;
__entry->desired_access = desired_access;
),
TP_printk("xid=%u sid=0x%llx tid=0x%x cr_opts=0x%x des_access=0x%x",
__entry->xid, __entry->sesid, __entry->tid,
__entry->create_options, __entry->desired_access)
)
#define DEFINE_SMB3_OPEN_ENTER_EVENT(name) \
DEFINE_EVENT(smb3_open_enter_class, smb3_##name, \
TP_PROTO(unsigned int xid, \
__u32 tid, \
__u64 sesid, \
int create_options, \
int desired_access), \
TP_ARGS(xid, tid, sesid, create_options, desired_access))
DEFINE_SMB3_OPEN_ENTER_EVENT(open_enter);
DEFINE_SMB3_OPEN_ENTER_EVENT(posix_mkdir_enter);
DECLARE_EVENT_CLASS(smb3_open_err_class,
TP_PROTO(unsigned int xid,
__u32 tid,
......
......@@ -33,6 +33,7 @@
#include <linux/uaccess.h>
#include <asm/processor.h>
#include <linux/mempool.h>
#include <linux/signal.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
......@@ -291,6 +292,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
int n_vec;
unsigned int send_length = 0;
unsigned int i, j;
sigset_t mask, oldmask;
size_t total_len = 0, sent, size;
struct socket *ssocket = server->ssocket;
struct msghdr smb_msg;
......@@ -301,8 +303,14 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
rc = smbd_send(server, rqst);
goto smbd_done;
}
if (ssocket == NULL)
return -ENOTSOCK;
return -EAGAIN;
if (signal_pending(current)) {
cifs_dbg(FYI, "signal is pending before sending any data\n");
return -EINTR;
}
/* cork the socket */
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
......@@ -312,6 +320,16 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
send_length += smb_rqst_len(server, &rqst[j]);
rfc1002_marker = cpu_to_be32(send_length);
/*
* We should not allow signals to interrupt the network send because
* any partial send will cause session reconnects thus increasing
* latency of system calls and overload a server with unnecessary
* requests.
*/
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
/* Generate a rfc1002 marker for SMB2+ */
if (server->vals->header_preamble_size == 0) {
struct kvec hiov = {
......@@ -321,7 +339,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
iov_iter_kvec(&smb_msg.msg_iter, WRITE, &hiov, 1, 4);
rc = smb_send_kvec(server, &smb_msg, &sent);
if (rc < 0)
goto uncork;
goto unmask;
total_len += sent;
send_length += 4;
......@@ -343,7 +361,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
rc = smb_send_kvec(server, &smb_msg, &sent);
if (rc < 0)
goto uncork;
goto unmask;
total_len += sent;
......@@ -365,7 +383,25 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
}
}
uncork:
unmask:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
/*
* If signal is pending but we have already sent the whole packet to
* the server we need to return success status to allow a corresponding
* mid entry to be kept in the pending requests queue thus allowing
* to handle responses from the server by the client.
*
* If only part of the packet has been sent there is no need to hide
* interrupt because the session will be reconnected anyway, so there
* won't be any response from the server to handle.
*/
if (signal_pending(current) && (total_len != send_length)) {
cifs_dbg(FYI, "signal is pending after attempt to send\n");
rc = -EINTR;
}
/* uncork it */
val = 0;
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
......@@ -451,15 +487,18 @@ smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
static int
wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
int *credits)
int *credits, unsigned int *instance)
{
int rc;
*instance = 0;
spin_lock(&server->req_lock);
if (timeout == CIFS_ASYNC_OP) {
/* oplock breaks must not be held up */
server->in_flight++;
*credits -= 1;
*instance = server->reconnect_instance;
spin_unlock(&server->req_lock);
return 0;
}
......@@ -489,6 +528,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
if (timeout != CIFS_BLOCKING_OP) {
*credits -= 1;
server->in_flight++;
*instance = server->reconnect_instance;
}
spin_unlock(&server->req_lock);
break;
......@@ -499,7 +539,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
static int
wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
const int optype)
const int optype, unsigned int *instance)
{
int *val;
......@@ -507,15 +547,16 @@ wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
/* Since an echo is already inflight, no need to wait to send another */
if (*val <= 0 && optype == CIFS_ECHO_OP)
return -EAGAIN;
return wait_for_free_credits(server, timeout, val);
return wait_for_free_credits(server, timeout, val, instance);
}
int
cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
unsigned int *num, unsigned int *credits)
unsigned int *num, struct cifs_credits *credits)
{
*num = size;
*credits = 0;
credits->value = 0;
credits->instance = server->reconnect_instance;
return 0;
}
......@@ -602,27 +643,43 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
int
cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
mid_receive_t *receive, mid_callback_t *callback,
mid_handle_t *handle, void *cbdata, const int flags)
mid_handle_t *handle, void *cbdata, const int flags,
const struct cifs_credits *exist_credits)
{
int rc, timeout, optype;
struct mid_q_entry *mid;
unsigned int credits = 0;
struct cifs_credits credits = { .value = 0, .instance = 0 };
unsigned int instance;
timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK;
if ((flags & CIFS_HAS_CREDITS) == 0) {
rc = wait_for_free_request(server, timeout, optype);
rc = wait_for_free_request(server, timeout, optype, &instance);
if (rc)
return rc;
credits = 1;
}
credits.value = 1;
credits.instance = instance;
} else
instance = exist_credits->instance;
mutex_lock(&server->srv_mutex);
/*
* We can't use credits obtained from the previous session to send this
* request. Check if there were reconnects after we obtained credits and
* return -EAGAIN in such cases to let callers handle it.
*/
if (instance != server->reconnect_instance) {
mutex_unlock(&server->srv_mutex);
add_credits_and_wake_if(server, &credits, optype);
return -EAGAIN;
}
mid = server->ops->setup_async_request(server, rqst);
if (IS_ERR(mid)) {
mutex_unlock(&server->srv_mutex);
add_credits_and_wake_if(server, credits, optype);
add_credits_and_wake_if(server, &credits, optype);
return PTR_ERR(mid);
}
......@@ -647,6 +704,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
cifs_in_send_dec(server);
if (rc < 0) {
revert_current_mid(server, mid->credits);
server->sequence_number -= 2;
cifs_delete_mid(mid);
}
......@@ -656,7 +714,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
if (rc == 0)
return 0;
add_credits_and_wake_if(server, credits, optype);
add_credits_and_wake_if(server, &credits, optype);
return rc;
}
......@@ -786,8 +844,12 @@ static void
cifs_compound_callback(struct mid_q_entry *mid)
{
struct TCP_Server_Info *server = mid->server;
struct cifs_credits credits;
credits.value = server->ops->get_credits(mid);
credits.instance = server->reconnect_instance;
add_credits(server, server->ops->get_credits(mid), mid->optype);
add_credits(server, &credits, mid->optype);
}
static void
......@@ -813,7 +875,11 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
int timeout, optype;
struct mid_q_entry *midQ[MAX_COMPOUND];
bool cancelled_mid[MAX_COMPOUND] = {false};
unsigned int credits[MAX_COMPOUND] = {0};
struct cifs_credits credits[MAX_COMPOUND] = {
{ .value = 0, .instance = 0 }
};
unsigned int instance;
unsigned int first_instance = 0;
char *buf;
timeout = flags & CIFS_TIMEOUT_MASK;
......@@ -830,16 +896,64 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
if (ses->server->tcpStatus == CifsExiting)
return -ENOENT;
spin_lock(&ses->server->req_lock);
if (ses->server->credits < num_rqst) {
/*
* Return immediately if not too many requests in flight since
* we will likely be stuck on waiting for credits.
*/
if (ses->server->in_flight < num_rqst - ses->server->credits) {
spin_unlock(&ses->server->req_lock);
return -ENOTSUPP;
}
} else {
/* enough credits to send the whole compounded request */
ses->server->credits -= num_rqst;
ses->server->in_flight += num_rqst;
first_instance = ses->server->reconnect_instance;
}
spin_unlock(&ses->server->req_lock);
if (first_instance) {
cifs_dbg(FYI, "Acquired %d credits at once\n", num_rqst);
for (i = 0; i < num_rqst; i++) {
credits[i].value = 1;
credits[i].instance = first_instance;
}
goto setup_rqsts;
}
/*
* There are not enough credits to send the whole compound request but
* there are requests in flight that may bring credits from the server.
* This approach still leaves the possibility to be stuck waiting for
* credits if the server doesn't grant credits to the outstanding
* requests. This should be fixed by returning immediately and letting
* a caller fallback to sequential commands instead of compounding.
* Ensure we obtain 1 credit per request in the compound chain.
* It can be optimized further by waiting for all the credits
* at once but this can wait long enough if we don't have enough
* credits due to some heavy operations in progress or the server
* not granting us much, so a fallback to the current approach is
* needed anyway.
*/
for (i = 0; i < num_rqst; i++) {
rc = wait_for_free_request(ses->server, timeout, optype);
rc = wait_for_free_request(ses->server, timeout, optype,
&instance);
if (rc == 0) {
credits[i].value = 1;
credits[i].instance = instance;
/*
* All parts of the compound chain must get credits from
* the same session, otherwise we may end up using more
* credits than the server granted. If there were
* reconnects in between, return -EAGAIN and let callers
* handle it.
*/
if (i == 0)
first_instance = instance;
else if (first_instance != instance) {
i++;
rc = -EAGAIN;
}
}
if (rc) {
/*
* We haven't sent an SMB packet to the server yet but
......@@ -851,12 +965,12 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
* requests correctly.
*/
for (j = 0; j < i; j++)
add_credits(ses->server, 1, optype);
add_credits(ses->server, &credits[j], optype);
return rc;
}
credits[i] = 1;
}
setup_rqsts:
/*
* Make sure that we sign in the same order that we send on this socket
* and avoid races inside tcp sendmsg code that could cause corruption
......@@ -865,16 +979,33 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
mutex_lock(&ses->server->srv_mutex);
/*
* All the parts of the compound chain belong obtained credits from the
* same session (see the appropriate checks above). In the same time
* there might be reconnects after those checks but before we acquired
* the srv_mutex. We can not use credits obtained from the previous
* session to send this request. Check if there were reconnects after
* we obtained credits and return -EAGAIN in such cases to let callers
* handle it.
*/
if (first_instance != ses->server->reconnect_instance) {
mutex_unlock(&ses->server->srv_mutex);
for (j = 0; j < num_rqst; j++)
add_credits(ses->server, &credits[j], optype);
return -EAGAIN;
}
for (i = 0; i < num_rqst; i++) {
midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]);
if (IS_ERR(midQ[i])) {
revert_current_mid(ses->server, i);
for (j = 0; j < i; j++)
cifs_delete_mid(midQ[j]);
mutex_unlock(&ses->server->srv_mutex);
/* Update # of requests on wire to server */
for (j = 0; j < num_rqst; j++)
add_credits(ses->server, credits[j], optype);
add_credits(ses->server, &credits[j], optype);
return PTR_ERR(midQ[i]);
}
......@@ -897,15 +1028,17 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
for (i = 0; i < num_rqst; i++)
cifs_save_when_sent(midQ[i]);
if (rc < 0)
if (rc < 0) {
revert_current_mid(ses->server, num_rqst);
ses->server->sequence_number -= 2;
}
mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) {
/* Sending failed for some reason - return credits back */
for (i = 0; i < num_rqst; i++)
add_credits(ses->server, credits[i], optype);
add_credits(ses->server, &credits[i], optype);
goto out;
}
......@@ -942,7 +1075,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
midQ[i]->callback = cifs_cancelled_callback;
cancelled_mid[i] = true;
credits[i] = 0;
credits[i].value = 0;
}
spin_unlock(&GlobalMid_Lock);
}
......@@ -1068,6 +1201,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
struct cifs_credits credits = { .value = 1, .instance = 0 };
if (ses == NULL) {
cifs_dbg(VFS, "Null smb session\n");
......@@ -1091,7 +1225,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
return -EIO;
}
rc = wait_for_free_request(ses->server, timeout, 0);
rc = wait_for_free_request(ses->server, timeout, 0, &credits.instance);
if (rc)
return rc;
......@@ -1105,7 +1239,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
if (rc) {
mutex_unlock(&ses->server->srv_mutex);
/* Update # of requests on wire to server */
add_credits(ses->server, 1, 0);
add_credits(ses->server, &credits, 0);
return rc;
}
......@@ -1141,7 +1275,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
/* no longer considered to be "in-flight" */
midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock);
add_credits(ses->server, 1, 0);
add_credits(ses->server, &credits, 0);
return rc;
}
spin_unlock(&GlobalMid_Lock);
......@@ -1149,7 +1283,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
rc = cifs_sync_mid_result(midQ, ses->server);
if (rc != 0) {
add_credits(ses->server, 1, 0);
add_credits(ses->server, &credits, 0);
return rc;
}
......@@ -1165,7 +1299,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
rc = cifs_check_receive(midQ, ses->server, 0);
out:
cifs_delete_mid(midQ);
add_credits(ses->server, 1, 0);
add_credits(ses->server, &credits, 0);
return rc;
}
......@@ -1207,6 +1341,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
unsigned int instance;
if (tcon == NULL || tcon->ses == NULL) {
cifs_dbg(VFS, "Null smb session\n");
......@@ -1232,7 +1367,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
return -EIO;
}
rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, 0);
rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, 0,
&instance);
if (rc)
return rc;
......
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