Commit 0c14e43a authored by Linus Torvalds's avatar Linus Torvalds

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

Pull cifs fixes from Steve French:

 - one smb3 (ACL related) fix for stable

 - one SMB3 security enhancement (when mounting -t smb3 forbid less
   secure dialects)

 - some RDMA and compounding fixes

* tag '4.18-fixes-smb3' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: fix a buffer leak in smb2_query_symlink
  smb3: do not allow insecure cifs mounts when using smb3
  CIFS: Fix NULL ptr deref
  CIFS: fix encryption in SMB3.1.1
  CIFS: Pass page offset for encrypting
  CIFS: Pass page offset for calculating signature
  CIFS: SMBD: Support page offset in memory registration
  CIFS: SMBD: Support page offset in RDMA recv
  CIFS: SMBD: Support page offset in RDMA send
  CIFS: When sending data on socket, pass the correct page offset
  CIFS: Introduce helper function to get page offset and length in smb_rqst
  CIFS: Calculate the correct request length based on page offset and tail size
  cifs: For SMB2 security informaion query, check for minimum sized security descriptor instead of sizeof FileAllInformation class
  CIFS: Fix signing for SMB2/3
parents bbaa1013 9d874c36
...@@ -98,4 +98,18 @@ struct cifs_ace { ...@@ -98,4 +98,18 @@ struct cifs_ace {
struct cifs_sid sid; /* ie UUID of user or group who gets these perms */ struct cifs_sid sid; /* ie UUID of user or group who gets these perms */
} __attribute__((packed)); } __attribute__((packed));
/*
* Minimum security identifier can be one for system defined Users
* and Groups such as NULL SID and World or Built-in accounts such
* as Administrator and Guest and consists of
* Revision + Num (Sub)Auths + Authority + Domain (one Subauthority)
*/
#define MIN_SID_LEN (1 + 1 + 6 + 4) /* in bytes */
/*
* Minimum security descriptor can be one without any SACL and DACL and can
* consist of revision, type, and two sids of minimum size for owner and group
*/
#define MIN_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + (2 * MIN_SID_LEN))
#endif /* _CIFSACL_H */ #endif /* _CIFSACL_H */
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <crypto/aead.h> #include <crypto/aead.h>
int __cifs_calc_signature(struct smb_rqst *rqst, int __cifs_calc_signature(struct smb_rqst *rqst,
int start,
struct TCP_Server_Info *server, char *signature, struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash) struct shash_desc *shash)
{ {
...@@ -45,10 +46,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst, ...@@ -45,10 +46,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec; int n_vec = rqst->rq_nvec;
if (n_vec < 2 || iov[0].iov_len != 4) for (i = start; i < n_vec; i++) {
return -EIO;
for (i = 1; i < n_vec; i++) {
if (iov[i].iov_len == 0) if (iov[i].iov_len == 0)
continue; continue;
if (iov[i].iov_base == NULL) { if (iov[i].iov_base == NULL) {
...@@ -68,11 +66,12 @@ int __cifs_calc_signature(struct smb_rqst *rqst, ...@@ -68,11 +66,12 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
/* now hash over the rq_pages array */ /* now hash over the rq_pages array */
for (i = 0; i < rqst->rq_npages; i++) { for (i = 0; i < rqst->rq_npages; i++) {
void *kaddr = kmap(rqst->rq_pages[i]); void *kaddr;
size_t len = rqst->rq_pagesz; unsigned int len, offset;
rqst_page_get_length(rqst, i, &len, &offset);
if (i == rqst->rq_npages - 1) kaddr = (char *) kmap(rqst->rq_pages[i]) + offset;
len = rqst->rq_tailsz;
crypto_shash_update(shash, kaddr, len); crypto_shash_update(shash, kaddr, len);
...@@ -119,7 +118,7 @@ static int cifs_calc_signature(struct smb_rqst *rqst, ...@@ -119,7 +118,7 @@ static int cifs_calc_signature(struct smb_rqst *rqst,
return rc; return rc;
} }
return __cifs_calc_signature(rqst, server, signature, return __cifs_calc_signature(rqst, 1, server, signature,
&server->secmech.sdescmd5->shash); &server->secmech.sdescmd5->shash);
} }
......
...@@ -698,8 +698,8 @@ static int cifs_set_super(struct super_block *sb, void *data) ...@@ -698,8 +698,8 @@ static int cifs_set_super(struct super_block *sb, void *data)
} }
static struct dentry * static struct dentry *
cifs_do_mount(struct file_system_type *fs_type, cifs_smb3_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data) int flags, const char *dev_name, void *data, bool is_smb3)
{ {
int rc; int rc;
struct super_block *sb; struct super_block *sb;
...@@ -710,7 +710,7 @@ cifs_do_mount(struct file_system_type *fs_type, ...@@ -710,7 +710,7 @@ cifs_do_mount(struct file_system_type *fs_type,
cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags); cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags);
volume_info = cifs_get_volume_info((char *)data, dev_name); volume_info = cifs_get_volume_info((char *)data, dev_name, is_smb3);
if (IS_ERR(volume_info)) if (IS_ERR(volume_info))
return ERR_CAST(volume_info); return ERR_CAST(volume_info);
...@@ -790,6 +790,20 @@ cifs_do_mount(struct file_system_type *fs_type, ...@@ -790,6 +790,20 @@ cifs_do_mount(struct file_system_type *fs_type,
goto out; goto out;
} }
static struct dentry *
smb3_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return cifs_smb3_do_mount(fs_type, flags, dev_name, data, true);
}
static struct dentry *
cifs_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return cifs_smb3_do_mount(fs_type, flags, dev_name, data, false);
}
static ssize_t static ssize_t
cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{ {
...@@ -925,7 +939,7 @@ MODULE_ALIAS_FS("cifs"); ...@@ -925,7 +939,7 @@ MODULE_ALIAS_FS("cifs");
static struct file_system_type smb3_fs_type = { static struct file_system_type smb3_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "smb3", .name = "smb3",
.mount = cifs_do_mount, .mount = smb3_do_mount,
.kill_sb = cifs_kill_sb, .kill_sb = cifs_kill_sb,
/* .fs_flags */ /* .fs_flags */
}; };
......
...@@ -1019,6 +1019,12 @@ tlink_tcon(struct tcon_link *tlink) ...@@ -1019,6 +1019,12 @@ tlink_tcon(struct tcon_link *tlink)
return tlink->tl_tcon; return tlink->tl_tcon;
} }
static inline struct tcon_link *
cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb)
{
return cifs_sb->master_tlink;
}
extern void cifs_put_tlink(struct tcon_link *tlink); extern void cifs_put_tlink(struct tcon_link *tlink);
static inline struct tcon_link * static inline struct tcon_link *
......
...@@ -211,7 +211,7 @@ extern int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, ...@@ -211,7 +211,7 @@ extern int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
extern int cifs_match_super(struct super_block *, void *); extern int cifs_match_super(struct super_block *, void *);
extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info); extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info);
extern struct smb_vol *cifs_get_volume_info(char *mount_data, extern struct smb_vol *cifs_get_volume_info(char *mount_data,
const char *devname); const char *devname, bool is_smb3);
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
extern void cifs_umount(struct cifs_sb_info *); extern void cifs_umount(struct cifs_sb_info *);
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
...@@ -544,7 +544,7 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, ...@@ -544,7 +544,7 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf, const unsigned char *path, char *pbuf,
unsigned int *pbytes_written); unsigned int *pbytes_written);
int __cifs_calc_signature(struct smb_rqst *rqst, int __cifs_calc_signature(struct smb_rqst *rqst, int start,
struct TCP_Server_Info *server, char *signature, struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash); struct shash_desc *shash);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
...@@ -557,4 +557,7 @@ int cifs_alloc_hash(const char *name, struct crypto_shash **shash, ...@@ -557,4 +557,7 @@ int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
struct sdesc **sdesc); struct sdesc **sdesc);
void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc); void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
unsigned int *len, unsigned int *offset);
#endif /* _CIFSPROTO_H */ #endif /* _CIFSPROTO_H */
...@@ -320,7 +320,7 @@ static int generic_ip_connect(struct TCP_Server_Info *server); ...@@ -320,7 +320,7 @@ 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, static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
const char *devname); const char *devname, bool is_smb3);
/* /*
* cifs tcp session reconnection * cifs tcp session reconnection
...@@ -1166,7 +1166,7 @@ cifs_parse_cache_flavor(char *value, struct smb_vol *vol) ...@@ -1166,7 +1166,7 @@ cifs_parse_cache_flavor(char *value, struct smb_vol *vol)
} }
static int static int
cifs_parse_smb_version(char *value, struct smb_vol *vol) cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3)
{ {
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
...@@ -1176,6 +1176,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol) ...@@ -1176,6 +1176,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
cifs_dbg(VFS, "mount with legacy dialect disabled\n"); cifs_dbg(VFS, "mount with legacy dialect disabled\n");
return 1; return 1;
} }
if (is_smb3) {
cifs_dbg(VFS, "vers=1.0 (cifs) not permitted when mounting with smb3\n");
return 1;
}
vol->ops = &smb1_operations; vol->ops = &smb1_operations;
vol->vals = &smb1_values; vol->vals = &smb1_values;
break; break;
...@@ -1184,6 +1188,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol) ...@@ -1184,6 +1188,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
cifs_dbg(VFS, "mount with legacy dialect disabled\n"); cifs_dbg(VFS, "mount with legacy dialect disabled\n");
return 1; return 1;
} }
if (is_smb3) {
cifs_dbg(VFS, "vers=2.0 not permitted when mounting with smb3\n");
return 1;
}
vol->ops = &smb20_operations; vol->ops = &smb20_operations;
vol->vals = &smb20_values; vol->vals = &smb20_values;
break; break;
...@@ -1272,7 +1280,7 @@ cifs_parse_devname(const char *devname, struct smb_vol *vol) ...@@ -1272,7 +1280,7 @@ cifs_parse_devname(const char *devname, struct smb_vol *vol)
static int static int
cifs_parse_mount_options(const char *mountdata, const char *devname, cifs_parse_mount_options(const char *mountdata, const char *devname,
struct smb_vol *vol) struct smb_vol *vol, bool is_smb3)
{ {
char *data, *end; char *data, *end;
char *mountdata_copy = NULL, *options; char *mountdata_copy = NULL, *options;
...@@ -1985,7 +1993,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1985,7 +1993,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
if (string == NULL) if (string == NULL)
goto out_nomem; goto out_nomem;
if (cifs_parse_smb_version(string, vol) != 0) if (cifs_parse_smb_version(string, vol, is_smb3) != 0)
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
got_version = true; got_version = true;
break; break;
...@@ -3116,12 +3124,6 @@ cifs_put_tlink(struct tcon_link *tlink) ...@@ -3116,12 +3124,6 @@ cifs_put_tlink(struct tcon_link *tlink)
return; return;
} }
static inline struct tcon_link *
cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb)
{
return cifs_sb->master_tlink;
}
static int static int
compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
{ {
...@@ -3803,7 +3805,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, ...@@ -3803,7 +3805,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
} else { } else {
cleanup_volume_info_contents(volume_info); cleanup_volume_info_contents(volume_info);
rc = cifs_setup_volume_info(volume_info, mdata, rc = cifs_setup_volume_info(volume_info, mdata,
fake_devname); fake_devname, false);
} }
kfree(fake_devname); kfree(fake_devname);
kfree(cifs_sb->mountdata); kfree(cifs_sb->mountdata);
...@@ -3816,11 +3818,11 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, ...@@ -3816,11 +3818,11 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
static int static 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) const char *devname, bool is_smb3)
{ {
int rc = 0; int rc = 0;
if (cifs_parse_mount_options(mount_data, devname, volume_info)) if (cifs_parse_mount_options(mount_data, devname, volume_info, is_smb3))
return -EINVAL; return -EINVAL;
if (volume_info->nullauth) { if (volume_info->nullauth) {
...@@ -3854,7 +3856,7 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, ...@@ -3854,7 +3856,7 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
} }
struct smb_vol * struct smb_vol *
cifs_get_volume_info(char *mount_data, const char *devname) cifs_get_volume_info(char *mount_data, const char *devname, bool is_smb3)
{ {
int rc; int rc;
struct smb_vol *volume_info; struct smb_vol *volume_info;
...@@ -3863,7 +3865,7 @@ cifs_get_volume_info(char *mount_data, const char *devname) ...@@ -3863,7 +3865,7 @@ cifs_get_volume_info(char *mount_data, const char *devname)
if (!volume_info) if (!volume_info)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
rc = cifs_setup_volume_info(volume_info, mount_data, devname); rc = cifs_setup_volume_info(volume_info, mount_data, devname, is_smb3);
if (rc) { if (rc) {
cifs_cleanup_volume_info(volume_info); cifs_cleanup_volume_info(volume_info);
volume_info = ERR_PTR(rc); volume_info = ERR_PTR(rc);
......
...@@ -421,7 +421,8 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, ...@@ -421,7 +421,8 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
return -ENOMEM; return -ENOMEM;
} }
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
NULL);
if (rc) if (rc)
goto qmf_out_open_fail; goto qmf_out_open_fail;
...@@ -478,7 +479,8 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, ...@@ -478,7 +479,8 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
NULL);
if (rc) { if (rc) {
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
......
...@@ -905,3 +905,20 @@ cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc) ...@@ -905,3 +905,20 @@ cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
crypto_free_shash(*shash); crypto_free_shash(*shash);
*shash = NULL; *shash = NULL;
} }
/**
* rqst_page_get_length - obtain the length and offset for a page in smb_rqst
* Input: rqst - a smb_rqst, page - a page index for rqst
* Output: *len - the length for this page, *offset - the offset for this page
*/
void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
unsigned int *len, unsigned int *offset)
{
*len = rqst->rq_pagesz;
*offset = (page == 0) ? rqst->rq_offset : 0;
if (rqst->rq_npages == 1 || page == rqst->rq_npages-1)
*len = rqst->rq_tailsz;
else if (page == 0)
*len = rqst->rq_pagesz - rqst->rq_offset;
}
...@@ -64,7 +64,8 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, ...@@ -64,7 +64,8 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL); rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL,
NULL);
if (rc) if (rc)
goto out; goto out;
......
...@@ -71,7 +71,8 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -71,7 +71,8 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
NULL);
if (rc) { if (rc) {
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
......
...@@ -453,8 +453,10 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) ...@@ -453,8 +453,10 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
start_of_path = from + 1; start_of_path = from + 1;
#ifdef CONFIG_CIFS_SMB311 #ifdef CONFIG_CIFS_SMB311
/* SMB311 POSIX extensions paths do not include leading slash */ /* SMB311 POSIX extensions paths do not include leading slash */
else if (cifs_sb_master_tcon(cifs_sb)->posix_extensions) else if (cifs_sb_master_tlink(cifs_sb) &&
cifs_sb_master_tcon(cifs_sb)->posix_extensions) {
start_of_path = from + 1; start_of_path = from + 1;
}
#endif /* 311 */ #endif /* 311 */
else else
start_of_path = from; start_of_path = from;
......
...@@ -348,7 +348,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) ...@@ -348,7 +348,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
oparams.fid = pfid; oparams.fid = pfid;
oparams.reconnect = false; oparams.reconnect = false;
rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL);
if (rc == 0) { if (rc == 0) {
memcpy(tcon->prfid, pfid, sizeof(struct cifs_fid)); memcpy(tcon->prfid, pfid, sizeof(struct cifs_fid));
tcon->valid_root_fid = true; tcon->valid_root_fid = true;
...@@ -375,7 +375,8 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -375,7 +375,8 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
oparms.reconnect = false; oparms.reconnect = false;
if (no_cached_open) if (no_cached_open)
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
NULL);
else else
rc = open_shroot(xid, tcon, &fid); rc = open_shroot(xid, tcon, &fid);
...@@ -413,7 +414,7 @@ smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -413,7 +414,7 @@ smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL);
if (rc) if (rc)
return; return;
...@@ -449,7 +450,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -449,7 +450,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
if (rc) { if (rc) {
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
...@@ -598,7 +599,7 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -598,7 +599,7 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path); kfree(utf16_path);
if (rc) { if (rc) {
cifs_dbg(FYI, "open failed rc=%d\n", rc); cifs_dbg(FYI, "open failed rc=%d\n", rc);
...@@ -677,7 +678,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -677,7 +678,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path); kfree(utf16_path);
if (rc) { if (rc) {
cifs_dbg(FYI, "open failed rc=%d\n", rc); cifs_dbg(FYI, "open failed rc=%d\n", rc);
...@@ -1261,7 +1262,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1261,7 +1262,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = fid; oparms.fid = fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path); kfree(utf16_path);
if (rc) { if (rc) {
cifs_dbg(FYI, "open dir failed rc=%d\n", rc); cifs_dbg(FYI, "open dir failed rc=%d\n", rc);
...@@ -1361,7 +1362,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1361,7 +1362,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL);
if (rc) if (rc)
return rc; return rc;
buf->f_type = SMB2_MAGIC_NUMBER; buf->f_type = SMB2_MAGIC_NUMBER;
...@@ -1515,7 +1516,8 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1515,7 +1516,8 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
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; 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;
...@@ -1535,18 +1537,18 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1535,18 +1537,18 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
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(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
&resp_buftype);
if (!rc || !err_iov.iov_base) { if (!rc || !err_iov.iov_base) {
kfree(utf16_path); rc = -ENOENT;
return -ENOENT; goto querty_exit;
} }
err_buf = err_iov.iov_base; err_buf = err_iov.iov_base;
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) || if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) { err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) {
kfree(utf16_path); rc = -ENOENT;
return -ENOENT; goto querty_exit;
} }
/* open must fail on symlink - reset rc */ /* open must fail on symlink - reset rc */
...@@ -1558,25 +1560,28 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1558,25 +1560,28 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
print_offset = le16_to_cpu(symlink->PrintNameOffset); print_offset = le16_to_cpu(symlink->PrintNameOffset);
if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) { if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
kfree(utf16_path); rc = -ENOENT;
return -ENOENT; goto querty_exit;
} }
if (err_iov.iov_len < if (err_iov.iov_len <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) { SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
kfree(utf16_path); rc = -ENOENT;
return -ENOENT; goto querty_exit;
} }
*target_path = cifs_strndup_from_utf16( *target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset, (char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls); sub_len, true, cifs_sb->local_nls);
if (!(*target_path)) { if (!(*target_path)) {
kfree(utf16_path); rc = -ENOMEM;
return -ENOMEM; goto querty_exit;
} }
convert_delimiter(*target_path, '/'); convert_delimiter(*target_path, '/');
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
querty_exit:
free_rsp_buf(resp_buftype, err_buf);
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
} }
...@@ -1649,7 +1654,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, ...@@ -1649,7 +1654,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path); kfree(utf16_path);
if (!rc) { if (!rc) {
rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
...@@ -1712,7 +1717,7 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, ...@@ -1712,7 +1717,7 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path); kfree(utf16_path);
if (!rc) { if (!rc) {
rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
...@@ -2189,9 +2194,10 @@ init_sg(struct smb_rqst *rqst, u8 *sign) ...@@ -2189,9 +2194,10 @@ init_sg(struct smb_rqst *rqst, u8 *sign)
smb2_sg_set_buf(&sg[i], rqst->rq_iov[i+1].iov_base, smb2_sg_set_buf(&sg[i], rqst->rq_iov[i+1].iov_base,
rqst->rq_iov[i+1].iov_len); rqst->rq_iov[i+1].iov_len);
for (j = 0; i < sg_len - 1; i++, j++) { for (j = 0; i < sg_len - 1; i++, j++) {
unsigned int len = (j < rqst->rq_npages - 1) ? rqst->rq_pagesz unsigned int len, offset;
: rqst->rq_tailsz;
sg_set_page(&sg[i], rqst->rq_pages[j], len, 0); rqst_page_get_length(rqst, j, &len, &offset);
sg_set_page(&sg[i], rqst->rq_pages[j], len, offset);
} }
smb2_sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE); smb2_sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE);
return sg; return sg;
...@@ -2229,7 +2235,7 @@ static int ...@@ -2229,7 +2235,7 @@ static int
crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc) crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
{ {
struct smb2_transform_hdr *tr_hdr = struct smb2_transform_hdr *tr_hdr =
(struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base; (struct smb2_transform_hdr *)rqst->rq_iov[1].iov_base;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
int rc = 0; int rc = 0;
struct scatterlist *sg; struct scatterlist *sg;
...@@ -2338,6 +2344,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, ...@@ -2338,6 +2344,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
return rc; return rc;
new_rq->rq_pages = pages; new_rq->rq_pages = pages;
new_rq->rq_offset = old_rq->rq_offset;
new_rq->rq_npages = old_rq->rq_npages; new_rq->rq_npages = old_rq->rq_npages;
new_rq->rq_pagesz = old_rq->rq_pagesz; new_rq->rq_pagesz = old_rq->rq_pagesz;
new_rq->rq_tailsz = old_rq->rq_tailsz; new_rq->rq_tailsz = old_rq->rq_tailsz;
...@@ -2379,10 +2386,14 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, ...@@ -2379,10 +2386,14 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
/* copy pages form the old */ /* copy pages form the old */
for (i = 0; i < npages; i++) { for (i = 0; i < npages; i++) {
char *dst = kmap(new_rq->rq_pages[i]); char *dst, *src;
char *src = kmap(old_rq->rq_pages[i]); unsigned int offset, len;
unsigned int len = (i < npages - 1) ? new_rq->rq_pagesz :
new_rq->rq_tailsz; rqst_page_get_length(new_rq, i, &len, &offset);
dst = (char *) kmap(new_rq->rq_pages[i]) + offset;
src = (char *) kmap(old_rq->rq_pages[i]) + offset;
memcpy(dst, src, len); memcpy(dst, src, len);
kunmap(new_rq->rq_pages[i]); kunmap(new_rq->rq_pages[i]);
kunmap(old_rq->rq_pages[i]); kunmap(old_rq->rq_pages[i]);
......
...@@ -1889,7 +1889,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, ...@@ -1889,7 +1889,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
int int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf, __u8 *oplock, struct smb2_file_all_info *buf,
struct kvec *err_iov) struct kvec *err_iov, int *buftype)
{ {
struct smb2_create_req *req; struct smb2_create_req *req;
struct smb2_create_rsp *rsp; struct smb2_create_rsp *rsp;
...@@ -2052,6 +2052,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -2052,6 +2052,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
if (err_iov && rsp) { if (err_iov && rsp) {
*err_iov = rsp_iov; *err_iov = rsp_iov;
*buftype = resp_buftype;
resp_buftype = CIFS_NO_BUFFER; resp_buftype = CIFS_NO_BUFFER;
rsp = NULL; rsp = NULL;
} }
...@@ -2492,8 +2493,7 @@ SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2492,8 +2493,7 @@ SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
return query_info(xid, tcon, persistent_fid, volatile_fid, return query_info(xid, tcon, persistent_fid, volatile_fid,
0, SMB2_O_INFO_SECURITY, additional_info, 0, SMB2_O_INFO_SECURITY, additional_info,
SMB2_MAX_BUFFER_SIZE, SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen);
sizeof(struct smb2_file_all_info), data, plen);
} }
int int
...@@ -2721,8 +2721,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len, ...@@ -2721,8 +2721,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
rdata->mr = smbd_register_mr( rdata->mr = smbd_register_mr(
server->smbd_conn, rdata->pages, server->smbd_conn, rdata->pages,
rdata->nr_pages, rdata->tailsz, rdata->nr_pages, rdata->page_offset,
true, need_invalidate); rdata->tailsz, true, need_invalidate);
if (!rdata->mr) if (!rdata->mr)
return -ENOBUFS; return -ENOBUFS;
...@@ -3108,16 +3108,22 @@ smb2_async_writev(struct cifs_writedata *wdata, ...@@ -3108,16 +3108,22 @@ smb2_async_writev(struct cifs_writedata *wdata,
wdata->mr = smbd_register_mr( wdata->mr = smbd_register_mr(
server->smbd_conn, wdata->pages, server->smbd_conn, wdata->pages,
wdata->nr_pages, wdata->tailsz, wdata->nr_pages, wdata->page_offset,
false, need_invalidate); wdata->tailsz, false, need_invalidate);
if (!wdata->mr) { if (!wdata->mr) {
rc = -ENOBUFS; rc = -ENOBUFS;
goto async_writev_out; goto async_writev_out;
} }
req->Length = 0; req->Length = 0;
req->DataOffset = 0; req->DataOffset = 0;
if (wdata->nr_pages > 1)
req->RemainingBytes = req->RemainingBytes =
cpu_to_le32((wdata->nr_pages-1)*PAGE_SIZE + wdata->tailsz); cpu_to_le32(
(wdata->nr_pages - 1) * wdata->pagesz -
wdata->page_offset + wdata->tailsz
);
else
req->RemainingBytes = cpu_to_le32(wdata->tailsz);
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
if (need_invalidate) if (need_invalidate)
req->Channel = SMB2_CHANNEL_RDMA_V1; req->Channel = SMB2_CHANNEL_RDMA_V1;
......
...@@ -125,7 +125,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); ...@@ -125,7 +125,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *path, __u8 *oplock, __le16 *path, __u8 *oplock,
struct smb2_file_all_info *buf, struct smb2_file_all_info *buf,
struct kvec *err_iov); struct kvec *err_iov, int *resp_buftype);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode, u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen, bool is_fsctl, char *in_data, u32 indatalen,
......
...@@ -171,7 +171,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -171,7 +171,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
unsigned char *sigptr = smb2_signature; unsigned char *sigptr = smb2_signature;
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0;
struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base;
struct cifs_ses *ses; struct cifs_ses *ses;
ses = smb2_find_smb_ses(server, shdr->SessionId); ses = smb2_find_smb_ses(server, shdr->SessionId);
...@@ -202,7 +204,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -202,7 +204,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc; return rc;
} }
rc = __cifs_calc_signature(rqst, server, sigptr, rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr,
&server->secmech.sdeschmacsha256->shash); &server->secmech.sdeschmacsha256->shash);
if (!rc) if (!rc)
...@@ -412,7 +414,9 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -412,7 +414,9 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
unsigned char smb3_signature[SMB2_CMACAES_SIZE]; unsigned char smb3_signature[SMB2_CMACAES_SIZE];
unsigned char *sigptr = smb3_signature; unsigned char *sigptr = smb3_signature;
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0;
struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base;
struct cifs_ses *ses; struct cifs_ses *ses;
ses = smb2_find_smb_ses(server, shdr->SessionId); ses = smb2_find_smb_ses(server, shdr->SessionId);
...@@ -443,7 +447,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -443,7 +447,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc; return rc;
} }
rc = __cifs_calc_signature(rqst, server, sigptr, rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr,
&server->secmech.sdesccmacaes->shash); &server->secmech.sdesccmacaes->shash);
if (!rc) if (!rc)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/highmem.h> #include <linux/highmem.h>
#include "smbdirect.h" #include "smbdirect.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifsproto.h"
static struct smbd_response *get_empty_queue_buffer( static struct smbd_response *get_empty_queue_buffer(
struct smbd_connection *info); struct smbd_connection *info);
...@@ -2003,10 +2004,12 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf, ...@@ -2003,10 +2004,12 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,
* return value: actual data read * return value: actual data read
*/ */
static int smbd_recv_page(struct smbd_connection *info, static int smbd_recv_page(struct smbd_connection *info,
struct page *page, unsigned int to_read) struct page *page, unsigned int page_offset,
unsigned int to_read)
{ {
int ret; int ret;
char *to_address; char *to_address;
void *page_address;
/* make sure we have the page ready for read */ /* make sure we have the page ready for read */
ret = wait_event_interruptible( ret = wait_event_interruptible(
...@@ -2014,16 +2017,17 @@ static int smbd_recv_page(struct smbd_connection *info, ...@@ -2014,16 +2017,17 @@ static int smbd_recv_page(struct smbd_connection *info,
info->reassembly_data_length >= to_read || info->reassembly_data_length >= to_read ||
info->transport_status != SMBD_CONNECTED); info->transport_status != SMBD_CONNECTED);
if (ret) if (ret)
return 0; return ret;
/* now we can read from reassembly queue and not sleep */ /* now we can read from reassembly queue and not sleep */
to_address = kmap_atomic(page); page_address = kmap_atomic(page);
to_address = (char *) page_address + page_offset;
log_read(INFO, "reading from page=%p address=%p to_read=%d\n", log_read(INFO, "reading from page=%p address=%p to_read=%d\n",
page, to_address, to_read); page, to_address, to_read);
ret = smbd_recv_buf(info, to_address, to_read); ret = smbd_recv_buf(info, to_address, to_read);
kunmap_atomic(to_address); kunmap_atomic(page_address);
return ret; return ret;
} }
...@@ -2037,7 +2041,7 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg) ...@@ -2037,7 +2041,7 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
{ {
char *buf; char *buf;
struct page *page; struct page *page;
unsigned int to_read; unsigned int to_read, page_offset;
int rc; int rc;
info->smbd_recv_pending++; info->smbd_recv_pending++;
...@@ -2051,15 +2055,16 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg) ...@@ -2051,15 +2055,16 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
case READ | ITER_BVEC: case READ | ITER_BVEC:
page = msg->msg_iter.bvec->bv_page; page = msg->msg_iter.bvec->bv_page;
page_offset = msg->msg_iter.bvec->bv_offset;
to_read = msg->msg_iter.bvec->bv_len; to_read = msg->msg_iter.bvec->bv_len;
rc = smbd_recv_page(info, page, to_read); rc = smbd_recv_page(info, page, page_offset, to_read);
break; break;
default: default:
/* 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 type %d\n", cifs_dbg(VFS, "CIFS: invalid msg type %d\n",
msg->msg_iter.type); msg->msg_iter.type);
rc = -EIO; rc = -EINVAL;
} }
info->smbd_recv_pending--; info->smbd_recv_pending--;
...@@ -2082,7 +2087,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) ...@@ -2082,7 +2087,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
struct kvec vec; struct kvec vec;
int nvecs; int nvecs;
int size; int size;
int buflen = 0, remaining_data_length; unsigned int buflen = 0, remaining_data_length;
int start, i, j; int start, i, j;
int max_iov_size = int max_iov_size =
info->max_send_size - sizeof(struct smbd_data_transfer); info->max_send_size - sizeof(struct smbd_data_transfer);
...@@ -2113,10 +2118,17 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) ...@@ -2113,10 +2118,17 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
buflen += iov[i].iov_len; buflen += iov[i].iov_len;
} }
/* add in the page array if there is one */ /*
* 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
* ends at page boundary
*/
if (rqst->rq_npages) { if (rqst->rq_npages) {
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1); if (rqst->rq_npages == 1)
buflen += rqst->rq_tailsz; buflen += rqst->rq_tailsz;
else
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
rqst->rq_offset + rqst->rq_tailsz;
} }
if (buflen + sizeof(struct smbd_data_transfer) > if (buflen + sizeof(struct smbd_data_transfer) >
...@@ -2213,8 +2225,9 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) ...@@ -2213,8 +2225,9 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
/* now sending pages if there are any */ /* now sending pages if there are any */
for (i = 0; i < rqst->rq_npages; i++) { for (i = 0; i < rqst->rq_npages; i++) {
buflen = (i == rqst->rq_npages-1) ? unsigned int offset;
rqst->rq_tailsz : rqst->rq_pagesz;
rqst_page_get_length(rqst, i, &buflen, &offset);
nvecs = (buflen + max_iov_size - 1) / max_iov_size; nvecs = (buflen + max_iov_size - 1) / max_iov_size;
log_write(INFO, "sending pages buflen=%d nvecs=%d\n", log_write(INFO, "sending pages buflen=%d nvecs=%d\n",
buflen, nvecs); buflen, nvecs);
...@@ -2225,9 +2238,11 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) ...@@ -2225,9 +2238,11 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
remaining_data_length -= size; remaining_data_length -= size;
log_write(INFO, "sending pages i=%d offset=%d size=%d" log_write(INFO, "sending pages i=%d offset=%d size=%d"
" remaining_data_length=%d\n", " remaining_data_length=%d\n",
i, j*max_iov_size, size, remaining_data_length); i, j*max_iov_size+offset, size,
remaining_data_length);
rc = smbd_post_send_page( rc = smbd_post_send_page(
info, rqst->rq_pages[i], j*max_iov_size, info, rqst->rq_pages[i],
j*max_iov_size + offset,
size, remaining_data_length); size, remaining_data_length);
if (rc) if (rc)
goto done; goto done;
...@@ -2284,14 +2299,6 @@ static void smbd_mr_recovery_work(struct work_struct *work) ...@@ -2284,14 +2299,6 @@ static void smbd_mr_recovery_work(struct work_struct *work)
if (smbdirect_mr->state == MR_INVALIDATED || if (smbdirect_mr->state == MR_INVALIDATED ||
smbdirect_mr->state == MR_ERROR) { smbdirect_mr->state == MR_ERROR) {
if (smbdirect_mr->state == MR_INVALIDATED) {
ib_dma_unmap_sg(
info->id->device, smbdirect_mr->sgl,
smbdirect_mr->sgl_count,
smbdirect_mr->dir);
smbdirect_mr->state = MR_READY;
} else if (smbdirect_mr->state == MR_ERROR) {
/* recover this MR entry */ /* recover this MR entry */
rc = ib_dereg_mr(smbdirect_mr->mr); rc = ib_dereg_mr(smbdirect_mr->mr);
if (rc) { if (rc) {
...@@ -2299,6 +2306,7 @@ static void smbd_mr_recovery_work(struct work_struct *work) ...@@ -2299,6 +2306,7 @@ static void smbd_mr_recovery_work(struct work_struct *work)
"ib_dereg_mr failed rc=%x\n", "ib_dereg_mr failed rc=%x\n",
rc); rc);
smbd_disconnect_rdma_connection(info); smbd_disconnect_rdma_connection(info);
continue;
} }
smbdirect_mr->mr = ib_alloc_mr( smbdirect_mr->mr = ib_alloc_mr(
...@@ -2311,10 +2319,17 @@ static void smbd_mr_recovery_work(struct work_struct *work) ...@@ -2311,10 +2319,17 @@ static void smbd_mr_recovery_work(struct work_struct *work)
info->mr_type, info->mr_type,
info->max_frmr_depth); info->max_frmr_depth);
smbd_disconnect_rdma_connection(info); smbd_disconnect_rdma_connection(info);
continue;
} }
if (smbdirect_mr->state == MR_INVALIDATED)
ib_dma_unmap_sg(
info->id->device, smbdirect_mr->sgl,
smbdirect_mr->sgl_count,
smbdirect_mr->dir);
smbdirect_mr->state = MR_READY; smbdirect_mr->state = MR_READY;
}
/* smbdirect_mr->state is updated by this function /* smbdirect_mr->state is updated by this function
* and is read and updated by I/O issuing CPUs trying * and is read and updated by I/O issuing CPUs trying
* to get a MR, the call to atomic_inc_return * to get a MR, the call to atomic_inc_return
...@@ -2460,7 +2475,7 @@ static struct smbd_mr *get_mr(struct smbd_connection *info) ...@@ -2460,7 +2475,7 @@ static struct smbd_mr *get_mr(struct smbd_connection *info)
*/ */
struct smbd_mr *smbd_register_mr( struct smbd_mr *smbd_register_mr(
struct smbd_connection *info, struct page *pages[], int num_pages, struct smbd_connection *info, struct page *pages[], int num_pages,
int tailsz, bool writing, bool need_invalidate) int offset, int tailsz, bool writing, bool need_invalidate)
{ {
struct smbd_mr *smbdirect_mr; struct smbd_mr *smbdirect_mr;
int rc, i; int rc, i;
...@@ -2483,17 +2498,31 @@ struct smbd_mr *smbd_register_mr( ...@@ -2483,17 +2498,31 @@ struct smbd_mr *smbd_register_mr(
smbdirect_mr->sgl_count = num_pages; smbdirect_mr->sgl_count = num_pages;
sg_init_table(smbdirect_mr->sgl, num_pages); sg_init_table(smbdirect_mr->sgl, num_pages);
for (i = 0; i < num_pages - 1; i++) log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n",
sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0); num_pages, offset, tailsz);
if (num_pages == 1) {
sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset);
goto skip_multiple_pages;
}
/* We have at least two pages to register */
sg_set_page(
&smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset);
i = 1;
while (i < num_pages - 1) {
sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0);
i++;
}
sg_set_page(&smbdirect_mr->sgl[i], pages[i], sg_set_page(&smbdirect_mr->sgl[i], pages[i],
tailsz ? tailsz : PAGE_SIZE, 0); tailsz ? tailsz : PAGE_SIZE, 0);
skip_multiple_pages:
dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE; dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
smbdirect_mr->dir = dir; smbdirect_mr->dir = dir;
rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir); rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir);
if (!rc) { if (!rc) {
log_rdma_mr(INFO, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n", log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n",
num_pages, dir, rc); num_pages, dir, rc);
goto dma_map_error; goto dma_map_error;
} }
...@@ -2501,8 +2530,8 @@ struct smbd_mr *smbd_register_mr( ...@@ -2501,8 +2530,8 @@ struct smbd_mr *smbd_register_mr(
rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages, rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages,
NULL, PAGE_SIZE); NULL, PAGE_SIZE);
if (rc != num_pages) { if (rc != num_pages) {
log_rdma_mr(INFO, log_rdma_mr(ERR,
"ib_map_mr_sg failed rc = %x num_pages = %x\n", "ib_map_mr_sg failed rc = %d num_pages = %x\n",
rc, num_pages); rc, num_pages);
goto map_mr_error; goto map_mr_error;
} }
......
...@@ -321,7 +321,7 @@ struct smbd_mr { ...@@ -321,7 +321,7 @@ struct smbd_mr {
/* Interfaces to register and deregister MR for RDMA read/write */ /* Interfaces to register and deregister MR for RDMA read/write */
struct smbd_mr *smbd_register_mr( struct smbd_mr *smbd_register_mr(
struct smbd_connection *info, struct page *pages[], int num_pages, struct smbd_connection *info, struct page *pages[], int num_pages,
int tailsz, bool writing, bool need_invalidate); int offset, int tailsz, bool writing, bool need_invalidate);
int smbd_deregister_mr(struct smbd_mr *mr); int smbd_deregister_mr(struct smbd_mr *mr);
#else #else
......
...@@ -212,11 +212,25 @@ rqst_len(struct smb_rqst *rqst) ...@@ -212,11 +212,25 @@ rqst_len(struct smb_rqst *rqst)
for (i = 0; i < rqst->rq_nvec; i++) for (i = 0; i < rqst->rq_nvec; i++)
buflen += iov[i].iov_len; buflen += iov[i].iov_len;
/* add in the page array if there is one */ /*
* Add in the page array if there is one. The caller needs to make
* sure rq_offset and rq_tailsz are set correctly. If a buffer of
* multiple pages ends at page boundary, rq_tailsz needs to be set to
* PAGE_SIZE.
*/
if (rqst->rq_npages) { if (rqst->rq_npages) {
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1); if (rqst->rq_npages == 1)
buflen += rqst->rq_tailsz;
else {
/*
* If there is more than one page, calculate the
* buffer length based on rq_offset and rq_tailsz
*/
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
rqst->rq_offset;
buflen += rqst->rq_tailsz; buflen += rqst->rq_tailsz;
} }
}
return buflen; return buflen;
} }
...@@ -274,15 +288,13 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) ...@@ -274,15 +288,13 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
/* now walk the page array and send each page in it */ /* now walk the page array and send each page in it */
for (i = 0; i < rqst->rq_npages; i++) { for (i = 0; i < rqst->rq_npages; i++) {
size_t len = i == rqst->rq_npages - 1 struct bio_vec bvec;
? rqst->rq_tailsz
: rqst->rq_pagesz; bvec.bv_page = rqst->rq_pages[i];
struct bio_vec bvec = { rqst_page_get_length(rqst, i, &bvec.bv_len, &bvec.bv_offset);
.bv_page = rqst->rq_pages[i],
.bv_len = len
};
iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC, iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC,
&bvec, 1, len); &bvec, 1, bvec.bv_len);
rc = smb_send_kvec(server, &smb_msg, &sent); rc = smb_send_kvec(server, &smb_msg, &sent);
if (rc < 0) if (rc < 0)
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment