smb3/cifs fixes including for large i/o error cases, fixes for 3 xfstests, improved crediting (smb3 flow control); improved tracing
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAlyCm8sACgkQiiy9cAdy T1HeQwwAvlV0Wkbg+c24musbdsKLZ0WpT8SFV1gaA0hA8090WxfETp+RxuUbHefY /pHfZj0zIsi2LEU22zw9tZtypKPD8d/Zt6LTZjCwW55c6HepTxnAwUDwpkGHD02e Rcau4ahGIWrcrbCJikG8XrAk+4ZOy52Vjf15JQR2Z8Ctt79UBJMqtKl6P90aZL/W HcpKNhrV+J7bLoWzZYRomKq/nnL+qKEV2JLk8ig065auBQWMKvvyBQAanmJG+vBf sgqXuE4x+cxpzClkJQ4aWMeEkgjyWuhRk6+y5qXaAGCHtUVotkZse1Dwtx52HjcW f6yC80CT2+vf7yoXW2YqJR28pJQoACbtZPgqMUPe+n6ojZIogWwJ2cBgVKqg8+dY IxTrGNHqvVy8jz3T8A9H4xRKse53Oob5gzAYnxk8zjFzt3aj/2mOw4YKENzHLzVI iJRLsBXo9JpSgEoiGDvGseabc5Z98BdQ7rzhg0kT2U9g+lopN42FRY5UOaG6n1oz V6tY2+FU =w3ho -----END PGP SIGNATURE----- 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() ...
This commit is contained in:
commit
e519a8c2c3
@ -159,6 +159,7 @@ config CIFS_DEBUG
|
|||||||
Enabling this option adds helpful debugging messages to
|
Enabling this option adds helpful debugging messages to
|
||||||
the cifs code which increases the size of the cifs module.
|
the cifs code which increases the size of the cifs module.
|
||||||
If unsure, say Y.
|
If unsure, say Y.
|
||||||
|
|
||||||
config CIFS_DEBUG2
|
config CIFS_DEBUG2
|
||||||
bool "Enable additional CIFS debugging routines"
|
bool "Enable additional CIFS debugging routines"
|
||||||
depends on CIFS_DEBUG
|
depends on CIFS_DEBUG
|
||||||
@ -215,4 +216,3 @@ config CIFS_FSCACHE
|
|||||||
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
|
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
|
to be cached locally on disk through the general filesystem cache
|
||||||
manager. If unsure, say N.
|
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: ref path: %s\n", ref->path_name);
|
||||||
cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_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);
|
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);
|
ref->ref_flag, ref->path_consumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ struct cifs_sb_info {
|
|||||||
spinlock_t tlink_tree_lock;
|
spinlock_t tlink_tree_lock;
|
||||||
struct tcon_link *master_tlink;
|
struct tcon_link *master_tlink;
|
||||||
struct nls_table *local_nls;
|
struct nls_table *local_nls;
|
||||||
|
unsigned int bsize;
|
||||||
unsigned int rsize;
|
unsigned int rsize;
|
||||||
unsigned int wsize;
|
unsigned int wsize;
|
||||||
unsigned long actimeo; /* attribute cache timeout (jiffies) */
|
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");
|
seq_puts(s, "ntlm");
|
||||||
break;
|
break;
|
||||||
case Kerberos:
|
case Kerberos:
|
||||||
seq_puts(s, "krb5");
|
seq_printf(s, "krb5,cruid=%u", from_kuid_munged(&init_user_ns,ses->cred_uid));
|
||||||
break;
|
break;
|
||||||
case RawNTLMSSP:
|
case RawNTLMSSP:
|
||||||
seq_puts(s, "ntlmssp");
|
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, ",rsize=%u", cifs_sb->rsize);
|
||||||
seq_printf(s, ",wsize=%u", cifs_sb->wsize);
|
seq_printf(s, ",wsize=%u", cifs_sb->wsize);
|
||||||
|
seq_printf(s, ",bsize=%u", cifs_sb->bsize);
|
||||||
seq_printf(s, ",echo_interval=%lu",
|
seq_printf(s, ",echo_interval=%lu",
|
||||||
tcon->ses->server->echo_interval / HZ);
|
tcon->ses->server->echo_interval / HZ);
|
||||||
if (tcon->snapshot_time)
|
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;
|
extern const struct export_operations cifs_export_ops;
|
||||||
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
||||||
|
|
||||||
#define CIFS_VERSION "2.17"
|
#define CIFS_VERSION "2.18"
|
||||||
#endif /* _CIFSFS_H */
|
#endif /* _CIFSFS_H */
|
||||||
|
@ -216,6 +216,7 @@ struct cifs_io_parms;
|
|||||||
struct cifs_search_info;
|
struct cifs_search_info;
|
||||||
struct cifsInodeInfo;
|
struct cifsInodeInfo;
|
||||||
struct cifs_open_parms;
|
struct cifs_open_parms;
|
||||||
|
struct cifs_credits;
|
||||||
|
|
||||||
struct smb_version_operations {
|
struct smb_version_operations {
|
||||||
int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
|
int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
|
||||||
@ -230,12 +231,15 @@ struct smb_version_operations {
|
|||||||
/* check response: verify signature, map error */
|
/* check response: verify signature, map error */
|
||||||
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
|
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
|
||||||
bool);
|
bool);
|
||||||
void (*add_credits)(struct TCP_Server_Info *, const unsigned int,
|
void (*add_credits)(struct TCP_Server_Info *server,
|
||||||
const int);
|
const struct cifs_credits *credits,
|
||||||
|
const int optype);
|
||||||
void (*set_credits)(struct TCP_Server_Info *, const int);
|
void (*set_credits)(struct TCP_Server_Info *, const int);
|
||||||
int * (*get_credits_field)(struct TCP_Server_Info *, const int);
|
int * (*get_credits_field)(struct TCP_Server_Info *, const int);
|
||||||
unsigned int (*get_credits)(struct mid_q_entry *);
|
unsigned int (*get_credits)(struct mid_q_entry *);
|
||||||
__u64 (*get_next_mid)(struct TCP_Server_Info *);
|
__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 */
|
/* data offset from read response message */
|
||||||
unsigned int (*read_data_offset)(char *);
|
unsigned int (*read_data_offset)(char *);
|
||||||
/*
|
/*
|
||||||
@ -383,8 +387,8 @@ struct smb_version_operations {
|
|||||||
struct cifs_fid *);
|
struct cifs_fid *);
|
||||||
/* calculate a size of SMB message */
|
/* calculate a size of SMB message */
|
||||||
unsigned int (*calc_smb_size)(void *buf, struct TCP_Server_Info *ptcpi);
|
unsigned int (*calc_smb_size)(void *buf, struct TCP_Server_Info *ptcpi);
|
||||||
/* check for STATUS_PENDING and process it in a positive case */
|
/* check for STATUS_PENDING and process the response if yes */
|
||||||
bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
|
bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server);
|
||||||
/* check for STATUS_NETWORK_SESSION_EXPIRED */
|
/* check for STATUS_NETWORK_SESSION_EXPIRED */
|
||||||
bool (*is_session_expired)(char *);
|
bool (*is_session_expired)(char *);
|
||||||
/* send oplock break response */
|
/* send oplock break response */
|
||||||
@ -452,7 +456,11 @@ struct smb_version_operations {
|
|||||||
unsigned int (*wp_retry_size)(struct inode *);
|
unsigned int (*wp_retry_size)(struct inode *);
|
||||||
/* get mtu credits */
|
/* get mtu credits */
|
||||||
int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int,
|
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 */
|
/* check if we need to issue closedir */
|
||||||
bool (*dir_needs_close)(struct cifsFileInfo *);
|
bool (*dir_needs_close)(struct cifsFileInfo *);
|
||||||
long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
|
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 resilient:1; /* noresilient not required since not fored for CA */
|
||||||
bool domainauto:1;
|
bool domainauto:1;
|
||||||
bool rdma:1;
|
bool rdma:1;
|
||||||
|
unsigned int bsize;
|
||||||
unsigned int rsize;
|
unsigned int rsize;
|
||||||
unsigned int wsize;
|
unsigned int wsize;
|
||||||
bool sockopt_tcp_nodelay:1;
|
bool sockopt_tcp_nodelay:1;
|
||||||
@ -710,6 +719,11 @@ struct TCP_Server_Info {
|
|||||||
int nr_targets;
|
int nr_targets;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct cifs_credits {
|
||||||
|
unsigned int value;
|
||||||
|
unsigned int instance;
|
||||||
|
};
|
||||||
|
|
||||||
static inline unsigned int
|
static inline unsigned int
|
||||||
in_flight(struct TCP_Server_Info *server)
|
in_flight(struct TCP_Server_Info *server)
|
||||||
{
|
{
|
||||||
@ -731,18 +745,18 @@ has_credits(struct TCP_Server_Info *server, int *credits)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
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)
|
const int optype)
|
||||||
{
|
{
|
||||||
server->ops->add_credits(server, add, optype);
|
server->ops->add_credits(server, credits, optype);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add,
|
add_credits_and_wake_if(struct TCP_Server_Info *server,
|
||||||
const int optype)
|
const struct cifs_credits *credits, const int optype)
|
||||||
{
|
{
|
||||||
if (add) {
|
if (credits->value) {
|
||||||
server->ops->add_credits(server, add, optype);
|
server->ops->add_credits(server, credits, optype);
|
||||||
wake_up(&server->request_q);
|
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);
|
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
|
static inline __le64
|
||||||
get_next_mid64(struct TCP_Server_Info *server)
|
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);
|
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
|
static inline __u16
|
||||||
get_mid(const struct smb_hdr *smb)
|
get_mid(const struct smb_hdr *smb)
|
||||||
{
|
{
|
||||||
@ -1234,7 +1272,7 @@ struct cifs_readdata {
|
|||||||
unsigned int pagesz;
|
unsigned int pagesz;
|
||||||
unsigned int page_offset;
|
unsigned int page_offset;
|
||||||
unsigned int tailsz;
|
unsigned int tailsz;
|
||||||
unsigned int credits;
|
struct cifs_credits credits;
|
||||||
unsigned int nr_pages;
|
unsigned int nr_pages;
|
||||||
struct page **pages;
|
struct page **pages;
|
||||||
};
|
};
|
||||||
@ -1260,7 +1298,7 @@ struct cifs_writedata {
|
|||||||
unsigned int pagesz;
|
unsigned int pagesz;
|
||||||
unsigned int page_offset;
|
unsigned int page_offset;
|
||||||
unsigned int tailsz;
|
unsigned int tailsz;
|
||||||
unsigned int credits;
|
struct cifs_credits credits;
|
||||||
unsigned int nr_pages;
|
unsigned int nr_pages;
|
||||||
struct page **pages;
|
struct page **pages;
|
||||||
};
|
};
|
||||||
@ -1422,6 +1460,7 @@ struct mid_q_entry {
|
|||||||
struct kref refcount;
|
struct kref refcount;
|
||||||
struct TCP_Server_Info *server; /* server corresponding to this mid */
|
struct TCP_Server_Info *server; /* server corresponding to this mid */
|
||||||
__u64 mid; /* multiplex id */
|
__u64 mid; /* multiplex id */
|
||||||
|
__u16 credits; /* number of credits consumed by this mid */
|
||||||
__u32 pid; /* process id */
|
__u32 pid; /* process id */
|
||||||
__u32 sequence_number; /* for CIFS signing */
|
__u32 sequence_number; /* for CIFS signing */
|
||||||
unsigned long when_alloc; /* when mid was created */
|
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,
|
extern int cifs_call_async(struct TCP_Server_Info *server,
|
||||||
struct smb_rqst *rqst,
|
struct smb_rqst *rqst,
|
||||||
mid_receive_t *receive, mid_callback_t *callback,
|
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,
|
extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||||
struct smb_rqst *rqst, int *resp_buf_type,
|
struct smb_rqst *rqst, int *resp_buf_type,
|
||||||
const int flags, struct kvec *resp_iov);
|
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);
|
struct TCP_Server_Info *server, bool log_error);
|
||||||
extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
|
extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
|
||||||
unsigned int size, unsigned int *num,
|
unsigned int size, unsigned int *num,
|
||||||
unsigned int *credits);
|
struct cifs_credits *credits);
|
||||||
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
|
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
|
||||||
struct kvec *, int /* nvec to send */,
|
struct kvec *, int /* nvec to send */,
|
||||||
int * /* type of buf returned */, const int flags,
|
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,
|
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
|
||||||
unsigned int bytes_written);
|
unsigned int bytes_written);
|
||||||
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
|
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 struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
|
||||||
extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server);
|
extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server);
|
||||||
extern int decode_negTokenInit(unsigned char *security_blob, int length,
|
extern int decode_negTokenInit(unsigned char *security_blob, int length,
|
||||||
|
@ -139,7 +139,7 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (tcon->ipc) {
|
if (tcon->ipc) {
|
||||||
snprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
|
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
|
||||||
tcon->ses->server->hostname);
|
tcon->ses->server->hostname);
|
||||||
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
|
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
|
||||||
goto out;
|
goto out;
|
||||||
@ -172,7 +172,7 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
|
scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
|
||||||
|
|
||||||
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
|
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
@ -822,9 +822,10 @@ static void
|
|||||||
cifs_echo_callback(struct mid_q_entry *mid)
|
cifs_echo_callback(struct mid_q_entry *mid)
|
||||||
{
|
{
|
||||||
struct TCP_Server_Info *server = mid->callback_data;
|
struct TCP_Server_Info *server = mid->callback_data;
|
||||||
|
struct cifs_credits credits = { .value = 1, .instance = 0 };
|
||||||
|
|
||||||
DeleteMidQEntry(mid);
|
DeleteMidQEntry(mid);
|
||||||
add_credits(server, 1, CIFS_ECHO_OP);
|
add_credits(server, &credits, CIFS_ECHO_OP);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -859,7 +860,7 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
|
|||||||
iov[1].iov_base = (char *)smb + 4;
|
iov[1].iov_base = (char *)smb + 4;
|
||||||
|
|
||||||
rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL,
|
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)
|
if (rc)
|
||||||
cifs_dbg(FYI, "Echo request failed: %d\n", 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 &&
|
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);
|
cifs_discard_remaining_data(server);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set up first two iov for signature check and to get credits */
|
/* set up first two iov for signature check and to get credits */
|
||||||
rdata->iov[0].iov_base = buf;
|
rdata->iov[0].iov_base = buf;
|
||||||
rdata->iov[0].iov_len = 4;
|
rdata->iov[0].iov_len = server->vals->header_preamble_size;
|
||||||
rdata->iov[1].iov_base = buf + 4;
|
rdata->iov[1].iov_base = buf + server->vals->header_preamble_size;
|
||||||
rdata->iov[1].iov_len = server->total_read - 4;
|
rdata->iov[1].iov_len =
|
||||||
|
server->total_read - server->vals->header_preamble_size;
|
||||||
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
|
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
|
||||||
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
|
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
|
||||||
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
|
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_npages = rdata->nr_pages,
|
||||||
.rq_pagesz = rdata->pagesz,
|
.rq_pagesz = rdata->pagesz,
|
||||||
.rq_tailsz = rdata->tailsz };
|
.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",
|
cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
|
||||||
__func__, mid->mid, mid->mid_state, rdata->result,
|
__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);
|
queue_work(cifsiod_wq, &rdata->work);
|
||||||
DeleteMidQEntry(mid);
|
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 */
|
/* 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);
|
kref_get(&rdata->refcount);
|
||||||
rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
|
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)
|
if (rc == 0)
|
||||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
|
||||||
@ -2123,14 +2126,18 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
|
|||||||
wdata2->tailsz = tailsz;
|
wdata2->tailsz = tailsz;
|
||||||
wdata2->bytes = cur_len;
|
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) {
|
if (!wdata2->cfile) {
|
||||||
cifs_dbg(VFS, "No writable handles for inode\n");
|
cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n",
|
||||||
|
rc);
|
||||||
|
if (!is_retryable_error(rc))
|
||||||
rc = -EBADF;
|
rc = -EBADF;
|
||||||
break;
|
} else {
|
||||||
}
|
|
||||||
wdata2->pid = wdata2->cfile->pid;
|
wdata2->pid = wdata2->cfile->pid;
|
||||||
rc = server->ops->async_writev(wdata2, cifs_writedata_release);
|
rc = server->ops->async_writev(wdata2,
|
||||||
|
cifs_writedata_release);
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < nr_pages; j++) {
|
for (j = 0; j < nr_pages; j++) {
|
||||||
unlock_page(wdata2->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);
|
kref_put(&wdata2->refcount, cifs_writedata_release);
|
||||||
if (is_retryable_error(rc))
|
if (is_retryable_error(rc))
|
||||||
continue;
|
continue;
|
||||||
|
i += nr_pages;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2152,6 +2160,13 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
|
|||||||
i += nr_pages;
|
i += nr_pages;
|
||||||
} while (i < wdata->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))
|
if (rc != 0 && !is_retryable_error(rc))
|
||||||
mapping_set_error(inode->i_mapping, rc);
|
mapping_set_error(inode->i_mapping, rc);
|
||||||
kref_put(&wdata->refcount, cifs_writedata_release);
|
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);
|
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
|
||||||
unsigned int written;
|
unsigned int written;
|
||||||
WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
|
WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
|
||||||
|
struct cifs_credits credits = { .value = 1, .instance = 0 };
|
||||||
|
|
||||||
switch (mid->mid_state) {
|
switch (mid->mid_state) {
|
||||||
case MID_RESPONSE_RECEIVED:
|
case MID_RESPONSE_RECEIVED:
|
||||||
@ -2261,7 +2277,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
|
|||||||
|
|
||||||
queue_work(cifsiod_wq, &wdata->work);
|
queue_work(cifsiod_wq, &wdata->work);
|
||||||
DeleteMidQEntry(mid);
|
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 */
|
/* 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);
|
kref_get(&wdata->refcount);
|
||||||
rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
|
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)
|
if (rc == 0)
|
||||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
|
||||||
|
@ -102,7 +102,7 @@ enum {
|
|||||||
Opt_backupuid, Opt_backupgid, Opt_uid,
|
Opt_backupuid, Opt_backupgid, Opt_uid,
|
||||||
Opt_cruid, Opt_gid, Opt_file_mode,
|
Opt_cruid, Opt_gid, Opt_file_mode,
|
||||||
Opt_dirmode, Opt_port,
|
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_echo_interval, Opt_max_credits,
|
||||||
Opt_snapshot,
|
Opt_snapshot,
|
||||||
|
|
||||||
@ -204,6 +204,7 @@ static const match_table_t cifs_mount_option_tokens = {
|
|||||||
{ Opt_dirmode, "dirmode=%s" },
|
{ Opt_dirmode, "dirmode=%s" },
|
||||||
{ Opt_dirmode, "dir_mode=%s" },
|
{ Opt_dirmode, "dir_mode=%s" },
|
||||||
{ Opt_port, "port=%s" },
|
{ Opt_port, "port=%s" },
|
||||||
|
{ Opt_blocksize, "bsize=%s" },
|
||||||
{ Opt_rsize, "rsize=%s" },
|
{ Opt_rsize, "rsize=%s" },
|
||||||
{ Opt_wsize, "wsize=%s" },
|
{ Opt_wsize, "wsize=%s" },
|
||||||
{ Opt_actimeo, "actimeo=%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__);
|
cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
snprintf(unc, len, "\\\\%s", server->hostname);
|
scnprintf(unc, len, "\\\\%s", server->hostname);
|
||||||
|
|
||||||
rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
|
rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
|
||||||
kfree(unc);
|
kfree(unc);
|
||||||
@ -592,6 +593,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||||||
msleep(3000);
|
msleep(3000);
|
||||||
} else {
|
} else {
|
||||||
atomic_inc(&tcpSesReconnectCount);
|
atomic_inc(&tcpSesReconnectCount);
|
||||||
|
set_credits(server, 1);
|
||||||
spin_lock(&GlobalMid_Lock);
|
spin_lock(&GlobalMid_Lock);
|
||||||
if (server->tcpStatus != CifsExiting)
|
if (server->tcpStatus != CifsExiting)
|
||||||
server->tcpStatus = CifsNeedNegotiate;
|
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 &&
|
if (server->ops->is_status_pending &&
|
||||||
server->ops->is_status_pending(buf, server, length))
|
server->ops->is_status_pending(buf, server))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!mid)
|
if (!mid)
|
||||||
@ -1063,6 +1065,26 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
|||||||
return 0;
|
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
|
static int
|
||||||
cifs_demultiplex_thread(void *p)
|
cifs_demultiplex_thread(void *p)
|
||||||
{
|
{
|
||||||
@ -1192,6 +1214,7 @@ next_pdu:
|
|||||||
} else if (server->ops->is_oplock_break &&
|
} else if (server->ops->is_oplock_break &&
|
||||||
server->ops->is_oplock_break(bufs[i],
|
server->ops->is_oplock_break(bufs[i],
|
||||||
server)) {
|
server)) {
|
||||||
|
smb2_add_credits_from_hdr(bufs[i], server);
|
||||||
cifs_dbg(FYI, "Received oplock break\n");
|
cifs_dbg(FYI, "Received oplock break\n");
|
||||||
} else {
|
} else {
|
||||||
cifs_dbg(VFS, "No task to wake, unknown frame "
|
cifs_dbg(VFS, "No task to wake, unknown frame "
|
||||||
@ -1203,6 +1226,7 @@ next_pdu:
|
|||||||
if (server->ops->dump_detail)
|
if (server->ops->dump_detail)
|
||||||
server->ops->dump_detail(bufs[i],
|
server->ops->dump_detail(bufs[i],
|
||||||
server);
|
server);
|
||||||
|
smb2_add_credits_from_hdr(bufs[i], server);
|
||||||
cifs_dump_mids(server);
|
cifs_dump_mids(server);
|
||||||
#endif /* CIFS_DEBUG2 */
|
#endif /* CIFS_DEBUG2 */
|
||||||
}
|
}
|
||||||
@ -1486,6 +1510,11 @@ cifs_parse_devname(const char *devname, struct smb_vol *vol)
|
|||||||
const char *delims = "/\\";
|
const char *delims = "/\\";
|
||||||
size_t len;
|
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 */
|
/* make sure we have a valid UNC double delimiter prefix */
|
||||||
len = strspn(devname, delims);
|
len = strspn(devname, delims);
|
||||||
if (len != 2)
|
if (len != 2)
|
||||||
@ -1571,7 +1600,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
|||||||
vol->cred_uid = current_uid();
|
vol->cred_uid = current_uid();
|
||||||
vol->linux_uid = current_uid();
|
vol->linux_uid = current_uid();
|
||||||
vol->linux_gid = current_gid();
|
vol->linux_gid = current_gid();
|
||||||
|
vol->bsize = 1024 * 1024; /* can improve cp performance significantly */
|
||||||
/*
|
/*
|
||||||
* default to SFM style remapping of seven reserved characters
|
* default to SFM style remapping of seven reserved characters
|
||||||
* unless user overrides it or we negotiate CIFS POSIX where
|
* 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;
|
port = (unsigned short)option;
|
||||||
break;
|
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:
|
case Opt_rsize:
|
||||||
if (get_option_ul(args, &option)) {
|
if (get_option_ul(args, &option)) {
|
||||||
cifs_dbg(VFS, "%s: Invalid rsize value\n",
|
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);
|
volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
|
||||||
tcp_ses->session_estab = false;
|
tcp_ses->session_estab = false;
|
||||||
tcp_ses->sequence_number = 0;
|
tcp_ses->sequence_number = 0;
|
||||||
tcp_ses->reconnect_instance = 0;
|
tcp_ses->reconnect_instance = 1;
|
||||||
tcp_ses->lstrp = jiffies;
|
tcp_ses->lstrp = jiffies;
|
||||||
spin_lock_init(&tcp_ses->req_lock);
|
spin_lock_init(&tcp_ses->req_lock);
|
||||||
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
|
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)
|
if (tcon == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
snprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname);
|
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname);
|
||||||
|
|
||||||
/* cannot fail */
|
/* cannot fail */
|
||||||
nls_codepage = load_nls_default();
|
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);
|
spin_lock_init(&cifs_sb->tlink_tree_lock);
|
||||||
cifs_sb->tlink_tree = RB_ROOT;
|
cifs_sb->tlink_tree = RB_ROOT;
|
||||||
|
|
||||||
|
cifs_sb->bsize = pvolume_info->bsize;
|
||||||
/*
|
/*
|
||||||
* Temporarily set r/wsize for matching superblock. If we end up using
|
* Temporarily set r/wsize for matching superblock. If we end up using
|
||||||
* new sb then client will later negotiate it downward if needed.
|
* 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);
|
new_unc = kmalloc(len, GFP_KERNEL);
|
||||||
if (!new_unc)
|
if (!new_unc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
snprintf(new_unc, len, "\\%s", tgt);
|
scnprintf(new_unc, len, "\\%s", tgt);
|
||||||
|
|
||||||
kfree(vol->UNC);
|
kfree(vol->UNC);
|
||||||
vol->UNC = new_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))
|
if (!server->ops->need_neg(server))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
set_credits(server, 1);
|
|
||||||
|
|
||||||
rc = server->ops->negotiate(xid, ses);
|
rc = server->ops->negotiate(xid, ses);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
spin_lock(&GlobalMid_Lock);
|
spin_lock(&GlobalMid_Lock);
|
||||||
|
247
fs/cifs/file.c
247
fs/cifs/file.c
@ -1842,24 +1842,30 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
|
/* Return -EBADF if no handle is found and general rc otherwise */
|
||||||
bool fsuid_only)
|
int
|
||||||
|
cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only,
|
||||||
|
struct cifsFileInfo **ret_file)
|
||||||
{
|
{
|
||||||
struct cifsFileInfo *open_file, *inv_file = NULL;
|
struct cifsFileInfo *open_file, *inv_file = NULL;
|
||||||
struct cifs_sb_info *cifs_sb;
|
struct cifs_sb_info *cifs_sb;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
bool any_available = false;
|
bool any_available = false;
|
||||||
int rc;
|
int rc = -EBADF;
|
||||||
unsigned int refind = 0;
|
unsigned int refind = 0;
|
||||||
|
|
||||||
/* Having a null inode here (because mapping->host was set to zero by
|
*ret_file = NULL;
|
||||||
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 */
|
/*
|
||||||
|
* 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) {
|
if (cifs_inode == NULL) {
|
||||||
cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n");
|
cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n");
|
||||||
dump_stack();
|
dump_stack();
|
||||||
return NULL;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
|
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:
|
refind_writable:
|
||||||
if (refind > MAX_REOPEN_ATT) {
|
if (refind > MAX_REOPEN_ATT) {
|
||||||
spin_unlock(&tcon->open_file_lock);
|
spin_unlock(&tcon->open_file_lock);
|
||||||
return NULL;
|
return rc;
|
||||||
}
|
}
|
||||||
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||||
if (!any_available && open_file->pid != current->tgid)
|
if (!any_available && open_file->pid != current->tgid)
|
||||||
@ -1885,7 +1891,8 @@ refind_writable:
|
|||||||
/* found a good writable file */
|
/* found a good writable file */
|
||||||
cifsFileInfo_get(open_file);
|
cifsFileInfo_get(open_file);
|
||||||
spin_unlock(&tcon->open_file_lock);
|
spin_unlock(&tcon->open_file_lock);
|
||||||
return open_file;
|
*ret_file = open_file;
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
if (!inv_file)
|
if (!inv_file)
|
||||||
inv_file = open_file;
|
inv_file = open_file;
|
||||||
@ -1907,12 +1914,13 @@ refind_writable:
|
|||||||
|
|
||||||
if (inv_file) {
|
if (inv_file) {
|
||||||
rc = cifs_reopen_file(inv_file, false);
|
rc = cifs_reopen_file(inv_file, false);
|
||||||
if (!rc)
|
if (!rc) {
|
||||||
return inv_file;
|
*ret_file = inv_file;
|
||||||
else {
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock(&tcon->open_file_lock);
|
spin_lock(&tcon->open_file_lock);
|
||||||
list_move_tail(&inv_file->flist,
|
list_move_tail(&inv_file->flist, &cifs_inode->openFileList);
|
||||||
&cifs_inode->openFileList);
|
|
||||||
spin_unlock(&tcon->open_file_lock);
|
spin_unlock(&tcon->open_file_lock);
|
||||||
cifsFileInfo_put(inv_file);
|
cifsFileInfo_put(inv_file);
|
||||||
++refind;
|
++refind;
|
||||||
@ -1920,9 +1928,21 @@ refind_writable:
|
|||||||
spin_lock(&tcon->open_file_lock);
|
spin_lock(&tcon->open_file_lock);
|
||||||
goto refind_writable;
|
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)
|
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)
|
if (mapping->host->i_size - offset < (loff_t)to)
|
||||||
to = (unsigned)(mapping->host->i_size - offset);
|
to = (unsigned)(mapping->host->i_size - offset);
|
||||||
|
|
||||||
open_file = find_writable_file(CIFS_I(mapping->host), false);
|
rc = cifs_get_writable_file(CIFS_I(mapping->host), false, &open_file);
|
||||||
if (open_file) {
|
if (!rc) {
|
||||||
bytes_written = cifs_write(open_file, open_file->pid,
|
bytes_written = cifs_write(open_file, open_file->pid,
|
||||||
write_data, to - from, &offset);
|
write_data, to - from, &offset);
|
||||||
cifsFileInfo_put(open_file);
|
cifsFileInfo_put(open_file);
|
||||||
@ -1970,8 +1990,11 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|||||||
rc = 0;
|
rc = 0;
|
||||||
else if (bytes_written < 0)
|
else if (bytes_written < 0)
|
||||||
rc = bytes_written;
|
rc = bytes_written;
|
||||||
|
else
|
||||||
|
rc = -EFAULT;
|
||||||
} else {
|
} else {
|
||||||
cifs_dbg(FYI, "No writeable filehandles for inode\n");
|
cifs_dbg(FYI, "No writable handle for write page rc=%d\n", rc);
|
||||||
|
if (!is_retryable_error(rc))
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2079,9 +2102,9 @@ static int
|
|||||||
wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
|
wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
|
||||||
struct address_space *mapping, struct writeback_control *wbc)
|
struct address_space *mapping, struct writeback_control *wbc)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc;
|
||||||
struct TCP_Server_Info *server;
|
struct TCP_Server_Info *server =
|
||||||
unsigned int i;
|
tlink_tcon(wdata->cfile->tlink)->ses->server;
|
||||||
|
|
||||||
wdata->sync_mode = wbc->sync_mode;
|
wdata->sync_mode = wbc->sync_mode;
|
||||||
wdata->nr_pages = nr_pages;
|
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]),
|
page_offset(wdata->pages[nr_pages - 1]),
|
||||||
(loff_t)PAGE_SIZE);
|
(loff_t)PAGE_SIZE);
|
||||||
wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz;
|
wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz;
|
||||||
|
|
||||||
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;
|
wdata->pid = wdata->cfile->pid;
|
||||||
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
|
|
||||||
rc = server->ops->async_writev(wdata, cifs_writedata_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < nr_pages; ++i)
|
rc = adjust_credits(server, &wdata->credits, wdata->bytes);
|
||||||
unlock_page(wdata->pages[i]);
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (wdata->cfile->invalidHandle)
|
||||||
|
rc = -EAGAIN;
|
||||||
|
else
|
||||||
|
rc = server->ops->async_writev(wdata, cifs_writedata_release);
|
||||||
|
|
||||||
return rc;
|
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,
|
static int cifs_writepages(struct address_space *mapping,
|
||||||
struct writeback_control *wbc)
|
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;
|
struct TCP_Server_Info *server;
|
||||||
bool done = false, scanned = false, range_whole = false;
|
bool done = false, scanned = false, range_whole = false;
|
||||||
pgoff_t end, index;
|
pgoff_t end, index;
|
||||||
struct cifs_writedata *wdata;
|
struct cifs_writedata *wdata;
|
||||||
|
struct cifsFileInfo *cfile = NULL;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int saved_rc = 0;
|
int saved_rc = 0;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
@ -2143,11 +2163,23 @@ static int cifs_writepages(struct address_space *mapping,
|
|||||||
server = cifs_sb_master_tcon(cifs_sb)->ses->server;
|
server = cifs_sb_master_tcon(cifs_sb)->ses->server;
|
||||||
retry:
|
retry:
|
||||||
while (!done && index <= end) {
|
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;
|
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,
|
rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
|
||||||
&wsize, &credits);
|
&wsize, credits);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
@ -2180,13 +2212,26 @@ retry:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
wdata->credits = credits;
|
wdata->credits = credits_on_stack;
|
||||||
|
wdata->cfile = cfile;
|
||||||
|
cfile = NULL;
|
||||||
|
|
||||||
|
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);
|
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 */
|
/* send failure -- clean up the mess */
|
||||||
if (rc != 0) {
|
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) {
|
for (i = 0; i < nr_pages; ++i) {
|
||||||
if (is_retryable_error(rc))
|
if (is_retryable_error(rc))
|
||||||
redirty_page_for_writepage(wbc,
|
redirty_page_for_writepage(wbc,
|
||||||
@ -2238,6 +2283,8 @@ retry:
|
|||||||
if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
|
if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
|
||||||
mapping->writeback_index = index;
|
mapping->writeback_index = index;
|
||||||
|
|
||||||
|
if (cfile)
|
||||||
|
cifsFileInfo_put(cfile);
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -2567,7 +2614,8 @@ static int
|
|||||||
cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
|
cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
|
||||||
struct cifs_aio_ctx *ctx)
|
struct cifs_aio_ctx *ctx)
|
||||||
{
|
{
|
||||||
unsigned int wsize, credits;
|
unsigned int wsize;
|
||||||
|
struct cifs_credits credits;
|
||||||
int rc;
|
int rc;
|
||||||
struct TCP_Server_Info *server =
|
struct TCP_Server_Info *server =
|
||||||
tlink_tcon(wdata->cfile->tlink)->ses->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
|
* Note: we are attempting to resend the whole wdata not in segments
|
||||||
*/
|
*/
|
||||||
do {
|
do {
|
||||||
rc = server->ops->wait_mtu_credits(
|
rc = server->ops->wait_mtu_credits(server, wdata->bytes, &wsize,
|
||||||
server, wdata->bytes, &wsize, &credits);
|
&credits);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (wsize < wdata->bytes) {
|
if (wsize < wdata->bytes) {
|
||||||
add_credits_and_wake_if(server, credits, 0);
|
add_credits_and_wake_if(server, &credits, 0);
|
||||||
msleep(1000);
|
msleep(1000);
|
||||||
}
|
}
|
||||||
} while (wsize < wdata->bytes);
|
} while (wsize < wdata->bytes);
|
||||||
|
|
||||||
|
wdata->credits = credits;
|
||||||
rc = -EAGAIN;
|
rc = -EAGAIN;
|
||||||
while (rc == -EAGAIN) {
|
while (rc == -EAGAIN) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
@ -2604,7 +2653,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_credits_and_wake_if(server, wdata->credits, 0);
|
add_credits_and_wake_if(server, &wdata->credits, 0);
|
||||||
out:
|
out:
|
||||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
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 TCP_Server_Info *server;
|
||||||
struct page **pagevec;
|
struct page **pagevec;
|
||||||
size_t start;
|
size_t start;
|
||||||
|
unsigned int xid;
|
||||||
|
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
|
||||||
pid = open_file->pid;
|
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;
|
pid = current->tgid;
|
||||||
|
|
||||||
server = tlink_tcon(open_file->tlink)->ses->server;
|
server = tlink_tcon(open_file->tlink)->ses->server;
|
||||||
|
xid = get_xid();
|
||||||
|
|
||||||
do {
|
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,
|
rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
|
||||||
&wsize, &credits);
|
&wsize, credits);
|
||||||
if (rc)
|
if (rc)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2731,16 +2792,22 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
|||||||
wdata->pid = pid;
|
wdata->pid = pid;
|
||||||
wdata->bytes = cur_len;
|
wdata->bytes = cur_len;
|
||||||
wdata->pagesz = PAGE_SIZE;
|
wdata->pagesz = PAGE_SIZE;
|
||||||
wdata->credits = credits;
|
wdata->credits = credits_on_stack;
|
||||||
wdata->ctx = ctx;
|
wdata->ctx = ctx;
|
||||||
kref_get(&ctx->refcount);
|
kref_get(&ctx->refcount);
|
||||||
|
|
||||||
if (!wdata->cfile->invalidHandle ||
|
rc = adjust_credits(server, &wdata->credits, wdata->bytes);
|
||||||
!(rc = cifs_reopen_file(wdata->cfile, false)))
|
|
||||||
|
if (!rc) {
|
||||||
|
if (wdata->cfile->invalidHandle)
|
||||||
|
rc = -EAGAIN;
|
||||||
|
else
|
||||||
rc = server->ops->async_writev(wdata,
|
rc = server->ops->async_writev(wdata,
|
||||||
cifs_uncached_writedata_release);
|
cifs_uncached_writedata_release);
|
||||||
|
}
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
add_credits_and_wake_if(server, wdata->credits, 0);
|
add_credits_and_wake_if(server, &wdata->credits, 0);
|
||||||
kref_put(&wdata->refcount,
|
kref_put(&wdata->refcount,
|
||||||
cifs_uncached_writedata_release);
|
cifs_uncached_writedata_release);
|
||||||
if (rc == -EAGAIN) {
|
if (rc == -EAGAIN) {
|
||||||
@ -2756,6 +2823,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
|||||||
len -= cur_len;
|
len -= cur_len;
|
||||||
} while (len > 0);
|
} while (len > 0);
|
||||||
|
|
||||||
|
free_xid(xid);
|
||||||
return rc;
|
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.
|
* these pages but not on the region from pos to ppos+len-1.
|
||||||
*/
|
*/
|
||||||
written = cifs_user_writev(iocb, from);
|
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
|
* We have read level caching and we have just sent a write
|
||||||
* request comes - break it on the client to prevent reading
|
* request to the server thus making data in the cache stale.
|
||||||
* an old data.
|
* 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_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);
|
inode);
|
||||||
cinode->oplock = 0;
|
cinode->oplock = 0;
|
||||||
}
|
}
|
||||||
@ -3260,7 +3330,8 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
|
|||||||
struct list_head *rdata_list,
|
struct list_head *rdata_list,
|
||||||
struct cifs_aio_ctx *ctx)
|
struct cifs_aio_ctx *ctx)
|
||||||
{
|
{
|
||||||
unsigned int rsize, credits;
|
unsigned int rsize;
|
||||||
|
struct cifs_credits credits;
|
||||||
int rc;
|
int rc;
|
||||||
struct TCP_Server_Info *server =
|
struct TCP_Server_Info *server =
|
||||||
tlink_tcon(rdata->cfile->tlink)->ses->server;
|
tlink_tcon(rdata->cfile->tlink)->ses->server;
|
||||||
@ -3277,11 +3348,12 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (rsize < rdata->bytes) {
|
if (rsize < rdata->bytes) {
|
||||||
add_credits_and_wake_if(server, credits, 0);
|
add_credits_and_wake_if(server, &credits, 0);
|
||||||
msleep(1000);
|
msleep(1000);
|
||||||
}
|
}
|
||||||
} while (rsize < rdata->bytes);
|
} while (rsize < rdata->bytes);
|
||||||
|
|
||||||
|
rdata->credits = credits;
|
||||||
rc = -EAGAIN;
|
rc = -EAGAIN;
|
||||||
while (rc == -EAGAIN) {
|
while (rc == -EAGAIN) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
@ -3297,7 +3369,7 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_credits_and_wake_if(server, rdata->credits, 0);
|
add_credits_and_wake_if(server, &rdata->credits, 0);
|
||||||
out:
|
out:
|
||||||
kref_put(&rdata->refcount,
|
kref_put(&rdata->refcount,
|
||||||
cifs_uncached_readdata_release);
|
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_aio_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct cifs_readdata *rdata;
|
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;
|
size_t cur_len;
|
||||||
int rc;
|
int rc;
|
||||||
pid_t pid;
|
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);
|
iov_iter_advance(&direct_iov, offset - ctx->pos);
|
||||||
|
|
||||||
do {
|
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,
|
rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
|
||||||
&rsize, &credits);
|
&rsize, credits);
|
||||||
if (rc)
|
if (rc)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3406,15 +3488,21 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
|
|||||||
rdata->pagesz = PAGE_SIZE;
|
rdata->pagesz = PAGE_SIZE;
|
||||||
rdata->read_into_pages = cifs_uncached_read_into_pages;
|
rdata->read_into_pages = cifs_uncached_read_into_pages;
|
||||||
rdata->copy_into_pages = cifs_uncached_copy_into_pages;
|
rdata->copy_into_pages = cifs_uncached_copy_into_pages;
|
||||||
rdata->credits = credits;
|
rdata->credits = credits_on_stack;
|
||||||
rdata->ctx = ctx;
|
rdata->ctx = ctx;
|
||||||
kref_get(&ctx->refcount);
|
kref_get(&ctx->refcount);
|
||||||
|
|
||||||
if (!rdata->cfile->invalidHandle ||
|
rc = adjust_credits(server, &rdata->credits, rdata->bytes);
|
||||||
!(rc = cifs_reopen_file(rdata->cfile, true)))
|
|
||||||
|
if (!rc) {
|
||||||
|
if (rdata->cfile->invalidHandle)
|
||||||
|
rc = -EAGAIN;
|
||||||
|
else
|
||||||
rc = server->ops->async_readv(rdata);
|
rc = server->ops->async_readv(rdata);
|
||||||
|
}
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
add_credits_and_wake_if(server, rdata->credits, 0);
|
add_credits_and_wake_if(server, &rdata->credits, 0);
|
||||||
kref_put(&rdata->refcount,
|
kref_put(&rdata->refcount,
|
||||||
cifs_uncached_readdata_release);
|
cifs_uncached_readdata_release);
|
||||||
if (rc == -EAGAIN) {
|
if (rc == -EAGAIN) {
|
||||||
@ -3533,8 +3621,6 @@ again:
|
|||||||
ctx->total_len = ctx->len - iov_iter_count(to);
|
ctx->total_len = ctx->len - iov_iter_count(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
cifs_stats_bytes_read(tcon, ctx->total_len);
|
|
||||||
|
|
||||||
/* mask nodata case */
|
/* mask nodata case */
|
||||||
if (rc == -ENODATA)
|
if (rc == -ENODATA)
|
||||||
rc = 0;
|
rc = 0;
|
||||||
@ -4095,10 +4181,19 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|||||||
loff_t offset;
|
loff_t offset;
|
||||||
struct page *page, *tpage;
|
struct page *page, *tpage;
|
||||||
struct cifs_readdata *rdata;
|
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,
|
rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
|
||||||
&rsize, &credits);
|
&rsize, credits);
|
||||||
if (rc)
|
if (rc)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -4144,18 +4239,24 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|||||||
rdata->tailsz = PAGE_SIZE;
|
rdata->tailsz = PAGE_SIZE;
|
||||||
rdata->read_into_pages = cifs_readpages_read_into_pages;
|
rdata->read_into_pages = cifs_readpages_read_into_pages;
|
||||||
rdata->copy_into_pages = cifs_readpages_copy_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_for_each_entry_safe(page, tpage, &tmplist, lru) {
|
||||||
list_del(&page->lru);
|
list_del(&page->lru);
|
||||||
rdata->pages[rdata->nr_pages++] = page;
|
rdata->pages[rdata->nr_pages++] = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rdata->cfile->invalidHandle ||
|
rc = adjust_credits(server, &rdata->credits, rdata->bytes);
|
||||||
!(rc = cifs_reopen_file(rdata->cfile, true)))
|
|
||||||
|
if (!rc) {
|
||||||
|
if (rdata->cfile->invalidHandle)
|
||||||
|
rc = -EAGAIN;
|
||||||
|
else
|
||||||
rc = server->ops->async_readv(rdata);
|
rc = server->ops->async_readv(rdata);
|
||||||
|
}
|
||||||
|
|
||||||
if (rc) {
|
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++) {
|
for (i = 0; i < rdata->nr_pages; i++) {
|
||||||
page = rdata->pages[i];
|
page = rdata->pages[i];
|
||||||
lru_cache_add_file(page);
|
lru_cache_add_file(page);
|
||||||
|
@ -2080,7 +2080,7 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
|
|||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
generic_fillattr(inode, stat);
|
generic_fillattr(inode, stat);
|
||||||
stat->blksize = CIFS_MAX_MSGSIZE;
|
stat->blksize = cifs_sb->bsize;
|
||||||
stat->ino = CIFS_I(inode)->uniqueid;
|
stat->ino = CIFS_I(inode)->uniqueid;
|
||||||
|
|
||||||
/* old CIFS Unix Extensions doesn't return create time */
|
/* old CIFS Unix Extensions doesn't return create time */
|
||||||
|
@ -103,7 +103,7 @@ parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(md5_str2, sizeof(md5_str2),
|
scnprintf(md5_str2, sizeof(md5_str2),
|
||||||
CIFS_MF_SYMLINK_MD5_FORMAT,
|
CIFS_MF_SYMLINK_MD5_FORMAT,
|
||||||
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
|
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(buf, buf_len,
|
scnprintf(buf, buf_len,
|
||||||
CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
|
CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
|
||||||
link_len,
|
link_len,
|
||||||
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
|
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
|
||||||
|
@ -117,11 +117,11 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add,
|
cifs_add_credits(struct TCP_Server_Info *server,
|
||||||
const int optype)
|
const struct cifs_credits *credits, const int optype)
|
||||||
{
|
{
|
||||||
spin_lock(&server->req_lock);
|
spin_lock(&server->req_lock);
|
||||||
server->credits += add;
|
server->credits += credits->value;
|
||||||
server->in_flight--;
|
server->in_flight--;
|
||||||
spin_unlock(&server->req_lock);
|
spin_unlock(&server->req_lock);
|
||||||
wake_up(&server->request_q);
|
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;
|
remaining = tgt_total_cnt - total_in_tgt;
|
||||||
|
|
||||||
if (remaining < 0) {
|
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);
|
tgt_total_cnt, total_in_tgt);
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
|
@ -517,7 +517,6 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
|
|||||||
__u8 lease_state;
|
__u8 lease_state;
|
||||||
struct list_head *tmp;
|
struct list_head *tmp;
|
||||||
struct cifsFileInfo *cfile;
|
struct cifsFileInfo *cfile;
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
|
||||||
struct cifs_pending_open *open;
|
struct cifs_pending_open *open;
|
||||||
struct cifsInodeInfo *cinode;
|
struct cifsInodeInfo *cinode;
|
||||||
int ack_req = le32_to_cpu(rsp->Flags &
|
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",
|
cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
|
||||||
le32_to_cpu(rsp->NewLeaseState));
|
le32_to_cpu(rsp->NewLeaseState));
|
||||||
|
|
||||||
server->ops->set_oplock_level(cinode, lease_state, 0, NULL);
|
|
||||||
|
|
||||||
if (ack_req)
|
if (ack_req)
|
||||||
cfile->oplock_break_cancelled = false;
|
cfile->oplock_break_cancelled = false;
|
||||||
else
|
else
|
||||||
cfile->oplock_break_cancelled = true;
|
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);
|
queue_work(cifsoplockd_wq, &cfile->oplock_break);
|
||||||
kfree(lw);
|
kfree(lw);
|
||||||
return true;
|
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)
|
if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK)
|
||||||
return false;
|
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 !=
|
if (rsp->StructureSize !=
|
||||||
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
|
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
|
||||||
if (le16_to_cpu(rsp->StructureSize) == 44)
|
if (le16_to_cpu(rsp->StructureSize) == 44)
|
||||||
|
@ -67,10 +67,13 @@ change_conf(struct TCP_Server_Info *server)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
|
smb2_add_credits(struct TCP_Server_Info *server,
|
||||||
const int optype)
|
const struct cifs_credits *credits, const int optype)
|
||||||
{
|
{
|
||||||
int *val, rc = -1;
|
int *val, rc = -1;
|
||||||
|
unsigned int add = credits->value;
|
||||||
|
unsigned int instance = credits->instance;
|
||||||
|
bool reconnect_detected = false;
|
||||||
|
|
||||||
spin_lock(&server->req_lock);
|
spin_lock(&server->req_lock);
|
||||||
val = server->ops->get_credits_field(server, optype);
|
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))
|
if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0))
|
||||||
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
|
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
|
||||||
server->hostname, *val);
|
server->hostname, *val);
|
||||||
|
if ((instance == 0) || (instance == server->reconnect_instance))
|
||||||
*val += add;
|
*val += add;
|
||||||
|
else
|
||||||
|
reconnect_detected = true;
|
||||||
|
|
||||||
if (*val > 65000) {
|
if (*val > 65000) {
|
||||||
*val = 65000; /* Don't get near 64K credits, avoid srv bugs */
|
*val = 65000; /* Don't get near 64K credits, avoid srv bugs */
|
||||||
printk_once(KERN_WARNING "server overflowed SMB3 credits\n");
|
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);
|
spin_unlock(&server->req_lock);
|
||||||
wake_up(&server->request_q);
|
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;
|
return;
|
||||||
|
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
@ -163,7 +174,7 @@ smb2_get_credits(struct mid_q_entry *mid)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
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;
|
int rc = 0;
|
||||||
unsigned int scredits;
|
unsigned int scredits;
|
||||||
@ -189,7 +200,8 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
|||||||
/* can deadlock with reopen */
|
/* can deadlock with reopen */
|
||||||
if (scredits <= 8) {
|
if (scredits <= 8) {
|
||||||
*num = SMB2_MAX_BUFFER_SIZE;
|
*num = SMB2_MAX_BUFFER_SIZE;
|
||||||
*credits = 0;
|
credits->value = 0;
|
||||||
|
credits->instance = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,8 +210,10 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
|||||||
*num = min_t(unsigned int, size,
|
*num = min_t(unsigned int, size,
|
||||||
scredits * SMB2_MAX_BUFFER_SIZE);
|
scredits * SMB2_MAX_BUFFER_SIZE);
|
||||||
|
|
||||||
*credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
|
credits->value =
|
||||||
server->credits -= *credits;
|
DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
|
||||||
|
credits->instance = server->reconnect_instance;
|
||||||
|
server->credits -= credits->value;
|
||||||
server->in_flight++;
|
server->in_flight++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -208,6 +222,38 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
|||||||
return rc;
|
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
|
static __u64
|
||||||
smb2_get_next_mid(struct TCP_Server_Info *server)
|
smb2_get_next_mid(struct TCP_Server_Info *server)
|
||||||
{
|
{
|
||||||
@ -219,6 +265,15 @@ smb2_get_next_mid(struct TCP_Server_Info *server)
|
|||||||
return mid;
|
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 *
|
static struct mid_q_entry *
|
||||||
smb2_find_mid(struct TCP_Server_Info *server, char *buf)
|
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;
|
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
|
||||||
memset(rsp_iov, 0, sizeof(rsp_iov));
|
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 */
|
/* Open */
|
||||||
memset(&open_iov, 0, sizeof(open_iov));
|
memset(&open_iov, 0, sizeof(open_iov));
|
||||||
rqst[0].rq_iov = 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.
|
* the number of credits and return true. Otherwise - return false.
|
||||||
*/
|
*/
|
||||||
static bool
|
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;
|
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
|
||||||
|
|
||||||
if (shdr->Status != STATUS_PENDING)
|
if (shdr->Status != STATUS_PENDING)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!length) {
|
if (shdr->CreditRequest) {
|
||||||
spin_lock(&server->req_lock);
|
spin_lock(&server->req_lock);
|
||||||
server->credits += le16_to_cpu(shdr->CreditRequest);
|
server->credits += le16_to_cpu(shdr->CreditRequest);
|
||||||
spin_unlock(&server->req_lock);
|
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);
|
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
|
static void
|
||||||
smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
||||||
unsigned int epoch, bool *purge_cache)
|
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 &&
|
if (server->ops->is_status_pending &&
|
||||||
server->ops->is_status_pending(buf, server, 0))
|
server->ops->is_status_pending(buf, server))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* set up first two iov to get credits */
|
/* set up first two iov to get credits */
|
||||||
rdata->iov[0].iov_base = buf;
|
rdata->iov[0].iov_base = buf;
|
||||||
rdata->iov[0].iov_len = 4;
|
rdata->iov[0].iov_len = 0;
|
||||||
rdata->iov[1].iov_base = buf + 4;
|
rdata->iov[1].iov_base = buf;
|
||||||
rdata->iov[1].iov_len =
|
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",
|
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
|
||||||
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
|
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
|
||||||
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
|
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,
|
.get_credits = smb2_get_credits,
|
||||||
.wait_mtu_credits = cifs_wait_mtu_credits,
|
.wait_mtu_credits = cifs_wait_mtu_credits,
|
||||||
.get_next_mid = smb2_get_next_mid,
|
.get_next_mid = smb2_get_next_mid,
|
||||||
|
.revert_current_mid = smb2_revert_current_mid,
|
||||||
.read_data_offset = smb2_read_data_offset,
|
.read_data_offset = smb2_read_data_offset,
|
||||||
.read_data_length = smb2_read_data_length,
|
.read_data_length = smb2_read_data_length,
|
||||||
.map_error = map_smb2_to_linux_error,
|
.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_field = smb2_get_credits_field,
|
||||||
.get_credits = smb2_get_credits,
|
.get_credits = smb2_get_credits,
|
||||||
.wait_mtu_credits = smb2_wait_mtu_credits,
|
.wait_mtu_credits = smb2_wait_mtu_credits,
|
||||||
|
.adjust_credits = smb2_adjust_credits,
|
||||||
.get_next_mid = smb2_get_next_mid,
|
.get_next_mid = smb2_get_next_mid,
|
||||||
|
.revert_current_mid = smb2_revert_current_mid,
|
||||||
.read_data_offset = smb2_read_data_offset,
|
.read_data_offset = smb2_read_data_offset,
|
||||||
.read_data_length = smb2_read_data_length,
|
.read_data_length = smb2_read_data_length,
|
||||||
.map_error = map_smb2_to_linux_error,
|
.map_error = map_smb2_to_linux_error,
|
||||||
@ -3646,7 +3723,7 @@ struct smb_version_operations smb21_operations = {
|
|||||||
.print_stats = smb2_print_stats,
|
.print_stats = smb2_print_stats,
|
||||||
.is_oplock_break = smb2_is_valid_oplock_break,
|
.is_oplock_break = smb2_is_valid_oplock_break,
|
||||||
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
||||||
.downgrade_oplock = smb2_downgrade_oplock,
|
.downgrade_oplock = smb21_downgrade_oplock,
|
||||||
.need_neg = smb2_need_neg,
|
.need_neg = smb2_need_neg,
|
||||||
.negotiate = smb2_negotiate,
|
.negotiate = smb2_negotiate,
|
||||||
.negotiate_wsize = smb2_negotiate_wsize,
|
.negotiate_wsize = smb2_negotiate_wsize,
|
||||||
@ -3731,7 +3808,9 @@ struct smb_version_operations smb30_operations = {
|
|||||||
.get_credits_field = smb2_get_credits_field,
|
.get_credits_field = smb2_get_credits_field,
|
||||||
.get_credits = smb2_get_credits,
|
.get_credits = smb2_get_credits,
|
||||||
.wait_mtu_credits = smb2_wait_mtu_credits,
|
.wait_mtu_credits = smb2_wait_mtu_credits,
|
||||||
|
.adjust_credits = smb2_adjust_credits,
|
||||||
.get_next_mid = smb2_get_next_mid,
|
.get_next_mid = smb2_get_next_mid,
|
||||||
|
.revert_current_mid = smb2_revert_current_mid,
|
||||||
.read_data_offset = smb2_read_data_offset,
|
.read_data_offset = smb2_read_data_offset,
|
||||||
.read_data_length = smb2_read_data_length,
|
.read_data_length = smb2_read_data_length,
|
||||||
.map_error = map_smb2_to_linux_error,
|
.map_error = map_smb2_to_linux_error,
|
||||||
@ -3743,7 +3822,7 @@ struct smb_version_operations smb30_operations = {
|
|||||||
.dump_share_caps = smb2_dump_share_caps,
|
.dump_share_caps = smb2_dump_share_caps,
|
||||||
.is_oplock_break = smb2_is_valid_oplock_break,
|
.is_oplock_break = smb2_is_valid_oplock_break,
|
||||||
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
||||||
.downgrade_oplock = smb2_downgrade_oplock,
|
.downgrade_oplock = smb21_downgrade_oplock,
|
||||||
.need_neg = smb2_need_neg,
|
.need_neg = smb2_need_neg,
|
||||||
.negotiate = smb2_negotiate,
|
.negotiate = smb2_negotiate,
|
||||||
.negotiate_wsize = smb3_negotiate_wsize,
|
.negotiate_wsize = smb3_negotiate_wsize,
|
||||||
@ -3836,7 +3915,9 @@ struct smb_version_operations smb311_operations = {
|
|||||||
.get_credits_field = smb2_get_credits_field,
|
.get_credits_field = smb2_get_credits_field,
|
||||||
.get_credits = smb2_get_credits,
|
.get_credits = smb2_get_credits,
|
||||||
.wait_mtu_credits = smb2_wait_mtu_credits,
|
.wait_mtu_credits = smb2_wait_mtu_credits,
|
||||||
|
.adjust_credits = smb2_adjust_credits,
|
||||||
.get_next_mid = smb2_get_next_mid,
|
.get_next_mid = smb2_get_next_mid,
|
||||||
|
.revert_current_mid = smb2_revert_current_mid,
|
||||||
.read_data_offset = smb2_read_data_offset,
|
.read_data_offset = smb2_read_data_offset,
|
||||||
.read_data_length = smb2_read_data_length,
|
.read_data_length = smb2_read_data_length,
|
||||||
.map_error = map_smb2_to_linux_error,
|
.map_error = map_smb2_to_linux_error,
|
||||||
@ -3848,7 +3929,7 @@ struct smb_version_operations smb311_operations = {
|
|||||||
.dump_share_caps = smb2_dump_share_caps,
|
.dump_share_caps = smb2_dump_share_caps,
|
||||||
.is_oplock_break = smb2_is_valid_oplock_break,
|
.is_oplock_break = smb2_is_valid_oplock_break,
|
||||||
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
||||||
.downgrade_oplock = smb2_downgrade_oplock,
|
.downgrade_oplock = smb21_downgrade_oplock,
|
||||||
.need_neg = smb2_need_neg,
|
.need_neg = smb2_need_neg,
|
||||||
.negotiate = smb2_negotiate,
|
.negotiate = smb2_negotiate,
|
||||||
.negotiate_wsize = smb3_negotiate_wsize,
|
.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;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
|
||||||
spin_lock(&server->req_lock);
|
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)
|
if (server->credits >= server->max_credits)
|
||||||
shdr->CreditRequest = cpu_to_le16(0);
|
shdr->CreditRequest = cpu_to_le16(0);
|
||||||
else
|
else
|
||||||
shdr->CreditRequest = cpu_to_le16(
|
shdr->CreditRequest = cpu_to_le16(
|
||||||
min_t(int, server->max_credits -
|
min_t(int, server->max_credits -
|
||||||
server->credits, 2));
|
server->credits, 10));
|
||||||
spin_unlock(&server->req_lock);
|
spin_unlock(&server->req_lock);
|
||||||
} else {
|
} else {
|
||||||
shdr->CreditRequest = cpu_to_le16(2);
|
shdr->CreditRequest = cpu_to_le16(2);
|
||||||
@ -173,7 +173,7 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (tcon->ipc) {
|
if (tcon->ipc) {
|
||||||
snprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
|
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
|
||||||
tcon->ses->server->hostname);
|
tcon->ses->server->hostname);
|
||||||
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
|
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
|
||||||
goto out;
|
goto out;
|
||||||
@ -206,7 +206,7 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
|
scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
|
||||||
|
|
||||||
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
|
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
|
||||||
if (!rc)
|
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->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE;
|
||||||
pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
|
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
|
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,
|
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||||
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
|
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
|
||||||
(char *)pneg_inbuf, inbuflen, (char **)&pneg_rsp, &rsplen);
|
(char *)pneg_inbuf, inbuflen, (char **)&pneg_rsp, &rsplen);
|
||||||
|
if (rc == -EOPNOTSUPP) {
|
||||||
if (rc != 0) {
|
/*
|
||||||
|
* 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);
|
cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
goto out_free_inbuf;
|
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_iov = iov;
|
||||||
rqst.rq_nvec = 2;
|
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);
|
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
|
||||||
cifs_small_buf_release(req);
|
cifs_small_buf_release(req);
|
||||||
rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
|
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_iov = iov;
|
||||||
rqst.rq_nvec = n_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 */
|
/* resource #4: response buffer */
|
||||||
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
|
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@ -2388,6 +2416,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
|||||||
if (rc)
|
if (rc)
|
||||||
goto creat_exit;
|
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,
|
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
|
||||||
&rsp_iov);
|
&rsp_iov);
|
||||||
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
|
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)
|
if (rc)
|
||||||
goto qinf_exit;
|
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);
|
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
|
||||||
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
|
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;
|
goto qinf_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_smb3_query_info_done(xid, persistent_fid, tcon->tid,
|
||||||
|
ses->Suid, info_class, (__u32)info_type);
|
||||||
|
|
||||||
if (dlen) {
|
if (dlen) {
|
||||||
*dlen = le32_to_cpu(rsp->OutputBufferLength);
|
*dlen = le32_to_cpu(rsp->OutputBufferLength);
|
||||||
if (!*data) {
|
if (!*data) {
|
||||||
@ -2924,14 +2961,16 @@ smb2_echo_callback(struct mid_q_entry *mid)
|
|||||||
{
|
{
|
||||||
struct TCP_Server_Info *server = mid->callback_data;
|
struct TCP_Server_Info *server = mid->callback_data;
|
||||||
struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf;
|
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
|
if (mid->mid_state == MID_RESPONSE_RECEIVED
|
||||||
|| mid->mid_state == MID_RESPONSE_MALFORMED)
|
|| mid->mid_state == 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;
|
||||||
|
}
|
||||||
|
|
||||||
DeleteMidQEntry(mid);
|
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)
|
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;
|
iov[0].iov_base = (char *)req;
|
||||||
|
|
||||||
rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
|
rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
|
||||||
server, CIFS_ECHO_OP);
|
server, CIFS_ECHO_OP, NULL);
|
||||||
if (rc)
|
if (rc)
|
||||||
cifs_dbg(FYI, "Echo request failed: %d\n", 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->MinimumCount = 0;
|
||||||
req->Length = cpu_to_le32(io_parms->length);
|
req->Length = cpu_to_le32(io_parms->length);
|
||||||
req->Offset = cpu_to_le64(io_parms->offset);
|
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
|
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||||
/*
|
/*
|
||||||
* If we want to do a RDMA write, fill in and append
|
* 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 TCP_Server_Info *server = tcon->ses->server;
|
||||||
struct smb2_sync_hdr *shdr =
|
struct smb2_sync_hdr *shdr =
|
||||||
(struct smb2_sync_hdr *)rdata->iov[0].iov_base;
|
(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,
|
struct smb_rqst rqst = { .rq_iov = rdata->iov,
|
||||||
.rq_nvec = 2,
|
.rq_nvec = 2,
|
||||||
.rq_pages = rdata->pages,
|
.rq_pages = rdata->pages,
|
||||||
@ -3199,7 +3243,8 @@ smb2_readv_callback(struct mid_q_entry *mid)
|
|||||||
|
|
||||||
switch (mid->mid_state) {
|
switch (mid->mid_state) {
|
||||||
case MID_RESPONSE_RECEIVED:
|
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 */
|
/* result already set, check signature */
|
||||||
if (server->sign && !mid->decrypted) {
|
if (server->sign && !mid->decrypted) {
|
||||||
int rc;
|
int rc;
|
||||||
@ -3224,10 +3269,10 @@ smb2_readv_callback(struct mid_q_entry *mid)
|
|||||||
cifs_stats_bytes_read(tcon, rdata->got_bytes);
|
cifs_stats_bytes_read(tcon, rdata->got_bytes);
|
||||||
break;
|
break;
|
||||||
case MID_RESPONSE_MALFORMED:
|
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 */
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
if (rdata->result != -ENODATA)
|
|
||||||
rdata->result = -EIO;
|
rdata->result = -EIO;
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||||
@ -3255,7 +3300,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
|
|||||||
|
|
||||||
queue_work(cifsiod_wq, &rdata->work);
|
queue_work(cifsiod_wq, &rdata->work);
|
||||||
DeleteMidQEntry(mid);
|
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 */
|
/* 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(
|
rc = smb2_new_read_req(
|
||||||
(void **) &buf, &total_len, &io_parms, rdata, 0, 0);
|
(void **) &buf, &total_len, &io_parms, rdata, 0, 0);
|
||||||
if (rc) {
|
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);
|
|
||||||
}
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
|
||||||
|
|
||||||
if (smb3_encryption_required(io_parms.tcon))
|
if (smb3_encryption_required(io_parms.tcon))
|
||||||
flags |= CIFS_TRANSFORM_REQ;
|
flags |= CIFS_TRANSFORM_REQ;
|
||||||
@ -3305,24 +3341,24 @@ smb2_async_readv(struct cifs_readdata *rdata)
|
|||||||
|
|
||||||
shdr = (struct smb2_sync_hdr *)buf;
|
shdr = (struct smb2_sync_hdr *)buf;
|
||||||
|
|
||||||
if (rdata->credits) {
|
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 =
|
shdr->CreditRequest =
|
||||||
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
|
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
|
||||||
spin_lock(&server->req_lock);
|
|
||||||
server->credits += rdata->credits -
|
rc = adjust_credits(server, &rdata->credits, rdata->bytes);
|
||||||
le16_to_cpu(shdr->CreditCharge);
|
if (rc)
|
||||||
spin_unlock(&server->req_lock);
|
goto async_readv_out;
|
||||||
wake_up(&server->request_q);
|
|
||||||
rdata->credits = le16_to_cpu(shdr->CreditCharge);
|
|
||||||
flags |= CIFS_HAS_CREDITS;
|
flags |= CIFS_HAS_CREDITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
kref_get(&rdata->refcount);
|
kref_get(&rdata->refcount);
|
||||||
rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
|
rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
|
||||||
cifs_readv_receive, smb2_readv_callback,
|
cifs_readv_receive, smb2_readv_callback,
|
||||||
smb3_handle_read_data, rdata, flags);
|
smb3_handle_read_data, rdata, flags,
|
||||||
|
&rdata->credits);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kref_put(&rdata->refcount, cifs_readdata_release);
|
kref_put(&rdata->refcount, cifs_readdata_release);
|
||||||
cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
|
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);
|
io_parms.offset, io_parms.length, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async_readv_out:
|
||||||
cifs_small_buf_release(buf);
|
cifs_small_buf_release(buf);
|
||||||
return rc;
|
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->tcon->tid, ses->Suid,
|
||||||
io_parms->offset, io_parms->length,
|
io_parms->offset, io_parms->length,
|
||||||
rc);
|
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);
|
free_rsp_buf(resp_buftype, rsp_iov.iov_base);
|
||||||
return rc == -ENODATA ? 0 : rc;
|
return rc == -ENODATA ? 0 : rc;
|
||||||
} else
|
} else
|
||||||
@ -3417,14 +3457,16 @@ smb2_writev_callback(struct mid_q_entry *mid)
|
|||||||
{
|
{
|
||||||
struct cifs_writedata *wdata = mid->callback_data;
|
struct cifs_writedata *wdata = mid->callback_data;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
unsigned int written;
|
unsigned int written;
|
||||||
struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
|
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) {
|
switch (mid->mid_state) {
|
||||||
case MID_RESPONSE_RECEIVED:
|
case MID_RESPONSE_RECEIVED:
|
||||||
credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
|
credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest);
|
||||||
wdata->result = smb2_check_receive(mid, tcon->ses->server, 0);
|
credits.instance = server->reconnect_instance;
|
||||||
|
wdata->result = smb2_check_receive(mid, server, 0);
|
||||||
if (wdata->result != 0)
|
if (wdata->result != 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3448,7 +3490,8 @@ smb2_writev_callback(struct mid_q_entry *mid)
|
|||||||
wdata->result = -EAGAIN;
|
wdata->result = -EAGAIN;
|
||||||
break;
|
break;
|
||||||
case MID_RESPONSE_MALFORMED:
|
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 */
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
wdata->result = -EIO;
|
wdata->result = -EIO;
|
||||||
@ -3481,7 +3524,7 @@ smb2_writev_callback(struct mid_q_entry *mid)
|
|||||||
|
|
||||||
queue_work(cifsiod_wq, &wdata->work);
|
queue_work(cifsiod_wq, &wdata->work);
|
||||||
DeleteMidQEntry(mid);
|
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 */
|
/* 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;
|
unsigned int total_len;
|
||||||
|
|
||||||
rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len);
|
rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len);
|
||||||
if (rc) {
|
if (rc)
|
||||||
if (rc == -EAGAIN && wdata->credits) {
|
return rc;
|
||||||
/* 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 (smb3_encryption_required(tcon))
|
if (smb3_encryption_required(tcon))
|
||||||
flags |= CIFS_TRANSFORM_REQ;
|
flags |= CIFS_TRANSFORM_REQ;
|
||||||
@ -3526,6 +3560,9 @@ smb2_async_writev(struct cifs_writedata *wdata,
|
|||||||
req->DataOffset = cpu_to_le16(
|
req->DataOffset = cpu_to_le16(
|
||||||
offsetof(struct smb2_write_req, Buffer));
|
offsetof(struct smb2_write_req, Buffer));
|
||||||
req->RemainingBytes = 0;
|
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
|
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||||
/*
|
/*
|
||||||
* If we want to do a server RDMA read, fill in and append
|
* 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);
|
req->Length = cpu_to_le32(wdata->bytes);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (wdata->credits) {
|
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 =
|
shdr->CreditRequest =
|
||||||
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
|
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
|
||||||
spin_lock(&server->req_lock);
|
|
||||||
server->credits += wdata->credits -
|
rc = adjust_credits(server, &wdata->credits, wdata->bytes);
|
||||||
le16_to_cpu(shdr->CreditCharge);
|
if (rc)
|
||||||
spin_unlock(&server->req_lock);
|
goto async_writev_out;
|
||||||
wake_up(&server->request_q);
|
|
||||||
wdata->credits = le16_to_cpu(shdr->CreditCharge);
|
|
||||||
flags |= CIFS_HAS_CREDITS;
|
flags |= CIFS_HAS_CREDITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
kref_get(&wdata->refcount);
|
kref_get(&wdata->refcount);
|
||||||
rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL,
|
rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL,
|
||||||
wdata, flags);
|
wdata, flags, &wdata->credits);
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
trace_smb3_write_err(0 /* no xid */, req->PersistentFileId,
|
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));
|
offsetof(struct smb2_write_req, Buffer));
|
||||||
req->RemainingBytes = 0;
|
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;
|
iov[0].iov_base = (char *)req;
|
||||||
/* 1 for Buffer */
|
/* 1 for Buffer */
|
||||||
iov[0].iov_len = total_len - 1;
|
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_iov = iov;
|
||||||
rqst.rq_nvec = 2;
|
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);
|
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
|
||||||
cifs_small_buf_release(req);
|
cifs_small_buf_release(req);
|
||||||
rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
|
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) {
|
||||||
if (rc == -ENODATA &&
|
if (rc == -ENODATA &&
|
||||||
rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) {
|
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;
|
srch_inf->endOfSearch = true;
|
||||||
rc = 0;
|
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);
|
cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
|
||||||
|
}
|
||||||
goto qdir_exit;
|
goto qdir_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
|
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
|
||||||
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
|
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
|
||||||
info_buf_size);
|
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;
|
goto qdir_exit;
|
||||||
|
}
|
||||||
|
|
||||||
srch_inf->unicode = true;
|
srch_inf->unicode = true;
|
||||||
|
|
||||||
@ -3882,6 +3933,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
|
|||||||
else
|
else
|
||||||
cifs_dbg(VFS, "illegal search buffer type\n");
|
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;
|
return rc;
|
||||||
|
|
||||||
qdir_exit:
|
qdir_exit:
|
||||||
|
@ -288,12 +288,12 @@ struct smb2_encryption_neg_context {
|
|||||||
__le16 Ciphers[1]; /* Ciphers[0] since only one used now */
|
__le16 Ciphers[1]; /* Ciphers[0] since only one used now */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define POSIX_CTXT_DATA_LEN 8
|
#define POSIX_CTXT_DATA_LEN 16
|
||||||
struct smb2_posix_neg_context {
|
struct smb2_posix_neg_context {
|
||||||
__le16 ContextType; /* 0x100 */
|
__le16 ContextType; /* 0x100 */
|
||||||
__le16 DataLength;
|
__le16 DataLength;
|
||||||
__le32 Reserved;
|
__le32 Reserved;
|
||||||
__le64 Reserved1; /* In case needed for future (eg version or caps) */
|
__u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct smb2_negotiate_rsp {
|
struct smb2_negotiate_rsp {
|
||||||
|
@ -576,6 +576,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
|
|||||||
struct TCP_Server_Info *server)
|
struct TCP_Server_Info *server)
|
||||||
{
|
{
|
||||||
struct mid_q_entry *temp;
|
struct mid_q_entry *temp;
|
||||||
|
unsigned int credits = le16_to_cpu(shdr->CreditCharge);
|
||||||
|
|
||||||
if (server == NULL) {
|
if (server == NULL) {
|
||||||
cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n");
|
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));
|
memset(temp, 0, sizeof(struct mid_q_entry));
|
||||||
kref_init(&temp->refcount);
|
kref_init(&temp->refcount);
|
||||||
temp->mid = le64_to_cpu(shdr->MessageId);
|
temp->mid = le64_to_cpu(shdr->MessageId);
|
||||||
|
temp->credits = credits > 0 ? credits : 1;
|
||||||
temp->pid = current->pid;
|
temp->pid = current->pid;
|
||||||
temp->command = shdr->Command; /* Always LE */
|
temp->command = shdr->Command; /* Always LE */
|
||||||
temp->when_alloc = jiffies;
|
temp->when_alloc = jiffies;
|
||||||
@ -600,6 +602,8 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
|
|||||||
|
|
||||||
atomic_inc(&midCount);
|
atomic_inc(&midCount);
|
||||||
temp->mid_state = MID_REQUEST_ALLOCATED;
|
temp->mid_state = MID_REQUEST_ALLOCATED;
|
||||||
|
trace_smb3_cmd_enter(shdr->TreeId, shdr->SessionId,
|
||||||
|
le16_to_cpu(shdr->Command), temp->mid);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,6 +619,10 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
|
|||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ses->server->tcpStatus == CifsNeedNegotiate &&
|
||||||
|
shdr->Command != SMB2_NEGOTIATE)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
if (ses->status == CifsNew) {
|
if (ses->status == CifsNew) {
|
||||||
if ((shdr->Command != SMB2_SESSION_SETUP) &&
|
if ((shdr->Command != SMB2_SESSION_SETUP) &&
|
||||||
(shdr->Command != SMB2_NEGOTIATE))
|
(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);
|
spin_lock(&GlobalMid_Lock);
|
||||||
list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
|
list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
|
|
||||||
return 0;
|
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);
|
smb2_seq_num_into_buf(ses->server, shdr);
|
||||||
|
|
||||||
rc = smb2_get_mid_entry(ses, shdr, &mid);
|
rc = smb2_get_mid_entry(ses, shdr, &mid);
|
||||||
if (rc)
|
if (rc) {
|
||||||
|
revert_current_mid_from_hdr(ses->server, shdr);
|
||||||
return ERR_PTR(rc);
|
return ERR_PTR(rc);
|
||||||
|
}
|
||||||
|
|
||||||
rc = smb2_sign_rqst(rqst, ses->server);
|
rc = smb2_sign_rqst(rqst, ses->server);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
revert_current_mid_from_hdr(ses->server, shdr);
|
||||||
cifs_delete_mid(mid);
|
cifs_delete_mid(mid);
|
||||||
return ERR_PTR(rc);
|
return ERR_PTR(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mid;
|
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 smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
struct mid_q_entry *mid;
|
struct mid_q_entry *mid;
|
||||||
|
|
||||||
|
if (server->tcpStatus == CifsNeedNegotiate &&
|
||||||
|
shdr->Command != SMB2_NEGOTIATE)
|
||||||
|
return ERR_PTR(-EAGAIN);
|
||||||
|
|
||||||
smb2_seq_num_into_buf(server, shdr);
|
smb2_seq_num_into_buf(server, shdr);
|
||||||
|
|
||||||
mid = smb2_mid_entry_alloc(shdr, server);
|
mid = smb2_mid_entry_alloc(shdr, server);
|
||||||
if (mid == NULL)
|
if (mid == NULL) {
|
||||||
|
revert_current_mid_from_hdr(server, shdr);
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
rc = smb2_sign_rqst(rqst, server);
|
rc = smb2_sign_rqst(rqst, server);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
revert_current_mid_from_hdr(server, shdr);
|
||||||
DeleteMidQEntry(mid);
|
DeleteMidQEntry(mid);
|
||||||
return ERR_PTR(rc);
|
return ERR_PTR(rc);
|
||||||
}
|
}
|
||||||
|
@ -1550,7 +1550,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
|
|||||||
char name[MAX_NAME_LEN];
|
char name[MAX_NAME_LEN];
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
snprintf(name, MAX_NAME_LEN, "smbd_request_%p", info);
|
scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info);
|
||||||
info->request_cache =
|
info->request_cache =
|
||||||
kmem_cache_create(
|
kmem_cache_create(
|
||||||
name,
|
name,
|
||||||
@ -1566,7 +1566,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
|
|||||||
if (!info->request_mempool)
|
if (!info->request_mempool)
|
||||||
goto out1;
|
goto out1;
|
||||||
|
|
||||||
snprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);
|
scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);
|
||||||
info->response_cache =
|
info->response_cache =
|
||||||
kmem_cache_create(
|
kmem_cache_create(
|
||||||
name,
|
name,
|
||||||
@ -1582,7 +1582,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info)
|
|||||||
if (!info->response_mempool)
|
if (!info->response_mempool)
|
||||||
goto out3;
|
goto out3;
|
||||||
|
|
||||||
snprintf(name, MAX_NAME_LEN, "smbd_%p", info);
|
scnprintf(name, MAX_NAME_LEN, "smbd_%p", info);
|
||||||
info->workqueue = create_workqueue(name);
|
info->workqueue = create_workqueue(name);
|
||||||
if (!info->workqueue)
|
if (!info->workqueue)
|
||||||
goto out4;
|
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(write_err);
|
||||||
DEFINE_SMB3_RW_ERR_EVENT(read_err);
|
DEFINE_SMB3_RW_ERR_EVENT(read_err);
|
||||||
|
DEFINE_SMB3_RW_ERR_EVENT(query_dir_err);
|
||||||
|
|
||||||
|
|
||||||
/* For logging successful read or write */
|
/* For logging successful read or write */
|
||||||
@ -100,8 +101,12 @@ DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \
|
|||||||
__u32 len), \
|
__u32 len), \
|
||||||
TP_ARGS(xid, fid, tid, sesid, offset, 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(write_done);
|
||||||
DEFINE_SMB3_RW_DONE_EVENT(read_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
|
* 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
|
* 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,
|
DECLARE_EVENT_CLASS(smb3_inf_err_class,
|
||||||
TP_PROTO(unsigned int xid,
|
TP_PROTO(unsigned int xid,
|
||||||
__u64 fid,
|
__u64 fid,
|
||||||
@ -270,6 +317,7 @@ DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \
|
|||||||
__u64 mid), \
|
__u64 mid), \
|
||||||
TP_ARGS(tid, sesid, cmd, 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(cmd_done);
|
||||||
DEFINE_SMB3_CMD_DONE_EVENT(ses_expired);
|
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,
|
DECLARE_EVENT_CLASS(smb3_open_err_class,
|
||||||
TP_PROTO(unsigned int xid,
|
TP_PROTO(unsigned int xid,
|
||||||
__u32 tid,
|
__u32 tid,
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <linux/mempool.h>
|
#include <linux/mempool.h>
|
||||||
|
#include <linux/signal.h>
|
||||||
#include "cifspdu.h"
|
#include "cifspdu.h"
|
||||||
#include "cifsglob.h"
|
#include "cifsglob.h"
|
||||||
#include "cifsproto.h"
|
#include "cifsproto.h"
|
||||||
@ -291,6 +292,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
|
|||||||
int n_vec;
|
int n_vec;
|
||||||
unsigned int send_length = 0;
|
unsigned int send_length = 0;
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
|
sigset_t mask, oldmask;
|
||||||
size_t total_len = 0, sent, size;
|
size_t total_len = 0, sent, size;
|
||||||
struct socket *ssocket = server->ssocket;
|
struct socket *ssocket = server->ssocket;
|
||||||
struct msghdr smb_msg;
|
struct msghdr smb_msg;
|
||||||
@ -301,8 +303,14 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
|
|||||||
rc = smbd_send(server, rqst);
|
rc = smbd_send(server, rqst);
|
||||||
goto smbd_done;
|
goto smbd_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssocket == NULL)
|
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 */
|
/* cork the socket */
|
||||||
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
|
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]);
|
send_length += smb_rqst_len(server, &rqst[j]);
|
||||||
rfc1002_marker = cpu_to_be32(send_length);
|
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+ */
|
/* Generate a rfc1002 marker for SMB2+ */
|
||||||
if (server->vals->header_preamble_size == 0) {
|
if (server->vals->header_preamble_size == 0) {
|
||||||
struct kvec hiov = {
|
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);
|
iov_iter_kvec(&smb_msg.msg_iter, WRITE, &hiov, 1, 4);
|
||||||
rc = smb_send_kvec(server, &smb_msg, &sent);
|
rc = smb_send_kvec(server, &smb_msg, &sent);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto uncork;
|
goto unmask;
|
||||||
|
|
||||||
total_len += sent;
|
total_len += sent;
|
||||||
send_length += 4;
|
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);
|
rc = smb_send_kvec(server, &smb_msg, &sent);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto uncork;
|
goto unmask;
|
||||||
|
|
||||||
total_len += sent;
|
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 */
|
/* uncork it */
|
||||||
val = 0;
|
val = 0;
|
||||||
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
|
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
|
static int
|
||||||
wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
||||||
int *credits)
|
int *credits, unsigned int *instance)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
*instance = 0;
|
||||||
|
|
||||||
spin_lock(&server->req_lock);
|
spin_lock(&server->req_lock);
|
||||||
if (timeout == CIFS_ASYNC_OP) {
|
if (timeout == CIFS_ASYNC_OP) {
|
||||||
/* oplock breaks must not be held up */
|
/* oplock breaks must not be held up */
|
||||||
server->in_flight++;
|
server->in_flight++;
|
||||||
*credits -= 1;
|
*credits -= 1;
|
||||||
|
*instance = server->reconnect_instance;
|
||||||
spin_unlock(&server->req_lock);
|
spin_unlock(&server->req_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -489,6 +528,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
|||||||
if (timeout != CIFS_BLOCKING_OP) {
|
if (timeout != CIFS_BLOCKING_OP) {
|
||||||
*credits -= 1;
|
*credits -= 1;
|
||||||
server->in_flight++;
|
server->in_flight++;
|
||||||
|
*instance = server->reconnect_instance;
|
||||||
}
|
}
|
||||||
spin_unlock(&server->req_lock);
|
spin_unlock(&server->req_lock);
|
||||||
break;
|
break;
|
||||||
@ -499,7 +539,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
|
wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
|
||||||
const int optype)
|
const int optype, unsigned int *instance)
|
||||||
{
|
{
|
||||||
int *val;
|
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 */
|
/* Since an echo is already inflight, no need to wait to send another */
|
||||||
if (*val <= 0 && optype == CIFS_ECHO_OP)
|
if (*val <= 0 && optype == CIFS_ECHO_OP)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
return wait_for_free_credits(server, timeout, val);
|
return wait_for_free_credits(server, timeout, val, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
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;
|
*num = size;
|
||||||
*credits = 0;
|
credits->value = 0;
|
||||||
|
credits->instance = server->reconnect_instance;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,27 +643,43 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
|||||||
int
|
int
|
||||||
cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
|
cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
|
||||||
mid_receive_t *receive, mid_callback_t *callback,
|
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;
|
int rc, timeout, optype;
|
||||||
struct mid_q_entry *mid;
|
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;
|
timeout = flags & CIFS_TIMEOUT_MASK;
|
||||||
optype = flags & CIFS_OP_MASK;
|
optype = flags & CIFS_OP_MASK;
|
||||||
|
|
||||||
if ((flags & CIFS_HAS_CREDITS) == 0) {
|
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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
credits = 1;
|
credits.value = 1;
|
||||||
}
|
credits.instance = instance;
|
||||||
|
} else
|
||||||
|
instance = exist_credits->instance;
|
||||||
|
|
||||||
mutex_lock(&server->srv_mutex);
|
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);
|
mid = server->ops->setup_async_request(server, rqst);
|
||||||
if (IS_ERR(mid)) {
|
if (IS_ERR(mid)) {
|
||||||
mutex_unlock(&server->srv_mutex);
|
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);
|
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);
|
cifs_in_send_dec(server);
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
revert_current_mid(server, mid->credits);
|
||||||
server->sequence_number -= 2;
|
server->sequence_number -= 2;
|
||||||
cifs_delete_mid(mid);
|
cifs_delete_mid(mid);
|
||||||
}
|
}
|
||||||
@ -656,7 +714,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
|
|||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
add_credits_and_wake_if(server, credits, optype);
|
add_credits_and_wake_if(server, &credits, optype);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,8 +844,12 @@ static void
|
|||||||
cifs_compound_callback(struct mid_q_entry *mid)
|
cifs_compound_callback(struct mid_q_entry *mid)
|
||||||
{
|
{
|
||||||
struct TCP_Server_Info *server = mid->server;
|
struct TCP_Server_Info *server = mid->server;
|
||||||
|
struct cifs_credits credits;
|
||||||
|
|
||||||
add_credits(server, server->ops->get_credits(mid), mid->optype);
|
credits.value = server->ops->get_credits(mid);
|
||||||
|
credits.instance = server->reconnect_instance;
|
||||||
|
|
||||||
|
add_credits(server, &credits, mid->optype);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -813,7 +875,11 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
int timeout, optype;
|
int timeout, optype;
|
||||||
struct mid_q_entry *midQ[MAX_COMPOUND];
|
struct mid_q_entry *midQ[MAX_COMPOUND];
|
||||||
bool cancelled_mid[MAX_COMPOUND] = {false};
|
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;
|
char *buf;
|
||||||
|
|
||||||
timeout = flags & CIFS_TIMEOUT_MASK;
|
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)
|
if (ses->server->tcpStatus == CifsExiting)
|
||||||
return -ENOENT;
|
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.
|
* 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++) {
|
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) {
|
if (rc) {
|
||||||
/*
|
/*
|
||||||
* We haven't sent an SMB packet to the server yet but
|
* 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.
|
* requests correctly.
|
||||||
*/
|
*/
|
||||||
for (j = 0; j < i; j++)
|
for (j = 0; j < i; j++)
|
||||||
add_credits(ses->server, 1, optype);
|
add_credits(ses->server, &credits[j], optype);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
credits[i] = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setup_rqsts:
|
||||||
/*
|
/*
|
||||||
* Make sure that we sign in the same order that we send on this socket
|
* 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
|
* 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);
|
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++) {
|
for (i = 0; i < num_rqst; i++) {
|
||||||
midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]);
|
midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]);
|
||||||
if (IS_ERR(midQ[i])) {
|
if (IS_ERR(midQ[i])) {
|
||||||
|
revert_current_mid(ses->server, i);
|
||||||
for (j = 0; j < i; j++)
|
for (j = 0; j < i; j++)
|
||||||
cifs_delete_mid(midQ[j]);
|
cifs_delete_mid(midQ[j]);
|
||||||
mutex_unlock(&ses->server->srv_mutex);
|
mutex_unlock(&ses->server->srv_mutex);
|
||||||
|
|
||||||
/* Update # of requests on wire to server */
|
/* Update # of requests on wire to server */
|
||||||
for (j = 0; j < num_rqst; j++)
|
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]);
|
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++)
|
for (i = 0; i < num_rqst; i++)
|
||||||
cifs_save_when_sent(midQ[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;
|
ses->server->sequence_number -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_unlock(&ses->server->srv_mutex);
|
mutex_unlock(&ses->server->srv_mutex);
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
/* Sending failed for some reason - return credits back */
|
/* Sending failed for some reason - return credits back */
|
||||||
for (i = 0; i < num_rqst; i++)
|
for (i = 0; i < num_rqst; i++)
|
||||||
add_credits(ses->server, credits[i], optype);
|
add_credits(ses->server, &credits[i], optype);
|
||||||
goto out;
|
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]->mid_flags |= MID_WAIT_CANCELLED;
|
||||||
midQ[i]->callback = cifs_cancelled_callback;
|
midQ[i]->callback = cifs_cancelled_callback;
|
||||||
cancelled_mid[i] = true;
|
cancelled_mid[i] = true;
|
||||||
credits[i] = 0;
|
credits[i].value = 0;
|
||||||
}
|
}
|
||||||
spin_unlock(&GlobalMid_Lock);
|
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);
|
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
|
||||||
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
|
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
|
||||||
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
|
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
|
||||||
|
struct cifs_credits credits = { .value = 1, .instance = 0 };
|
||||||
|
|
||||||
if (ses == NULL) {
|
if (ses == NULL) {
|
||||||
cifs_dbg(VFS, "Null smb session\n");
|
cifs_dbg(VFS, "Null smb session\n");
|
||||||
@ -1091,7 +1225,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = wait_for_free_request(ses->server, timeout, 0);
|
rc = wait_for_free_request(ses->server, timeout, 0, &credits.instance);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
@ -1105,7 +1239,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
if (rc) {
|
if (rc) {
|
||||||
mutex_unlock(&ses->server->srv_mutex);
|
mutex_unlock(&ses->server->srv_mutex);
|
||||||
/* Update # of requests on wire to server */
|
/* Update # of requests on wire to server */
|
||||||
add_credits(ses->server, 1, 0);
|
add_credits(ses->server, &credits, 0);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1141,7 +1275,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
/* no longer considered to be "in-flight" */
|
/* no longer considered to be "in-flight" */
|
||||||
midQ->callback = DeleteMidQEntry;
|
midQ->callback = DeleteMidQEntry;
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
add_credits(ses->server, 1, 0);
|
add_credits(ses->server, &credits, 0);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
spin_unlock(&GlobalMid_Lock);
|
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);
|
rc = cifs_sync_mid_result(midQ, ses->server);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
add_credits(ses->server, 1, 0);
|
add_credits(ses->server, &credits, 0);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1165,7 +1299,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
rc = cifs_check_receive(midQ, ses->server, 0);
|
rc = cifs_check_receive(midQ, ses->server, 0);
|
||||||
out:
|
out:
|
||||||
cifs_delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
add_credits(ses->server, 1, 0);
|
add_credits(ses->server, &credits, 0);
|
||||||
|
|
||||||
return rc;
|
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);
|
unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
|
||||||
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
|
struct kvec iov = { .iov_base = in_buf, .iov_len = len };
|
||||||
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
|
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
|
||||||
|
unsigned int instance;
|
||||||
|
|
||||||
if (tcon == NULL || tcon->ses == NULL) {
|
if (tcon == NULL || tcon->ses == NULL) {
|
||||||
cifs_dbg(VFS, "Null smb session\n");
|
cifs_dbg(VFS, "Null smb session\n");
|
||||||
@ -1232,7 +1367,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
|||||||
return -EIO;
|
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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user