Merge tag '5.7-rc-smb3-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 12 Apr 2020 16:41:01 +0000 (09:41 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 12 Apr 2020 16:41:01 +0000 (09:41 -0700)
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

12 files changed:
fs/cifs/Kconfig
fs/cifs/cifs_debug.c
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/readdir.c
fs/cifs/smb2misc.c
fs/cifs/smb2proto.h
fs/cifs/smb2transport.c
fs/cifs/smbdirect.c
fs/cifs/smbdirect.h

index 22cf04f..604f65f 100644 (file)
@@ -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"
index 276e4b5..916567d 100644 (file)
@@ -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,
index 94e3ed4..c31f362 100644 (file)
@@ -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);
index 0d95636..05dd3de 100644 (file)
@@ -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 */
index 5920820..0b1528e 100644 (file)
@@ -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,
 };
 
 /*
index 8d01ec2..8fbbdcd 100644 (file)
@@ -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)
index 19e4a5d..50f776a 100644 (file)
@@ -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));
index 0511aaf..497afb0 100644 (file)
@@ -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);
 
index 4d1ff7b..087d5f1 100644 (file)
@@ -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,
index 20cc79e..1a6c227 100644 (file)
 #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)
 {
        struct cifs_secmech *p = &server->secmech;
@@ -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;
index 8da43a5..1a5834a 100644 (file)
@@ -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,45 +781,96 @@ 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)
+/* Post the send request */
+static int smbd_post_send(struct smbd_connection *info,
+               struct smbd_request *request)
+{
+       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;
+
+       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_data_transfer *packet;
-       int header_length;
-       int rc;
+       int new_credits;
+       struct scatterlist *sg;
 
+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\n");
-               return -EAGAIN;
+               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;
        }
-       atomic_dec(&info->send_credits);
 
        request = mempool_alloc(info->request_mempool, GFP_KERNEL);
        if (!request) {
                rc = -ENOMEM;
-               goto err;
+               goto err_alloc;
        }
 
        request->info = info;
@@ -852,8 +878,11 @@ static int smbd_create_header(struct smbd_connection *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));
+
+       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;
@@ -861,11 +890,11 @@ static int smbd_create_header(struct smbd_connection *info,
                packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED);
 
        packet->reserved = 0;
-       if (!size)
+       if (!data_length)
                packet->data_offset = 0;
        else
                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->padding = 0;
 
@@ -880,7 +909,7 @@ static int smbd_create_header(struct smbd_connection *info,
        /* 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)
+       if (!data_length)
                header_length = offsetof(struct smbd_data_transfer, padding);
 
        request->num_sge = 1;
@@ -889,102 +918,15 @@ static int smbd_create_header(struct smbd_connection *info,
                                                 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].addr = 0;
+               goto err_dma;
        }
 
        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 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;
-
+       /* 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;
 }
index 8ede915..a87fca8 100644 (file)
@@ -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;