Commit 4119bf9f authored by Linus Torvalds's avatar Linus Torvalds

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

Pull cifs fixes from Steve French:
 "Ten cifs/smb fixes:

   - five RDMA (smbdirect) related fixes

   - add experimental support for swap over SMB3 mounts

   - also a fix which improves performance of signed connections"

* tag '5.7-rc-smb3-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6:
  smb3: enable swap on SMB3 mounts
  smb3: change noisy error message to FYI
  smb3: smbdirect support can be configured by default
  cifs: smbd: Do not schedule work to send immediate packet on every receive
  cifs: smbd: Properly process errors on ib_post_send
  cifs: Allocate crypto structures on the fly for calculating signatures of incoming packets
  cifs: smbd: Update receive credits before sending and deal with credits roll back on failure before sending
  cifs: smbd: Check send queue size before posting a send
  cifs: smbd: Merge code to track pending packets
  cifs: ignore cached share root handle closing errors
parents 50bda5fa 4e8aea30
...@@ -202,7 +202,7 @@ config CIFS_SMB_DIRECT ...@@ -202,7 +202,7 @@ config CIFS_SMB_DIRECT
help help
Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1.
SMB Direct allows transferring SMB packets over RDMA. If unsure, SMB Direct allows transferring SMB packets over RDMA. If unsure,
say N. say Y.
config CIFS_FSCACHE config CIFS_FSCACHE
bool "Provide CIFS client caching support" bool "Provide CIFS client caching support"
......
...@@ -323,10 +323,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -323,10 +323,8 @@ 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 " seq_printf(m, "\nPending send_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));
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,
......
...@@ -1208,6 +1208,10 @@ static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, ...@@ -1208,6 +1208,10 @@ static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
{ {
unsigned int xid = get_xid(); unsigned int xid = get_xid();
ssize_t rc; ssize_t rc;
struct cifsFileInfo *cfile = dst_file->private_data;
if (cfile->swapfile)
return -EOPNOTSUPP;
rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff, rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff,
len, flags); len, flags);
......
...@@ -426,7 +426,8 @@ struct smb_version_operations { ...@@ -426,7 +426,8 @@ struct smb_version_operations {
/* generate new lease key */ /* generate new lease key */
void (*new_lease_key)(struct cifs_fid *); void (*new_lease_key)(struct cifs_fid *);
int (*generate_signingkey)(struct cifs_ses *); int (*generate_signingkey)(struct cifs_ses *);
int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *); int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *,
bool allocate_crypto);
int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
struct cifsFileInfo *src_file); struct cifsFileInfo *src_file);
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
...@@ -1312,6 +1313,7 @@ struct cifsFileInfo { ...@@ -1312,6 +1313,7 @@ struct cifsFileInfo {
struct tcon_link *tlink; struct tcon_link *tlink;
unsigned int f_flags; unsigned int f_flags;
bool invalidHandle:1; /* file closed via session abend */ bool invalidHandle:1; /* file closed via session abend */
bool swapfile:1;
bool oplock_break_cancelled:1; bool oplock_break_cancelled:1;
unsigned int oplock_epoch; /* epoch from the lease break */ unsigned int oplock_epoch; /* epoch from the lease break */
__u32 oplock_level; /* oplock/lease level from the lease break */ __u32 oplock_level; /* oplock/lease level from the lease break */
......
...@@ -4808,6 +4808,60 @@ cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter) ...@@ -4808,6 +4808,60 @@ cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter)
return -EINVAL; return -EINVAL;
} }
static int cifs_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span)
{
struct cifsFileInfo *cfile = swap_file->private_data;
struct inode *inode = swap_file->f_mapping->host;
unsigned long blocks;
long long isize;
cifs_dbg(FYI, "swap activate\n");
spin_lock(&inode->i_lock);
blocks = inode->i_blocks;
isize = inode->i_size;
spin_unlock(&inode->i_lock);
if (blocks*512 < isize) {
pr_warn("swap activate: swapfile has holes\n");
return -EINVAL;
}
*span = sis->pages;
printk_once(KERN_WARNING "Swap support over SMB3 is experimental\n");
/*
* TODO: consider adding ACL (or documenting how) to prevent other
* users (on this or other systems) from reading it
*/
/* TODO: add sk_set_memalloc(inet) or similar */
if (cfile)
cfile->swapfile = true;
/*
* TODO: Since file already open, we can't open with DENY_ALL here
* but we could add call to grab a byte range lock to prevent others
* from reading or writing the file
*/
return 0;
}
static void cifs_swap_deactivate(struct file *file)
{
struct cifsFileInfo *cfile = file->private_data;
cifs_dbg(FYI, "swap deactivate\n");
/* TODO: undo sk_set_memalloc(inet) will eventually be needed */
if (cfile)
cfile->swapfile = false;
/* do we need to unpin (or unlock) the file */
}
const struct address_space_operations cifs_addr_ops = { const struct address_space_operations cifs_addr_ops = {
.readpage = cifs_readpage, .readpage = cifs_readpage,
...@@ -4821,6 +4875,13 @@ const struct address_space_operations cifs_addr_ops = { ...@@ -4821,6 +4875,13 @@ const struct address_space_operations cifs_addr_ops = {
.direct_IO = cifs_direct_io, .direct_IO = cifs_direct_io,
.invalidatepage = cifs_invalidate_page, .invalidatepage = cifs_invalidate_page,
.launder_page = cifs_launder_page, .launder_page = cifs_launder_page,
/*
* TODO: investigate and if useful we could add an cifs_migratePage
* helper (under an CONFIG_MIGRATION) in the future, and also
* investigate and add an is_dirty_writeback helper if needed
*/
.swap_activate = cifs_swap_activate,
.swap_deactivate = cifs_swap_deactivate,
}; };
/* /*
......
...@@ -2026,6 +2026,10 @@ cifs_revalidate_mapping(struct inode *inode) ...@@ -2026,6 +2026,10 @@ cifs_revalidate_mapping(struct inode *inode)
int rc; int rc;
unsigned long *flags = &CIFS_I(inode)->flags; unsigned long *flags = &CIFS_I(inode)->flags;
/* swapfiles are not supposed to be shared */
if (IS_SWAPFILE(inode))
return 0;
rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable,
TASK_KILLABLE); TASK_KILLABLE);
if (rc) if (rc)
......
...@@ -246,7 +246,7 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, ...@@ -246,7 +246,7 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
*/ */
fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT;
cifs_dbg(VFS, "XXX dev %d, reparse %d, mode %o", cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o",
le32_to_cpu(info->DeviceId), le32_to_cpu(info->DeviceId),
le32_to_cpu(info->ReparseTag), le32_to_cpu(info->ReparseTag),
le32_to_cpu(info->Mode)); le32_to_cpu(info->Mode));
......
...@@ -766,6 +766,20 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, ...@@ -766,6 +766,20 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if (tcon->tc_count <= 0) {
struct TCP_Server_Info *server = NULL;
WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative");
spin_unlock(&cifs_tcp_ses_lock);
if (tcon->ses)
server = tcon->ses->server;
cifs_server_dbg(FYI, "tid=%u: tcon is closing, skipping async close retry of fid %llu %llu\n",
tcon->tid, persistent_fid, volatile_fid);
return 0;
}
tcon->tc_count++; tcon->tc_count++;
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
......
...@@ -55,9 +55,11 @@ extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, ...@@ -55,9 +55,11 @@ extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server,
__u64 ses_id, __u32 tid); __u64 ses_id, __u32 tid);
extern int smb2_calc_signature(struct smb_rqst *rqst, extern int smb2_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server); struct TCP_Server_Info *server,
bool allocate_crypto);
extern int smb3_calc_signature(struct smb_rqst *rqst, extern int smb3_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server); struct TCP_Server_Info *server,
bool allocate_crypto);
extern void smb2_echo_request(struct work_struct *work); extern void smb2_echo_request(struct work_struct *work);
extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
extern bool smb2_is_valid_oplock_break(char *buffer, extern bool smb2_is_valid_oplock_break(char *buffer,
......
...@@ -40,14 +40,6 @@ ...@@ -40,14 +40,6 @@
#include "smb2status.h" #include "smb2status.h"
#include "smb2glob.h" #include "smb2glob.h"
static int
smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
{
return cifs_alloc_hash("hmac(sha256)",
&server->secmech.hmacsha256,
&server->secmech.sdeschmacsha256);
}
static int static int
smb3_crypto_shash_allocate(struct TCP_Server_Info *server) smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
{ {
...@@ -219,7 +211,8 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) ...@@ -219,7 +211,8 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
} }
int int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
bool allocate_crypto)
{ {
int rc; int rc;
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
...@@ -228,6 +221,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -228,6 +221,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
struct cifs_ses *ses; struct cifs_ses *ses;
struct shash_desc *shash; struct shash_desc *shash;
struct crypto_shash *hash;
struct sdesc *sdesc = NULL;
struct smb_rqst drqst; struct smb_rqst drqst;
ses = smb2_find_smb_ses(server, shdr->SessionId); ses = smb2_find_smb_ses(server, shdr->SessionId);
...@@ -239,24 +234,32 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -239,24 +234,32 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
rc = smb2_crypto_shash_allocate(server); if (allocate_crypto) {
if (rc) { rc = cifs_alloc_hash("hmac(sha256)", &hash, &sdesc);
cifs_server_dbg(VFS, "%s: sha256 alloc failed\n", __func__); if (rc) {
return rc; cifs_server_dbg(VFS,
"%s: sha256 alloc failed\n", __func__);
return rc;
}
shash = &sdesc->shash;
} else {
hash = server->secmech.hmacsha256;
shash = &server->secmech.sdeschmacsha256->shash;
} }
rc = crypto_shash_setkey(server->secmech.hmacsha256, rc = crypto_shash_setkey(hash, ses->auth_key.response,
ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) { if (rc) {
cifs_server_dbg(VFS, "%s: Could not update with response\n", __func__); cifs_server_dbg(VFS,
return rc; "%s: Could not update with response\n",
__func__);
goto out;
} }
shash = &server->secmech.sdeschmacsha256->shash;
rc = crypto_shash_init(shash); rc = crypto_shash_init(shash);
if (rc) { if (rc) {
cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); cifs_server_dbg(VFS, "%s: Could not init sha256", __func__);
return rc; goto out;
} }
/* /*
...@@ -271,9 +274,10 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -271,9 +274,10 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = crypto_shash_update(shash, iov[0].iov_base, rc = crypto_shash_update(shash, iov[0].iov_base,
iov[0].iov_len); iov[0].iov_len);
if (rc) { if (rc) {
cifs_server_dbg(VFS, "%s: Could not update with payload\n", cifs_server_dbg(VFS,
__func__); "%s: Could not update with payload\n",
return rc; __func__);
goto out;
} }
drqst.rq_iov++; drqst.rq_iov++;
drqst.rq_nvec--; drqst.rq_nvec--;
...@@ -283,6 +287,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -283,6 +287,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if (!rc) if (!rc)
memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
out:
if (allocate_crypto)
cifs_free_hash(&hash, &sdesc);
return rc; return rc;
} }
...@@ -504,14 +511,17 @@ generate_smb311signingkey(struct cifs_ses *ses) ...@@ -504,14 +511,17 @@ generate_smb311signingkey(struct cifs_ses *ses)
} }
int int
smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
bool allocate_crypto)
{ {
int rc; int rc;
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[0].iov_base; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
struct shash_desc *shash = &server->secmech.sdesccmacaes->shash; struct shash_desc *shash;
struct crypto_shash *hash;
struct sdesc *sdesc = NULL;
struct smb_rqst drqst; struct smb_rqst drqst;
u8 key[SMB3_SIGN_KEY_SIZE]; u8 key[SMB3_SIGN_KEY_SIZE];
...@@ -519,14 +529,24 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -519,14 +529,24 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if (rc) if (rc)
return 0; return 0;
if (allocate_crypto) {
rc = cifs_alloc_hash("cmac(aes)", &hash, &sdesc);
if (rc)
return rc;
shash = &sdesc->shash;
} else {
hash = server->secmech.cmacaes;
shash = &server->secmech.sdesccmacaes->shash;
}
memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
rc = crypto_shash_setkey(server->secmech.cmacaes, rc = crypto_shash_setkey(hash, key, SMB2_CMACAES_SIZE);
key, SMB2_CMACAES_SIZE);
if (rc) { if (rc) {
cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
return rc; goto out;
} }
/* /*
...@@ -537,7 +557,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -537,7 +557,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = crypto_shash_init(shash); rc = crypto_shash_init(shash);
if (rc) { if (rc) {
cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
return rc; goto out;
} }
/* /*
...@@ -554,7 +574,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -554,7 +574,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if (rc) { if (rc) {
cifs_server_dbg(VFS, "%s: Could not update with payload\n", cifs_server_dbg(VFS, "%s: Could not update with payload\n",
__func__); __func__);
return rc; goto out;
} }
drqst.rq_iov++; drqst.rq_iov++;
drqst.rq_nvec--; drqst.rq_nvec--;
...@@ -564,6 +584,9 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -564,6 +584,9 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if (!rc) if (!rc)
memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
out:
if (allocate_crypto)
cifs_free_hash(&hash, &sdesc);
return rc; return rc;
} }
...@@ -593,7 +616,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -593,7 +616,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return 0; return 0;
} }
rc = server->ops->calc_signature(rqst, server); rc = server->ops->calc_signature(rqst, server, false);
return rc; return rc;
} }
...@@ -631,9 +654,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -631,9 +654,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
mutex_lock(&server->srv_mutex); rc = server->ops->calc_signature(rqst, server, true);
rc = server->ops->calc_signature(rqst, server);
mutex_unlock(&server->srv_mutex);
if (rc) if (rc)
return rc; return rc;
......
...@@ -284,13 +284,10 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) ...@@ -284,13 +284,10 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
request->sge[i].length, request->sge[i].length,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (request->has_payload) { if (atomic_dec_and_test(&request->info->send_pending))
if (atomic_dec_and_test(&request->info->send_payload_pending)) wake_up(&request->info->wait_send_pending);
wake_up(&request->info->wait_send_payload_pending);
} else { wake_up(&request->info->wait_post_send);
if (atomic_dec_and_test(&request->info->send_pending))
wake_up(&request->info->wait_send_pending);
}
mempool_free(request, request->info->request_mempool); mempool_free(request, request->info->request_mempool);
} }
...@@ -383,27 +380,6 @@ static bool process_negotiation_response( ...@@ -383,27 +380,6 @@ static bool process_negotiation_response(
return true; return true;
} }
/*
* Check and schedule to send an immediate packet
* This is used to extend credtis to remote peer to keep the transport busy
*/
static void check_and_send_immediate(struct smbd_connection *info)
{
if (info->transport_status != SMBD_CONNECTED)
return;
info->send_immediate = true;
/*
* Promptly send a packet if our peer is running low on receive
* credits
*/
if (atomic_read(&info->receive_credits) <
info->receive_credit_target - 1)
queue_delayed_work(
info->workqueue, &info->send_immediate_work, 0);
}
static void smbd_post_send_credits(struct work_struct *work) static void smbd_post_send_credits(struct work_struct *work)
{ {
int ret = 0; int ret = 0;
...@@ -453,10 +429,16 @@ static void smbd_post_send_credits(struct work_struct *work) ...@@ -453,10 +429,16 @@ static void smbd_post_send_credits(struct work_struct *work)
info->new_credits_offered += ret; info->new_credits_offered += ret;
spin_unlock(&info->lock_new_credits_offered); spin_unlock(&info->lock_new_credits_offered);
atomic_add(ret, &info->receive_credits); /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
info->send_immediate = true;
/* Check if we can post new receive and grant credits to peer */ if (atomic_read(&info->receive_credits) <
check_and_send_immediate(info); info->receive_credit_target - 1) {
if (info->keep_alive_requested == KEEP_ALIVE_PENDING ||
info->send_immediate) {
log_keep_alive(INFO, "send an empty message\n");
smbd_post_send_empty(info);
}
}
} }
/* Called from softirq, when recv is done */ /* Called from softirq, when recv is done */
...@@ -551,12 +533,6 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) ...@@ -551,12 +533,6 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
info->keep_alive_requested = KEEP_ALIVE_PENDING; info->keep_alive_requested = KEEP_ALIVE_PENDING;
} }
/*
* Check if we need to send something to remote peer to
* grant more credits or respond to KEEP_ALIVE packet
*/
check_and_send_immediate(info);
return; return;
default: default:
...@@ -749,7 +725,6 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info) ...@@ -749,7 +725,6 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info)
request->sge[0].addr, request->sge[0].addr,
request->sge[0].length, request->sge[0].lkey); request->sge[0].length, request->sge[0].lkey);
request->has_payload = false;
atomic_inc(&info->send_pending); atomic_inc(&info->send_pending);
rc = ib_post_send(info->id->qp, &send_wr, NULL); rc = ib_post_send(info->id->qp, &send_wr, NULL);
if (!rc) if (!rc)
...@@ -806,45 +781,96 @@ static int manage_keep_alive_before_sending(struct smbd_connection *info) ...@@ -806,45 +781,96 @@ static int manage_keep_alive_before_sending(struct smbd_connection *info)
return 0; return 0;
} }
/* /* Post the send request */
* Build and prepare the SMBD packet header static int smbd_post_send(struct smbd_connection *info,
* This function waits for avaialbe send credits and build a SMBD packet struct smbd_request *request)
* header. The caller then optional append payload to the packet after {
* the header struct ib_send_wr send_wr;
* intput values int rc, i;
* size: the size of the payload
* remaining_data_length: remaining data to send if this is part of a for (i = 0; i < request->num_sge; i++) {
* fragmented packet log_rdma_send(INFO,
* output values "rdma_request sge[%d] addr=%llu length=%u\n",
* request_out: the request allocated from this function i, request->sge[i].addr, request->sge[i].length);
* return values: 0 on success, otherwise actual error code returned ib_dma_sync_single_for_device(
*/ info->id->device,
static int smbd_create_header(struct smbd_connection *info, request->sge[i].addr,
int size, int remaining_data_length, request->sge[i].length,
struct smbd_request **request_out) DMA_TO_DEVICE);
}
request->cqe.done = send_done;
send_wr.next = NULL;
send_wr.wr_cqe = &request->cqe;
send_wr.sg_list = request->sge;
send_wr.num_sge = request->num_sge;
send_wr.opcode = IB_WR_SEND;
send_wr.send_flags = IB_SEND_SIGNALED;
rc = ib_post_send(info->id->qp, &send_wr, NULL);
if (rc) {
log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
smbd_disconnect_rdma_connection(info);
rc = -EAGAIN;
} else
/* Reset timer for idle connection after packet is sent */
mod_delayed_work(info->workqueue, &info->idle_timer_work,
info->keep_alive_interval*HZ);
return rc;
}
static int smbd_post_send_sgl(struct smbd_connection *info,
struct scatterlist *sgl, int data_length, int remaining_data_length)
{ {
int num_sgs;
int i, rc;
int header_length;
struct smbd_request *request; struct smbd_request *request;
struct smbd_data_transfer *packet; struct smbd_data_transfer *packet;
int header_length; int new_credits;
int rc; struct scatterlist *sg;
wait_credit:
/* Wait for send credits. A SMBD packet needs one credit */ /* Wait for send credits. A SMBD packet needs one credit */
rc = wait_event_interruptible(info->wait_send_queue, rc = wait_event_interruptible(info->wait_send_queue,
atomic_read(&info->send_credits) > 0 || atomic_read(&info->send_credits) > 0 ||
info->transport_status != SMBD_CONNECTED); info->transport_status != SMBD_CONNECTED);
if (rc) if (rc)
return rc; goto err_wait_credit;
if (info->transport_status != SMBD_CONNECTED) {
log_outgoing(ERR, "disconnected not sending on wait_credit\n");
rc = -EAGAIN;
goto err_wait_credit;
}
if (unlikely(atomic_dec_return(&info->send_credits) < 0)) {
atomic_inc(&info->send_credits);
goto wait_credit;
}
wait_send_queue:
wait_event(info->wait_post_send,
atomic_read(&info->send_pending) < info->send_credit_target ||
info->transport_status != SMBD_CONNECTED);
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 on wait_send_queue\n");
return -EAGAIN; rc = -EAGAIN;
goto err_wait_send_queue;
}
if (unlikely(atomic_inc_return(&info->send_pending) >
info->send_credit_target)) {
atomic_dec(&info->send_pending);
goto wait_send_queue;
} }
atomic_dec(&info->send_credits);
request = mempool_alloc(info->request_mempool, GFP_KERNEL); request = mempool_alloc(info->request_mempool, GFP_KERNEL);
if (!request) { if (!request) {
rc = -ENOMEM; rc = -ENOMEM;
goto err; goto err_alloc;
} }
request->info = info; request->info = info;
...@@ -852,8 +878,11 @@ static int smbd_create_header(struct smbd_connection *info, ...@@ -852,8 +878,11 @@ static int smbd_create_header(struct smbd_connection *info,
/* Fill in the packet header */ /* Fill in the packet header */
packet = smbd_request_payload(request); packet = smbd_request_payload(request);
packet->credits_requested = cpu_to_le16(info->send_credit_target); packet->credits_requested = cpu_to_le16(info->send_credit_target);
packet->credits_granted =
cpu_to_le16(manage_credits_prior_sending(info)); new_credits = manage_credits_prior_sending(info);
atomic_add(new_credits, &info->receive_credits);
packet->credits_granted = cpu_to_le16(new_credits);
info->send_immediate = false; info->send_immediate = false;
packet->flags = 0; packet->flags = 0;
...@@ -861,11 +890,11 @@ static int smbd_create_header(struct smbd_connection *info, ...@@ -861,11 +890,11 @@ static int smbd_create_header(struct smbd_connection *info,
packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED); packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED);
packet->reserved = 0; packet->reserved = 0;
if (!size) if (!data_length)
packet->data_offset = 0; packet->data_offset = 0;
else else
packet->data_offset = cpu_to_le32(24); packet->data_offset = cpu_to_le32(24);
packet->data_length = cpu_to_le32(size); packet->data_length = cpu_to_le32(data_length);
packet->remaining_data_length = cpu_to_le32(remaining_data_length); packet->remaining_data_length = cpu_to_le32(remaining_data_length);
packet->padding = 0; packet->padding = 0;
...@@ -880,7 +909,7 @@ static int smbd_create_header(struct smbd_connection *info, ...@@ -880,7 +909,7 @@ static int smbd_create_header(struct smbd_connection *info,
/* Map the packet to DMA */ /* Map the packet to DMA */
header_length = sizeof(struct smbd_data_transfer); header_length = sizeof(struct smbd_data_transfer);
/* If this is a packet without payload, don't send padding */ /* If this is a packet without payload, don't send padding */
if (!size) if (!data_length)
header_length = offsetof(struct smbd_data_transfer, padding); header_length = offsetof(struct smbd_data_transfer, padding);
request->num_sge = 1; request->num_sge = 1;
...@@ -889,102 +918,15 @@ static int smbd_create_header(struct smbd_connection *info, ...@@ -889,102 +918,15 @@ static int smbd_create_header(struct smbd_connection *info,
header_length, header_length,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) {
mempool_free(request, info->request_mempool);
rc = -EIO; rc = -EIO;
goto err; request->sge[0].addr = 0;
goto err_dma;
} }
request->sge[0].length = header_length; request->sge[0].length = header_length;
request->sge[0].lkey = info->pd->local_dma_lkey; request->sge[0].lkey = info->pd->local_dma_lkey;
*request_out = request; /* Fill in the packet data payload */
return 0;
err:
atomic_inc(&info->send_credits);
return rc;
}
static void smbd_destroy_header(struct smbd_connection *info,
struct smbd_request *request)
{
ib_dma_unmap_single(info->id->device,
request->sge[0].addr,
request->sge[0].length,
DMA_TO_DEVICE);
mempool_free(request, info->request_mempool);
atomic_inc(&info->send_credits);
}
/* Post the send request */
static int smbd_post_send(struct smbd_connection *info,
struct smbd_request *request, bool has_payload)
{
struct ib_send_wr send_wr;
int rc, i;
for (i = 0; i < request->num_sge; i++) {
log_rdma_send(INFO,
"rdma_request sge[%d] addr=%llu length=%u\n",
i, request->sge[i].addr, request->sge[i].length);
ib_dma_sync_single_for_device(
info->id->device,
request->sge[i].addr,
request->sge[i].length,
DMA_TO_DEVICE);
}
request->cqe.done = send_done;
send_wr.next = NULL;
send_wr.wr_cqe = &request->cqe;
send_wr.sg_list = request->sge;
send_wr.num_sge = request->num_sge;
send_wr.opcode = IB_WR_SEND;
send_wr.send_flags = IB_SEND_SIGNALED;
if (has_payload) {
request->has_payload = true;
atomic_inc(&info->send_payload_pending);
} else {
request->has_payload = false;
atomic_inc(&info->send_pending);
}
rc = ib_post_send(info->id->qp, &send_wr, NULL);
if (rc) {
log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
if (has_payload) {
if (atomic_dec_and_test(&info->send_payload_pending))
wake_up(&info->wait_send_payload_pending);
} else {
if (atomic_dec_and_test(&info->send_pending))
wake_up(&info->wait_send_pending);
}
smbd_disconnect_rdma_connection(info);
rc = -EAGAIN;
} else
/* Reset timer for idle connection after packet is sent */
mod_delayed_work(info->workqueue, &info->idle_timer_work,
info->keep_alive_interval*HZ);
return rc;
}
static int smbd_post_send_sgl(struct smbd_connection *info,
struct scatterlist *sgl, int data_length, int remaining_data_length)
{
int num_sgs;
int i, rc;
struct smbd_request *request;
struct scatterlist *sg;
rc = smbd_create_header(
info, data_length, remaining_data_length, &request);
if (rc)
return rc;
num_sgs = sgl ? sg_nents(sgl) : 0; num_sgs = sgl ? sg_nents(sgl) : 0;
for_each_sg(sgl, sg, num_sgs, i) { for_each_sg(sgl, sg, num_sgs, i) {
request->sge[i+1].addr = request->sge[i+1].addr =
...@@ -994,25 +936,41 @@ static int smbd_post_send_sgl(struct smbd_connection *info, ...@@ -994,25 +936,41 @@ static int smbd_post_send_sgl(struct smbd_connection *info,
info->id->device, request->sge[i+1].addr)) { info->id->device, request->sge[i+1].addr)) {
rc = -EIO; rc = -EIO;
request->sge[i+1].addr = 0; request->sge[i+1].addr = 0;
goto dma_mapping_failure; goto err_dma;
} }
request->sge[i+1].length = sg->length; request->sge[i+1].length = sg->length;
request->sge[i+1].lkey = info->pd->local_dma_lkey; request->sge[i+1].lkey = info->pd->local_dma_lkey;
request->num_sge++; request->num_sge++;
} }
rc = smbd_post_send(info, request, data_length); rc = smbd_post_send(info, request);
if (!rc) if (!rc)
return 0; return 0;
dma_mapping_failure: err_dma:
for (i = 1; i < request->num_sge; i++) for (i = 0; i < request->num_sge; i++)
if (request->sge[i].addr) if (request->sge[i].addr)
ib_dma_unmap_single(info->id->device, ib_dma_unmap_single(info->id->device,
request->sge[i].addr, request->sge[i].addr,
request->sge[i].length, request->sge[i].length,
DMA_TO_DEVICE); DMA_TO_DEVICE);
smbd_destroy_header(info, request); mempool_free(request, info->request_mempool);
/* roll back receive credits and credits to be offered */
spin_lock(&info->lock_new_credits_offered);
info->new_credits_offered += new_credits;
spin_unlock(&info->lock_new_credits_offered);
atomic_sub(new_credits, &info->receive_credits);
err_alloc:
if (atomic_dec_and_test(&info->send_pending))
wake_up(&info->wait_send_pending);
err_wait_send_queue:
/* roll back send credits and pending */
atomic_inc(&info->send_credits);
err_wait_credit:
return rc; return rc;
} }
...@@ -1334,25 +1292,6 @@ static void destroy_receive_buffers(struct smbd_connection *info) ...@@ -1334,25 +1292,6 @@ static void destroy_receive_buffers(struct smbd_connection *info)
mempool_free(response, info->response_mempool); mempool_free(response, info->response_mempool);
} }
/*
* Check and send an immediate or keep alive packet
* The condition to send those packets are defined in [MS-SMBD] 3.1.1.1
* Connection.KeepaliveRequested and Connection.SendImmediate
* The idea is to extend credits to server as soon as it becomes available
*/
static void send_immediate_work(struct work_struct *work)
{
struct smbd_connection *info = container_of(
work, struct smbd_connection,
send_immediate_work.work);
if (info->keep_alive_requested == KEEP_ALIVE_PENDING ||
info->send_immediate) {
log_keep_alive(INFO, "send an empty message\n");
smbd_post_send_empty(info);
}
}
/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */ /* Implement idle connection timer [MS-SMBD] 3.1.6.2 */
static void idle_connection_timer(struct work_struct *work) static void idle_connection_timer(struct work_struct *work)
{ {
...@@ -1407,14 +1346,10 @@ void smbd_destroy(struct TCP_Server_Info *server) ...@@ -1407,14 +1346,10 @@ void smbd_destroy(struct TCP_Server_Info *server)
log_rdma_event(INFO, "cancelling idle timer\n"); log_rdma_event(INFO, "cancelling idle timer\n");
cancel_delayed_work_sync(&info->idle_timer_work); 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 posted to IB to finish\n"); log_rdma_event(INFO, "wait for all send posted to IB to finish\n");
wait_event(info->wait_send_pending, wait_event(info->wait_send_pending,
atomic_read(&info->send_pending) == 0); 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 */ /* It's not posssible for upper layer to get to reassembly */
log_rdma_event(INFO, "drain the reassembly queue\n"); log_rdma_event(INFO, "drain the reassembly queue\n");
...@@ -1744,15 +1679,13 @@ static struct smbd_connection *_smbd_get_connection( ...@@ -1744,15 +1679,13 @@ static struct smbd_connection *_smbd_get_connection(
init_waitqueue_head(&info->wait_send_queue); init_waitqueue_head(&info->wait_send_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);
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_send_pending); init_waitqueue_head(&info->wait_send_pending);
atomic_set(&info->send_pending, 0); atomic_set(&info->send_pending, 0);
init_waitqueue_head(&info->wait_send_payload_pending); init_waitqueue_head(&info->wait_post_send);
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->post_send_credits_work, smbd_post_send_credits); INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits);
...@@ -2226,8 +2159,8 @@ int smbd_send(struct TCP_Server_Info *server, ...@@ -2226,8 +2159,8 @@ int smbd_send(struct TCP_Server_Info *server,
* that means all the I/Os have been out and we are good to return * that means all the I/Os have been out and we are good to return
*/ */
wait_event(info->wait_send_payload_pending, wait_event(info->wait_send_pending,
atomic_read(&info->send_payload_pending) == 0); atomic_read(&info->send_pending) == 0);
return rc; return rc;
} }
......
...@@ -114,8 +114,7 @@ struct smbd_connection { ...@@ -114,8 +114,7 @@ struct smbd_connection {
/* Activity accoutning */ /* Activity accoutning */
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; wait_queue_head_t wait_post_send;
wait_queue_head_t wait_send_payload_pending;
/* Receive queue */ /* Receive queue */
struct list_head receive_queue; struct list_head receive_queue;
...@@ -154,7 +153,6 @@ struct smbd_connection { ...@@ -154,7 +153,6 @@ struct smbd_connection {
struct workqueue_struct *workqueue; struct workqueue_struct *workqueue;
struct delayed_work idle_timer_work; struct delayed_work idle_timer_work;
struct delayed_work send_immediate_work;
/* Memory pool for preallocating buffers */ /* Memory pool for preallocating buffers */
/* request pool for RDMA send */ /* request pool for RDMA send */
...@@ -234,9 +232,6 @@ struct smbd_request { ...@@ -234,9 +232,6 @@ struct smbd_request {
struct smbd_connection *info; struct smbd_connection *info;
struct ib_cqe cqe; struct ib_cqe cqe;
/* true if this request carries upper layer payload */
bool has_payload;
/* the SGE entries for this packet */ /* the SGE entries for this packet */
struct ib_sge sge[SMBDIRECT_MAX_SGE]; struct ib_sge sge[SMBDIRECT_MAX_SGE];
int num_sge; int num_sge;
......
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