mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
10 smb fixes most RDMA (smbdirect) related, also add experimental support for swap over SMB3 mounts, and also adds fix which improves performance of signed connections
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAl6SeJgACgkQiiy9cAdy T1GqsQwAiZ/fNCrCFNHT3il6KfYVk4/meU1AYovjXjQ0//xIvn3FqG/sJCOEtWDL bH1D9xsvVz7sDT9l3GNeMtR17O3Lkc4VDVy1LtNEu4a2oA1MxksPLuJVI9S+Wcu6 Du7Fb4hzbTrGRj7C+6GL9mx1uOldoaGsbUV21D9dcQeAA0kNodrm6FAUMG/55wZO qB7H/ODaT2CF6s8H9o4GFV5vcasZSBJwaJeFsgoY/BOeQzMQgNnzyvleJTRZnSHk BAnqiocK2jg0Q/EjJs402/UKTEjZRy4q3mS/+Rpa2BRax0rPqdfRIXAJx2SAShNU pvinNge5PdWANZ1fti7hcL+e6n5TyinnkS9AOdzuS38yrpFVfJnrRD1igfUAuuME L64aODnLiAonnAlGcEk341ESt/FMxWnnXIxEPgEGtlyFiyoH13BI763IQYc4HLXQ 8Xo6GkyirDmnflVpFsfmuydhacLjwIxYLnHCAsDspDPEAjRhiKAXTV9d+VpkFgw+ hBNYP/dm =tstz -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
4119bf9f1d
@ -202,7 +202,7 @@ config CIFS_SMB_DIRECT
|
||||
help
|
||||
Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1.
|
||||
SMB Direct allows transferring SMB packets over RDMA. If unsure,
|
||||
say N.
|
||||
say Y.
|
||||
|
||||
config CIFS_FSCACHE
|
||||
bool "Provide CIFS client caching support"
|
||||
|
@ -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->receive_credits),
|
||||
server->smbd_conn->receive_credit_target);
|
||||
seq_printf(m, "\nPending send_pending: %x "
|
||||
"send_payload_pending: %x",
|
||||
atomic_read(&server->smbd_conn->send_pending),
|
||||
atomic_read(&server->smbd_conn->send_payload_pending));
|
||||
seq_printf(m, "\nPending send_pending: %x ",
|
||||
atomic_read(&server->smbd_conn->send_pending));
|
||||
seq_printf(m, "\nReceive buffers count_receive_queue: %x "
|
||||
"count_empty_packet_queue: %x",
|
||||
server->smbd_conn->count_receive_queue,
|
||||
|
@ -1208,6 +1208,10 @@ static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
|
||||
{
|
||||
unsigned int xid = get_xid();
|
||||
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,
|
||||
len, flags);
|
||||
|
@ -426,7 +426,8 @@ struct smb_version_operations {
|
||||
/* generate new lease key */
|
||||
void (*new_lease_key)(struct cifs_fid *);
|
||||
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,
|
||||
struct cifsFileInfo *src_file);
|
||||
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
@ -1312,6 +1313,7 @@ struct cifsFileInfo {
|
||||
struct tcon_link *tlink;
|
||||
unsigned int f_flags;
|
||||
bool invalidHandle:1; /* file closed via session abend */
|
||||
bool swapfile:1;
|
||||
bool oplock_break_cancelled:1;
|
||||
unsigned int oplock_epoch; /* epoch 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)
|
||||
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 = {
|
||||
.readpage = cifs_readpage,
|
||||
@ -4821,6 +4875,13 @@ const struct address_space_operations cifs_addr_ops = {
|
||||
.direct_IO = cifs_direct_io,
|
||||
.invalidatepage = cifs_invalidate_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)
|
||||
int rc;
|
||||
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,
|
||||
TASK_KILLABLE);
|
||||
if (rc)
|
||||
|
@ -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;
|
||||
|
||||
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->ReparseTag),
|
||||
le32_to_cpu(info->Mode));
|
||||
|
@ -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);
|
||||
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++;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
|
@ -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,
|
||||
__u64 ses_id, __u32 tid);
|
||||
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,
|
||||
struct TCP_Server_Info *server);
|
||||
struct TCP_Server_Info *server,
|
||||
bool allocate_crypto);
|
||||
extern void smb2_echo_request(struct work_struct *work);
|
||||
extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
|
||||
extern bool smb2_is_valid_oplock_break(char *buffer,
|
||||
|
@ -40,14 +40,6 @@
|
||||
#include "smb2status.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
|
||||
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)
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
|
||||
@ -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 cifs_ses *ses;
|
||||
struct shash_desc *shash;
|
||||
struct crypto_shash *hash;
|
||||
struct sdesc *sdesc = NULL;
|
||||
struct smb_rqst drqst;
|
||||
|
||||
ses = smb2_find_smb_ses(server, shdr->SessionId);
|
||||
@ -239,24 +234,32 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
|
||||
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
rc = smb2_crypto_shash_allocate(server);
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: sha256 alloc failed\n", __func__);
|
||||
return rc;
|
||||
if (allocate_crypto) {
|
||||
rc = cifs_alloc_hash("hmac(sha256)", &hash, &sdesc);
|
||||
if (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,
|
||||
ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
|
||||
rc = crypto_shash_setkey(hash, ses->auth_key.response,
|
||||
SMB2_NTLMV2_SESSKEY_SIZE);
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: Could not update with response\n", __func__);
|
||||
return rc;
|
||||
cifs_server_dbg(VFS,
|
||||
"%s: Could not update with response\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
shash = &server->secmech.sdeschmacsha256->shash;
|
||||
rc = crypto_shash_init(shash);
|
||||
if (rc) {
|
||||
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)
|
||||
rc = crypto_shash_update(shash, iov[0].iov_base,
|
||||
iov[0].iov_len);
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: Could not update with payload\n",
|
||||
__func__);
|
||||
return rc;
|
||||
cifs_server_dbg(VFS,
|
||||
"%s: Could not update with payload\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
drqst.rq_iov++;
|
||||
drqst.rq_nvec--;
|
||||
@ -283,6 +287,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
if (!rc)
|
||||
memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
out:
|
||||
if (allocate_crypto)
|
||||
cifs_free_hash(&hash, &sdesc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -504,14 +511,17 @@ generate_smb311signingkey(struct cifs_ses *ses)
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned char smb3_signature[SMB2_CMACAES_SIZE];
|
||||
unsigned char *sigptr = smb3_signature;
|
||||
struct kvec *iov = rqst->rq_iov;
|
||||
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;
|
||||
u8 key[SMB3_SIGN_KEY_SIZE];
|
||||
|
||||
@ -519,14 +529,24 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
if (rc)
|
||||
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(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
rc = crypto_shash_setkey(server->secmech.cmacaes,
|
||||
key, SMB2_CMACAES_SIZE);
|
||||
rc = crypto_shash_setkey(hash, key, SMB2_CMACAES_SIZE);
|
||||
if (rc) {
|
||||
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)
|
||||
rc = crypto_shash_init(shash);
|
||||
if (rc) {
|
||||
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)
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: Could not update with payload\n",
|
||||
__func__);
|
||||
return rc;
|
||||
goto out;
|
||||
}
|
||||
drqst.rq_iov++;
|
||||
drqst.rq_nvec--;
|
||||
@ -564,6 +584,9 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
if (!rc)
|
||||
memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
out:
|
||||
if (allocate_crypto)
|
||||
cifs_free_hash(&hash, &sdesc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -593,7 +616,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = server->ops->calc_signature(rqst, server);
|
||||
rc = server->ops->calc_signature(rqst, server, false);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -631,9 +654,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
|
||||
memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
rc = server->ops->calc_signature(rqst, server);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
rc = server->ops->calc_signature(rqst, server, true);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -284,13 +284,10 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
request->sge[i].length,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (request->has_payload) {
|
||||
if (atomic_dec_and_test(&request->info->send_payload_pending))
|
||||
wake_up(&request->info->wait_send_payload_pending);
|
||||
} else {
|
||||
if (atomic_dec_and_test(&request->info->send_pending))
|
||||
wake_up(&request->info->wait_send_pending);
|
||||
}
|
||||
if (atomic_dec_and_test(&request->info->send_pending))
|
||||
wake_up(&request->info->wait_send_pending);
|
||||
|
||||
wake_up(&request->info->wait_post_send);
|
||||
|
||||
mempool_free(request, request->info->request_mempool);
|
||||
}
|
||||
@ -383,27 +380,6 @@ static bool process_negotiation_response(
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -453,10 +429,16 @@ static void smbd_post_send_credits(struct work_struct *work)
|
||||
info->new_credits_offered += ret;
|
||||
spin_unlock(&info->lock_new_credits_offered);
|
||||
|
||||
atomic_add(ret, &info->receive_credits);
|
||||
|
||||
/* Check if we can post new receive and grant credits to peer */
|
||||
check_and_send_immediate(info);
|
||||
/* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
|
||||
info->send_immediate = true;
|
||||
if (atomic_read(&info->receive_credits) <
|
||||
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 */
|
||||
@ -551,12 +533,6 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
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;
|
||||
|
||||
default:
|
||||
@ -749,7 +725,6 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info)
|
||||
request->sge[0].addr,
|
||||
request->sge[0].length, request->sge[0].lkey);
|
||||
|
||||
request->has_payload = false;
|
||||
atomic_inc(&info->send_pending);
|
||||
rc = ib_post_send(info->id->qp, &send_wr, NULL);
|
||||
if (!rc)
|
||||
@ -806,120 +781,9 @@ static int manage_keep_alive_before_sending(struct smbd_connection *info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build and prepare the SMBD packet header
|
||||
* This function waits for avaialbe send credits and build a SMBD packet
|
||||
* header. The caller then optional append payload to the packet after
|
||||
* the header
|
||||
* intput values
|
||||
* size: the size of the payload
|
||||
* remaining_data_length: remaining data to send if this is part of a
|
||||
* fragmented packet
|
||||
* output values
|
||||
* request_out: the request allocated from this function
|
||||
* return values: 0 on success, otherwise actual error code returned
|
||||
*/
|
||||
static int smbd_create_header(struct smbd_connection *info,
|
||||
int size, int remaining_data_length,
|
||||
struct smbd_request **request_out)
|
||||
{
|
||||
struct smbd_request *request;
|
||||
struct smbd_data_transfer *packet;
|
||||
int header_length;
|
||||
int rc;
|
||||
|
||||
/* Wait for send credits. A SMBD packet needs one credit */
|
||||
rc = wait_event_interruptible(info->wait_send_queue,
|
||||
atomic_read(&info->send_credits) > 0 ||
|
||||
info->transport_status != SMBD_CONNECTED);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (info->transport_status != SMBD_CONNECTED) {
|
||||
log_outgoing(ERR, "disconnected not sending\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
atomic_dec(&info->send_credits);
|
||||
|
||||
request = mempool_alloc(info->request_mempool, GFP_KERNEL);
|
||||
if (!request) {
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
request->info = info;
|
||||
|
||||
/* Fill in the packet header */
|
||||
packet = smbd_request_payload(request);
|
||||
packet->credits_requested = cpu_to_le16(info->send_credit_target);
|
||||
packet->credits_granted =
|
||||
cpu_to_le16(manage_credits_prior_sending(info));
|
||||
info->send_immediate = false;
|
||||
|
||||
packet->flags = 0;
|
||||
if (manage_keep_alive_before_sending(info))
|
||||
packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED);
|
||||
|
||||
packet->reserved = 0;
|
||||
if (!size)
|
||||
packet->data_offset = 0;
|
||||
else
|
||||
packet->data_offset = cpu_to_le32(24);
|
||||
packet->data_length = cpu_to_le32(size);
|
||||
packet->remaining_data_length = cpu_to_le32(remaining_data_length);
|
||||
packet->padding = 0;
|
||||
|
||||
log_outgoing(INFO, "credits_requested=%d credits_granted=%d "
|
||||
"data_offset=%d data_length=%d remaining_data_length=%d\n",
|
||||
le16_to_cpu(packet->credits_requested),
|
||||
le16_to_cpu(packet->credits_granted),
|
||||
le32_to_cpu(packet->data_offset),
|
||||
le32_to_cpu(packet->data_length),
|
||||
le32_to_cpu(packet->remaining_data_length));
|
||||
|
||||
/* Map the packet to DMA */
|
||||
header_length = sizeof(struct smbd_data_transfer);
|
||||
/* If this is a packet without payload, don't send padding */
|
||||
if (!size)
|
||||
header_length = offsetof(struct smbd_data_transfer, padding);
|
||||
|
||||
request->num_sge = 1;
|
||||
request->sge[0].addr = ib_dma_map_single(info->id->device,
|
||||
(void *)packet,
|
||||
header_length,
|
||||
DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) {
|
||||
mempool_free(request, info->request_mempool);
|
||||
rc = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
request->sge[0].length = header_length;
|
||||
request->sge[0].lkey = info->pd->local_dma_lkey;
|
||||
|
||||
*request_out = request;
|
||||
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 smbd_request *request)
|
||||
{
|
||||
struct ib_send_wr send_wr;
|
||||
int rc, i;
|
||||
@ -944,24 +808,9 @@ static int smbd_post_send(struct smbd_connection *info,
|
||||
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
|
||||
@ -977,14 +826,107 @@ static int smbd_post_send_sgl(struct smbd_connection *info,
|
||||
{
|
||||
int num_sgs;
|
||||
int i, rc;
|
||||
int header_length;
|
||||
struct smbd_request *request;
|
||||
struct smbd_data_transfer *packet;
|
||||
int new_credits;
|
||||
struct scatterlist *sg;
|
||||
|
||||
rc = smbd_create_header(
|
||||
info, data_length, remaining_data_length, &request);
|
||||
wait_credit:
|
||||
/* Wait for send credits. A SMBD packet needs one credit */
|
||||
rc = wait_event_interruptible(info->wait_send_queue,
|
||||
atomic_read(&info->send_credits) > 0 ||
|
||||
info->transport_status != SMBD_CONNECTED);
|
||||
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) {
|
||||
log_outgoing(ERR, "disconnected not sending on wait_send_queue\n");
|
||||
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;
|
||||
}
|
||||
|
||||
request = mempool_alloc(info->request_mempool, GFP_KERNEL);
|
||||
if (!request) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
request->info = info;
|
||||
|
||||
/* Fill in the packet header */
|
||||
packet = smbd_request_payload(request);
|
||||
packet->credits_requested = cpu_to_le16(info->send_credit_target);
|
||||
|
||||
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;
|
||||
|
||||
packet->flags = 0;
|
||||
if (manage_keep_alive_before_sending(info))
|
||||
packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED);
|
||||
|
||||
packet->reserved = 0;
|
||||
if (!data_length)
|
||||
packet->data_offset = 0;
|
||||
else
|
||||
packet->data_offset = cpu_to_le32(24);
|
||||
packet->data_length = cpu_to_le32(data_length);
|
||||
packet->remaining_data_length = cpu_to_le32(remaining_data_length);
|
||||
packet->padding = 0;
|
||||
|
||||
log_outgoing(INFO, "credits_requested=%d credits_granted=%d "
|
||||
"data_offset=%d data_length=%d remaining_data_length=%d\n",
|
||||
le16_to_cpu(packet->credits_requested),
|
||||
le16_to_cpu(packet->credits_granted),
|
||||
le32_to_cpu(packet->data_offset),
|
||||
le32_to_cpu(packet->data_length),
|
||||
le32_to_cpu(packet->remaining_data_length));
|
||||
|
||||
/* Map the packet to DMA */
|
||||
header_length = sizeof(struct smbd_data_transfer);
|
||||
/* If this is a packet without payload, don't send padding */
|
||||
if (!data_length)
|
||||
header_length = offsetof(struct smbd_data_transfer, padding);
|
||||
|
||||
request->num_sge = 1;
|
||||
request->sge[0].addr = ib_dma_map_single(info->id->device,
|
||||
(void *)packet,
|
||||
header_length,
|
||||
DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) {
|
||||
rc = -EIO;
|
||||
request->sge[0].addr = 0;
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
request->sge[0].length = header_length;
|
||||
request->sge[0].lkey = info->pd->local_dma_lkey;
|
||||
|
||||
/* Fill in the packet data payload */
|
||||
num_sgs = sgl ? sg_nents(sgl) : 0;
|
||||
for_each_sg(sgl, sg, num_sgs, i) {
|
||||
request->sge[i+1].addr =
|
||||
@ -994,25 +936,41 @@ static int smbd_post_send_sgl(struct smbd_connection *info,
|
||||
info->id->device, request->sge[i+1].addr)) {
|
||||
rc = -EIO;
|
||||
request->sge[i+1].addr = 0;
|
||||
goto dma_mapping_failure;
|
||||
goto err_dma;
|
||||
}
|
||||
request->sge[i+1].length = sg->length;
|
||||
request->sge[i+1].lkey = info->pd->local_dma_lkey;
|
||||
request->num_sge++;
|
||||
}
|
||||
|
||||
rc = smbd_post_send(info, request, data_length);
|
||||
rc = smbd_post_send(info, request);
|
||||
if (!rc)
|
||||
return 0;
|
||||
|
||||
dma_mapping_failure:
|
||||
for (i = 1; i < request->num_sge; i++)
|
||||
err_dma:
|
||||
for (i = 0; i < request->num_sge; i++)
|
||||
if (request->sge[i].addr)
|
||||
ib_dma_unmap_single(info->id->device,
|
||||
request->sge[i].addr,
|
||||
request->sge[i].length,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1334,25 +1292,6 @@ static void destroy_receive_buffers(struct smbd_connection *info)
|
||||
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 */
|
||||
static void idle_connection_timer(struct work_struct *work)
|
||||
{
|
||||
@ -1407,14 +1346,10 @@ void smbd_destroy(struct TCP_Server_Info *server)
|
||||
|
||||
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 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");
|
||||
@ -1744,15 +1679,13 @@ static struct smbd_connection *_smbd_get_connection(
|
||||
|
||||
init_waitqueue_head(&info->wait_send_queue);
|
||||
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,
|
||||
info->keep_alive_interval*HZ);
|
||||
|
||||
init_waitqueue_head(&info->wait_send_pending);
|
||||
atomic_set(&info->send_pending, 0);
|
||||
|
||||
init_waitqueue_head(&info->wait_send_payload_pending);
|
||||
atomic_set(&info->send_payload_pending, 0);
|
||||
init_waitqueue_head(&info->wait_post_send);
|
||||
|
||||
INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work);
|
||||
INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits);
|
||||
@ -2226,8 +2159,8 @@ done:
|
||||
* that means all the I/Os have been out and we are good to return
|
||||
*/
|
||||
|
||||
wait_event(info->wait_send_payload_pending,
|
||||
atomic_read(&info->send_payload_pending) == 0);
|
||||
wait_event(info->wait_send_pending,
|
||||
atomic_read(&info->send_pending) == 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -114,8 +114,7 @@ struct smbd_connection {
|
||||
/* Activity accoutning */
|
||||
atomic_t send_pending;
|
||||
wait_queue_head_t wait_send_pending;
|
||||
atomic_t send_payload_pending;
|
||||
wait_queue_head_t wait_send_payload_pending;
|
||||
wait_queue_head_t wait_post_send;
|
||||
|
||||
/* Receive queue */
|
||||
struct list_head receive_queue;
|
||||
@ -154,7 +153,6 @@ struct smbd_connection {
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
struct delayed_work idle_timer_work;
|
||||
struct delayed_work send_immediate_work;
|
||||
|
||||
/* Memory pool for preallocating buffers */
|
||||
/* request pool for RDMA send */
|
||||
@ -234,9 +232,6 @@ struct smbd_request {
|
||||
struct smbd_connection *info;
|
||||
struct ib_cqe cqe;
|
||||
|
||||
/* true if this request carries upper layer payload */
|
||||
bool has_payload;
|
||||
|
||||
/* the SGE entries for this packet */
|
||||
struct ib_sge sge[SMBDIRECT_MAX_SGE];
|
||||
int num_sge;
|
||||
|
Loading…
Reference in New Issue
Block a user