CIFS/SMB3 changes, three for stable, adds fiemap support, improves zero-range support, and includes various RDMA (smb direct fixes)
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAlzTHtoACgkQiiy9cAdy T1HMTwwAizpsu/coczGhe5s6CKsyckuToQtMcM8KVcDipzJ/8tDNzWpwAanBffy/ X/g/+EOeplf9blMP4P7KDGV9hp1vfidqm1sa00mb/IEmEhQDQUXW3Mmg8CDKoOtv 0xYKcgOdvaPSsCwI2sgBGZnT1RAFa1kg/pM9ObZN7qCSrLXtFapBfz8HEmPjCMBH Cv0Dra6+XuOQmOF0LfLvgUsrS2cbBkXBV2RX+BV1ouSOLV0r08cHk2gAUib+9tG2 qaQpk3BgW2BIb4Od0TnYOP1V6yqe/jQSfC+J1RMO6evNXT0YCF7A6RpAaZYm674U efp4NJpjqKIfuWWl/VDiuFykAj4JLSDiM6O8xpxl63KjjarYl2HLUSXAfmklDOrB kpnKF4W948kZ75ngGlUIQthRvcI0ynOweGBeaUg5SgNqaww879vfFT0GuTvw4Izv tuc8IIb1k1BD+5s69bYbyjs4INl1Ikb+/Mxl6xx28mISCc/ISQF1d0cBcPkb3ChS qf8Ai+tK =dpRC -----END PGP SIGNATURE----- Merge tag '5.2-smb3' of git://git.samba.org/sfrench/cifs-2.6 Pull cifs fixes from Steve French: "CIFS/SMB3 changes: - three fixes for stable - add fiemap support - improve zero-range support - various RDMA (smb direct fixes) I have an additional set of fixes (for improved handling of sparse files, mode bits, POSIX extensions) that are still being tested that are not included in this pull request but I expect to send in the next week" * tag '5.2-smb3' of git://git.samba.org/sfrench/cifs-2.6: (29 commits) cifs: update module internal version number SMB3: Clean up query symlink when reparse point cifs: fix strcat buffer overflow and reduce raciness in smb21_set_oplock_level() Negotiate and save preferred compression algorithms cifs: rename and clarify CIFS_ASYNC_OP and CIFS_NO_RESP cifs: fix credits leak for SMB1 oplock breaks smb3: Add protocol structs for change notify support cifs: fix smb3_zero_range for Azure cifs: zero-range does not require the file is sparse Add new flag on SMB3.1.1 read cifs: add fiemap support SMB3: Add defines for new negotiate contexts cifs: fix bi-directional fsctl passthrough calls cifs: smbd: take an array of reqeusts when sending upper layer data SMB3: Add handling for different FSCTL access flags cifs: Add support for FSCTL passthrough that write data to the server cifs: remove superfluous inode_lock in cifs_{strict_}fsync cifs: Call MID callback before destroying transport cifs: smbd: Retry on memory registration failure cifs: smbd: Indicate to retry on transport sending failure ...
This commit is contained in:
commit
78d9affbb0
|
@ -312,12 +312,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||||
atomic_read(&server->smbd_conn->send_credits),
|
atomic_read(&server->smbd_conn->send_credits),
|
||||||
atomic_read(&server->smbd_conn->receive_credits),
|
atomic_read(&server->smbd_conn->receive_credits),
|
||||||
server->smbd_conn->receive_credit_target);
|
server->smbd_conn->receive_credit_target);
|
||||||
seq_printf(m, "\nPending send_pending: %x send_payload_pending:"
|
seq_printf(m, "\nPending send_pending: %x "
|
||||||
" %x smbd_send_pending: %x smbd_recv_pending: %x",
|
"send_payload_pending: %x",
|
||||||
atomic_read(&server->smbd_conn->send_pending),
|
atomic_read(&server->smbd_conn->send_pending),
|
||||||
atomic_read(&server->smbd_conn->send_payload_pending),
|
atomic_read(&server->smbd_conn->send_payload_pending));
|
||||||
server->smbd_conn->smbd_send_pending,
|
|
||||||
server->smbd_conn->smbd_recv_pending);
|
|
||||||
seq_printf(m, "\nReceive buffers count_receive_queue: %x "
|
seq_printf(m, "\nReceive buffers count_receive_queue: %x "
|
||||||
"count_empty_packet_queue: %x",
|
"count_empty_packet_queue: %x",
|
||||||
server->smbd_conn->count_receive_queue,
|
server->smbd_conn->count_receive_queue,
|
||||||
|
@ -334,6 +332,12 @@ skip_rdma:
|
||||||
#endif
|
#endif
|
||||||
seq_printf(m, "\nNumber of credits: %d Dialect 0x%x",
|
seq_printf(m, "\nNumber of credits: %d Dialect 0x%x",
|
||||||
server->credits, server->dialect);
|
server->credits, server->dialect);
|
||||||
|
if (server->compress_algorithm == SMB3_COMPRESS_LZNT1)
|
||||||
|
seq_printf(m, " COMPRESS_LZNT1");
|
||||||
|
else if (server->compress_algorithm == SMB3_COMPRESS_LZ77)
|
||||||
|
seq_printf(m, " COMPRESS_LZ77");
|
||||||
|
else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF)
|
||||||
|
seq_printf(m, " COMPRESS_LZ77_HUFF");
|
||||||
if (server->sign)
|
if (server->sign)
|
||||||
seq_printf(m, " signed");
|
seq_printf(m, " signed");
|
||||||
if (server->posix_ext_supported)
|
if (server->posix_ext_supported)
|
||||||
|
@ -462,8 +466,13 @@ static ssize_t cifs_stats_proc_write(struct file *file,
|
||||||
server = list_entry(tmp1, struct TCP_Server_Info,
|
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||||
tcp_ses_list);
|
tcp_ses_list);
|
||||||
#ifdef CONFIG_CIFS_STATS2
|
#ifdef CONFIG_CIFS_STATS2
|
||||||
for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++)
|
for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) {
|
||||||
|
atomic_set(&server->num_cmds[i], 0);
|
||||||
atomic_set(&server->smb2slowcmd[i], 0);
|
atomic_set(&server->smb2slowcmd[i], 0);
|
||||||
|
server->time_per_cmd[i] = 0;
|
||||||
|
server->slowest_cmd[i] = 0;
|
||||||
|
server->fastest_cmd[0] = 0;
|
||||||
|
}
|
||||||
#endif /* CONFIG_CIFS_STATS2 */
|
#endif /* CONFIG_CIFS_STATS2 */
|
||||||
list_for_each(tmp2, &server->smb_ses_list) {
|
list_for_each(tmp2, &server->smb_ses_list) {
|
||||||
ses = list_entry(tmp2, struct cifs_ses,
|
ses = list_entry(tmp2, struct cifs_ses,
|
||||||
|
@ -531,9 +540,19 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
|
||||||
server = list_entry(tmp1, struct TCP_Server_Info,
|
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||||
tcp_ses_list);
|
tcp_ses_list);
|
||||||
#ifdef CONFIG_CIFS_STATS2
|
#ifdef CONFIG_CIFS_STATS2
|
||||||
|
seq_puts(m, "\nTotal time spent processing by command. Time ");
|
||||||
|
seq_printf(m, "units are jiffies (%d per second)\n", HZ);
|
||||||
|
seq_puts(m, " SMB3 CMD\tNumber\tTotal Time\tFastest\tSlowest\n");
|
||||||
|
seq_puts(m, " --------\t------\t----------\t-------\t-------\n");
|
||||||
|
for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
|
||||||
|
seq_printf(m, " %d\t\t%d\t%llu\t\t%u\t%u\n", j,
|
||||||
|
atomic_read(&server->num_cmds[j]),
|
||||||
|
server->time_per_cmd[j],
|
||||||
|
server->fastest_cmd[j],
|
||||||
|
server->slowest_cmd[j]);
|
||||||
for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
|
for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
|
||||||
if (atomic_read(&server->smb2slowcmd[j]))
|
if (atomic_read(&server->smb2slowcmd[j]))
|
||||||
seq_printf(m, "%d slow responses from %s for command %d\n",
|
seq_printf(m, " %d slow responses from %s for command %d\n",
|
||||||
atomic_read(&server->smb2slowcmd[j]),
|
atomic_read(&server->smb2slowcmd[j]),
|
||||||
server->hostname, j);
|
server->hostname, j);
|
||||||
#endif /* STATS2 */
|
#endif /* STATS2 */
|
||||||
|
|
|
@ -483,6 +483,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||||
seq_puts(s, ",seal");
|
seq_puts(s, ",seal");
|
||||||
if (tcon->nocase)
|
if (tcon->nocase)
|
||||||
seq_puts(s, ",nocase");
|
seq_puts(s, ",nocase");
|
||||||
|
if (tcon->local_lease)
|
||||||
|
seq_puts(s, ",locallease");
|
||||||
if (tcon->retry)
|
if (tcon->retry)
|
||||||
seq_puts(s, ",hard");
|
seq_puts(s, ",hard");
|
||||||
else
|
else
|
||||||
|
@ -984,6 +986,7 @@ const struct inode_operations cifs_file_inode_ops = {
|
||||||
.getattr = cifs_getattr,
|
.getattr = cifs_getattr,
|
||||||
.permission = cifs_permission,
|
.permission = cifs_permission,
|
||||||
.listxattr = cifs_listxattr,
|
.listxattr = cifs_listxattr,
|
||||||
|
.fiemap = cifs_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations cifs_symlink_inode_ops = {
|
const struct inode_operations cifs_symlink_inode_ops = {
|
||||||
|
|
|
@ -84,6 +84,8 @@ extern int cifs_revalidate_mapping(struct inode *inode);
|
||||||
extern int cifs_zap_mapping(struct inode *inode);
|
extern int cifs_zap_mapping(struct inode *inode);
|
||||||
extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int);
|
extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int);
|
||||||
extern int cifs_setattr(struct dentry *, struct iattr *);
|
extern int cifs_setattr(struct dentry *, struct iattr *);
|
||||||
|
extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start,
|
||||||
|
u64 len);
|
||||||
|
|
||||||
extern const struct inode_operations cifs_file_inode_ops;
|
extern const struct inode_operations cifs_file_inode_ops;
|
||||||
extern const struct inode_operations cifs_symlink_inode_ops;
|
extern const struct inode_operations cifs_symlink_inode_ops;
|
||||||
|
@ -150,5 +152,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.19"
|
#define CIFS_VERSION "2.20"
|
||||||
#endif /* _CIFSFS_H */
|
#endif /* _CIFSFS_H */
|
||||||
|
|
|
@ -355,7 +355,8 @@ struct smb_version_operations {
|
||||||
struct cifs_sb_info *);
|
struct cifs_sb_info *);
|
||||||
/* query symlink target */
|
/* query symlink target */
|
||||||
int (*query_symlink)(const unsigned int, struct cifs_tcon *,
|
int (*query_symlink)(const unsigned int, struct cifs_tcon *,
|
||||||
const char *, char **, struct cifs_sb_info *);
|
struct cifs_sb_info *, const char *,
|
||||||
|
char **, bool);
|
||||||
/* open a file for non-posix mounts */
|
/* open a file for non-posix mounts */
|
||||||
int (*open)(const unsigned int, struct cifs_open_parms *,
|
int (*open)(const unsigned int, struct cifs_open_parms *,
|
||||||
__u32 *, FILE_ALL_INFO *);
|
__u32 *, FILE_ALL_INFO *);
|
||||||
|
@ -493,6 +494,9 @@ struct smb_version_operations {
|
||||||
char *full_path,
|
char *full_path,
|
||||||
umode_t mode,
|
umode_t mode,
|
||||||
dev_t device_number);
|
dev_t device_number);
|
||||||
|
/* version specific fiemap implementation */
|
||||||
|
int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *,
|
||||||
|
struct fiemap_extent_info *, u64, u64);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values {
|
struct smb_version_values {
|
||||||
|
@ -596,6 +600,10 @@ struct smb_vol {
|
||||||
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
|
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CIFS superblock mount flags (mnt_cifs_flags) to consider when
|
||||||
|
* trying to reuse existing superblock for a new mount
|
||||||
|
*/
|
||||||
#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
|
#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
|
||||||
CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \
|
CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \
|
||||||
CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \
|
CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \
|
||||||
|
@ -606,8 +614,13 @@ struct smb_vol {
|
||||||
CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \
|
CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \
|
||||||
CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \
|
CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \
|
||||||
CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \
|
CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \
|
||||||
CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID)
|
CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \
|
||||||
|
CIFS_MOUNT_NO_DFS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic VFS superblock mount flags (s_flags) to consider when
|
||||||
|
* trying to reuse existing superblock for a new mount
|
||||||
|
*/
|
||||||
#define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \
|
#define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \
|
||||||
SB_NODEV | SB_SYNCHRONOUS)
|
SB_NODEV | SB_SYNCHRONOUS)
|
||||||
|
|
||||||
|
@ -714,10 +727,15 @@ struct TCP_Server_Info {
|
||||||
#ifdef CONFIG_CIFS_STATS2
|
#ifdef CONFIG_CIFS_STATS2
|
||||||
atomic_t in_send; /* requests trying to send */
|
atomic_t in_send; /* requests trying to send */
|
||||||
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
|
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
|
||||||
|
atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */
|
||||||
atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */
|
atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */
|
||||||
|
__u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */
|
||||||
|
__u32 slowest_cmd[NUMBER_OF_SMB2_COMMANDS];
|
||||||
|
__u32 fastest_cmd[NUMBER_OF_SMB2_COMMANDS];
|
||||||
#endif /* STATS2 */
|
#endif /* STATS2 */
|
||||||
unsigned int max_read;
|
unsigned int max_read;
|
||||||
unsigned int max_write;
|
unsigned int max_write;
|
||||||
|
__le16 compress_algorithm;
|
||||||
__le16 cipher_type;
|
__le16 cipher_type;
|
||||||
/* save initital negprot hash */
|
/* save initital negprot hash */
|
||||||
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
|
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
|
||||||
|
@ -1673,11 +1691,11 @@ static inline bool is_retryable_error(int error)
|
||||||
|
|
||||||
/* Type of Request to SendReceive2 */
|
/* Type of Request to SendReceive2 */
|
||||||
#define CIFS_BLOCKING_OP 1 /* operation can block */
|
#define CIFS_BLOCKING_OP 1 /* operation can block */
|
||||||
#define CIFS_ASYNC_OP 2 /* do not wait for response */
|
#define CIFS_NON_BLOCKING 2 /* do not block waiting for credits */
|
||||||
#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */
|
#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */
|
||||||
#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */
|
#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */
|
||||||
#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */
|
#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */
|
||||||
#define CIFS_NO_RESP 0x040 /* no response buffer required */
|
#define CIFS_NO_RSP_BUF 0x040 /* no response buffer required */
|
||||||
|
|
||||||
/* Type of request operation */
|
/* Type of request operation */
|
||||||
#define CIFS_ECHO_OP 0x080 /* echo request */
|
#define CIFS_ECHO_OP 0x080 /* echo request */
|
||||||
|
@ -1687,6 +1705,7 @@ static inline bool is_retryable_error(int error)
|
||||||
|
|
||||||
#define CIFS_HAS_CREDITS 0x0400 /* already has credits */
|
#define CIFS_HAS_CREDITS 0x0400 /* already has credits */
|
||||||
#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */
|
#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */
|
||||||
|
#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */
|
||||||
|
|
||||||
/* Security Flags: indicate type of session setup needed */
|
/* Security Flags: indicate type of session setup needed */
|
||||||
#define CIFSSEC_MAY_SIGN 0x00001
|
#define CIFSSEC_MAY_SIGN 0x00001
|
||||||
|
|
|
@ -526,12 +526,21 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
|
||||||
const struct nls_table *codepage);
|
const struct nls_table *codepage);
|
||||||
extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
|
extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
|
||||||
unsigned char *p24);
|
unsigned char *p24);
|
||||||
|
|
||||||
|
extern int
|
||||||
|
cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
|
||||||
|
const char *devname, bool is_smb3);
|
||||||
extern void
|
extern void
|
||||||
cifs_cleanup_volume_info_contents(struct smb_vol *volume_info);
|
cifs_cleanup_volume_info_contents(struct smb_vol *volume_info);
|
||||||
|
|
||||||
extern struct TCP_Server_Info *
|
extern struct TCP_Server_Info *
|
||||||
cifs_find_tcp_session(struct smb_vol *vol);
|
cifs_find_tcp_session(struct smb_vol *vol);
|
||||||
|
|
||||||
|
extern void cifs_put_smb_ses(struct cifs_ses *ses);
|
||||||
|
|
||||||
|
extern struct cifs_ses *
|
||||||
|
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info);
|
||||||
|
|
||||||
void cifs_readdata_release(struct kref *refcount);
|
void cifs_readdata_release(struct kref *refcount);
|
||||||
int cifs_async_readv(struct cifs_readdata *rdata);
|
int cifs_async_readv(struct cifs_readdata *rdata);
|
||||||
int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
|
int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
|
||||||
|
|
|
@ -860,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, NULL);
|
server, CIFS_NON_BLOCKING | 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);
|
||||||
|
|
||||||
|
@ -2508,8 +2508,8 @@ int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
|
iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
|
||||||
|
|
||||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
|
||||||
rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP,
|
rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type,
|
||||||
&rsp_iov);
|
CIFS_NO_RSP_BUF, &rsp_iov);
|
||||||
cifs_small_buf_release(pSMB);
|
cifs_small_buf_release(pSMB);
|
||||||
if (rc)
|
if (rc)
|
||||||
cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc);
|
cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc);
|
||||||
|
@ -2540,7 +2540,7 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
|
if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
|
||||||
/* no response expected */
|
/* no response expected */
|
||||||
flags = CIFS_ASYNC_OP | CIFS_OBREAK_OP;
|
flags = CIFS_NO_SRV_RSP | CIFS_NON_BLOCKING | CIFS_OBREAK_OP;
|
||||||
pSMB->Timeout = 0;
|
pSMB->Timeout = 0;
|
||||||
} else if (waitFlag) {
|
} else if (waitFlag) {
|
||||||
flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
|
flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
|
||||||
|
@ -6567,93 +6567,3 @@ SetEARetry:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* BB unused temporarily */
|
|
||||||
/*
|
|
||||||
* Years ago the kernel added a "dnotify" function for Samba server,
|
|
||||||
* to allow network clients (such as Windows) to display updated
|
|
||||||
* lists of files in directory listings automatically when
|
|
||||||
* files are added by one user when another user has the
|
|
||||||
* same directory open on their desktop. The Linux cifs kernel
|
|
||||||
* client hooked into the kernel side of this interface for
|
|
||||||
* the same reason, but ironically when the VFS moved from
|
|
||||||
* "dnotify" to "inotify" it became harder to plug in Linux
|
|
||||||
* network file system clients (the most obvious use case
|
|
||||||
* for notify interfaces is when multiple users can update
|
|
||||||
* the contents of the same directory - exactly what network
|
|
||||||
* file systems can do) although the server (Samba) could
|
|
||||||
* still use it. For the short term we leave the worker
|
|
||||||
* function ifdeffed out (below) until inotify is fixed
|
|
||||||
* in the VFS to make it easier to plug in network file
|
|
||||||
* system clients. If inotify turns out to be permanently
|
|
||||||
* incompatible for network fs clients, we could instead simply
|
|
||||||
* expose this config flag by adding a future cifs (and smb2) notify ioctl.
|
|
||||||
*/
|
|
||||||
int CIFSSMBNotify(const unsigned int xid, struct cifs_tcon *tcon,
|
|
||||||
const int notify_subdirs, const __u16 netfid,
|
|
||||||
__u32 filter, struct file *pfile, int multishot,
|
|
||||||
const struct nls_table *nls_codepage)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
struct smb_com_transaction_change_notify_req *pSMB = NULL;
|
|
||||||
struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
|
|
||||||
struct dir_notify_req *dnotify_req;
|
|
||||||
int bytes_returned;
|
|
||||||
|
|
||||||
cifs_dbg(FYI, "In CIFSSMBNotify for file handle %d\n", (int)netfid);
|
|
||||||
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
|
|
||||||
(void **) &pSMBr);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
pSMB->TotalParameterCount = 0 ;
|
|
||||||
pSMB->TotalDataCount = 0;
|
|
||||||
pSMB->MaxParameterCount = cpu_to_le32(2);
|
|
||||||
pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
|
|
||||||
pSMB->MaxSetupCount = 4;
|
|
||||||
pSMB->Reserved = 0;
|
|
||||||
pSMB->ParameterOffset = 0;
|
|
||||||
pSMB->DataCount = 0;
|
|
||||||
pSMB->DataOffset = 0;
|
|
||||||
pSMB->SetupCount = 4; /* single byte does not need le conversion */
|
|
||||||
pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
|
|
||||||
pSMB->ParameterCount = pSMB->TotalParameterCount;
|
|
||||||
if (notify_subdirs)
|
|
||||||
pSMB->WatchTree = 1; /* one byte - no le conversion needed */
|
|
||||||
pSMB->Reserved2 = 0;
|
|
||||||
pSMB->CompletionFilter = cpu_to_le32(filter);
|
|
||||||
pSMB->Fid = netfid; /* file handle always le */
|
|
||||||
pSMB->ByteCount = 0;
|
|
||||||
|
|
||||||
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
|
|
||||||
(struct smb_hdr *)pSMBr, &bytes_returned,
|
|
||||||
CIFS_ASYNC_OP);
|
|
||||||
if (rc) {
|
|
||||||
cifs_dbg(FYI, "Error in Notify = %d\n", rc);
|
|
||||||
} else {
|
|
||||||
/* Add file to outstanding requests */
|
|
||||||
/* BB change to kmem cache alloc */
|
|
||||||
dnotify_req = kmalloc(
|
|
||||||
sizeof(struct dir_notify_req),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (dnotify_req) {
|
|
||||||
dnotify_req->Pid = pSMB->hdr.Pid;
|
|
||||||
dnotify_req->PidHigh = pSMB->hdr.PidHigh;
|
|
||||||
dnotify_req->Mid = pSMB->hdr.Mid;
|
|
||||||
dnotify_req->Tid = pSMB->hdr.Tid;
|
|
||||||
dnotify_req->Uid = pSMB->hdr.Uid;
|
|
||||||
dnotify_req->netfid = netfid;
|
|
||||||
dnotify_req->pfile = pfile;
|
|
||||||
dnotify_req->filter = filter;
|
|
||||||
dnotify_req->multishot = multishot;
|
|
||||||
spin_lock(&GlobalMid_Lock);
|
|
||||||
list_add_tail(&dnotify_req->lhead,
|
|
||||||
&GlobalDnotifyReqList);
|
|
||||||
spin_unlock(&GlobalMid_Lock);
|
|
||||||
} else
|
|
||||||
rc = -ENOMEM;
|
|
||||||
}
|
|
||||||
cifs_buf_release(pSMB);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */
|
|
||||||
|
|
|
@ -323,8 +323,6 @@ static int ip_connect(struct TCP_Server_Info *server);
|
||||||
static int generic_ip_connect(struct TCP_Server_Info *server);
|
static int generic_ip_connect(struct TCP_Server_Info *server);
|
||||||
static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
|
static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
|
||||||
static void cifs_prune_tlinks(struct work_struct *work);
|
static void cifs_prune_tlinks(struct work_struct *work);
|
||||||
static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
|
|
||||||
const char *devname, bool is_smb3);
|
|
||||||
static char *extract_hostname(const char *unc);
|
static char *extract_hostname(const char *unc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -530,21 +528,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||||
/* do not want to be sending data on a socket we are freeing */
|
/* do not want to be sending data on a socket we are freeing */
|
||||||
cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
|
cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
|
||||||
mutex_lock(&server->srv_mutex);
|
mutex_lock(&server->srv_mutex);
|
||||||
if (server->ssocket) {
|
|
||||||
cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n",
|
|
||||||
server->ssocket->state, server->ssocket->flags);
|
|
||||||
kernel_sock_shutdown(server->ssocket, SHUT_WR);
|
|
||||||
cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n",
|
|
||||||
server->ssocket->state, server->ssocket->flags);
|
|
||||||
sock_release(server->ssocket);
|
|
||||||
server->ssocket = NULL;
|
|
||||||
}
|
|
||||||
server->sequence_number = 0;
|
|
||||||
server->session_estab = false;
|
|
||||||
kfree(server->session_key.response);
|
|
||||||
server->session_key.response = NULL;
|
|
||||||
server->session_key.len = 0;
|
|
||||||
server->lstrp = jiffies;
|
|
||||||
|
|
||||||
/* mark submitted MIDs for retry and issue callback */
|
/* mark submitted MIDs for retry and issue callback */
|
||||||
INIT_LIST_HEAD(&retry_list);
|
INIT_LIST_HEAD(&retry_list);
|
||||||
|
@ -557,7 +540,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||||
list_move(&mid_entry->qhead, &retry_list);
|
list_move(&mid_entry->qhead, &retry_list);
|
||||||
}
|
}
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
mutex_unlock(&server->srv_mutex);
|
|
||||||
|
|
||||||
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
|
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
|
||||||
list_for_each_safe(tmp, tmp2, &retry_list) {
|
list_for_each_safe(tmp, tmp2, &retry_list) {
|
||||||
|
@ -566,6 +548,25 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||||
mid_entry->callback(mid_entry);
|
mid_entry->callback(mid_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server->ssocket) {
|
||||||
|
cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n",
|
||||||
|
server->ssocket->state, server->ssocket->flags);
|
||||||
|
kernel_sock_shutdown(server->ssocket, SHUT_WR);
|
||||||
|
cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n",
|
||||||
|
server->ssocket->state, server->ssocket->flags);
|
||||||
|
sock_release(server->ssocket);
|
||||||
|
server->ssocket = NULL;
|
||||||
|
} else if (cifs_rdma_enabled(server))
|
||||||
|
smbd_destroy(server);
|
||||||
|
server->sequence_number = 0;
|
||||||
|
server->session_estab = false;
|
||||||
|
kfree(server->session_key.response);
|
||||||
|
server->session_key.response = NULL;
|
||||||
|
server->session_key.len = 0;
|
||||||
|
server->lstrp = jiffies;
|
||||||
|
|
||||||
|
mutex_unlock(&server->srv_mutex);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try_to_freeze();
|
try_to_freeze();
|
||||||
|
|
||||||
|
@ -931,10 +932,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||||
wake_up_all(&server->request_q);
|
wake_up_all(&server->request_q);
|
||||||
/* give those requests time to exit */
|
/* give those requests time to exit */
|
||||||
msleep(125);
|
msleep(125);
|
||||||
if (cifs_rdma_enabled(server) && server->smbd_conn) {
|
if (cifs_rdma_enabled(server))
|
||||||
smbd_destroy(server->smbd_conn);
|
smbd_destroy(server);
|
||||||
server->smbd_conn = NULL;
|
|
||||||
}
|
|
||||||
if (server->ssocket) {
|
if (server->ssocket) {
|
||||||
sock_release(server->ssocket);
|
sock_release(server->ssocket);
|
||||||
server->ssocket = NULL;
|
server->ssocket = NULL;
|
||||||
|
@ -2904,8 +2903,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void cifs_put_smb_ses(struct cifs_ses *ses)
|
||||||
cifs_put_smb_ses(struct cifs_ses *ses)
|
|
||||||
{
|
{
|
||||||
unsigned int rc, xid;
|
unsigned int rc, xid;
|
||||||
struct TCP_Server_Info *server = ses->server;
|
struct TCP_Server_Info *server = ses->server;
|
||||||
|
@ -3082,7 +3080,7 @@ cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)),
|
||||||
* already got a server reference (server refcount +1). See
|
* already got a server reference (server refcount +1). See
|
||||||
* cifs_get_tcon() for refcount explanations.
|
* cifs_get_tcon() for refcount explanations.
|
||||||
*/
|
*/
|
||||||
static struct cifs_ses *
|
struct cifs_ses *
|
||||||
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
|
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
|
||||||
{
|
{
|
||||||
int rc = -ENOMEM;
|
int rc = -ENOMEM;
|
||||||
|
@ -4389,7 +4387,7 @@ static int mount_do_dfs_failover(const char *path,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int
|
int
|
||||||
cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
|
cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
|
||||||
const char *devname, bool is_smb3)
|
const char *devname, bool is_smb3)
|
||||||
{
|
{
|
||||||
|
@ -4543,7 +4541,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
|
||||||
struct cifs_tcon *tcon = NULL;
|
struct cifs_tcon *tcon = NULL;
|
||||||
struct TCP_Server_Info *server;
|
struct TCP_Server_Info *server;
|
||||||
char *root_path = NULL, *full_path = NULL;
|
char *root_path = NULL, *full_path = NULL;
|
||||||
char *old_mountdata;
|
char *old_mountdata, *origin_mountdata = NULL;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
|
rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
|
||||||
|
@ -4602,6 +4600,14 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Save DFS root volume information for DFS refresh worker */
|
||||||
|
origin_mountdata = kstrndup(cifs_sb->mountdata,
|
||||||
|
strlen(cifs_sb->mountdata), GFP_KERNEL);
|
||||||
|
if (!origin_mountdata) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
if (cifs_sb->mountdata != old_mountdata) {
|
if (cifs_sb->mountdata != old_mountdata) {
|
||||||
/* If we were redirected, reconnect to new target server */
|
/* If we were redirected, reconnect to new target server */
|
||||||
mount_put_conns(cifs_sb, xid, server, ses, tcon);
|
mount_put_conns(cifs_sb, xid, server, ses, tcon);
|
||||||
|
@ -4710,7 +4716,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
|
||||||
}
|
}
|
||||||
spin_unlock(&cifs_tcp_ses_lock);
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
|
||||||
rc = dfs_cache_add_vol(vol, cifs_sb->origin_fullpath);
|
rc = dfs_cache_add_vol(origin_mountdata, vol, cifs_sb->origin_fullpath);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kfree(cifs_sb->origin_fullpath);
|
kfree(cifs_sb->origin_fullpath);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -4728,6 +4734,7 @@ out:
|
||||||
error:
|
error:
|
||||||
kfree(full_path);
|
kfree(full_path);
|
||||||
kfree(root_path);
|
kfree(root_path);
|
||||||
|
kfree(origin_mountdata);
|
||||||
mount_put_conns(cifs_sb, xid, server, ses, tcon);
|
mount_put_conns(cifs_sb, xid, server, ses, tcon);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* DFS referral cache routines
|
* DFS referral cache routines
|
||||||
*
|
*
|
||||||
* Copyright (c) 2018 Paulo Alcantara <palcantara@suse.de>
|
* Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
@ -52,6 +52,7 @@ static struct kmem_cache *dfs_cache_slab __read_mostly;
|
||||||
struct dfs_cache_vol_info {
|
struct dfs_cache_vol_info {
|
||||||
char *vi_fullpath;
|
char *vi_fullpath;
|
||||||
struct smb_vol vi_vol;
|
struct smb_vol vi_vol;
|
||||||
|
char *vi_mntdata;
|
||||||
struct list_head vi_list;
|
struct list_head vi_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -529,6 +530,7 @@ static inline void free_vol(struct dfs_cache_vol_info *vi)
|
||||||
{
|
{
|
||||||
list_del(&vi->vi_list);
|
list_del(&vi->vi_list);
|
||||||
kfree(vi->vi_fullpath);
|
kfree(vi->vi_fullpath);
|
||||||
|
kfree(vi->vi_mntdata);
|
||||||
cifs_cleanup_volume_info_contents(&vi->vi_vol);
|
cifs_cleanup_volume_info_contents(&vi->vi_vol);
|
||||||
kfree(vi);
|
kfree(vi);
|
||||||
}
|
}
|
||||||
|
@ -1139,17 +1141,18 @@ err_free_username:
|
||||||
* dfs_cache_add_vol - add a cifs volume during mount() that will be handled by
|
* dfs_cache_add_vol - add a cifs volume during mount() that will be handled by
|
||||||
* DFS cache refresh worker.
|
* DFS cache refresh worker.
|
||||||
*
|
*
|
||||||
|
* @mntdata: mount data.
|
||||||
* @vol: cifs volume.
|
* @vol: cifs volume.
|
||||||
* @fullpath: origin full path.
|
* @fullpath: origin full path.
|
||||||
*
|
*
|
||||||
* Return zero if volume was set up correctly, otherwise non-zero.
|
* Return zero if volume was set up correctly, otherwise non-zero.
|
||||||
*/
|
*/
|
||||||
int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath)
|
int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct dfs_cache_vol_info *vi;
|
struct dfs_cache_vol_info *vi;
|
||||||
|
|
||||||
if (!vol || !fullpath)
|
if (!vol || !fullpath || !mntdata)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
|
cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
|
||||||
|
@ -1168,6 +1171,8 @@ int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath)
|
||||||
if (rc)
|
if (rc)
|
||||||
goto err_free_fullpath;
|
goto err_free_fullpath;
|
||||||
|
|
||||||
|
vi->vi_mntdata = mntdata;
|
||||||
|
|
||||||
mutex_lock(&dfs_cache.dc_lock);
|
mutex_lock(&dfs_cache.dc_lock);
|
||||||
list_add_tail(&vi->vi_list, &dfs_cache.dc_vol_list);
|
list_add_tail(&vi->vi_list, &dfs_cache.dc_vol_list);
|
||||||
mutex_unlock(&dfs_cache.dc_lock);
|
mutex_unlock(&dfs_cache.dc_lock);
|
||||||
|
@ -1275,8 +1280,102 @@ static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
|
||||||
spin_unlock(&cifs_tcp_ses_lock);
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_dfs_link(const char *path)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
s = strchr(path + 1, '\\');
|
||||||
|
if (!s)
|
||||||
|
return false;
|
||||||
|
return !!strchr(s + 1, '\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *get_dfs_root(const char *path)
|
||||||
|
{
|
||||||
|
char *s, *npath;
|
||||||
|
|
||||||
|
s = strchr(path + 1, '\\');
|
||||||
|
if (!s)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
s = strchr(s + 1, '\\');
|
||||||
|
if (!s)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
npath = kstrndup(path, s - path, GFP_KERNEL);
|
||||||
|
if (!npath)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
return npath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find root SMB session out of a DFS link path */
|
||||||
|
static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi,
|
||||||
|
struct cifs_tcon *tcon, const char *path)
|
||||||
|
{
|
||||||
|
char *rpath;
|
||||||
|
int rc;
|
||||||
|
struct dfs_info3_param ref = {0};
|
||||||
|
char *mdata = NULL, *devname = NULL;
|
||||||
|
bool is_smb3 = tcon->ses->server->vals->header_preamble_size == 0;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
|
struct cifs_ses *ses;
|
||||||
|
struct smb_vol vol;
|
||||||
|
|
||||||
|
rpath = get_dfs_root(path);
|
||||||
|
if (IS_ERR(rpath))
|
||||||
|
return ERR_CAST(rpath);
|
||||||
|
|
||||||
|
memset(&vol, 0, sizeof(vol));
|
||||||
|
|
||||||
|
rc = dfs_cache_noreq_find(rpath, &ref, NULL);
|
||||||
|
if (rc) {
|
||||||
|
ses = ERR_PTR(rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mdata = cifs_compose_mount_options(vi->vi_mntdata, rpath, &ref,
|
||||||
|
&devname);
|
||||||
|
free_dfs_info_param(&ref);
|
||||||
|
|
||||||
|
if (IS_ERR(mdata)) {
|
||||||
|
ses = ERR_CAST(mdata);
|
||||||
|
mdata = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = cifs_setup_volume_info(&vol, mdata, devname, is_smb3);
|
||||||
|
kfree(devname);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
ses = ERR_PTR(rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = cifs_find_tcp_session(&vol);
|
||||||
|
if (IS_ERR_OR_NULL(server)) {
|
||||||
|
ses = ERR_PTR(-EHOSTDOWN);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (server->tcpStatus != CifsGood) {
|
||||||
|
cifs_put_tcp_session(server, 0);
|
||||||
|
ses = ERR_PTR(-EHOSTDOWN);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ses = cifs_get_smb_ses(server, &vol);
|
||||||
|
|
||||||
|
out:
|
||||||
|
cifs_cleanup_volume_info_contents(&vol);
|
||||||
|
kfree(mdata);
|
||||||
|
kfree(rpath);
|
||||||
|
|
||||||
|
return ses;
|
||||||
|
}
|
||||||
|
|
||||||
/* Refresh DFS cache entry from a given tcon */
|
/* Refresh DFS cache entry from a given tcon */
|
||||||
static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon)
|
static void do_refresh_tcon(struct dfs_cache *dc, struct dfs_cache_vol_info *vi,
|
||||||
|
struct cifs_tcon *tcon)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
|
@ -1285,6 +1384,7 @@ static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon)
|
||||||
struct dfs_cache_entry *ce;
|
struct dfs_cache_entry *ce;
|
||||||
struct dfs_info3_param *refs = NULL;
|
struct dfs_info3_param *refs = NULL;
|
||||||
int numrefs = 0;
|
int numrefs = 0;
|
||||||
|
struct cifs_ses *root_ses = NULL, *ses;
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
|
||||||
|
@ -1306,13 +1406,24 @@ static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon)
|
||||||
if (!cache_entry_expired(ce))
|
if (!cache_entry_expired(ce))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (unlikely(!tcon->ses->server->ops->get_dfs_refer)) {
|
/* If it's a DFS Link, then use root SMB session for refreshing it */
|
||||||
|
if (is_dfs_link(npath)) {
|
||||||
|
ses = root_ses = find_root_ses(vi, tcon, npath);
|
||||||
|
if (IS_ERR(ses)) {
|
||||||
|
rc = PTR_ERR(ses);
|
||||||
|
root_ses = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ses = tcon->ses;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(!ses->server->ops->get_dfs_refer)) {
|
||||||
rc = -EOPNOTSUPP;
|
rc = -EOPNOTSUPP;
|
||||||
} else {
|
} else {
|
||||||
rc = tcon->ses->server->ops->get_dfs_refer(xid, tcon->ses, path,
|
rc = ses->server->ops->get_dfs_refer(xid, ses, path, &refs,
|
||||||
&refs, &numrefs,
|
&numrefs, dc->dc_nlsc,
|
||||||
dc->dc_nlsc,
|
tcon->remap);
|
||||||
tcon->remap);
|
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
mutex_lock(&dfs_cache_list_lock);
|
mutex_lock(&dfs_cache_list_lock);
|
||||||
ce = __update_cache_entry(npath, refs, numrefs);
|
ce = __update_cache_entry(npath, refs, numrefs);
|
||||||
|
@ -1323,9 +1434,11 @@ static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon)
|
||||||
rc = PTR_ERR(ce);
|
rc = PTR_ERR(ce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rc)
|
|
||||||
cifs_dbg(FYI, "%s: failed to update expired entry\n", __func__);
|
|
||||||
out:
|
out:
|
||||||
|
if (root_ses)
|
||||||
|
cifs_put_smb_ses(root_ses);
|
||||||
|
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
free_normalized_path(path, npath);
|
free_normalized_path(path, npath);
|
||||||
}
|
}
|
||||||
|
@ -1333,9 +1446,6 @@ out:
|
||||||
/*
|
/*
|
||||||
* Worker that will refresh DFS cache based on lowest TTL value from a DFS
|
* Worker that will refresh DFS cache based on lowest TTL value from a DFS
|
||||||
* referral.
|
* referral.
|
||||||
*
|
|
||||||
* FIXME: ensure that all requests are sent to DFS root for refreshing the
|
|
||||||
* cache.
|
|
||||||
*/
|
*/
|
||||||
static void refresh_cache_worker(struct work_struct *work)
|
static void refresh_cache_worker(struct work_struct *work)
|
||||||
{
|
{
|
||||||
|
@ -1356,7 +1466,7 @@ static void refresh_cache_worker(struct work_struct *work)
|
||||||
goto next;
|
goto next;
|
||||||
get_tcons(server, &list);
|
get_tcons(server, &list);
|
||||||
list_for_each_entry_safe(tcon, ntcon, &list, ulist) {
|
list_for_each_entry_safe(tcon, ntcon, &list, ulist) {
|
||||||
do_refresh_tcon(dc, tcon);
|
do_refresh_tcon(dc, vi, tcon);
|
||||||
list_del_init(&tcon->ulist);
|
list_del_init(&tcon->ulist);
|
||||||
cifs_put_tcon(tcon);
|
cifs_put_tcon(tcon);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* DFS referral cache routines
|
* DFS referral cache routines
|
||||||
*
|
*
|
||||||
* Copyright (c) 2018 Paulo Alcantara <palcantara@suse.de>
|
* Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CIFS_DFS_CACHE_H
|
#ifndef _CIFS_DFS_CACHE_H
|
||||||
|
@ -43,7 +43,8 @@ dfs_cache_noreq_update_tgthint(const char *path,
|
||||||
extern int dfs_cache_get_tgt_referral(const char *path,
|
extern int dfs_cache_get_tgt_referral(const char *path,
|
||||||
const struct dfs_cache_tgt_iterator *it,
|
const struct dfs_cache_tgt_iterator *it,
|
||||||
struct dfs_info3_param *ref);
|
struct dfs_info3_param *ref);
|
||||||
extern int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath);
|
extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol,
|
||||||
|
const char *fullpath);
|
||||||
extern int dfs_cache_update_vol(const char *fullpath,
|
extern int dfs_cache_update_vol(const char *fullpath,
|
||||||
struct TCP_Server_Info *server);
|
struct TCP_Server_Info *server);
|
||||||
extern void dfs_cache_del_vol(const char *fullpath);
|
extern void dfs_cache_del_vol(const char *fullpath);
|
||||||
|
|
|
@ -2443,7 +2443,6 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
|
||||||
rc = file_write_and_wait_range(file, start, end);
|
rc = file_write_and_wait_range(file, start, end);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
inode_lock(inode);
|
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
|
||||||
|
@ -2468,7 +2467,6 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
|
||||||
}
|
}
|
||||||
|
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
inode_unlock(inode);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2480,12 +2478,10 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||||
struct TCP_Server_Info *server;
|
struct TCP_Server_Info *server;
|
||||||
struct cifsFileInfo *smbfile = file->private_data;
|
struct cifsFileInfo *smbfile = file->private_data;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
|
struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
|
||||||
struct inode *inode = file->f_mapping->host;
|
|
||||||
|
|
||||||
rc = file_write_and_wait_range(file, start, end);
|
rc = file_write_and_wait_range(file, start, end);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
inode_lock(inode);
|
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
|
||||||
|
@ -2502,7 +2498,6 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||||
}
|
}
|
||||||
|
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
inode_unlock(inode);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2116,6 +2116,43 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
|
||||||
|
u64 len)
|
||||||
|
{
|
||||||
|
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->vfs_inode.i_sb);
|
||||||
|
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
struct cifsFileInfo *cfile;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to be sure that all dirty pages are written as they
|
||||||
|
* might fill holes on the server.
|
||||||
|
*/
|
||||||
|
if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
|
||||||
|
inode->i_mapping->nrpages != 0) {
|
||||||
|
rc = filemap_fdatawait(inode->i_mapping);
|
||||||
|
if (rc) {
|
||||||
|
mapping_set_error(inode->i_mapping, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfile = find_readable_file(cifs_i, false);
|
||||||
|
if (cfile == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (server->ops->fiemap) {
|
||||||
|
rc = server->ops->fiemap(tcon, cfile, fei, start, len);
|
||||||
|
cifsFileInfo_put(cfile);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cifsFileInfo_put(cfile);
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
static int cifs_truncate_page(struct address_space *mapping, loff_t from)
|
static int cifs_truncate_page(struct address_space *mapping, loff_t from)
|
||||||
{
|
{
|
||||||
pgoff_t index = from >> PAGE_SHIFT;
|
pgoff_t index = from >> PAGE_SHIFT;
|
||||||
|
|
|
@ -648,9 +648,16 @@ cifs_get_link(struct dentry *direntry, struct inode *inode,
|
||||||
rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
|
rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
|
||||||
&target_path);
|
&target_path);
|
||||||
|
|
||||||
if (rc != 0 && server->ops->query_symlink)
|
if (rc != 0 && server->ops->query_symlink) {
|
||||||
rc = server->ops->query_symlink(xid, tcon, full_path,
|
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||||
&target_path, cifs_sb);
|
bool reparse_point = false;
|
||||||
|
|
||||||
|
if (cifsi->cifsAttrs & ATTR_REPARSE)
|
||||||
|
reparse_point = true;
|
||||||
|
|
||||||
|
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
|
||||||
|
&target_path, reparse_point);
|
||||||
|
}
|
||||||
|
|
||||||
kfree(full_path);
|
kfree(full_path);
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
|
|
|
@ -950,8 +950,8 @@ cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *full_path, char **target_path,
|
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||||
struct cifs_sb_info *cifs_sb)
|
char **target_path, bool is_reparse_point)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
int oplock = 0;
|
int oplock = 0;
|
||||||
|
@ -960,6 +960,11 @@ cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
||||||
|
|
||||||
|
if (is_reparse_point) {
|
||||||
|
cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for unix extensions */
|
/* Check for unix extensions */
|
||||||
if (cap_unix(tcon->ses)) {
|
if (cap_unix(tcon->ses)) {
|
||||||
rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path,
|
rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path,
|
||||||
|
|
|
@ -1382,6 +1382,26 @@ smb2_ioctl_query_info(const unsigned int xid,
|
||||||
oparms.fid = &fid;
|
oparms.fid = &fid;
|
||||||
oparms.reconnect = false;
|
oparms.reconnect = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FSCTL codes encode the special access they need in the fsctl code.
|
||||||
|
*/
|
||||||
|
if (qi.flags & PASSTHRU_FSCTL) {
|
||||||
|
switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) {
|
||||||
|
case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS:
|
||||||
|
oparms.desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
|
||||||
|
break;
|
||||||
|
case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS:
|
||||||
|
oparms.desired_access = GENERIC_ALL;
|
||||||
|
break;
|
||||||
|
case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS:
|
||||||
|
oparms.desired_access = GENERIC_READ;
|
||||||
|
break;
|
||||||
|
case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS:
|
||||||
|
oparms.desired_access = GENERIC_WRITE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path);
|
rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto iqinf_exit;
|
goto iqinf_exit;
|
||||||
|
@ -1399,8 +1419,9 @@ smb2_ioctl_query_info(const unsigned int xid,
|
||||||
|
|
||||||
rc = SMB2_ioctl_init(tcon, &rqst[1],
|
rc = SMB2_ioctl_init(tcon, &rqst[1],
|
||||||
COMPOUND_FID, COMPOUND_FID,
|
COMPOUND_FID, COMPOUND_FID,
|
||||||
qi.info_type, true, NULL,
|
qi.info_type, true, buffer,
|
||||||
0, CIFSMaxBufSize);
|
qi.output_buffer_length,
|
||||||
|
CIFSMaxBufSize);
|
||||||
}
|
}
|
||||||
} else if (qi.flags == PASSTHRU_QUERY_INFO) {
|
} else if (qi.flags == PASSTHRU_QUERY_INFO) {
|
||||||
memset(&qi_iov, 0, sizeof(qi_iov));
|
memset(&qi_iov, 0, sizeof(qi_iov));
|
||||||
|
@ -1441,12 +1462,19 @@ smb2_ioctl_query_info(const unsigned int xid,
|
||||||
io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base;
|
io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base;
|
||||||
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
|
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
|
||||||
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
|
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
|
||||||
|
if (qi.input_buffer_length > 0 &&
|
||||||
|
le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length > rsp_iov[1].iov_len) {
|
||||||
|
rc = -EFAULT;
|
||||||
|
goto iqinf_exit;
|
||||||
|
}
|
||||||
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
|
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
|
||||||
sizeof(qi.input_buffer_length))) {
|
sizeof(qi.input_buffer_length))) {
|
||||||
rc = -EFAULT;
|
rc = -EFAULT;
|
||||||
goto iqinf_exit;
|
goto iqinf_exit;
|
||||||
}
|
}
|
||||||
if (copy_to_user(pqi + 1, &io_rsp[1], qi.input_buffer_length)) {
|
if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info),
|
||||||
|
(const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset),
|
||||||
|
qi.input_buffer_length)) {
|
||||||
rc = -EFAULT;
|
rc = -EFAULT;
|
||||||
goto iqinf_exit;
|
goto iqinf_exit;
|
||||||
}
|
}
|
||||||
|
@ -1821,6 +1849,14 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u32 max_response_size;
|
u32 max_response_size;
|
||||||
struct smb_snapshot_array snapshot_in;
|
struct smb_snapshot_array snapshot_in;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On the first query to enumerate the list of snapshots available
|
||||||
|
* for this volume the buffer begins with 0 (number of snapshots
|
||||||
|
* which can be returned is zero since at that point we do not know
|
||||||
|
* how big the buffer needs to be). On the second query,
|
||||||
|
* it (ret_data_len) is set to number of snapshots so we can
|
||||||
|
* know to set the maximum response size larger (see below).
|
||||||
|
*/
|
||||||
if (get_user(ret_data_len, (unsigned int __user *)ioc_buf))
|
if (get_user(ret_data_len, (unsigned int __user *)ioc_buf))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
@ -2354,46 +2390,129 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *full_path, char **target_path,
|
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||||
struct cifs_sb_info *cifs_sb)
|
char **target_path, bool is_reparse_point)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
__le16 *utf16_path;
|
__le16 *utf16_path = NULL;
|
||||||
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||||
struct cifs_open_parms oparms;
|
struct cifs_open_parms oparms;
|
||||||
struct cifs_fid fid;
|
struct cifs_fid fid;
|
||||||
struct kvec err_iov = {NULL, 0};
|
struct kvec err_iov = {NULL, 0};
|
||||||
struct smb2_err_rsp *err_buf = NULL;
|
struct smb2_err_rsp *err_buf = NULL;
|
||||||
int resp_buftype;
|
|
||||||
struct smb2_symlink_err_rsp *symlink;
|
struct smb2_symlink_err_rsp *symlink;
|
||||||
unsigned int sub_len;
|
unsigned int sub_len;
|
||||||
unsigned int sub_offset;
|
unsigned int sub_offset;
|
||||||
unsigned int print_len;
|
unsigned int print_len;
|
||||||
unsigned int print_offset;
|
unsigned int print_offset;
|
||||||
|
int flags = 0;
|
||||||
|
struct smb_rqst rqst[3];
|
||||||
|
int resp_buftype[3];
|
||||||
|
struct kvec rsp_iov[3];
|
||||||
|
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
|
||||||
|
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
|
||||||
|
struct kvec close_iov[1];
|
||||||
|
struct smb2_create_rsp *create_rsp;
|
||||||
|
struct smb2_ioctl_rsp *ioctl_rsp;
|
||||||
|
char *ioctl_buf;
|
||||||
|
u32 plen;
|
||||||
|
|
||||||
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
||||||
|
|
||||||
|
if (smb3_encryption_required(tcon))
|
||||||
|
flags |= CIFS_TRANSFORM_REQ;
|
||||||
|
|
||||||
|
memset(rqst, 0, sizeof(rqst));
|
||||||
|
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
|
||||||
|
memset(rsp_iov, 0, sizeof(rsp_iov));
|
||||||
|
|
||||||
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
||||||
if (!utf16_path)
|
if (!utf16_path)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Open */
|
||||||
|
memset(&open_iov, 0, sizeof(open_iov));
|
||||||
|
rqst[0].rq_iov = open_iov;
|
||||||
|
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
|
||||||
|
|
||||||
|
memset(&oparms, 0, sizeof(oparms));
|
||||||
oparms.tcon = tcon;
|
oparms.tcon = tcon;
|
||||||
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
||||||
oparms.disposition = FILE_OPEN;
|
oparms.disposition = FILE_OPEN;
|
||||||
|
|
||||||
if (backup_cred(cifs_sb))
|
if (backup_cred(cifs_sb))
|
||||||
oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
|
oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
|
||||||
else
|
else
|
||||||
oparms.create_options = 0;
|
oparms.create_options = 0;
|
||||||
|
if (is_reparse_point)
|
||||||
|
oparms.create_options = OPEN_REPARSE_POINT;
|
||||||
|
|
||||||
oparms.fid = &fid;
|
oparms.fid = &fid;
|
||||||
oparms.reconnect = false;
|
oparms.reconnect = false;
|
||||||
|
|
||||||
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
|
rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, utf16_path);
|
||||||
&resp_buftype);
|
if (rc)
|
||||||
if (!rc)
|
goto querty_exit;
|
||||||
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
smb2_set_next_command(tcon, &rqst[0]);
|
||||||
|
|
||||||
|
|
||||||
|
/* IOCTL */
|
||||||
|
memset(&io_iov, 0, sizeof(io_iov));
|
||||||
|
rqst[1].rq_iov = io_iov;
|
||||||
|
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
|
||||||
|
|
||||||
|
rc = SMB2_ioctl_init(tcon, &rqst[1], fid.persistent_fid,
|
||||||
|
fid.volatile_fid, FSCTL_GET_REPARSE_POINT,
|
||||||
|
true /* is_fctl */, NULL, 0, CIFSMaxBufSize);
|
||||||
|
if (rc)
|
||||||
|
goto querty_exit;
|
||||||
|
|
||||||
|
smb2_set_next_command(tcon, &rqst[1]);
|
||||||
|
smb2_set_related(&rqst[1]);
|
||||||
|
|
||||||
|
|
||||||
|
/* Close */
|
||||||
|
memset(&close_iov, 0, sizeof(close_iov));
|
||||||
|
rqst[2].rq_iov = close_iov;
|
||||||
|
rqst[2].rq_nvec = 1;
|
||||||
|
|
||||||
|
rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
|
||||||
|
if (rc)
|
||||||
|
goto querty_exit;
|
||||||
|
|
||||||
|
smb2_set_related(&rqst[2]);
|
||||||
|
|
||||||
|
rc = compound_send_recv(xid, tcon->ses, flags, 3, rqst,
|
||||||
|
resp_buftype, rsp_iov);
|
||||||
|
|
||||||
|
create_rsp = rsp_iov[0].iov_base;
|
||||||
|
if (create_rsp && create_rsp->sync_hdr.Status)
|
||||||
|
err_iov = rsp_iov[0];
|
||||||
|
ioctl_rsp = rsp_iov[1].iov_base;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open was successful and we got an ioctl response.
|
||||||
|
*/
|
||||||
|
if ((rc == 0) && (is_reparse_point)) {
|
||||||
|
/* See MS-FSCC 2.3.23 */
|
||||||
|
|
||||||
|
ioctl_buf = (char *)ioctl_rsp + le32_to_cpu(ioctl_rsp->OutputOffset);
|
||||||
|
plen = le32_to_cpu(ioctl_rsp->OutputCount);
|
||||||
|
|
||||||
|
if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
|
||||||
|
rsp_iov[1].iov_len) {
|
||||||
|
cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", plen);
|
||||||
|
rc = -EIO;
|
||||||
|
goto querty_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do stuff with ioctl_buf/plen */
|
||||||
|
goto querty_exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (!rc || !err_iov.iov_base) {
|
if (!rc || !err_iov.iov_base) {
|
||||||
rc = -ENOENT;
|
rc = -ENOENT;
|
||||||
goto free_path;
|
goto querty_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
err_buf = err_iov.iov_base;
|
err_buf = err_iov.iov_base;
|
||||||
|
@ -2433,9 +2552,14 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
|
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
|
||||||
|
|
||||||
querty_exit:
|
querty_exit:
|
||||||
free_rsp_buf(resp_buftype, err_buf);
|
cifs_dbg(FYI, "query symlink rc %d\n", rc);
|
||||||
free_path:
|
|
||||||
kfree(utf16_path);
|
kfree(utf16_path);
|
||||||
|
SMB2_open_free(&rqst[0]);
|
||||||
|
SMB2_ioctl_free(&rqst[1]);
|
||||||
|
SMB2_close_free(&rqst[2]);
|
||||||
|
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||||
|
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||||
|
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2612,16 +2736,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||||
struct cifsInodeInfo *cifsi;
|
struct cifsInodeInfo *cifsi;
|
||||||
struct cifsFileInfo *cfile = file->private_data;
|
struct cifsFileInfo *cfile = file->private_data;
|
||||||
struct file_zero_data_information fsctl_buf;
|
struct file_zero_data_information fsctl_buf;
|
||||||
struct smb_rqst rqst[2];
|
|
||||||
int resp_buftype[2];
|
|
||||||
struct kvec rsp_iov[2];
|
|
||||||
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
|
|
||||||
struct kvec si_iov[1];
|
|
||||||
unsigned int size[1];
|
|
||||||
void *data[1];
|
|
||||||
long rc;
|
long rc;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
int num = 0, flags = 0;
|
|
||||||
__le64 eof;
|
__le64 eof;
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
@ -2643,39 +2759,16 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Must check if file sparse since fallocate -z (zero range) assumes
|
|
||||||
* non-sparse allocation
|
|
||||||
*/
|
|
||||||
if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) {
|
|
||||||
rc = -EOPNOTSUPP;
|
|
||||||
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
|
||||||
ses->Suid, offset, len, rc);
|
|
||||||
free_xid(xid);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
cifs_dbg(FYI, "offset %lld len %lld", offset, len);
|
cifs_dbg(FYI, "offset %lld len %lld", offset, len);
|
||||||
|
|
||||||
fsctl_buf.FileOffset = cpu_to_le64(offset);
|
fsctl_buf.FileOffset = cpu_to_le64(offset);
|
||||||
fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
|
fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
|
||||||
|
|
||||||
if (smb3_encryption_required(tcon))
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
flags |= CIFS_TRANSFORM_REQ;
|
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, true,
|
||||||
|
(char *)&fsctl_buf,
|
||||||
memset(rqst, 0, sizeof(rqst));
|
sizeof(struct file_zero_data_information),
|
||||||
resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
|
0, NULL, NULL);
|
||||||
memset(rsp_iov, 0, sizeof(rsp_iov));
|
|
||||||
|
|
||||||
|
|
||||||
memset(&io_iov, 0, sizeof(io_iov));
|
|
||||||
rqst[num].rq_iov = io_iov;
|
|
||||||
rqst[num].rq_nvec = SMB2_IOCTL_IOV_SIZE;
|
|
||||||
rc = SMB2_ioctl_init(tcon, &rqst[num++], cfile->fid.persistent_fid,
|
|
||||||
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
|
|
||||||
true /* is_fctl */, (char *)&fsctl_buf,
|
|
||||||
sizeof(struct file_zero_data_information),
|
|
||||||
CIFSMaxBufSize);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto zero_range_exit;
|
goto zero_range_exit;
|
||||||
|
|
||||||
|
@ -2683,33 +2776,12 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||||
* do we also need to change the size of the file?
|
* do we also need to change the size of the file?
|
||||||
*/
|
*/
|
||||||
if (keep_size == false && i_size_read(inode) < offset + len) {
|
if (keep_size == false && i_size_read(inode) < offset + len) {
|
||||||
smb2_set_next_command(tcon, &rqst[0]);
|
|
||||||
|
|
||||||
memset(&si_iov, 0, sizeof(si_iov));
|
|
||||||
rqst[num].rq_iov = si_iov;
|
|
||||||
rqst[num].rq_nvec = 1;
|
|
||||||
|
|
||||||
eof = cpu_to_le64(offset + len);
|
eof = cpu_to_le64(offset + len);
|
||||||
size[0] = 8; /* sizeof __le64 */
|
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||||
data[0] = &eof;
|
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||||
|
|
||||||
rc = SMB2_set_info_init(tcon, &rqst[num++],
|
|
||||||
cfile->fid.persistent_fid,
|
|
||||||
cfile->fid.persistent_fid,
|
|
||||||
current->tgid,
|
|
||||||
FILE_END_OF_FILE_INFORMATION,
|
|
||||||
SMB2_O_INFO_FILE, 0, data, size);
|
|
||||||
smb2_set_related(&rqst[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = compound_send_recv(xid, ses, flags, num, rqst,
|
|
||||||
resp_buftype, rsp_iov);
|
|
||||||
|
|
||||||
zero_range_exit:
|
zero_range_exit:
|
||||||
SMB2_ioctl_free(&rqst[0]);
|
|
||||||
SMB2_set_info_free(&rqst[1]);
|
|
||||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
|
||||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
if (rc)
|
if (rc)
|
||||||
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||||
|
@ -2850,6 +2922,79 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int smb3_fiemap(struct cifs_tcon *tcon,
|
||||||
|
struct cifsFileInfo *cfile,
|
||||||
|
struct fiemap_extent_info *fei, u64 start, u64 len)
|
||||||
|
{
|
||||||
|
unsigned int xid;
|
||||||
|
struct file_allocated_range_buffer in_data, *out_data;
|
||||||
|
u32 out_data_len;
|
||||||
|
int i, num, rc, flags, last_blob;
|
||||||
|
u64 next;
|
||||||
|
|
||||||
|
if (fiemap_check_flags(fei, FIEMAP_FLAG_SYNC))
|
||||||
|
return -EBADR;
|
||||||
|
|
||||||
|
xid = get_xid();
|
||||||
|
again:
|
||||||
|
in_data.file_offset = cpu_to_le64(start);
|
||||||
|
in_data.length = cpu_to_le64(len);
|
||||||
|
|
||||||
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid,
|
||||||
|
FSCTL_QUERY_ALLOCATED_RANGES, true,
|
||||||
|
(char *)&in_data, sizeof(in_data),
|
||||||
|
1024 * sizeof(struct file_allocated_range_buffer),
|
||||||
|
(char **)&out_data, &out_data_len);
|
||||||
|
if (rc == -E2BIG) {
|
||||||
|
last_blob = 0;
|
||||||
|
rc = 0;
|
||||||
|
} else
|
||||||
|
last_blob = 1;
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (out_data_len % sizeof(struct file_allocated_range_buffer)) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
num = out_data_len / sizeof(struct file_allocated_range_buffer);
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
flags = 0;
|
||||||
|
if (i == num - 1 && last_blob)
|
||||||
|
flags |= FIEMAP_EXTENT_LAST;
|
||||||
|
|
||||||
|
rc = fiemap_fill_next_extent(fei,
|
||||||
|
le64_to_cpu(out_data[i].file_offset),
|
||||||
|
le64_to_cpu(out_data[i].file_offset),
|
||||||
|
le64_to_cpu(out_data[i].length),
|
||||||
|
flags);
|
||||||
|
if (rc < 0)
|
||||||
|
goto out;
|
||||||
|
if (rc == 1) {
|
||||||
|
rc = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!last_blob) {
|
||||||
|
next = le64_to_cpu(out_data[num - 1].file_offset) +
|
||||||
|
le64_to_cpu(out_data[num - 1].length);
|
||||||
|
len = len - (next - start);
|
||||||
|
start = next;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free_xid(xid);
|
||||||
|
kfree(out_data);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
|
static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
|
||||||
loff_t off, loff_t len)
|
loff_t off, loff_t len)
|
||||||
|
@ -2917,26 +3062,28 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
||||||
unsigned int epoch, bool *purge_cache)
|
unsigned int epoch, bool *purge_cache)
|
||||||
{
|
{
|
||||||
char message[5] = {0};
|
char message[5] = {0};
|
||||||
|
unsigned int new_oplock = 0;
|
||||||
|
|
||||||
oplock &= 0xFF;
|
oplock &= 0xFF;
|
||||||
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
|
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cinode->oplock = 0;
|
|
||||||
if (oplock & SMB2_LEASE_READ_CACHING_HE) {
|
if (oplock & SMB2_LEASE_READ_CACHING_HE) {
|
||||||
cinode->oplock |= CIFS_CACHE_READ_FLG;
|
new_oplock |= CIFS_CACHE_READ_FLG;
|
||||||
strcat(message, "R");
|
strcat(message, "R");
|
||||||
}
|
}
|
||||||
if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) {
|
if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) {
|
||||||
cinode->oplock |= CIFS_CACHE_HANDLE_FLG;
|
new_oplock |= CIFS_CACHE_HANDLE_FLG;
|
||||||
strcat(message, "H");
|
strcat(message, "H");
|
||||||
}
|
}
|
||||||
if (oplock & SMB2_LEASE_WRITE_CACHING_HE) {
|
if (oplock & SMB2_LEASE_WRITE_CACHING_HE) {
|
||||||
cinode->oplock |= CIFS_CACHE_WRITE_FLG;
|
new_oplock |= CIFS_CACHE_WRITE_FLG;
|
||||||
strcat(message, "W");
|
strcat(message, "W");
|
||||||
}
|
}
|
||||||
if (!cinode->oplock)
|
if (!new_oplock)
|
||||||
strcat(message, "None");
|
strncpy(message, "None", sizeof(message));
|
||||||
|
|
||||||
|
cinode->oplock = new_oplock;
|
||||||
cifs_dbg(FYI, "%s Lease granted on inode %p\n", message,
|
cifs_dbg(FYI, "%s Lease granted on inode %p\n", message,
|
||||||
&cinode->vfs_inode);
|
&cinode->vfs_inode);
|
||||||
}
|
}
|
||||||
|
@ -4018,6 +4165,7 @@ struct smb_version_operations smb20_operations = {
|
||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb21_operations = {
|
struct smb_version_operations smb21_operations = {
|
||||||
|
@ -4117,6 +4265,7 @@ struct smb_version_operations smb21_operations = {
|
||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb30_operations = {
|
struct smb_version_operations smb30_operations = {
|
||||||
|
@ -4225,6 +4374,7 @@ struct smb_version_operations smb30_operations = {
|
||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb311_operations = {
|
struct smb_version_operations smb311_operations = {
|
||||||
|
@ -4334,6 +4484,7 @@ struct smb_version_operations smb311_operations = {
|
||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb20_values = {
|
struct smb_version_values smb20_values = {
|
||||||
|
|
|
@ -459,10 +459,7 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */
|
||||||
#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1)
|
|
||||||
#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2)
|
|
||||||
#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100)
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt)
|
build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt)
|
||||||
|
@ -475,6 +472,19 @@ build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt)
|
||||||
pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512;
|
pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt)
|
||||||
|
{
|
||||||
|
pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES;
|
||||||
|
pneg_ctxt->DataLength =
|
||||||
|
cpu_to_le16(sizeof(struct smb2_compression_capabilities_context)
|
||||||
|
- sizeof(struct smb2_neg_context));
|
||||||
|
pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3);
|
||||||
|
pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77;
|
||||||
|
pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF;
|
||||||
|
pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt)
|
build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt)
|
||||||
{
|
{
|
||||||
|
@ -541,10 +551,17 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
|
||||||
*total_len += ctxt_len;
|
*total_len += ctxt_len;
|
||||||
pneg_ctxt += ctxt_len;
|
pneg_ctxt += ctxt_len;
|
||||||
|
|
||||||
|
build_compression_ctxt((struct smb2_compression_capabilities_context *)
|
||||||
|
pneg_ctxt);
|
||||||
|
ctxt_len = DIV_ROUND_UP(
|
||||||
|
sizeof(struct smb2_compression_capabilities_context), 8) * 8;
|
||||||
|
*total_len += ctxt_len;
|
||||||
|
pneg_ctxt += ctxt_len;
|
||||||
|
|
||||||
build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
|
build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
|
||||||
*total_len += sizeof(struct smb2_posix_neg_context);
|
*total_len += sizeof(struct smb2_posix_neg_context);
|
||||||
|
|
||||||
req->NegotiateContextCount = cpu_to_le16(3);
|
req->NegotiateContextCount = cpu_to_le16(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
|
static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
|
||||||
|
@ -562,6 +579,27 @@ static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
|
||||||
printk_once(KERN_WARNING "unknown SMB3 hash algorithm\n");
|
printk_once(KERN_WARNING "unknown SMB3 hash algorithm\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void decode_compress_ctx(struct TCP_Server_Info *server,
|
||||||
|
struct smb2_compression_capabilities_context *ctxt)
|
||||||
|
{
|
||||||
|
unsigned int len = le16_to_cpu(ctxt->DataLength);
|
||||||
|
|
||||||
|
/* sizeof compress context is a one element compression capbility struct */
|
||||||
|
if (len < 10) {
|
||||||
|
printk_once(KERN_WARNING "server sent bad compression cntxt\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) {
|
||||||
|
printk_once(KERN_WARNING "illegal SMB3 compress algorithm count\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) {
|
||||||
|
printk_once(KERN_WARNING "unknown compression algorithm\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server->compress_algorithm = ctxt->CompressionAlgorithms[0];
|
||||||
|
}
|
||||||
|
|
||||||
static int decode_encrypt_ctx(struct TCP_Server_Info *server,
|
static int decode_encrypt_ctx(struct TCP_Server_Info *server,
|
||||||
struct smb2_encryption_neg_context *ctxt)
|
struct smb2_encryption_neg_context *ctxt)
|
||||||
{
|
{
|
||||||
|
@ -626,6 +664,9 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
|
||||||
else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES)
|
else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES)
|
||||||
rc = decode_encrypt_ctx(server,
|
rc = decode_encrypt_ctx(server,
|
||||||
(struct smb2_encryption_neg_context *)pctx);
|
(struct smb2_encryption_neg_context *)pctx);
|
||||||
|
else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES)
|
||||||
|
decode_compress_ctx(server,
|
||||||
|
(struct smb2_compression_capabilities_context *)pctx);
|
||||||
else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE)
|
else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE)
|
||||||
server->posix_ext_supported = true;
|
server->posix_ext_supported = true;
|
||||||
else
|
else
|
||||||
|
@ -1541,7 +1582,7 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
|
||||||
else if (server->sign)
|
else if (server->sign)
|
||||||
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
|
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
|
||||||
|
|
||||||
flags |= CIFS_NO_RESP;
|
flags |= CIFS_NO_RSP_BUF;
|
||||||
|
|
||||||
iov[0].iov_base = (char *)req;
|
iov[0].iov_base = (char *)req;
|
||||||
iov[0].iov_len = total_len;
|
iov[0].iov_len = total_len;
|
||||||
|
@ -1742,7 +1783,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
|
||||||
if (smb3_encryption_required(tcon))
|
if (smb3_encryption_required(tcon))
|
||||||
flags |= CIFS_TRANSFORM_REQ;
|
flags |= CIFS_TRANSFORM_REQ;
|
||||||
|
|
||||||
flags |= CIFS_NO_RESP;
|
flags |= CIFS_NO_RSP_BUF;
|
||||||
|
|
||||||
iov[0].iov_base = (char *)req;
|
iov[0].iov_base = (char *)req;
|
||||||
iov[0].iov_len = total_len;
|
iov[0].iov_len = total_len;
|
||||||
|
@ -2625,7 +2666,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||||
trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid,
|
trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid,
|
||||||
ses->Suid, 0, opcode, rc);
|
ses->Suid, 0, opcode, rc);
|
||||||
|
|
||||||
if ((rc != 0) && (rc != -EINVAL)) {
|
if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) {
|
||||||
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||||
goto ioctl_exit;
|
goto ioctl_exit;
|
||||||
} else if (rc == -EINVAL) {
|
} else if (rc == -EINVAL) {
|
||||||
|
@ -2634,6 +2675,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||||
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||||
goto ioctl_exit;
|
goto ioctl_exit;
|
||||||
}
|
}
|
||||||
|
} else if (rc == -E2BIG) {
|
||||||
|
if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) {
|
||||||
|
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||||
|
goto ioctl_exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if caller wants to look at return data or just return rc */
|
/* check if caller wants to look at return data or just return rc */
|
||||||
|
@ -3223,7 +3269,7 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
|
||||||
rdata->nr_pages, rdata->page_offset,
|
rdata->nr_pages, rdata->page_offset,
|
||||||
rdata->tailsz, true, need_invalidate);
|
rdata->tailsz, true, need_invalidate);
|
||||||
if (!rdata->mr)
|
if (!rdata->mr)
|
||||||
return -ENOBUFS;
|
return -EAGAIN;
|
||||||
|
|
||||||
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
|
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
|
||||||
if (need_invalidate)
|
if (need_invalidate)
|
||||||
|
@ -3628,7 +3674,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
|
||||||
wdata->nr_pages, wdata->page_offset,
|
wdata->nr_pages, wdata->page_offset,
|
||||||
wdata->tailsz, false, need_invalidate);
|
wdata->tailsz, false, need_invalidate);
|
||||||
if (!wdata->mr) {
|
if (!wdata->mr) {
|
||||||
rc = -ENOBUFS;
|
rc = -EAGAIN;
|
||||||
goto async_writev_out;
|
goto async_writev_out;
|
||||||
}
|
}
|
||||||
req->Length = 0;
|
req->Length = 0;
|
||||||
|
@ -4164,7 +4210,7 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
req->OplockLevel = oplock_level;
|
req->OplockLevel = oplock_level;
|
||||||
req->sync_hdr.CreditRequest = cpu_to_le16(1);
|
req->sync_hdr.CreditRequest = cpu_to_le16(1);
|
||||||
|
|
||||||
flags |= CIFS_NO_RESP;
|
flags |= CIFS_NO_RSP_BUF;
|
||||||
|
|
||||||
iov[0].iov_base = (char *)req;
|
iov[0].iov_base = (char *)req;
|
||||||
iov[0].iov_len = total_len;
|
iov[0].iov_len = total_len;
|
||||||
|
@ -4438,7 +4484,7 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
struct kvec rsp_iov;
|
struct kvec rsp_iov;
|
||||||
int resp_buf_type;
|
int resp_buf_type;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
int flags = CIFS_NO_RESP;
|
int flags = CIFS_NO_RSP_BUF;
|
||||||
unsigned int total_len;
|
unsigned int total_len;
|
||||||
|
|
||||||
cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
|
cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
|
||||||
|
@ -4531,7 +4577,7 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
memcpy(req->LeaseKey, lease_key, 16);
|
memcpy(req->LeaseKey, lease_key, 16);
|
||||||
req->LeaseState = lease_state;
|
req->LeaseState = lease_state;
|
||||||
|
|
||||||
flags |= CIFS_NO_RESP;
|
flags |= CIFS_NO_RSP_BUF;
|
||||||
|
|
||||||
iov[0].iov_base = (char *)req;
|
iov[0].iov_base = (char *)req;
|
||||||
iov[0].iov_len = total_len;
|
iov[0].iov_len = total_len;
|
||||||
|
|
|
@ -251,6 +251,14 @@ struct smb2_negotiate_req {
|
||||||
#define SMB2_NT_FIND 0x00100000
|
#define SMB2_NT_FIND 0x00100000
|
||||||
#define SMB2_LARGE_FILES 0x00200000
|
#define SMB2_LARGE_FILES 0x00200000
|
||||||
|
|
||||||
|
|
||||||
|
/* Negotiate Contexts - ContextTypes. See MS-SMB2 section 2.2.3.1 for details */
|
||||||
|
#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1)
|
||||||
|
#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2)
|
||||||
|
#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3)
|
||||||
|
#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5)
|
||||||
|
#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100)
|
||||||
|
|
||||||
struct smb2_neg_context {
|
struct smb2_neg_context {
|
||||||
__le16 ContextType;
|
__le16 ContextType;
|
||||||
__le16 DataLength;
|
__le16 DataLength;
|
||||||
|
@ -288,6 +296,27 @@ 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;
|
||||||
|
|
||||||
|
/* See MS-SMB2 2.2.3.1.3 */
|
||||||
|
#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000)
|
||||||
|
#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001)
|
||||||
|
#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002)
|
||||||
|
#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003)
|
||||||
|
|
||||||
|
struct smb2_compression_capabilities_context {
|
||||||
|
__le16 ContextType; /* 3 */
|
||||||
|
__le16 DataLength;
|
||||||
|
__u32 Reserved;
|
||||||
|
__le16 CompressionAlgorithmCount;
|
||||||
|
__u16 Padding;
|
||||||
|
__u32 Reserved1;
|
||||||
|
__le16 CompressionAlgorithms[3];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For smb2_netname_negotiate_context_id See MS-SMB2 2.2.3.1.4.
|
||||||
|
* Its struct simply contains NetName, an array of Unicode characters
|
||||||
|
*/
|
||||||
|
|
||||||
#define POSIX_CTXT_DATA_LEN 16
|
#define POSIX_CTXT_DATA_LEN 16
|
||||||
struct smb2_posix_neg_context {
|
struct smb2_posix_neg_context {
|
||||||
__le16 ContextType; /* 0x100 */
|
__le16 ContextType; /* 0x100 */
|
||||||
|
@ -842,6 +871,11 @@ struct fsctl_get_integrity_information_rsp {
|
||||||
__le32 ClusterSizeInBytes;
|
__le32 ClusterSizeInBytes;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct file_allocated_range_buffer {
|
||||||
|
__le64 file_offset;
|
||||||
|
__le64 length;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* Integrity ChecksumAlgorithm choices for above */
|
/* Integrity ChecksumAlgorithm choices for above */
|
||||||
#define CHECKSUM_TYPE_NONE 0x0000
|
#define CHECKSUM_TYPE_NONE 0x0000
|
||||||
#define CHECKSUM_TYPE_CRC64 0x0002
|
#define CHECKSUM_TYPE_CRC64 0x0002
|
||||||
|
@ -1047,6 +1081,7 @@ struct smb2_flush_rsp {
|
||||||
|
|
||||||
/* For read request Flags field below, following flag is defined for SMB3.02 */
|
/* For read request Flags field below, following flag is defined for SMB3.02 */
|
||||||
#define SMB2_READFLAG_READ_UNBUFFERED 0x01
|
#define SMB2_READFLAG_READ_UNBUFFERED 0x01
|
||||||
|
#define SMB2_READFLAG_REQUEST_COMPRESSED 0x02 /* See MS-SMB2 2.2.19 */
|
||||||
|
|
||||||
/* Channel field for read and write: exactly one of following flags can be set*/
|
/* Channel field for read and write: exactly one of following flags can be set*/
|
||||||
#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000)
|
#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000)
|
||||||
|
@ -1113,6 +1148,42 @@ struct smb2_write_rsp {
|
||||||
__u8 Buffer[1];
|
__u8 Buffer[1];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/* notify flags */
|
||||||
|
#define SMB2_WATCH_TREE 0x0001
|
||||||
|
|
||||||
|
/* notify completion filter flags. See MS-FSCC 2.6 and MS-SMB2 2.2.35 */
|
||||||
|
#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
|
||||||
|
#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
|
||||||
|
#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
|
||||||
|
#define FILE_NOTIFY_CHANGE_SIZE 0x00000008
|
||||||
|
#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
|
||||||
|
#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
|
||||||
|
#define FILE_NOTIFY_CHANGE_CREATION 0x00000040
|
||||||
|
#define FILE_NOTIFY_CHANGE_EA 0x00000080
|
||||||
|
#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100
|
||||||
|
#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
|
||||||
|
#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
|
||||||
|
#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
|
||||||
|
|
||||||
|
struct smb2_change_notify_req {
|
||||||
|
struct smb2_sync_hdr sync_hdr;
|
||||||
|
__le16 StructureSize;
|
||||||
|
__le16 Flags;
|
||||||
|
__le32 OutputBufferLength;
|
||||||
|
__u64 PersistentFileId; /* opaque endianness */
|
||||||
|
__u64 VolatileFileId; /* opaque endianness */
|
||||||
|
__le32 CompletionFilter;
|
||||||
|
__u32 Reserved;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_change_notify_rsp {
|
||||||
|
struct smb2_sync_hdr sync_hdr;
|
||||||
|
__le16 StructureSize; /* Must be 9 */
|
||||||
|
__le16 OutputBufferOffset;
|
||||||
|
__le32 OutputBufferLength;
|
||||||
|
__u8 Buffer[1]; /* array of file notify structs */
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define SMB2_LOCKFLAG_SHARED_LOCK 0x0001
|
#define SMB2_LOCKFLAG_SHARED_LOCK 0x0001
|
||||||
#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK 0x0002
|
#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK 0x0002
|
||||||
#define SMB2_LOCKFLAG_UNLOCK 0x0004
|
#define SMB2_LOCKFLAG_UNLOCK 0x0004
|
||||||
|
|
3480
fs/cifs/smb2status.h
3480
fs/cifs/smb2status.h
File diff suppressed because it is too large
Load Diff
|
@ -164,95 +164,6 @@ do { \
|
||||||
#define log_rdma_mr(level, fmt, args...) \
|
#define log_rdma_mr(level, fmt, args...) \
|
||||||
log_rdma(level, LOG_RDMA_MR, fmt, ##args)
|
log_rdma(level, LOG_RDMA_MR, fmt, ##args)
|
||||||
|
|
||||||
/*
|
|
||||||
* Destroy the transport and related RDMA and memory resources
|
|
||||||
* Need to go through all the pending counters and make sure on one is using
|
|
||||||
* the transport while it is destroyed
|
|
||||||
*/
|
|
||||||
static void smbd_destroy_rdma_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct smbd_response *response;
|
|
||||||
struct smbd_connection *info =
|
|
||||||
container_of(work, struct smbd_connection, destroy_work);
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "destroying qp\n");
|
|
||||||
ib_drain_qp(info->id->qp);
|
|
||||||
rdma_destroy_qp(info->id);
|
|
||||||
|
|
||||||
/* Unblock all I/O waiting on the send queue */
|
|
||||||
wake_up_interruptible_all(&info->wait_send_queue);
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "cancelling idle timer\n");
|
|
||||||
cancel_delayed_work_sync(&info->idle_timer_work);
|
|
||||||
log_rdma_event(INFO, "cancelling send immediate work\n");
|
|
||||||
cancel_delayed_work_sync(&info->send_immediate_work);
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "wait for all send to finish\n");
|
|
||||||
wait_event(info->wait_smbd_send_pending,
|
|
||||||
info->smbd_send_pending == 0);
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "wait for all recv to finish\n");
|
|
||||||
wake_up_interruptible(&info->wait_reassembly_queue);
|
|
||||||
wait_event(info->wait_smbd_recv_pending,
|
|
||||||
info->smbd_recv_pending == 0);
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "wait for all send posted to IB to finish\n");
|
|
||||||
wait_event(info->wait_send_pending,
|
|
||||||
atomic_read(&info->send_pending) == 0);
|
|
||||||
wait_event(info->wait_send_payload_pending,
|
|
||||||
atomic_read(&info->send_payload_pending) == 0);
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "freeing mr list\n");
|
|
||||||
wake_up_interruptible_all(&info->wait_mr);
|
|
||||||
wait_event(info->wait_for_mr_cleanup,
|
|
||||||
atomic_read(&info->mr_used_count) == 0);
|
|
||||||
destroy_mr_list(info);
|
|
||||||
|
|
||||||
/* It's not posssible for upper layer to get to reassembly */
|
|
||||||
log_rdma_event(INFO, "drain the reassembly queue\n");
|
|
||||||
do {
|
|
||||||
spin_lock_irqsave(&info->reassembly_queue_lock, flags);
|
|
||||||
response = _get_first_reassembly(info);
|
|
||||||
if (response) {
|
|
||||||
list_del(&response->list);
|
|
||||||
spin_unlock_irqrestore(
|
|
||||||
&info->reassembly_queue_lock, flags);
|
|
||||||
put_receive_buffer(info, response);
|
|
||||||
} else
|
|
||||||
spin_unlock_irqrestore(&info->reassembly_queue_lock, flags);
|
|
||||||
} while (response);
|
|
||||||
|
|
||||||
info->reassembly_data_length = 0;
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "free receive buffers\n");
|
|
||||||
wait_event(info->wait_receive_queues,
|
|
||||||
info->count_receive_queue + info->count_empty_packet_queue
|
|
||||||
== info->receive_credit_max);
|
|
||||||
destroy_receive_buffers(info);
|
|
||||||
|
|
||||||
ib_free_cq(info->send_cq);
|
|
||||||
ib_free_cq(info->recv_cq);
|
|
||||||
ib_dealloc_pd(info->pd);
|
|
||||||
rdma_destroy_id(info->id);
|
|
||||||
|
|
||||||
/* free mempools */
|
|
||||||
mempool_destroy(info->request_mempool);
|
|
||||||
kmem_cache_destroy(info->request_cache);
|
|
||||||
|
|
||||||
mempool_destroy(info->response_mempool);
|
|
||||||
kmem_cache_destroy(info->response_cache);
|
|
||||||
|
|
||||||
info->transport_status = SMBD_DESTROYED;
|
|
||||||
wake_up_all(&info->wait_destroy);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int smbd_process_disconnected(struct smbd_connection *info)
|
|
||||||
{
|
|
||||||
schedule_work(&info->destroy_work);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void smbd_disconnect_rdma_work(struct work_struct *work)
|
static void smbd_disconnect_rdma_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct smbd_connection *info =
|
struct smbd_connection *info =
|
||||||
|
@ -319,7 +230,9 @@ static int smbd_conn_upcall(
|
||||||
}
|
}
|
||||||
|
|
||||||
info->transport_status = SMBD_DISCONNECTED;
|
info->transport_status = SMBD_DISCONNECTED;
|
||||||
smbd_process_disconnected(info);
|
wake_up_interruptible(&info->disconn_wait);
|
||||||
|
wake_up_interruptible(&info->wait_reassembly_queue);
|
||||||
|
wake_up_interruptible_all(&info->wait_send_queue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -940,7 +853,7 @@ static int smbd_create_header(struct smbd_connection *info,
|
||||||
|
|
||||||
if (info->transport_status != SMBD_CONNECTED) {
|
if (info->transport_status != SMBD_CONNECTED) {
|
||||||
log_outgoing(ERR, "disconnected not sending\n");
|
log_outgoing(ERR, "disconnected not sending\n");
|
||||||
return -ENOENT;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
atomic_dec(&info->send_credits);
|
atomic_dec(&info->send_credits);
|
||||||
|
|
||||||
|
@ -1066,6 +979,7 @@ static int smbd_post_send(struct smbd_connection *info,
|
||||||
wake_up(&info->wait_send_pending);
|
wake_up(&info->wait_send_pending);
|
||||||
}
|
}
|
||||||
smbd_disconnect_rdma_connection(info);
|
smbd_disconnect_rdma_connection(info);
|
||||||
|
rc = -EAGAIN;
|
||||||
} else
|
} else
|
||||||
/* Reset timer for idle connection after packet is sent */
|
/* Reset timer for idle connection after packet is sent */
|
||||||
mod_delayed_work(info->workqueue, &info->idle_timer_work,
|
mod_delayed_work(info->workqueue, &info->idle_timer_work,
|
||||||
|
@ -1478,17 +1392,97 @@ static void idle_connection_timer(struct work_struct *work)
|
||||||
info->keep_alive_interval*HZ);
|
info->keep_alive_interval*HZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Destroy this SMBD connection, called from upper layer */
|
/*
|
||||||
void smbd_destroy(struct smbd_connection *info)
|
* Destroy the transport and related RDMA and memory resources
|
||||||
|
* Need to go through all the pending counters and make sure on one is using
|
||||||
|
* the transport while it is destroyed
|
||||||
|
*/
|
||||||
|
void smbd_destroy(struct TCP_Server_Info *server)
|
||||||
{
|
{
|
||||||
|
struct smbd_connection *info = server->smbd_conn;
|
||||||
|
struct smbd_response *response;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
log_rdma_event(INFO, "rdma session already destroyed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log_rdma_event(INFO, "destroying rdma session\n");
|
log_rdma_event(INFO, "destroying rdma session\n");
|
||||||
|
if (info->transport_status != SMBD_DISCONNECTED) {
|
||||||
|
rdma_disconnect(server->smbd_conn->id);
|
||||||
|
log_rdma_event(INFO, "wait for transport being disconnected\n");
|
||||||
|
wait_event_interruptible(
|
||||||
|
info->disconn_wait,
|
||||||
|
info->transport_status == SMBD_DISCONNECTED);
|
||||||
|
}
|
||||||
|
|
||||||
/* Kick off the disconnection process */
|
log_rdma_event(INFO, "destroying qp\n");
|
||||||
smbd_disconnect_rdma_connection(info);
|
ib_drain_qp(info->id->qp);
|
||||||
|
rdma_destroy_qp(info->id);
|
||||||
|
|
||||||
log_rdma_event(INFO, "wait for transport being destroyed\n");
|
log_rdma_event(INFO, "cancelling idle timer\n");
|
||||||
wait_event(info->wait_destroy,
|
cancel_delayed_work_sync(&info->idle_timer_work);
|
||||||
info->transport_status == SMBD_DESTROYED);
|
log_rdma_event(INFO, "cancelling send immediate work\n");
|
||||||
|
cancel_delayed_work_sync(&info->send_immediate_work);
|
||||||
|
|
||||||
|
log_rdma_event(INFO, "wait for all send posted to IB to finish\n");
|
||||||
|
wait_event(info->wait_send_pending,
|
||||||
|
atomic_read(&info->send_pending) == 0);
|
||||||
|
wait_event(info->wait_send_payload_pending,
|
||||||
|
atomic_read(&info->send_payload_pending) == 0);
|
||||||
|
|
||||||
|
/* It's not posssible for upper layer to get to reassembly */
|
||||||
|
log_rdma_event(INFO, "drain the reassembly queue\n");
|
||||||
|
do {
|
||||||
|
spin_lock_irqsave(&info->reassembly_queue_lock, flags);
|
||||||
|
response = _get_first_reassembly(info);
|
||||||
|
if (response) {
|
||||||
|
list_del(&response->list);
|
||||||
|
spin_unlock_irqrestore(
|
||||||
|
&info->reassembly_queue_lock, flags);
|
||||||
|
put_receive_buffer(info, response);
|
||||||
|
} else
|
||||||
|
spin_unlock_irqrestore(
|
||||||
|
&info->reassembly_queue_lock, flags);
|
||||||
|
} while (response);
|
||||||
|
info->reassembly_data_length = 0;
|
||||||
|
|
||||||
|
log_rdma_event(INFO, "free receive buffers\n");
|
||||||
|
wait_event(info->wait_receive_queues,
|
||||||
|
info->count_receive_queue + info->count_empty_packet_queue
|
||||||
|
== info->receive_credit_max);
|
||||||
|
destroy_receive_buffers(info);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For performance reasons, memory registration and deregistration
|
||||||
|
* are not locked by srv_mutex. It is possible some processes are
|
||||||
|
* blocked on transport srv_mutex while holding memory registration.
|
||||||
|
* Release the transport srv_mutex to allow them to hit the failure
|
||||||
|
* path when sending data, and then release memory registartions.
|
||||||
|
*/
|
||||||
|
log_rdma_event(INFO, "freeing mr list\n");
|
||||||
|
wake_up_interruptible_all(&info->wait_mr);
|
||||||
|
while (atomic_read(&info->mr_used_count)) {
|
||||||
|
mutex_unlock(&server->srv_mutex);
|
||||||
|
msleep(1000);
|
||||||
|
mutex_lock(&server->srv_mutex);
|
||||||
|
}
|
||||||
|
destroy_mr_list(info);
|
||||||
|
|
||||||
|
ib_free_cq(info->send_cq);
|
||||||
|
ib_free_cq(info->recv_cq);
|
||||||
|
ib_dealloc_pd(info->pd);
|
||||||
|
rdma_destroy_id(info->id);
|
||||||
|
|
||||||
|
/* free mempools */
|
||||||
|
mempool_destroy(info->request_mempool);
|
||||||
|
kmem_cache_destroy(info->request_cache);
|
||||||
|
|
||||||
|
mempool_destroy(info->response_mempool);
|
||||||
|
kmem_cache_destroy(info->response_cache);
|
||||||
|
|
||||||
|
info->transport_status = SMBD_DESTROYED;
|
||||||
|
|
||||||
destroy_workqueue(info->workqueue);
|
destroy_workqueue(info->workqueue);
|
||||||
kfree(info);
|
kfree(info);
|
||||||
|
@ -1513,17 +1507,9 @@ int smbd_reconnect(struct TCP_Server_Info *server)
|
||||||
*/
|
*/
|
||||||
if (server->smbd_conn->transport_status == SMBD_CONNECTED) {
|
if (server->smbd_conn->transport_status == SMBD_CONNECTED) {
|
||||||
log_rdma_event(INFO, "disconnecting transport\n");
|
log_rdma_event(INFO, "disconnecting transport\n");
|
||||||
smbd_disconnect_rdma_connection(server->smbd_conn);
|
smbd_destroy(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait until the transport is destroyed */
|
|
||||||
if (!wait_event_timeout(server->smbd_conn->wait_destroy,
|
|
||||||
server->smbd_conn->transport_status == SMBD_DESTROYED, 5*HZ))
|
|
||||||
return -EAGAIN;
|
|
||||||
|
|
||||||
destroy_workqueue(server->smbd_conn->workqueue);
|
|
||||||
kfree(server->smbd_conn);
|
|
||||||
|
|
||||||
create_conn:
|
create_conn:
|
||||||
log_rdma_event(INFO, "creating rdma session\n");
|
log_rdma_event(INFO, "creating rdma session\n");
|
||||||
server->smbd_conn = smbd_get_connection(
|
server->smbd_conn = smbd_get_connection(
|
||||||
|
@ -1739,12 +1725,13 @@ static struct smbd_connection *_smbd_get_connection(
|
||||||
conn_param.retry_count = SMBD_CM_RETRY;
|
conn_param.retry_count = SMBD_CM_RETRY;
|
||||||
conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY;
|
conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY;
|
||||||
conn_param.flow_control = 0;
|
conn_param.flow_control = 0;
|
||||||
init_waitqueue_head(&info->wait_destroy);
|
|
||||||
|
|
||||||
log_rdma_event(INFO, "connecting to IP %pI4 port %d\n",
|
log_rdma_event(INFO, "connecting to IP %pI4 port %d\n",
|
||||||
&addr_in->sin_addr, port);
|
&addr_in->sin_addr, port);
|
||||||
|
|
||||||
init_waitqueue_head(&info->conn_wait);
|
init_waitqueue_head(&info->conn_wait);
|
||||||
|
init_waitqueue_head(&info->disconn_wait);
|
||||||
|
init_waitqueue_head(&info->wait_reassembly_queue);
|
||||||
rc = rdma_connect(info->id, &conn_param);
|
rc = rdma_connect(info->id, &conn_param);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc);
|
log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc);
|
||||||
|
@ -1768,19 +1755,11 @@ static struct smbd_connection *_smbd_get_connection(
|
||||||
}
|
}
|
||||||
|
|
||||||
init_waitqueue_head(&info->wait_send_queue);
|
init_waitqueue_head(&info->wait_send_queue);
|
||||||
init_waitqueue_head(&info->wait_reassembly_queue);
|
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer);
|
INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer);
|
||||||
INIT_DELAYED_WORK(&info->send_immediate_work, send_immediate_work);
|
INIT_DELAYED_WORK(&info->send_immediate_work, send_immediate_work);
|
||||||
queue_delayed_work(info->workqueue, &info->idle_timer_work,
|
queue_delayed_work(info->workqueue, &info->idle_timer_work,
|
||||||
info->keep_alive_interval*HZ);
|
info->keep_alive_interval*HZ);
|
||||||
|
|
||||||
init_waitqueue_head(&info->wait_smbd_send_pending);
|
|
||||||
info->smbd_send_pending = 0;
|
|
||||||
|
|
||||||
init_waitqueue_head(&info->wait_smbd_recv_pending);
|
|
||||||
info->smbd_recv_pending = 0;
|
|
||||||
|
|
||||||
init_waitqueue_head(&info->wait_send_pending);
|
init_waitqueue_head(&info->wait_send_pending);
|
||||||
atomic_set(&info->send_pending, 0);
|
atomic_set(&info->send_pending, 0);
|
||||||
|
|
||||||
|
@ -1788,7 +1767,6 @@ static struct smbd_connection *_smbd_get_connection(
|
||||||
atomic_set(&info->send_payload_pending, 0);
|
atomic_set(&info->send_payload_pending, 0);
|
||||||
|
|
||||||
INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work);
|
INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work);
|
||||||
INIT_WORK(&info->destroy_work, smbd_destroy_rdma_work);
|
|
||||||
INIT_WORK(&info->recv_done_work, smbd_recv_done_work);
|
INIT_WORK(&info->recv_done_work, smbd_recv_done_work);
|
||||||
INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits);
|
INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits);
|
||||||
info->new_credits_offered = 0;
|
info->new_credits_offered = 0;
|
||||||
|
@ -1810,7 +1788,7 @@ static struct smbd_connection *_smbd_get_connection(
|
||||||
|
|
||||||
allocate_mr_failed:
|
allocate_mr_failed:
|
||||||
/* At this point, need to a full transport shutdown */
|
/* At this point, need to a full transport shutdown */
|
||||||
smbd_destroy(info);
|
smbd_destroy(server);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
negotiation_failed:
|
negotiation_failed:
|
||||||
|
@ -1882,11 +1860,6 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
if (info->transport_status != SMBD_CONNECTED) {
|
|
||||||
log_read(ERR, "disconnected\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No need to hold the reassembly queue lock all the time as we are
|
* No need to hold the reassembly queue lock all the time as we are
|
||||||
* the only one reading from the front of the queue. The transport
|
* the only one reading from the front of the queue. The transport
|
||||||
|
@ -2000,7 +1973,12 @@ read_rfc1002_done:
|
||||||
info->transport_status != SMBD_CONNECTED);
|
info->transport_status != SMBD_CONNECTED);
|
||||||
/* Don't return any data if interrupted */
|
/* Don't return any data if interrupted */
|
||||||
if (rc)
|
if (rc)
|
||||||
return -ENODEV;
|
return rc;
|
||||||
|
|
||||||
|
if (info->transport_status != SMBD_CONNECTED) {
|
||||||
|
log_read(ERR, "disconnected\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
|
@ -2052,8 +2030,6 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
|
||||||
unsigned int to_read, page_offset;
|
unsigned int to_read, page_offset;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
info->smbd_recv_pending++;
|
|
||||||
|
|
||||||
if (iov_iter_rw(&msg->msg_iter) == WRITE) {
|
if (iov_iter_rw(&msg->msg_iter) == WRITE) {
|
||||||
/* It's a bug in upper layer to get there */
|
/* It's a bug in upper layer to get there */
|
||||||
cifs_dbg(VFS, "CIFS: invalid msg iter dir %u\n",
|
cifs_dbg(VFS, "CIFS: invalid msg iter dir %u\n",
|
||||||
|
@ -2084,9 +2060,6 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
info->smbd_recv_pending--;
|
|
||||||
wake_up(&info->wait_smbd_recv_pending);
|
|
||||||
|
|
||||||
/* SMBDirect will read it all or nothing */
|
/* SMBDirect will read it all or nothing */
|
||||||
if (rc > 0)
|
if (rc > 0)
|
||||||
msg->msg_iter.count = 0;
|
msg->msg_iter.count = 0;
|
||||||
|
@ -2099,7 +2072,8 @@ out:
|
||||||
* rqst: the data to write
|
* rqst: the data to write
|
||||||
* return value: 0 if successfully write, otherwise error code
|
* return value: 0 if successfully write, otherwise error code
|
||||||
*/
|
*/
|
||||||
int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
int smbd_send(struct TCP_Server_Info *server,
|
||||||
|
int num_rqst, struct smb_rqst *rqst_array)
|
||||||
{
|
{
|
||||||
struct smbd_connection *info = server->smbd_conn;
|
struct smbd_connection *info = server->smbd_conn;
|
||||||
struct kvec vec;
|
struct kvec vec;
|
||||||
|
@ -2111,54 +2085,49 @@ int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
||||||
info->max_send_size - sizeof(struct smbd_data_transfer);
|
info->max_send_size - sizeof(struct smbd_data_transfer);
|
||||||
struct kvec *iov;
|
struct kvec *iov;
|
||||||
int rc;
|
int rc;
|
||||||
|
struct smb_rqst *rqst;
|
||||||
|
int rqst_idx;
|
||||||
|
|
||||||
info->smbd_send_pending++;
|
|
||||||
if (info->transport_status != SMBD_CONNECTED) {
|
if (info->transport_status != SMBD_CONNECTED) {
|
||||||
rc = -ENODEV;
|
rc = -EAGAIN;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip the RFC1002 length defined in MS-SMB2 section 2.1
|
|
||||||
* It is used only for TCP transport in the iov[0]
|
|
||||||
* In future we may want to add a transport layer under protocol
|
|
||||||
* layer so this will only be issued to TCP transport
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (rqst->rq_iov[0].iov_len != 4) {
|
|
||||||
log_write(ERR, "expected the pdu length in 1st iov, but got %zu\n", rqst->rq_iov[0].iov_len);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add in the page array if there is one. The caller needs to set
|
* Add in the page array if there is one. The caller needs to set
|
||||||
* rq_tailsz to PAGE_SIZE when the buffer has multiple pages and
|
* rq_tailsz to PAGE_SIZE when the buffer has multiple pages and
|
||||||
* ends at page boundary
|
* ends at page boundary
|
||||||
*/
|
*/
|
||||||
buflen = smb_rqst_len(server, rqst);
|
remaining_data_length = 0;
|
||||||
|
for (i = 0; i < num_rqst; i++)
|
||||||
|
remaining_data_length += smb_rqst_len(server, &rqst_array[i]);
|
||||||
|
|
||||||
if (buflen + sizeof(struct smbd_data_transfer) >
|
if (remaining_data_length + sizeof(struct smbd_data_transfer) >
|
||||||
info->max_fragmented_send_size) {
|
info->max_fragmented_send_size) {
|
||||||
log_write(ERR, "payload size %d > max size %d\n",
|
log_write(ERR, "payload size %d > max size %d\n",
|
||||||
buflen, info->max_fragmented_send_size);
|
remaining_data_length, info->max_fragmented_send_size);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
iov = &rqst->rq_iov[1];
|
rqst_idx = 0;
|
||||||
|
|
||||||
cifs_dbg(FYI, "Sending smb (RDMA): smb_len=%u\n", buflen);
|
next_rqst:
|
||||||
for (i = 0; i < rqst->rq_nvec-1; i++)
|
rqst = &rqst_array[rqst_idx];
|
||||||
|
iov = rqst->rq_iov;
|
||||||
|
|
||||||
|
cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n",
|
||||||
|
rqst_idx, smb_rqst_len(server, rqst));
|
||||||
|
for (i = 0; i < rqst->rq_nvec; i++)
|
||||||
dump_smb(iov[i].iov_base, iov[i].iov_len);
|
dump_smb(iov[i].iov_base, iov[i].iov_len);
|
||||||
|
|
||||||
remaining_data_length = buflen;
|
|
||||||
|
|
||||||
log_write(INFO, "rqst->rq_nvec=%d rqst->rq_npages=%d rq_pagesz=%d "
|
log_write(INFO, "rqst_idx=%d nvec=%d rqst->rq_npages=%d rq_pagesz=%d "
|
||||||
"rq_tailsz=%d buflen=%d\n",
|
"rq_tailsz=%d buflen=%lu\n",
|
||||||
rqst->rq_nvec, rqst->rq_npages, rqst->rq_pagesz,
|
rqst_idx, rqst->rq_nvec, rqst->rq_npages, rqst->rq_pagesz,
|
||||||
rqst->rq_tailsz, buflen);
|
rqst->rq_tailsz, smb_rqst_len(server, rqst));
|
||||||
|
|
||||||
start = i = iov[0].iov_len ? 0 : 1;
|
start = i = 0;
|
||||||
buflen = 0;
|
buflen = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
buflen += iov[i].iov_len;
|
buflen += iov[i].iov_len;
|
||||||
|
@ -2206,14 +2175,14 @@ int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
if (i == rqst->rq_nvec-1)
|
if (i == rqst->rq_nvec)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
start = i;
|
start = i;
|
||||||
buflen = 0;
|
buflen = 0;
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
if (i == rqst->rq_nvec-1) {
|
if (i == rqst->rq_nvec) {
|
||||||
/* send out all remaining vecs */
|
/* send out all remaining vecs */
|
||||||
remaining_data_length -= buflen;
|
remaining_data_length -= buflen;
|
||||||
log_write(INFO,
|
log_write(INFO,
|
||||||
|
@ -2257,6 +2226,10 @@ int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rqst_idx++;
|
||||||
|
if (rqst_idx < num_rqst)
|
||||||
|
goto next_rqst;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
/*
|
/*
|
||||||
* As an optimization, we don't wait for individual I/O to finish
|
* As an optimization, we don't wait for individual I/O to finish
|
||||||
|
@ -2268,9 +2241,6 @@ done:
|
||||||
wait_event(info->wait_send_payload_pending,
|
wait_event(info->wait_send_payload_pending,
|
||||||
atomic_read(&info->send_payload_pending) == 0);
|
atomic_read(&info->send_payload_pending) == 0);
|
||||||
|
|
||||||
info->smbd_send_pending--;
|
|
||||||
wake_up(&info->wait_smbd_send_pending);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,12 +70,11 @@ struct smbd_connection {
|
||||||
int ri_rc;
|
int ri_rc;
|
||||||
struct completion ri_done;
|
struct completion ri_done;
|
||||||
wait_queue_head_t conn_wait;
|
wait_queue_head_t conn_wait;
|
||||||
wait_queue_head_t wait_destroy;
|
wait_queue_head_t disconn_wait;
|
||||||
|
|
||||||
struct completion negotiate_completion;
|
struct completion negotiate_completion;
|
||||||
bool negotiate_done;
|
bool negotiate_done;
|
||||||
|
|
||||||
struct work_struct destroy_work;
|
|
||||||
struct work_struct disconnect_work;
|
struct work_struct disconnect_work;
|
||||||
struct work_struct recv_done_work;
|
struct work_struct recv_done_work;
|
||||||
struct work_struct post_send_credits_work;
|
struct work_struct post_send_credits_work;
|
||||||
|
@ -123,13 +122,6 @@ struct smbd_connection {
|
||||||
wait_queue_head_t wait_for_mr_cleanup;
|
wait_queue_head_t wait_for_mr_cleanup;
|
||||||
|
|
||||||
/* Activity accoutning */
|
/* Activity accoutning */
|
||||||
/* Pending reqeusts issued from upper layer */
|
|
||||||
int smbd_send_pending;
|
|
||||||
wait_queue_head_t wait_smbd_send_pending;
|
|
||||||
|
|
||||||
int smbd_recv_pending;
|
|
||||||
wait_queue_head_t wait_smbd_recv_pending;
|
|
||||||
|
|
||||||
atomic_t send_pending;
|
atomic_t send_pending;
|
||||||
wait_queue_head_t wait_send_pending;
|
wait_queue_head_t wait_send_pending;
|
||||||
atomic_t send_payload_pending;
|
atomic_t send_payload_pending;
|
||||||
|
@ -288,11 +280,12 @@ struct smbd_connection *smbd_get_connection(
|
||||||
/* Reconnect SMBDirect session */
|
/* Reconnect SMBDirect session */
|
||||||
int smbd_reconnect(struct TCP_Server_Info *server);
|
int smbd_reconnect(struct TCP_Server_Info *server);
|
||||||
/* Destroy SMBDirect session */
|
/* Destroy SMBDirect session */
|
||||||
void smbd_destroy(struct smbd_connection *info);
|
void smbd_destroy(struct TCP_Server_Info *server);
|
||||||
|
|
||||||
/* Interface for carrying upper layer I/O through send/recv */
|
/* Interface for carrying upper layer I/O through send/recv */
|
||||||
int smbd_recv(struct smbd_connection *info, struct msghdr *msg);
|
int smbd_recv(struct smbd_connection *info, struct msghdr *msg);
|
||||||
int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst);
|
int smbd_send(struct TCP_Server_Info *server,
|
||||||
|
int num_rqst, struct smb_rqst *rqst);
|
||||||
|
|
||||||
enum mr_state {
|
enum mr_state {
|
||||||
MR_READY,
|
MR_READY,
|
||||||
|
@ -330,9 +323,9 @@ struct smbd_connection {};
|
||||||
static inline void *smbd_get_connection(
|
static inline void *smbd_get_connection(
|
||||||
struct TCP_Server_Info *server, struct sockaddr *dstaddr) {return NULL;}
|
struct TCP_Server_Info *server, struct sockaddr *dstaddr) {return NULL;}
|
||||||
static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; }
|
static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; }
|
||||||
static inline void smbd_destroy(struct smbd_connection *info) {}
|
static inline void smbd_destroy(struct TCP_Server_Info *server) {}
|
||||||
static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; }
|
static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; }
|
||||||
static inline int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst) {return -1; }
|
static inline int smbd_send(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst) {return -1; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -35,6 +35,33 @@
|
||||||
* below). Additional detail on less common ones can be found in MS-FSCC
|
* below). Additional detail on less common ones can be found in MS-FSCC
|
||||||
* section 2.3.
|
* section 2.3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FSCTL values are 32 bits and are constructed as
|
||||||
|
* <device 16bits> <access 2bits> <function 12bits> <method 2bits>
|
||||||
|
*/
|
||||||
|
/* Device */
|
||||||
|
#define FSCTL_DEVICE_DFS (0x0006 << 16)
|
||||||
|
#define FSCTL_DEVICE_FILE_SYSTEM (0x0009 << 16)
|
||||||
|
#define FSCTL_DEVICE_NAMED_PIPE (0x0011 << 16)
|
||||||
|
#define FSCTL_DEVICE_NETWORK_FILE_SYSTEM (0x0014 << 16)
|
||||||
|
#define FSCTL_DEVICE_MASK 0xffff0000
|
||||||
|
/* Access */
|
||||||
|
#define FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS (0x00 << 14)
|
||||||
|
#define FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS (0x01 << 14)
|
||||||
|
#define FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS (0x02 << 14)
|
||||||
|
#define FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS (0x03 << 14)
|
||||||
|
#define FSCTL_DEVICE_ACCESS_MASK 0x0000c000
|
||||||
|
/* Function */
|
||||||
|
#define FSCTL_DEVICE_FUNCTION_MASK 0x00003ffc
|
||||||
|
/* Method */
|
||||||
|
#define FSCTL_DEVICE_METHOD_BUFFERED 0x00
|
||||||
|
#define FSCTL_DEVICE_METHOD_IN_DIRECT 0x01
|
||||||
|
#define FSCTL_DEVICE_METHOD_OUT_DIRECT 0x02
|
||||||
|
#define FSCTL_DEVICE_METHOD_NEITHER 0x03
|
||||||
|
#define FSCTL_DEVICE_METHOD_MASK 0x00000003
|
||||||
|
|
||||||
|
|
||||||
#define FSCTL_DFS_GET_REFERRALS 0x00060194
|
#define FSCTL_DFS_GET_REFERRALS 0x00060194
|
||||||
#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0
|
#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0
|
||||||
#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
|
#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
|
||||||
|
@ -76,7 +103,7 @@
|
||||||
#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
|
#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
|
||||||
#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
|
#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
|
||||||
#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C
|
#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C
|
||||||
#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */
|
#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF
|
||||||
#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
|
#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
|
||||||
#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */
|
#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */
|
||||||
#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344
|
#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344
|
||||||
|
|
|
@ -104,7 +104,10 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_CIFS_STATS2
|
#ifdef CONFIG_CIFS_STATS2
|
||||||
__le16 command = midEntry->server->vals->lock_cmd;
|
__le16 command = midEntry->server->vals->lock_cmd;
|
||||||
|
__u16 smb_cmd = le16_to_cpu(midEntry->command);
|
||||||
unsigned long now;
|
unsigned long now;
|
||||||
|
unsigned long roundtrip_time;
|
||||||
|
struct TCP_Server_Info *server = midEntry->server;
|
||||||
#endif
|
#endif
|
||||||
midEntry->mid_state = MID_FREE;
|
midEntry->mid_state = MID_FREE;
|
||||||
atomic_dec(&midCount);
|
atomic_dec(&midCount);
|
||||||
|
@ -114,6 +117,23 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||||
cifs_small_buf_release(midEntry->resp_buf);
|
cifs_small_buf_release(midEntry->resp_buf);
|
||||||
#ifdef CONFIG_CIFS_STATS2
|
#ifdef CONFIG_CIFS_STATS2
|
||||||
now = jiffies;
|
now = jiffies;
|
||||||
|
if (now < midEntry->when_alloc)
|
||||||
|
cifs_dbg(VFS, "invalid mid allocation time\n");
|
||||||
|
roundtrip_time = now - midEntry->when_alloc;
|
||||||
|
|
||||||
|
if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) {
|
||||||
|
if (atomic_read(&server->num_cmds[smb_cmd]) == 0) {
|
||||||
|
server->slowest_cmd[smb_cmd] = roundtrip_time;
|
||||||
|
server->fastest_cmd[smb_cmd] = roundtrip_time;
|
||||||
|
} else {
|
||||||
|
if (server->slowest_cmd[smb_cmd] < roundtrip_time)
|
||||||
|
server->slowest_cmd[smb_cmd] = roundtrip_time;
|
||||||
|
else if (server->fastest_cmd[smb_cmd] > roundtrip_time)
|
||||||
|
server->fastest_cmd[smb_cmd] = roundtrip_time;
|
||||||
|
}
|
||||||
|
cifs_stats_inc(&server->num_cmds[smb_cmd]);
|
||||||
|
server->time_per_cmd[smb_cmd] += roundtrip_time;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* commands taking longer than one second (default) can be indications
|
* commands taking longer than one second (default) can be indications
|
||||||
* that something is wrong, unless it is quite a slow link or a very
|
* that something is wrong, unless it is quite a slow link or a very
|
||||||
|
@ -131,11 +151,10 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||||
* smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command
|
* smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command
|
||||||
* NB: le16_to_cpu returns unsigned so can not be negative below
|
* NB: le16_to_cpu returns unsigned so can not be negative below
|
||||||
*/
|
*/
|
||||||
if (le16_to_cpu(midEntry->command) < NUMBER_OF_SMB2_COMMANDS)
|
if (smb_cmd < NUMBER_OF_SMB2_COMMANDS)
|
||||||
cifs_stats_inc(&midEntry->server->smb2slowcmd[le16_to_cpu(midEntry->command)]);
|
cifs_stats_inc(&server->smb2slowcmd[smb_cmd]);
|
||||||
|
|
||||||
trace_smb3_slow_rsp(le16_to_cpu(midEntry->command),
|
trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
|
||||||
midEntry->mid, midEntry->pid,
|
|
||||||
midEntry->when_sent, midEntry->when_received);
|
midEntry->when_sent, midEntry->when_received);
|
||||||
if (cifsFYI & CIFS_TIMER) {
|
if (cifsFYI & CIFS_TIMER) {
|
||||||
pr_debug(" CIFS slow rsp: cmd %d mid %llu",
|
pr_debug(" CIFS slow rsp: cmd %d mid %llu",
|
||||||
|
@ -300,7 +319,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
|
||||||
__be32 rfc1002_marker;
|
__be32 rfc1002_marker;
|
||||||
|
|
||||||
if (cifs_rdma_enabled(server) && server->smbd_conn) {
|
if (cifs_rdma_enabled(server) && server->smbd_conn) {
|
||||||
rc = smbd_send(server, rqst);
|
rc = smbd_send(server, num_rqst, rqst);
|
||||||
goto smbd_done;
|
goto smbd_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +529,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
spin_lock(&server->req_lock);
|
spin_lock(&server->req_lock);
|
||||||
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP) {
|
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) {
|
||||||
/* oplock breaks must not be held up */
|
/* oplock breaks must not be held up */
|
||||||
server->in_flight++;
|
server->in_flight++;
|
||||||
*credits -= 1;
|
*credits -= 1;
|
||||||
|
@ -819,7 +838,7 @@ SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
|
||||||
iov[0].iov_base = in_buf;
|
iov[0].iov_base = in_buf;
|
||||||
iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
|
iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
|
||||||
flags |= CIFS_NO_RESP;
|
flags |= CIFS_NO_RSP_BUF;
|
||||||
rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
|
rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
|
||||||
cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
|
cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
|
||||||
|
|
||||||
|
@ -1054,8 +1073,11 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
|
||||||
mutex_unlock(&ses->server->srv_mutex);
|
mutex_unlock(&ses->server->srv_mutex);
|
||||||
|
|
||||||
if (rc < 0) {
|
/*
|
||||||
/* Sending failed for some reason - return credits back */
|
* If sending failed for some reason or it is an oplock break that we
|
||||||
|
* will not receive a response to - return credits back
|
||||||
|
*/
|
||||||
|
if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) {
|
||||||
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;
|
||||||
|
@ -1076,9 +1098,6 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||||
smb311_update_preauth_hash(ses, rqst[0].rq_iov,
|
smb311_update_preauth_hash(ses, rqst[0].rq_iov,
|
||||||
rqst[0].rq_nvec);
|
rqst[0].rq_nvec);
|
||||||
|
|
||||||
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
for (i = 0; i < num_rqst; i++) {
|
for (i = 0; i < num_rqst; i++) {
|
||||||
rc = wait_for_response(ses->server, midQ[i]);
|
rc = wait_for_response(ses->server, midQ[i]);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
|
@ -1132,7 +1151,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||||
flags & CIFS_LOG_ERROR);
|
flags & CIFS_LOG_ERROR);
|
||||||
|
|
||||||
/* mark it so buf will not be freed by cifs_delete_mid */
|
/* mark it so buf will not be freed by cifs_delete_mid */
|
||||||
if ((flags & CIFS_NO_RESP) == 0)
|
if ((flags & CIFS_NO_RSP_BUF) == 0)
|
||||||
midQ[i]->resp_buf = NULL;
|
midQ[i]->resp_buf = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1283,9 +1302,6 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
rc = wait_for_response(ses->server, midQ);
|
rc = wait_for_response(ses->server, midQ);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
send_cancel(ses->server, &rqst, midQ);
|
send_cancel(ses->server, &rqst, midQ);
|
||||||
|
|
Loading…
Reference in New Issue