commit:     38ba8f749d762758fdaa2fb95ac3a00c07ebcc40
Author:     Arisu Tachibana <alicef <AT> gentoo <DOT> org>
AuthorDate: Tue Feb 17 00:33:34 2026 +0000
Commit:     Arisu Tachibana <alicef <AT> gentoo <DOT> org>
CommitDate: Tue Feb 17 00:33:34 2026 +0000
URL:        https://gitweb.gentoo.org/proj/linux-patches.git/commit/?id=38ba8f74

Linux patch 6.18.11

Signed-off-by: Arisu Tachibana <alicef <AT> gentoo.org>

 0000_README              |    4 +
 1010_linux-6.18.11.patch | 2225 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2229 insertions(+)

diff --git a/0000_README b/0000_README
index 03bcc6a9..4acccc30 100644
--- a/0000_README
+++ b/0000_README
@@ -83,6 +83,10 @@ Patch:  1009_linux-6.18.10.patch
 From:   https://www.kernel.org
 Desc:   Linux 6.18.10
 
+Patch:  1010_linux-6.18.11.patch
+From:   https://www.kernel.org
+Desc:   Linux 6.18.11
+
 Patch:  1510_fs-enable-link-security-restrictions-by-default.patch
 From:   
http://sources.debian.net/src/linux/3.16.7-ckt4-3/debian/patches/debian/fs-enable-link-security-restrictions-by-default.patch/
 Desc:   Enable link security restrictions by default.

diff --git a/1010_linux-6.18.11.patch b/1010_linux-6.18.11.patch
new file mode 100644
index 00000000..85b883f9
--- /dev/null
+++ b/1010_linux-6.18.11.patch
@@ -0,0 +1,2225 @@
+diff --git a/Makefile b/Makefile
+index 6d2269cbb0b25c..1d8d4b2c1da72b 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ VERSION = 6
+ PATCHLEVEL = 18
+-SUBLEVEL = 10
++SUBLEVEL = 11
+ EXTRAVERSION =
+ NAME = Baby Opossum Posse
+ 
+diff --git a/drivers/base/base.h b/drivers/base/base.h
+index 86fa7fbb354891..30459906987e00 100644
+--- a/drivers/base/base.h
++++ b/drivers/base/base.h
+@@ -166,9 +166,18 @@ void device_set_deferred_probe_reason(const struct device 
*dev, struct va_format
+ static inline int driver_match_device(const struct device_driver *drv,
+                                     struct device *dev)
+ {
++      device_lock_assert(dev);
++
+       return drv->bus->match ? drv->bus->match(dev, drv) : 1;
+ }
+ 
++static inline int driver_match_device_locked(const struct device_driver *drv,
++                                           struct device *dev)
++{
++      guard(device)(dev);
++      return driver_match_device(drv, dev);
++}
++
+ static inline void dev_sync_state(struct device *dev)
+ {
+       if (dev->bus->sync_state)
+diff --git a/drivers/base/bus.c b/drivers/base/bus.c
+index 5e75e1bce5516d..999d371bbf3501 100644
+--- a/drivers/base/bus.c
++++ b/drivers/base/bus.c
+@@ -263,7 +263,7 @@ static ssize_t bind_store(struct device_driver *drv, const 
char *buf,
+       int err = -ENODEV;
+ 
+       dev = bus_find_device_by_name(bus, NULL, buf);
+-      if (dev && driver_match_device(drv, dev)) {
++      if (dev && driver_match_device_locked(drv, dev)) {
+               err = device_driver_attach(drv, dev);
+               if (!err) {
+                       /* success */
+diff --git a/drivers/base/dd.c b/drivers/base/dd.c
+index 13ab98e033eaa1..b6b9132e1f94f0 100644
+--- a/drivers/base/dd.c
++++ b/drivers/base/dd.c
+@@ -1170,7 +1170,7 @@ static int __driver_attach(struct device *dev, void 
*data)
+        * is an error.
+        */
+ 
+-      ret = driver_match_device(drv, dev);
++      ret = driver_match_device_locked(drv, dev);
+       if (ret == 0) {
+               /* no match */
+               return 0;
+diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
+index 3420f711f0f089..a953fa9af85c64 100644
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -527,6 +527,8 @@ static const struct usb_device_id quirks_table[] = {
+                                                    BTUSB_WIDEBAND_SPEECH },
+       { USB_DEVICE(0x2001, 0x332a), .driver_info = BTUSB_REALTEK |
+                                                    BTUSB_WIDEBAND_SPEECH },
++      { USB_DEVICE(0x7392, 0xe611), .driver_info = BTUSB_REALTEK |
++                                                   BTUSB_WIDEBAND_SPEECH },
+ 
+       /* Realtek 8852AE Bluetooth devices */
+       { USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK |
+diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
+index 25845c04e5620a..a97baf2cbcdd51 100644
+--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
++++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
+@@ -202,8 +202,12 @@ static ssize_t driver_override_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+ {
+       struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
++      ssize_t len;
+ 
+-      return sysfs_emit(buf, "%s\n", mc_dev->driver_override);
++      device_lock(dev);
++      len = sysfs_emit(buf, "%s\n", mc_dev->driver_override);
++      device_unlock(dev);
++      return len;
+ }
+ static DEVICE_ATTR_RW(driver_override);
+ 
+diff --git a/drivers/crypto/intel/iaa/iaa_crypto_main.c 
b/drivers/crypto/intel/iaa/iaa_crypto_main.c
+index d0058757b0000d..da9b2bc515194f 100644
+--- a/drivers/crypto/intel/iaa/iaa_crypto_main.c
++++ b/drivers/crypto/intel/iaa/iaa_crypto_main.c
+@@ -221,15 +221,13 @@ static struct iaa_compression_mode 
*iaa_compression_modes[IAA_COMP_MODES_MAX];
+ 
+ static int find_empty_iaa_compression_mode(void)
+ {
+-      int i = -EINVAL;
++      int i;
+ 
+-      for (i = 0; i < IAA_COMP_MODES_MAX; i++) {
+-              if (iaa_compression_modes[i])
+-                      continue;
+-              break;
+-      }
++      for (i = 0; i < IAA_COMP_MODES_MAX; i++)
++              if (!iaa_compression_modes[i])
++                      return i;
+ 
+-      return i;
++      return -EINVAL;
+ }
+ 
+ static struct iaa_compression_mode *find_iaa_compression_mode(const char 
*name, int *idx)
+diff --git a/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c 
b/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c
+index 9f5601c0280bf1..417a48f4135053 100644
+--- a/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c
++++ b/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c
+@@ -1326,7 +1326,7 @@ static ssize_t ucode_load_store(struct device *dev,
+       int del_grp_idx = -1;
+       int ucode_idx = 0;
+ 
+-      if (strlen(buf) > OTX_CPT_UCODE_NAME_LENGTH)
++      if (count >= OTX_CPT_UCODE_NAME_LENGTH)
+               return -EINVAL;
+ 
+       eng_grps = container_of(attr, struct otx_cpt_eng_grps, ucode_load_attr);
+diff --git a/drivers/crypto/omap-crypto.c b/drivers/crypto/omap-crypto.c
+index a4cc6bf146ec09..0345c9383d5097 100644
+--- a/drivers/crypto/omap-crypto.c
++++ b/drivers/crypto/omap-crypto.c
+@@ -21,7 +21,7 @@ static int omap_crypto_copy_sg_lists(int total, int bs,
+       struct scatterlist *tmp;
+ 
+       if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) {
+-              new_sg = kmalloc_array(n, sizeof(*sg), GFP_KERNEL);
++              new_sg = kmalloc_array(n, sizeof(*new_sg), GFP_KERNEL);
+               if (!new_sg)
+                       return -ENOMEM;
+ 
+diff --git a/drivers/crypto/virtio/virtio_crypto_core.c 
b/drivers/crypto/virtio/virtio_crypto_core.c
+index 3d241446099cc9..ccc6b5c1b24b37 100644
+--- a/drivers/crypto/virtio/virtio_crypto_core.c
++++ b/drivers/crypto/virtio/virtio_crypto_core.c
+@@ -75,15 +75,20 @@ static void virtcrypto_done_task(unsigned long data)
+       struct data_queue *data_vq = (struct data_queue *)data;
+       struct virtqueue *vq = data_vq->vq;
+       struct virtio_crypto_request *vc_req;
++      unsigned long flags;
+       unsigned int len;
+ 
++      spin_lock_irqsave(&data_vq->lock, flags);
+       do {
+               virtqueue_disable_cb(vq);
+               while ((vc_req = virtqueue_get_buf(vq, &len)) != NULL) {
++                      spin_unlock_irqrestore(&data_vq->lock, flags);
+                       if (vc_req->alg_cb)
+                               vc_req->alg_cb(vc_req, len);
++                      spin_lock_irqsave(&data_vq->lock, flags);
+               }
+       } while (!virtqueue_enable_cb(vq));
++      spin_unlock_irqrestore(&data_vq->lock, flags);
+ }
+ 
+ static void virtcrypto_dataq_callback(struct virtqueue *vq)
+diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
+index 1b3fb21a2a7de2..11053d1786d4d2 100644
+--- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
++++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
+@@ -541,8 +541,6 @@ int virtio_crypto_skcipher_crypt_req(
+       if (ret < 0)
+               return ret;
+ 
+-      virtqueue_kick(data_vq->vq);
+-
+       return 0;
+ }
+ 
+diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
+index a268c76bdca62d..8693544304fa8f 100644
+--- a/drivers/gpio/gpio-omap.c
++++ b/drivers/gpio/gpio-omap.c
+@@ -799,10 +799,13 @@ static struct platform_device omap_mpuio_device = {
+ 
+ static inline void omap_mpuio_init(struct gpio_bank *bank)
+ {
+-      platform_set_drvdata(&omap_mpuio_device, bank);
++      static bool registered;
+ 
+-      if (platform_driver_register(&omap_mpuio_driver) == 0)
+-              (void) platform_device_register(&omap_mpuio_device);
++      platform_set_drvdata(&omap_mpuio_device, bank);
++      if (!registered) {
++              (void)platform_device_register(&omap_mpuio_device);
++              registered = true;
++      }
+ }
+ 
+ /*---------------------------------------------------------------------*/
+@@ -1576,13 +1579,24 @@ static struct platform_driver omap_gpio_driver = {
+  */
+ static int __init omap_gpio_drv_reg(void)
+ {
+-      return platform_driver_register(&omap_gpio_driver);
++      int ret;
++
++      ret = platform_driver_register(&omap_mpuio_driver);
++      if (ret)
++              return ret;
++
++      ret = platform_driver_register(&omap_gpio_driver);
++      if (ret)
++              platform_driver_unregister(&omap_mpuio_driver);
++
++      return ret;
+ }
+ postcore_initcall(omap_gpio_drv_reg);
+ 
+ static void __exit omap_gpio_exit(void)
+ {
+       platform_driver_unregister(&omap_gpio_driver);
++      platform_driver_unregister(&omap_mpuio_driver);
+ }
+ module_exit(omap_gpio_exit);
+ 
+diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c 
b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+index e094b8bbc0f124..b80410a3e4aadb 100644
+--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
++++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+@@ -186,29 +186,37 @@ static ssize_t vidi_store_connection(struct device *dev,
+                               const char *buf, size_t len)
+ {
+       struct vidi_context *ctx = dev_get_drvdata(dev);
+-      int ret;
++      int ret, new_connected;
+ 
+-      ret = kstrtoint(buf, 0, &ctx->connected);
++      ret = kstrtoint(buf, 0, &new_connected);
+       if (ret)
+               return ret;
+-
+-      if (ctx->connected > 1)
++      if (new_connected > 1)
+               return -EINVAL;
+ 
++      mutex_lock(&ctx->lock);
++
+       /*
+        * Use fake edid data for test. If raw_edid is set then it can't be
+        * tested.
+        */
+       if (ctx->raw_edid) {
+               DRM_DEV_DEBUG_KMS(dev, "edid data is not fake data.\n");
+-              return -EINVAL;
++              ret = -EINVAL;
++              goto fail;
+       }
+ 
++      ctx->connected = new_connected;
++      mutex_unlock(&ctx->lock);
++
+       DRM_DEV_DEBUG_KMS(dev, "requested connection.\n");
+ 
+       drm_helper_hpd_irq_event(ctx->drm_dev);
+ 
+       return len;
++fail:
++      mutex_unlock(&ctx->lock);
++      return ret;
+ }
+ 
+ static DEVICE_ATTR(connection, 0644, vidi_show_connection,
+@@ -238,11 +246,14 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, 
void *data,
+               return -EINVAL;
+       }
+ 
++      mutex_lock(&ctx->lock);
+       if (ctx->connected == vidi->connection) {
++              mutex_unlock(&ctx->lock);
+               DRM_DEV_DEBUG_KMS(ctx->dev,
+                                 "same connection request.\n");
+               return -EINVAL;
+       }
++      mutex_unlock(&ctx->lock);
+ 
+       if (vidi->connection) {
+               const struct drm_edid *drm_edid;
+@@ -262,14 +273,21 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, 
void *data,
+                                         "edid data is invalid.\n");
+                       return -EINVAL;
+               }
++              mutex_lock(&ctx->lock);
+               ctx->raw_edid = drm_edid;
++              mutex_unlock(&ctx->lock);
+       } else {
+               /* with connection = 0, free raw_edid */
++              mutex_lock(&ctx->lock);
+               drm_edid_free(ctx->raw_edid);
+               ctx->raw_edid = NULL;
++              mutex_unlock(&ctx->lock);
+       }
+ 
++      mutex_lock(&ctx->lock);
+       ctx->connected = vidi->connection;
++      mutex_unlock(&ctx->lock);
++
+       drm_helper_hpd_irq_event(ctx->drm_dev);
+ 
+       return 0;
+@@ -284,7 +302,7 @@ static enum drm_connector_status vidi_detect(struct 
drm_connector *connector,
+        * connection request would come from user side
+        * to do hotplug through specific ioctl.
+        */
+-      return ctx->connected ? connector_status_connected :
++      return READ_ONCE(ctx->connected) ? connector_status_connected :
+                       connector_status_disconnected;
+ }
+ 
+@@ -307,11 +325,15 @@ static int vidi_get_modes(struct drm_connector 
*connector)
+       const struct drm_edid *drm_edid;
+       int count;
+ 
++      mutex_lock(&ctx->lock);
++
+       if (ctx->raw_edid)
+               drm_edid = drm_edid_dup(ctx->raw_edid);
+       else
+               drm_edid = drm_edid_alloc(fake_edid_info, 
sizeof(fake_edid_info));
+ 
++      mutex_unlock(&ctx->lock);
++
+       drm_edid_connector_update(connector, drm_edid);
+ 
+       count = drm_edid_connector_add_modes(connector);
+@@ -456,9 +478,13 @@ static void vidi_remove(struct platform_device *pdev)
+ {
+       struct vidi_context *ctx = platform_get_drvdata(pdev);
+ 
++      mutex_lock(&ctx->lock);
++
+       drm_edid_free(ctx->raw_edid);
+       ctx->raw_edid = NULL;
+ 
++      mutex_unlock(&ctx->lock);
++
+       component_del(&pdev->dev, &vidi_component_ops);
+ }
+ 
+diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c 
b/drivers/net/wireless/realtek/rtl8xxxu/core.c
+index 3e87c571e24195..a17c1084931b0e 100644
+--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
+@@ -7927,6 +7927,7 @@ static int rtl8xxxu_probe(struct usb_interface 
*interface,
+               goto err_set_intfdata;
+ 
+       hw->vif_data_size = sizeof(struct rtl8xxxu_vif);
++      hw->sta_data_size = sizeof(struct rtl8xxxu_sta_info);
+ 
+       hw->wiphy->max_scan_ssids = 1;
+       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+diff --git a/drivers/net/wireless/realtek/rtw88/main.c 
b/drivers/net/wireless/realtek/rtw88/main.c
+index fa0ed39cb1992a..d93d21656f26c8 100644
+--- a/drivers/net/wireless/realtek/rtw88/main.c
++++ b/drivers/net/wireless/realtek/rtw88/main.c
+@@ -2444,10 +2444,10 @@ void rtw_core_enable_beacon(struct rtw_dev *rtwdev, 
bool enable)
+ 
+       if (enable) {
+               rtw_write32_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
+-              rtw_write32_clr(rtwdev, REG_TXPAUSE, BIT_HIGH_QUEUE);
++              rtw_write8_clr(rtwdev, REG_TXPAUSE, BIT_HIGH_QUEUE);
+       } else {
+               rtw_write32_clr(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
+-              rtw_write32_set(rtwdev, REG_TXPAUSE, BIT_HIGH_QUEUE);
++              rtw_write8_set(rtwdev, REG_TXPAUSE, BIT_HIGH_QUEUE);
+       }
+ }
+ 
+diff --git a/drivers/pci/endpoint/pci-ep-cfs.c 
b/drivers/pci/endpoint/pci-ep-cfs.c
+index ef50c82e647f4d..43feb6139fa36c 100644
+--- a/drivers/pci/endpoint/pci-ep-cfs.c
++++ b/drivers/pci/endpoint/pci-ep-cfs.c
+@@ -23,7 +23,6 @@ struct pci_epf_group {
+       struct config_group group;
+       struct config_group primary_epc_group;
+       struct config_group secondary_epc_group;
+-      struct delayed_work cfs_work;
+       struct pci_epf *epf;
+       int index;
+ };
+@@ -103,7 +102,7 @@ static struct config_group
+       secondary_epc_group = &epf_group->secondary_epc_group;
+       config_group_init_type_name(secondary_epc_group, "secondary",
+                                   &pci_secondary_epc_type);
+-      configfs_register_group(&epf_group->group, secondary_epc_group);
++      configfs_add_default_group(secondary_epc_group, &epf_group->group);
+ 
+       return secondary_epc_group;
+ }
+@@ -166,7 +165,7 @@ static struct config_group
+ 
+       config_group_init_type_name(primary_epc_group, "primary",
+                                   &pci_primary_epc_type);
+-      configfs_register_group(&epf_group->group, primary_epc_group);
++      configfs_add_default_group(primary_epc_group, &epf_group->group);
+ 
+       return primary_epc_group;
+ }
+@@ -570,15 +569,13 @@ static void pci_ep_cfs_add_type_group(struct 
pci_epf_group *epf_group)
+               return;
+       }
+ 
+-      configfs_register_group(&epf_group->group, group);
++      configfs_add_default_group(group, &epf_group->group);
+ }
+ 
+-static void pci_epf_cfs_work(struct work_struct *work)
++static void pci_epf_cfs_add_sub_groups(struct pci_epf_group *epf_group)
+ {
+-      struct pci_epf_group *epf_group;
+       struct config_group *group;
+ 
+-      epf_group = container_of(work, struct pci_epf_group, cfs_work.work);
+       group = pci_ep_cfs_add_primary_group(epf_group);
+       if (IS_ERR(group)) {
+               pr_err("failed to create 'primary' EPC interface\n");
+@@ -637,9 +634,7 @@ static struct config_group *pci_epf_make(struct 
config_group *group,
+ 
+       kfree(epf_name);
+ 
+-      INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work);
+-      queue_delayed_work(system_wq, &epf_group->cfs_work,
+-                         msecs_to_jiffies(1));
++      pci_epf_cfs_add_sub_groups(epf_group);
+ 
+       return &epf_group->group;
+ 
+diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
+index 51c7cea71f9022..880cd73feaca45 100644
+--- a/drivers/scsi/qla2xxx/qla_gs.c
++++ b/drivers/scsi/qla2xxx/qla_gs.c
+@@ -3266,9 +3266,6 @@ login_logout:
+                           atomic_read(&fcport->state) == FCS_ONLINE) ||
+                               do_delete) {
+                               if (fcport->loop_id != FC_NO_LOOP_ID) {
+-                                      if (fcport->flags & FCF_FCP2_DEVICE)
+-                                              continue;
+-
+                                       ql_log(ql_log_warn, vha, 0x20f0,
+                                              "%s %d %8phC post del sess\n",
+                                              __func__, __LINE__,
+@@ -3535,8 +3532,8 @@ int qla_fab_async_scan(scsi_qla_host_t *vha, srb_t *sp)
+       if (vha->scan.scan_flags & SF_SCANNING) {
+               spin_unlock_irqrestore(&vha->work_lock, flags);
+               ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0x2012,
+-                  "%s: scan active\n", __func__);
+-              return rval;
++                  "%s: scan active for sp:%p\n", __func__, sp);
++              goto done_free_sp;
+       }
+       vha->scan.scan_flags |= SF_SCANNING;
+       if (!sp)
+@@ -3701,23 +3698,25 @@ int qla_fab_async_scan(scsi_qla_host_t *vha, srb_t *sp)
+       return rval;
+ 
+ done_free_sp:
+-      if (sp->u.iocb_cmd.u.ctarg.req) {
+-              dma_free_coherent(&vha->hw->pdev->dev,
+-                  sp->u.iocb_cmd.u.ctarg.req_allocated_size,
+-                  sp->u.iocb_cmd.u.ctarg.req,
+-                  sp->u.iocb_cmd.u.ctarg.req_dma);
+-              sp->u.iocb_cmd.u.ctarg.req = NULL;
+-      }
+-      if (sp->u.iocb_cmd.u.ctarg.rsp) {
+-              dma_free_coherent(&vha->hw->pdev->dev,
+-                  sp->u.iocb_cmd.u.ctarg.rsp_allocated_size,
+-                  sp->u.iocb_cmd.u.ctarg.rsp,
+-                  sp->u.iocb_cmd.u.ctarg.rsp_dma);
+-              sp->u.iocb_cmd.u.ctarg.rsp = NULL;
+-      }
++      if (sp) {
++              if (sp->u.iocb_cmd.u.ctarg.req) {
++                      dma_free_coherent(&vha->hw->pdev->dev,
++                          sp->u.iocb_cmd.u.ctarg.req_allocated_size,
++                          sp->u.iocb_cmd.u.ctarg.req,
++                          sp->u.iocb_cmd.u.ctarg.req_dma);
++                      sp->u.iocb_cmd.u.ctarg.req = NULL;
++              }
++              if (sp->u.iocb_cmd.u.ctarg.rsp) {
++                      dma_free_coherent(&vha->hw->pdev->dev,
++                          sp->u.iocb_cmd.u.ctarg.rsp_allocated_size,
++                          sp->u.iocb_cmd.u.ctarg.rsp,
++                          sp->u.iocb_cmd.u.ctarg.rsp_dma);
++                      sp->u.iocb_cmd.u.ctarg.rsp = NULL;
++              }
+ 
+-      /* ref: INIT */
+-      kref_put(&sp->cmd_kref, qla2x00_sp_release);
++              /* ref: INIT */
++              kref_put(&sp->cmd_kref, qla2x00_sp_release);
++      }
+ 
+       spin_lock_irqsave(&vha->work_lock, flags);
+       vha->scan.scan_flags &= ~SF_SCANNING;
+diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
+index 6a2e1c7fd1251a..84f89445c74705 100644
+--- a/drivers/scsi/qla2xxx/qla_init.c
++++ b/drivers/scsi/qla2xxx/qla_init.c
+@@ -1859,15 +1859,6 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct 
event_arg *ea)
+       case RSCN_PORT_ADDR:
+               fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1);
+               if (fcport) {
+-                      if (ql2xfc2target &&
+-                          fcport->flags & FCF_FCP2_DEVICE &&
+-                          atomic_read(&fcport->state) == FCS_ONLINE) {
+-                              ql_dbg(ql_dbg_disc, vha, 0x2115,
+-                                     "Delaying session delete for FCP2 
portid=%06x %8phC ",
+-                                      fcport->d_id.b24, fcport->port_name);
+-                              return;
+-                      }
+-
+                       if (vha->hw->flags.edif_enabled && DBELL_ACTIVE(vha)) {
+                               /*
+                                * On ipsec start by remote port, Target port
+@@ -2471,8 +2462,23 @@ qla24xx_handle_plogi_done_event(struct scsi_qla_host 
*vha, struct event_arg *ea)
+           ea->sp->gen1, fcport->rscn_gen,
+           ea->data[0], ea->data[1], ea->iop[0], ea->iop[1]);
+ 
+-      if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) ||
+-          (fcport->fw_login_state == DSC_LS_PRLI_PEND)) {
++      if (fcport->fw_login_state == DSC_LS_PLOGI_PEND) {
++              ql_dbg(ql_dbg_disc, vha, 0x20ea,
++                  "%s %d %8phC Remote is trying to login\n",
++                  __func__, __LINE__, fcport->port_name);
++              /*
++               * If we get here, there is port thats already logged in,
++               * but it's state has not moved ahead. Recheck with FW on
++               * what state it is in and proceed ahead
++               */
++              if (!N2N_TOPO(vha->hw)) {
++                      fcport->fw_login_state = DSC_LS_PRLI_COMP;
++                      qla24xx_post_gpdb_work(vha, fcport, 0);
++              }
++              return;
++      }
++
++      if (fcport->fw_login_state == DSC_LS_PRLI_PEND) {
+               ql_dbg(ql_dbg_disc, vha, 0x20ea,
+                   "%s %d %8phC Remote is trying to login\n",
+                   __func__, __LINE__, fcport->port_name);
+diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
+index a04a5aa0d00572..608d2f36e7b4ff 100644
+--- a/drivers/scsi/qla2xxx/qla_isr.c
++++ b/drivers/scsi/qla2xxx/qla_isr.c
+@@ -1676,13 +1676,28 @@ skip_rio:
+ 
+                       /* Port logout */
+                       fcport = qla2x00_find_fcport_by_loopid(vha, mb[1]);
+-                      if (!fcport)
++                      if (!fcport) {
++                              ql_dbg(ql_dbg_async, vha, 0x5011,
++                                      "Could not find fcport:%04x %04x 
%04x\n",
++                                      mb[1], mb[2], mb[3]);
+                               break;
+-                      if (atomic_read(&fcport->state) != FCS_ONLINE)
++                      }
++
++                      if (atomic_read(&fcport->state) != FCS_ONLINE) {
++                              ql_dbg(ql_dbg_async, vha, 0x5012,
++                                      "Port state is not online State:0x%x 
\n",
++                                      atomic_read(&fcport->state));
++                              ql_dbg(ql_dbg_async, vha, 0x5012,
++                                      "Scheduling session for deletion \n");
++                              fcport->logout_on_delete = 0;
++                              qlt_schedule_sess_for_deletion(fcport);
+                               break;
++                      }
++
+                       ql_dbg(ql_dbg_async, vha, 0x508a,
+                           "Marking port lost loopid=%04x portid=%06x.\n",
+                           fcport->loop_id, fcport->d_id.b24);
++
+                       if (qla_ini_mode_enabled(vha)) {
+                               fcport->logout_on_delete = 0;
+                               qlt_schedule_sess_for_deletion(fcport);
+diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
+index 8ad0c19bdf4a9b..59ca4adcb43154 100644
+--- a/drivers/scsi/qla2xxx/qla_os.c
++++ b/drivers/scsi/qla2xxx/qla_os.c
+@@ -1182,7 +1182,8 @@ qla2x00_wait_for_hba_ready(scsi_qla_host_t *vha)
+       while ((qla2x00_reset_active(vha) || ha->dpc_active ||
+               ha->flags.mbox_busy) ||
+              test_bit(FX00_RESET_RECOVERY, &vha->dpc_flags) ||
+-             test_bit(FX00_TARGET_SCAN, &vha->dpc_flags)) {
++             test_bit(FX00_TARGET_SCAN, &vha->dpc_flags) ||
++             (vha->scan.scan_flags & SF_SCANNING)) {
+               if (test_bit(UNLOADING, &base_vha->dpc_flags))
+                       break;
+               msleep(1000);
+diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
+index b7b3432a9882e0..a47c6bab98ff99 100644
+--- a/fs/erofs/fileio.c
++++ b/fs/erofs/fileio.c
+@@ -10,6 +10,7 @@ struct erofs_fileio_rq {
+       struct bio bio;
+       struct kiocb iocb;
+       struct super_block *sb;
++      refcount_t ref;
+ };
+ 
+ struct erofs_fileio {
+@@ -42,7 +43,8 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, 
long ret)
+               }
+       }
+       bio_uninit(&rq->bio);
+-      kfree(rq);
++      if (refcount_dec_and_test(&rq->ref))
++              kfree(rq);
+ }
+ 
+ static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)
+@@ -66,6 +68,8 @@ static void erofs_fileio_rq_submit(struct erofs_fileio_rq 
*rq)
+       revert_creds(old_cred);
+       if (ret != -EIOCBQUEUED)
+               erofs_fileio_ki_complete(&rq->iocb, ret);
++      if (refcount_dec_and_test(&rq->ref))
++              kfree(rq);
+ }
+ 
+ static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct erofs_map_dev 
*mdev)
+@@ -76,6 +80,7 @@ static struct erofs_fileio_rq *erofs_fileio_rq_alloc(struct 
erofs_map_dev *mdev)
+       bio_init(&rq->bio, NULL, rq->bvecs, ARRAY_SIZE(rq->bvecs), REQ_OP_READ);
+       rq->iocb.ki_filp = mdev->m_dif->file;
+       rq->sb = mdev->m_sb;
++      refcount_set(&rq->ref, 2);
+       return rq;
+ }
+ 
+diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
+index 53f3fae6021797..f28cd24dee8425 100644
+--- a/fs/hfs/mdb.c
++++ b/fs/hfs/mdb.c
+@@ -92,7 +92,7 @@ int hfs_mdb_get(struct super_block *sb)
+               /* See if this is an HFS filesystem */
+               bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
+               if (!bh)
+-                      goto out;
++                      return -EIO;
+ 
+               if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC))
+                       break;
+@@ -102,13 +102,14 @@ int hfs_mdb_get(struct super_block *sb)
+                * (should do this only for cdrom/loop though)
+                */
+               if (hfs_part_find(sb, &part_start, &part_size))
+-                      goto out;
++                      return -EIO;
+       }
+ 
+       HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz);
+       if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
+               pr_err("bad allocation block size %d\n", size);
+-              goto out_bh;
++              brelse(bh);
++              return -EIO;
+       }
+ 
+       size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE);
+@@ -125,14 +126,16 @@ int hfs_mdb_get(struct super_block *sb)
+       brelse(bh);
+       if (!sb_set_blocksize(sb, size)) {
+               pr_err("unable to set blocksize to %u\n", size);
+-              goto out;
++              return -EIO;
+       }
+ 
+       bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
+       if (!bh)
+-              goto out;
+-      if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC))
+-              goto out_bh;
++              return -EIO;
++      if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) {
++              brelse(bh);
++              return -EIO;
++      }
+ 
+       HFS_SB(sb)->mdb_bh = bh;
+       HFS_SB(sb)->mdb = mdb;
+@@ -174,7 +177,7 @@ int hfs_mdb_get(struct super_block *sb)
+ 
+       HFS_SB(sb)->bitmap = kzalloc(8192, GFP_KERNEL);
+       if (!HFS_SB(sb)->bitmap)
+-              goto out;
++              return -EIO;
+ 
+       /* read in the bitmap */
+       block = be16_to_cpu(mdb->drVBMSt) + part_start;
+@@ -185,7 +188,7 @@ int hfs_mdb_get(struct super_block *sb)
+               bh = sb_bread(sb, off >> sb->s_blocksize_bits);
+               if (!bh) {
+                       pr_err("unable to read volume bitmap\n");
+-                      goto out;
++                      return -EIO;
+               }
+               off2 = off & (sb->s_blocksize - 1);
+               len = min((int)sb->s_blocksize - off2, size);
+@@ -199,12 +202,12 @@ int hfs_mdb_get(struct super_block *sb)
+       HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp);
+       if (!HFS_SB(sb)->ext_tree) {
+               pr_err("unable to open extent tree\n");
+-              goto out;
++              return -EIO;
+       }
+       HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
+       if (!HFS_SB(sb)->cat_tree) {
+               pr_err("unable to open catalog tree\n");
+-              goto out;
++              return -EIO;
+       }
+ 
+       attrib = mdb->drAtrb;
+@@ -229,12 +232,6 @@ int hfs_mdb_get(struct super_block *sb)
+       }
+ 
+       return 0;
+-
+-out_bh:
+-      brelse(bh);
+-out:
+-      hfs_mdb_put(sb);
+-      return -EIO;
+ }
+ 
+ /*
+@@ -359,8 +356,6 @@ void hfs_mdb_close(struct super_block *sb)
+  * Release the resources associated with the in-core MDB.  */
+ void hfs_mdb_put(struct super_block *sb)
+ {
+-      if (!HFS_SB(sb))
+-              return;
+       /* free the B-trees */
+       hfs_btree_close(HFS_SB(sb)->ext_tree);
+       hfs_btree_close(HFS_SB(sb)->cat_tree);
+@@ -373,6 +368,4 @@ void hfs_mdb_put(struct super_block *sb)
+       unload_nls(HFS_SB(sb)->nls_disk);
+ 
+       kfree(HFS_SB(sb)->bitmap);
+-      kfree(HFS_SB(sb));
+-      sb->s_fs_info = NULL;
+ }
+diff --git a/fs/hfs/super.c b/fs/hfs/super.c
+index 47f50fa555a457..df289cbdd4e85b 100644
+--- a/fs/hfs/super.c
++++ b/fs/hfs/super.c
+@@ -431,10 +431,18 @@ static int hfs_init_fs_context(struct fs_context *fc)
+       return 0;
+ }
+ 
++static void hfs_kill_super(struct super_block *sb)
++{
++      struct hfs_sb_info *hsb = HFS_SB(sb);
++
++      kill_block_super(sb);
++      kfree(hsb);
++}
++
+ static struct file_system_type hfs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "hfs",
+-      .kill_sb        = kill_block_super,
++      .kill_sb        = hfs_kill_super,
+       .fs_flags       = FS_REQUIRES_DEV,
+       .init_fs_context = hfs_init_fs_context,
+ };
+diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c
+index 330f269abedf5d..71ee217eb072cf 100644
+--- a/fs/nilfs2/sufile.c
++++ b/fs/nilfs2/sufile.c
+@@ -1093,6 +1093,9 @@ int nilfs_sufile_trim_fs(struct inode *sufile, struct 
fstrim_range *range)
+       else
+               end_block = start_block + len - 1;
+ 
++      if (end_block < nilfs->ns_first_data_block)
++              goto out;
++
+       segnum = nilfs_get_segnum_of_block(nilfs, start_block);
+       segnum_end = nilfs_get_segnum_of_block(nilfs, end_block);
+ 
+@@ -1191,6 +1194,7 @@ int nilfs_sufile_trim_fs(struct inode *sufile, struct 
fstrim_range *range)
+ out_sem:
+       up_read(&NILFS_MDT(sufile)->mi_sem);
+ 
++out:
+       range->len = ndiscarded << nilfs->ns_blocksize_bits;
+       return ret;
+ }
+diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
+index 1e383db7c33743..5091bf45345e81 100644
+--- a/fs/smb/client/cached_dir.h
++++ b/fs/smb/client/cached_dir.h
+@@ -36,10 +36,10 @@ struct cached_fid {
+       struct list_head entry;
+       struct cached_fids *cfids;
+       const char *path;
+-      bool has_lease:1;
+-      bool is_open:1;
+-      bool on_list:1;
+-      bool file_all_info_is_valid:1;
++      bool has_lease;
++      bool is_open;
++      bool on_list;
++      bool file_all_info_is_valid;
+       unsigned long time; /* jiffies of when lease was taken */
+       unsigned long last_access_time; /* jiffies of when last accessed */
+       struct kref refcount;
+diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c
+index 788a0670c4a8da..01d55bcc6d0f9c 100644
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -35,6 +35,10 @@ static void enqueue_reassembly(
+ static struct smbdirect_recv_io *_get_first_reassembly(
+               struct smbdirect_socket *sc);
+ 
++static int smbd_post_send(struct smbdirect_socket *sc,
++                        struct smbdirect_send_batch *batch,
++                        struct smbdirect_send_io *request);
++
+ static int smbd_post_recv(
+               struct smbdirect_socket *sc,
+               struct smbdirect_recv_io *response);
+@@ -493,27 +497,103 @@ static inline void *smbdirect_recv_io_payload(struct 
smbdirect_recv_io *response
+       return (void *)response->packet;
+ }
+ 
++static struct smbdirect_send_io *smbd_alloc_send_io(struct smbdirect_socket 
*sc)
++{
++      struct smbdirect_send_io *msg;
++
++      msg = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
++      if (!msg)
++              return ERR_PTR(-ENOMEM);
++      msg->socket = sc;
++      INIT_LIST_HEAD(&msg->sibling_list);
++      msg->num_sge = 0;
++
++      return msg;
++}
++
++static void smbd_free_send_io(struct smbdirect_send_io *msg)
++{
++      struct smbdirect_socket *sc = msg->socket;
++      size_t i;
++
++      /*
++       * The list needs to be empty!
++       * The caller should take care of it.
++       */
++      WARN_ON_ONCE(!list_empty(&msg->sibling_list));
++
++      /*
++       * Note we call ib_dma_unmap_page(), even if some sges are mapped using
++       * ib_dma_map_single().
++       *
++       * The difference between _single() and _page() only matters for the
++       * ib_dma_map_*() case.
++       *
++       * For the ib_dma_unmap_*() case it does not matter as both take the
++       * dma_addr_t and dma_unmap_single_attrs() is just an alias to
++       * dma_unmap_page_attrs().
++       */
++      for (i = 0; i < msg->num_sge; i++)
++              ib_dma_unmap_page(sc->ib.dev,
++                                msg->sge[i].addr,
++                                msg->sge[i].length,
++                                DMA_TO_DEVICE);
++
++      mempool_free(msg, sc->send_io.mem.pool);
++}
++
+ /* Called when a RDMA send is done */
+ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
+ {
+-      int i;
+       struct smbdirect_send_io *request =
+               container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
+       struct smbdirect_socket *sc = request->socket;
++      struct smbdirect_send_io *sibling, *next;
+       int lcredits = 0;
+ 
+       log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
+               request, ib_wc_status_msg(wc->status));
+ 
+-      for (i = 0; i < request->num_sge; i++)
+-              ib_dma_unmap_single(sc->ib.dev,
+-                      request->sge[i].addr,
+-                      request->sge[i].length,
+-                      DMA_TO_DEVICE);
+-      mempool_free(request, sc->send_io.mem.pool);
++      if (unlikely(!(request->wr.send_flags & IB_SEND_SIGNALED))) {
++              /*
++               * This happens when smbdirect_send_io is a sibling
++               * before the final message, it is signaled on
++               * error anyway, so we need to skip
++               * smbdirect_connection_free_send_io here,
++               * otherwise is will destroy the memory
++               * of the siblings too, which will cause
++               * use after free problems for the others
++               * triggered from ib_drain_qp().
++               */
++              if (wc->status != IB_WC_SUCCESS)
++                      goto skip_free;
++
++              /*
++               * This should not happen!
++               * But we better just close the
++               * connection...
++               */
++              log_rdma_send(ERR,
++                      "unexpected send completion wc->status=%s (%d) 
wc->opcode=%d\n",
++                      ib_wc_status_msg(wc->status), wc->status, wc->opcode);
++              smbd_disconnect_rdma_connection(sc);
++              return;
++      }
++
++      /*
++       * Free possible siblings and then the main send_io
++       */
++      list_for_each_entry_safe(sibling, next, &request->sibling_list, 
sibling_list) {
++              list_del_init(&sibling->sibling_list);
++              smbd_free_send_io(sibling);
++              lcredits += 1;
++      }
++      /* Note this frees wc->wr_cqe, but not wc */
++      smbd_free_send_io(request);
+       lcredits += 1;
+ 
+       if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
++skip_free:
+               if (wc->status != IB_WC_WR_FLUSH_ERR)
+                       log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n",
+                               ib_wc_status_msg(wc->status), wc->opcode);
+@@ -608,6 +688,7 @@ static bool process_negotiation_response(
+                       sp->max_frmr_depth * PAGE_SIZE);
+       sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
+ 
++      atomic_set(&sc->send_io.bcredits.count, 1);
+       sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
+       return true;
+ }
+@@ -618,6 +699,7 @@ static void smbd_post_send_credits(struct work_struct 
*work)
+       struct smbdirect_recv_io *response;
+       struct smbdirect_socket *sc =
+               container_of(work, struct smbdirect_socket, 
recv_io.posted.refill_work);
++      int posted = 0;
+ 
+       if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+               return;
+@@ -640,9 +722,21 @@ static void smbd_post_send_credits(struct work_struct 
*work)
+                       }
+ 
+                       atomic_inc(&sc->recv_io.posted.count);
++                      posted += 1;
+               }
+       }
+ 
++      atomic_add(posted, &sc->recv_io.credits.available);
++
++      /*
++       * If the last send credit is waiting for credits
++       * it can grant we need to wake it up
++       */
++      if (posted &&
++          atomic_read(&sc->send_io.bcredits.count) == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0)
++              wake_up(&sc->send_io.credits.wait_queue);
++
+       /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
+       if (atomic_read(&sc->recv_io.credits.count) <
+               sc->recv_io.credits.target - 1) {
+@@ -659,6 +753,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
+               container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
+       struct smbdirect_socket *sc = response->socket;
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
++      int current_recv_credits;
+       u16 old_recv_credit_target;
+       u32 data_offset = 0;
+       u32 data_length = 0;
+@@ -743,7 +838,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
+               }
+ 
+               atomic_dec(&sc->recv_io.posted.count);
+-              atomic_dec(&sc->recv_io.credits.count);
++              current_recv_credits = 
atomic_dec_return(&sc->recv_io.credits.count);
++
+               old_recv_credit_target = sc->recv_io.credits.target;
+               sc->recv_io.credits.target =
+                       le16_to_cpu(data_transfer->credits_requested);
+@@ -779,7 +875,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
+                * reassembly queue and wake up the reading thread
+                */
+               if (data_length) {
+-                      if (sc->recv_io.credits.target > old_recv_credit_target)
++                      if (current_recv_credits <= (sc->recv_io.credits.target 
/ 4) ||
++                          sc->recv_io.credits.target > old_recv_credit_target)
+                               queue_work(sc->workqueue, 
&sc->recv_io.posted.refill_work);
+ 
+                       enqueue_reassembly(sc, response, data_length);
+@@ -955,16 +1052,13 @@ out1:
+ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
+ {
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+-      struct ib_send_wr send_wr;
+-      int rc = -ENOMEM;
++      int rc;
+       struct smbdirect_send_io *request;
+       struct smbdirect_negotiate_req *packet;
+ 
+-      request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
+-      if (!request)
+-              return rc;
+-
+-      request->socket = sc;
++      request = smbd_alloc_send_io(sc);
++      if (IS_ERR(request))
++              return PTR_ERR(request);
+ 
+       packet = smbdirect_send_io_payload(request);
+       packet->min_version = cpu_to_le16(SMBDIRECT_V1);
+@@ -976,7 +1070,6 @@ static int smbd_post_send_negotiate_req(struct 
smbdirect_socket *sc)
+       packet->max_fragmented_size =
+               cpu_to_le32(sp->max_fragmented_recv_size);
+ 
+-      request->num_sge = 1;
+       request->sge[0].addr = ib_dma_map_single(
+                               sc->ib.dev, (void *)packet,
+                               sizeof(*packet), DMA_TO_DEVICE);
+@@ -984,42 +1077,20 @@ static int smbd_post_send_negotiate_req(struct 
smbdirect_socket *sc)
+               rc = -EIO;
+               goto dma_mapping_failed;
+       }
++      request->num_sge = 1;
+ 
+       request->sge[0].length = sizeof(*packet);
+       request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
+ 
+-      ib_dma_sync_single_for_device(
+-              sc->ib.dev, request->sge[0].addr,
+-              request->sge[0].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;
+-
+-      log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n",
+-              request->sge[0].addr,
+-              request->sge[0].length, request->sge[0].lkey);
+-
+-      atomic_inc(&sc->send_io.pending.count);
+-      rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
++      rc = smbd_post_send(sc, NULL, request);
+       if (!rc)
+               return 0;
+ 
+-      /* if we reach here, post send failed */
+-      log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+-      atomic_dec(&sc->send_io.pending.count);
+-      ib_dma_unmap_single(sc->ib.dev, request->sge[0].addr,
+-              request->sge[0].length, DMA_TO_DEVICE);
+-
+-      smbd_disconnect_rdma_connection(sc);
++      if (rc == -EAGAIN)
++              rc = -EIO;
+ 
+ dma_mapping_failed:
+-      mempool_free(request, sc->send_io.mem.pool);
++      smbd_free_send_io(request);
+       return rc;
+ }
+ 
+@@ -1033,19 +1104,38 @@ dma_mapping_failed:
+  */
+ static int manage_credits_prior_sending(struct smbdirect_socket *sc)
+ {
++      int missing;
++      int available;
+       int new_credits;
+ 
+       if (atomic_read(&sc->recv_io.credits.count) >= 
sc->recv_io.credits.target)
+               return 0;
+ 
+-      new_credits = atomic_read(&sc->recv_io.posted.count);
+-      if (new_credits == 0)
++      missing = (int)sc->recv_io.credits.target - 
atomic_read(&sc->recv_io.credits.count);
++      available = atomic_xchg(&sc->recv_io.credits.available, 0);
++      new_credits = (u16)min3(U16_MAX, missing, available);
++      if (new_credits <= 0) {
++              /*
++               * If credits are available, but not granted
++               * we need to re-add them again.
++               */
++              if (available)
++                      atomic_add(available, &sc->recv_io.credits.available);
+               return 0;
++      }
+ 
+-      new_credits -= atomic_read(&sc->recv_io.credits.count);
+-      if (new_credits <= 0)
+-              return 0;
++      if (new_credits < available) {
++              /*
++               * Readd the remaining available again.
++               */
++              available -= new_credits;
++              atomic_add(available, &sc->recv_io.credits.available);
++      }
+ 
++      /*
++       * Remember we granted the credits
++       */
++      atomic_add(new_credits, &sc->recv_io.credits.count);
+       return new_credits;
+ }
+ 
+@@ -1075,12 +1165,27 @@ static int manage_keep_alive_before_sending(struct 
smbdirect_socket *sc)
+       return 0;
+ }
+ 
++static int smbd_ib_post_send(struct smbdirect_socket *sc,
++                           struct ib_send_wr *wr)
++{
++      int ret;
++
++      atomic_inc(&sc->send_io.pending.count);
++      ret = ib_post_send(sc->ib.qp, wr, NULL);
++      if (ret) {
++              pr_err("failed to post send: %d\n", ret);
++              smbd_disconnect_rdma_connection(sc);
++              ret = -EAGAIN;
++      }
++      return ret;
++}
++
+ /* Post the send request */
+ static int smbd_post_send(struct smbdirect_socket *sc,
+-              struct smbdirect_send_io *request)
++                        struct smbdirect_send_batch *batch,
++                        struct smbdirect_send_io *request)
+ {
+-      struct ib_send_wr send_wr;
+-      int rc, i;
++      int i;
+ 
+       for (i = 0; i < request->num_sge; i++) {
+               log_rdma_send(INFO,
+@@ -1094,79 +1199,245 @@ static int smbd_post_send(struct smbdirect_socket *sc,
+       }
+ 
+       request->cqe.done = send_done;
++      request->wr.next = NULL;
++      request->wr.sg_list = request->sge;
++      request->wr.num_sge = request->num_sge;
++      request->wr.opcode = IB_WR_SEND;
++
++      if (batch) {
++              request->wr.wr_cqe = NULL;
++              request->wr.send_flags = 0;
++              if (!list_empty(&batch->msg_list)) {
++                      struct smbdirect_send_io *last;
++
++                      last = list_last_entry(&batch->msg_list,
++                                             struct smbdirect_send_io,
++                                             sibling_list);
++                      last->wr.next = &request->wr;
++              }
++              list_add_tail(&request->sibling_list, &batch->msg_list);
++              batch->wr_cnt++;
++              return 0;
++      }
+ 
+-      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;
++      request->wr.wr_cqe = &request->cqe;
++      request->wr.send_flags = IB_SEND_SIGNALED;
++      return smbd_ib_post_send(sc, &request->wr);
++}
+ 
+-      rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
+-      if (rc) {
+-              log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+-              smbd_disconnect_rdma_connection(sc);
+-              rc = -EAGAIN;
++static void smbd_send_batch_init(struct smbdirect_send_batch *batch,
++                               bool need_invalidate_rkey,
++                               unsigned int remote_key)
++{
++      INIT_LIST_HEAD(&batch->msg_list);
++      batch->wr_cnt = 0;
++      batch->need_invalidate_rkey = need_invalidate_rkey;
++      batch->remote_key = remote_key;
++      batch->credit = 0;
++}
++
++static int smbd_send_batch_flush(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch,
++                               bool is_last)
++{
++      struct smbdirect_send_io *first, *last;
++      int ret = 0;
++
++      if (list_empty(&batch->msg_list))
++              goto release_credit;
++
++      first = list_first_entry(&batch->msg_list,
++                               struct smbdirect_send_io,
++                               sibling_list);
++      last = list_last_entry(&batch->msg_list,
++                             struct smbdirect_send_io,
++                             sibling_list);
++
++      if (batch->need_invalidate_rkey) {
++              first->wr.opcode = IB_WR_SEND_WITH_INV;
++              first->wr.ex.invalidate_rkey = batch->remote_key;
++              batch->need_invalidate_rkey = false;
++              batch->remote_key = 0;
+       }
+ 
+-      return rc;
++      last->wr.send_flags = IB_SEND_SIGNALED;
++      last->wr.wr_cqe = &last->cqe;
++
++      /*
++       * Remove last from batch->msg_list
++       * and splice the rest of batch->msg_list
++       * to last->sibling_list.
++       *
++       * batch->msg_list is a valid empty list
++       * at the end.
++       */
++      list_del_init(&last->sibling_list);
++      list_splice_tail_init(&batch->msg_list, &last->sibling_list);
++      batch->wr_cnt = 0;
++
++      ret = smbd_ib_post_send(sc, &first->wr);
++      if (ret) {
++              struct smbdirect_send_io *sibling, *next;
++
++              list_for_each_entry_safe(sibling, next, &last->sibling_list, 
sibling_list) {
++                      list_del_init(&sibling->sibling_list);
++                      smbd_free_send_io(sibling);
++              }
++              smbd_free_send_io(last);
++      }
++
++release_credit:
++      if (is_last && !ret && batch->credit) {
++              atomic_add(batch->credit, &sc->send_io.bcredits.count);
++              batch->credit = 0;
++              wake_up(&sc->send_io.bcredits.wait_queue);
++      }
++
++      return ret;
++}
++
++static int wait_for_credits(struct smbdirect_socket *sc,
++                          wait_queue_head_t *waitq, atomic_t *total_credits,
++                          int needed)
++{
++      int ret;
++
++      do {
++              if (atomic_sub_return(needed, total_credits) >= 0)
++                      return 0;
++
++              atomic_add(needed, total_credits);
++              ret = wait_event_interruptible(*waitq,
++                                             atomic_read(total_credits) >= 
needed ||
++                                             sc->status != 
SMBDIRECT_SOCKET_CONNECTED);
++
++              if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
++                      return -ENOTCONN;
++              else if (ret < 0)
++                      return ret;
++      } while (true);
++}
++
++static int wait_for_send_bcredit(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch)
++{
++      int ret;
++
++      if (batch->credit)
++              return 0;
++
++      ret = wait_for_credits(sc,
++                             &sc->send_io.bcredits.wait_queue,
++                             &sc->send_io.bcredits.count,
++                             1);
++      if (ret)
++              return ret;
++
++      batch->credit = 1;
++      return 0;
++}
++
++static int wait_for_send_lcredit(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch)
++{
++      if (batch && (atomic_read(&sc->send_io.lcredits.count) <= 1)) {
++              int ret;
++
++              ret = smbd_send_batch_flush(sc, batch, false);
++              if (ret)
++                      return ret;
++      }
++
++      return wait_for_credits(sc,
++                              &sc->send_io.lcredits.wait_queue,
++                              &sc->send_io.lcredits.count,
++                              1);
++}
++
++static int wait_for_send_credits(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch)
++{
++      if (batch &&
++          (batch->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 
1)) {
++              int ret;
++
++              ret = smbd_send_batch_flush(sc, batch, false);
++              if (ret)
++                      return ret;
++      }
++
++      return wait_for_credits(sc,
++                              &sc->send_io.credits.wait_queue,
++                              &sc->send_io.credits.count,
++                              1);
+ }
+ 
+ static int smbd_post_send_iter(struct smbdirect_socket *sc,
++                             struct smbdirect_send_batch *batch,
+                              struct iov_iter *iter,
+                              int *_remaining_data_length)
+ {
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+-      int i, rc;
++      int rc;
+       int header_length;
+       int data_length;
+       struct smbdirect_send_io *request;
+       struct smbdirect_data_transfer *packet;
+       int new_credits = 0;
++      struct smbdirect_send_batch _batch;
+ 
+-wait_lcredit:
+-      /* Wait for local send credits */
+-      rc = wait_event_interruptible(sc->send_io.lcredits.wait_queue,
+-              atomic_read(&sc->send_io.lcredits.count) > 0 ||
+-              sc->status != SMBDIRECT_SOCKET_CONNECTED);
+-      if (rc)
+-              goto err_wait_lcredit;
++      if (!batch) {
++              smbd_send_batch_init(&_batch, false, 0);
++              batch = &_batch;
++      }
+ 
+-      if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+-              log_outgoing(ERR, "disconnected not sending on wait_credit\n");
++      rc = wait_for_send_bcredit(sc, batch);
++      if (rc) {
++              log_outgoing(ERR, "disconnected not sending on wait_bcredit\n");
+               rc = -EAGAIN;
+-              goto err_wait_lcredit;
+-      }
+-      if (unlikely(atomic_dec_return(&sc->send_io.lcredits.count) < 0)) {
+-              atomic_inc(&sc->send_io.lcredits.count);
+-              goto wait_lcredit;
++              goto err_wait_bcredit;
+       }
+ 
+-wait_credit:
+-      /* Wait for send credits. A SMBD packet needs one credit */
+-      rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
+-              atomic_read(&sc->send_io.credits.count) > 0 ||
+-              sc->status != SMBDIRECT_SOCKET_CONNECTED);
+-      if (rc)
+-              goto err_wait_credit;
++      rc = wait_for_send_lcredit(sc, batch);
++      if (rc) {
++              log_outgoing(ERR, "disconnected not sending on wait_lcredit\n");
++              rc = -EAGAIN;
++              goto err_wait_lcredit;
++      }
+ 
+-      if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
++      rc = wait_for_send_credits(sc, batch);
++      if (rc) {
+               log_outgoing(ERR, "disconnected not sending on wait_credit\n");
+               rc = -EAGAIN;
+               goto err_wait_credit;
+       }
+-      if (unlikely(atomic_dec_return(&sc->send_io.credits.count) < 0)) {
+-              atomic_inc(&sc->send_io.credits.count);
+-              goto wait_credit;
++
++      new_credits = manage_credits_prior_sending(sc);
++      if (new_credits == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0 &&
++          atomic_read(&sc->recv_io.credits.count) == 0) {
++              queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
++              rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
++                                            
atomic_read(&sc->send_io.credits.count) >= 1 ||
++                                            
atomic_read(&sc->recv_io.credits.available) >= 1 ||
++                                            sc->status != 
SMBDIRECT_SOCKET_CONNECTED);
++              if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
++                      rc = -ENOTCONN;
++              if (rc < 0) {
++                      log_outgoing(ERR, "disconnected not sending on last 
credit\n");
++                      rc = -EAGAIN;
++                      goto err_wait_credit;
++              }
++
++              new_credits = manage_credits_prior_sending(sc);
+       }
+ 
+-      request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
+-      if (!request) {
+-              rc = -ENOMEM;
++      request = smbd_alloc_send_io(sc);
++      if (IS_ERR(request)) {
++              rc = PTR_ERR(request);
+               goto err_alloc;
+       }
+ 
+-      request->socket = sc;
+       memset(request->sge, 0, sizeof(request->sge));
+ 
+       /* Map the packet to DMA */
+@@ -1215,9 +1486,6 @@ wait_credit:
+ 
+       /* Fill in the packet header */
+       packet->credits_requested = cpu_to_le16(sp->send_credit_target);
+-
+-      new_credits = manage_credits_prior_sending(sc);
+-      atomic_add(new_credits, &sc->recv_io.credits.count);
+       packet->credits_granted = cpu_to_le16(new_credits);
+ 
+       packet->flags = 0;
+@@ -1240,32 +1508,18 @@ wait_credit:
+                    le32_to_cpu(packet->data_length),
+                    le32_to_cpu(packet->remaining_data_length));
+ 
+-      /*
+-       * Now that we got a local and a remote credit
+-       * we add us as pending
+-       */
+-      atomic_inc(&sc->send_io.pending.count);
+-
+-      rc = smbd_post_send(sc, request);
+-      if (!rc)
+-              return 0;
+-
+-      if (atomic_dec_and_test(&sc->send_io.pending.count))
+-              wake_up(&sc->send_io.pending.zero_wait_queue);
++      rc = smbd_post_send(sc, batch, request);
++      if (!rc) {
++              if (batch != &_batch)
++                      return 0;
+ 
+-      wake_up(&sc->send_io.pending.dec_wait_queue);
++              rc = smbd_send_batch_flush(sc, batch, true);
++              if (!rc)
++                      return 0;
++      }
+ 
+ err_dma:
+-      for (i = 0; i < request->num_sge; i++)
+-              if (request->sge[i].addr)
+-                      ib_dma_unmap_single(sc->ib.dev,
+-                                          request->sge[i].addr,
+-                                          request->sge[i].length,
+-                                          DMA_TO_DEVICE);
+-      mempool_free(request, sc->send_io.mem.pool);
+-
+-      /* roll back the granted receive credits */
+-      atomic_sub(new_credits, &sc->recv_io.credits.count);
++      smbd_free_send_io(request);
+ 
+ err_alloc:
+       atomic_inc(&sc->send_io.credits.count);
+@@ -1276,6 +1530,11 @@ err_wait_credit:
+       wake_up(&sc->send_io.lcredits.wait_queue);
+ 
+ err_wait_lcredit:
++      atomic_add(batch->credit, &sc->send_io.bcredits.count);
++      batch->credit = 0;
++      wake_up(&sc->send_io.bcredits.wait_queue);
++
++err_wait_bcredit:
+       return rc;
+ }
+ 
+@@ -1289,10 +1548,11 @@ static int smbd_post_send_empty(struct 
smbdirect_socket *sc)
+       int remaining_data_length = 0;
+ 
+       sc->statistics.send_empty++;
+-      return smbd_post_send_iter(sc, NULL, &remaining_data_length);
++      return smbd_post_send_iter(sc, NULL, NULL, &remaining_data_length);
+ }
+ 
+ static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
++                                  struct smbdirect_send_batch *batch,
+                                   struct iov_iter *iter,
+                                   int *_remaining_data_length)
+ {
+@@ -1305,7 +1565,7 @@ static int smbd_post_send_full_iter(struct 
smbdirect_socket *sc,
+        */
+ 
+       while (iov_iter_count(iter) > 0) {
+-              rc = smbd_post_send_iter(sc, iter, _remaining_data_length);
++              rc = smbd_post_send_iter(sc, batch, iter, 
_remaining_data_length);
+               if (rc < 0)
+                       break;
+       }
+@@ -2227,8 +2487,10 @@ int smbd_send(struct TCP_Server_Info *server,
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+       struct smb_rqst *rqst;
+       struct iov_iter iter;
++      struct smbdirect_send_batch batch;
+       unsigned int remaining_data_length, klen;
+       int rc, i, rqst_idx;
++      int error = 0;
+ 
+       if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+               return -EAGAIN;
+@@ -2253,6 +2515,7 @@ int smbd_send(struct TCP_Server_Info *server,
+                       num_rqst, remaining_data_length);
+ 
+       rqst_idx = 0;
++      smbd_send_batch_init(&batch, false, 0);
+       do {
+               rqst = &rqst_array[rqst_idx];
+ 
+@@ -2271,20 +2534,28 @@ int smbd_send(struct TCP_Server_Info *server,
+                       klen += rqst->rq_iov[i].iov_len;
+               iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, 
klen);
+ 
+-              rc = smbd_post_send_full_iter(sc, &iter, 
&remaining_data_length);
+-              if (rc < 0)
++              rc = smbd_post_send_full_iter(sc, &batch, &iter, 
&remaining_data_length);
++              if (rc < 0) {
++                      error = rc;
+                       break;
++              }
+ 
+               if (iov_iter_count(&rqst->rq_iter) > 0) {
+                       /* And then the data pages if there are any */
+-                      rc = smbd_post_send_full_iter(sc, &rqst->rq_iter,
++                      rc = smbd_post_send_full_iter(sc, &batch, 
&rqst->rq_iter,
+                                                     &remaining_data_length);
+-                      if (rc < 0)
++                      if (rc < 0) {
++                              error = rc;
+                               break;
++                      }
+               }
+ 
+       } while (++rqst_idx < num_rqst);
+ 
++      rc = smbd_send_batch_flush(sc, &batch, true);
++      if (unlikely(!rc && error))
++              rc = error;
++
+       /*
+        * As an optimization, we don't wait for individual I/O to finish
+        * before sending the next one.
+diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h 
b/fs/smb/common/smbdirect/smbdirect_socket.h
+index 384b19177e1c37..ca22b7c2d3a7fe 100644
+--- a/fs/smb/common/smbdirect/smbdirect_socket.h
++++ b/fs/smb/common/smbdirect/smbdirect_socket.h
+@@ -154,6 +154,17 @@ struct smbdirect_socket {
+                       mempool_t               *pool;
+               } mem;
+ 
++              /*
++               * This is a coordination for smbdirect_send_batch.
++               *
++               * There's only one possible credit, which means
++               * only one instance is running at a time.
++               */
++              struct {
++                      atomic_t count;
++                      wait_queue_head_t wait_queue;
++              } bcredits;
++
+               /*
+                * The local credit state for ib_post_send()
+                */
+@@ -231,6 +242,7 @@ struct smbdirect_socket {
+                */
+               struct {
+                       u16 target;
++                      atomic_t available;
+                       atomic_t count;
+               } credits;
+ 
+@@ -358,6 +370,9 @@ static __always_inline void smbdirect_socket_init(struct 
smbdirect_socket *sc)
+       INIT_DELAYED_WORK(&sc->idle.timer_work, 
__smbdirect_socket_disabled_work);
+       disable_delayed_work_sync(&sc->idle.timer_work);
+ 
++      atomic_set(&sc->send_io.bcredits.count, 0);
++      init_waitqueue_head(&sc->send_io.bcredits.wait_queue);
++
+       atomic_set(&sc->send_io.lcredits.count, 0);
+       init_waitqueue_head(&sc->send_io.lcredits.wait_queue);
+ 
+@@ -375,6 +390,7 @@ static __always_inline void smbdirect_socket_init(struct 
smbdirect_socket *sc)
+       INIT_WORK(&sc->recv_io.posted.refill_work, 
__smbdirect_socket_disabled_work);
+       disable_work_sync(&sc->recv_io.posted.refill_work);
+ 
++      atomic_set(&sc->recv_io.credits.available, 0);
+       atomic_set(&sc->recv_io.credits.count, 0);
+ 
+       INIT_LIST_HEAD(&sc->recv_io.reassembly.list);
+@@ -471,6 +487,8 @@ struct smbdirect_send_batch {
+        */
+       bool need_invalidate_rkey;
+       u32 remote_key;
++
++      int credit;
+ };
+ 
+ struct smbdirect_recv_io {
+diff --git a/fs/smb/server/mgmt/user_session.c 
b/fs/smb/server/mgmt/user_session.c
+index 7d880ff34402e0..26cb87625f1c6b 100644
+--- a/fs/smb/server/mgmt/user_session.c
++++ b/fs/smb/server/mgmt/user_session.c
+@@ -32,12 +32,14 @@ static void free_channel_list(struct ksmbd_session *sess)
+       struct channel *chann;
+       unsigned long index;
+ 
++      down_write(&sess->chann_lock);
+       xa_for_each(&sess->ksmbd_chann_list, index, chann) {
+               xa_erase(&sess->ksmbd_chann_list, index);
+               kfree(chann);
+       }
+ 
+       xa_destroy(&sess->ksmbd_chann_list);
++      up_write(&sess->chann_lock);
+ }
+ 
+ static void __session_rpc_close(struct ksmbd_session *sess,
+@@ -220,7 +222,9 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct 
ksmbd_session *sess)
+ {
+       struct channel *chann;
+ 
++      down_write(&sess->chann_lock);
+       chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
++      up_write(&sess->chann_lock);
+       if (!chann)
+               return -ENOENT;
+ 
+@@ -454,6 +458,7 @@ static struct ksmbd_session *__session_create(int protocol)
+       rwlock_init(&sess->tree_conns_lock);
+       atomic_set(&sess->refcnt, 2);
+       init_rwsem(&sess->rpc_lock);
++      init_rwsem(&sess->chann_lock);
+ 
+       ret = __init_smb2_session(sess);
+       if (ret)
+diff --git a/fs/smb/server/mgmt/user_session.h 
b/fs/smb/server/mgmt/user_session.h
+index c5749d6ec7151c..cba7f688f6b577 100644
+--- a/fs/smb/server/mgmt/user_session.h
++++ b/fs/smb/server/mgmt/user_session.h
+@@ -49,6 +49,7 @@ struct ksmbd_session {
+       char                            sess_key[CIFS_KEY_SIZE];
+ 
+       struct hlist_node               hlist;
++      struct rw_semaphore             chann_lock;
+       struct xarray                   ksmbd_chann_list;
+       struct xarray                   tree_conns;
+       struct ida                      tree_conn_ida;
+diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
+index 40420544cc25a2..ef7a24e31f506f 100644
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -126,21 +126,21 @@ static int __process_request(struct ksmbd_work *work, 
struct ksmbd_conn *conn,
+ andx_again:
+       if (command >= conn->max_cmds) {
+               conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
+-              return SERVER_HANDLER_CONTINUE;
++              return SERVER_HANDLER_ABORT;
+       }
+ 
+       cmds = &conn->cmds[command];
+       if (!cmds->proc) {
+               ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command);
+               conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED);
+-              return SERVER_HANDLER_CONTINUE;
++              return SERVER_HANDLER_ABORT;
+       }
+ 
+       if (work->sess && conn->ops->is_sign_req(work, command)) {
+               ret = conn->ops->check_sign_req(work);
+               if (!ret) {
+                       conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);
+-                      return SERVER_HANDLER_CONTINUE;
++                      return SERVER_HANDLER_ABORT;
+               }
+       }
+ 
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 470b274f4cc98a..bf8c4805943672 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -79,7 +79,13 @@ static inline bool check_session_id(struct ksmbd_conn 
*conn, u64 id)
+ 
+ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct 
ksmbd_conn *conn)
+ {
+-      return xa_load(&sess->ksmbd_chann_list, (long)conn);
++      struct channel *chann;
++
++      down_read(&sess->chann_lock);
++      chann = xa_load(&sess->ksmbd_chann_list, (long)conn);
++      up_read(&sess->chann_lock);
++
++      return chann;
+ }
+ 
+ /**
+@@ -1563,8 +1569,10 @@ binding_session:
+                               return -ENOMEM;
+ 
+                       chann->conn = conn;
++                      down_write(&sess->chann_lock);
+                       old = xa_store(&sess->ksmbd_chann_list, (long)conn, 
chann,
+                                       KSMBD_DEFAULT_GFP);
++                      up_write(&sess->chann_lock);
+                       if (xa_is_err(old)) {
+                               kfree(chann);
+                               return xa_err(old);
+@@ -1661,8 +1669,10 @@ binding_session:
+                               return -ENOMEM;
+ 
+                       chann->conn = conn;
++                      down_write(&sess->chann_lock);
+                       old = xa_store(&sess->ksmbd_chann_list, (long)conn,
+                                       chann, KSMBD_DEFAULT_GFP);
++                      up_write(&sess->chann_lock);
+                       if (xa_is_err(old)) {
+                               kfree(chann);
+                               return xa_err(old);
+diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c
+index 7d2ad73839e860..4e74934e1f2778 100644
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -221,6 +221,7 @@ static void smb_direct_disconnect_wake_up_all(struct 
smbdirect_socket *sc)
+        * in order to notice the broken connection.
+        */
+       wake_up_all(&sc->status_wait);
++      wake_up_all(&sc->send_io.bcredits.wait_queue);
+       wake_up_all(&sc->send_io.lcredits.wait_queue);
+       wake_up_all(&sc->send_io.credits.wait_queue);
+       wake_up_all(&sc->send_io.pending.zero_wait_queue);
+@@ -661,6 +662,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
+               struct smbdirect_data_transfer *data_transfer =
+                       (struct smbdirect_data_transfer *)recvmsg->packet;
+               u32 remaining_data_length, data_offset, data_length;
++              int current_recv_credits;
+               u16 old_recv_credit_target;
+ 
+               if (wc->byte_len <
+@@ -699,7 +701,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
+               }
+ 
+               atomic_dec(&sc->recv_io.posted.count);
+-              atomic_dec(&sc->recv_io.credits.count);
++              current_recv_credits = 
atomic_dec_return(&sc->recv_io.credits.count);
+ 
+               old_recv_credit_target = sc->recv_io.credits.target;
+               sc->recv_io.credits.target =
+@@ -719,7 +721,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
+                       wake_up(&sc->send_io.credits.wait_queue);
+ 
+               if (data_length) {
+-                      if (sc->recv_io.credits.target > old_recv_credit_target)
++                      if (current_recv_credits <= (sc->recv_io.credits.target 
/ 4) ||
++                          sc->recv_io.credits.target > old_recv_credit_target)
+                               queue_work(sc->workqueue, 
&sc->recv_io.posted.refill_work);
+ 
+                       enqueue_reassembly(sc, recvmsg, (int)data_length);
+@@ -926,6 +929,17 @@ static void smb_direct_post_recv_credits(struct 
work_struct *work)
+               }
+       }
+ 
++      atomic_add(credits, &sc->recv_io.credits.available);
++
++      /*
++       * If the last send credit is waiting for credits
++       * it can grant we need to wake it up
++       */
++      if (credits &&
++          atomic_read(&sc->send_io.bcredits.count) == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0)
++              wake_up(&sc->send_io.credits.wait_queue);
++
+       if (credits)
+               queue_work(sc->workqueue, &sc->idle.immediate_work);
+ }
+@@ -943,6 +957,31 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
+                   ib_wc_status_msg(wc->status), wc->status,
+                   wc->opcode);
+ 
++      if (unlikely(!(sendmsg->wr.send_flags & IB_SEND_SIGNALED))) {
++              /*
++               * This happens when smbdirect_send_io is a sibling
++               * before the final message, it is signaled on
++               * error anyway, so we need to skip
++               * smbdirect_connection_free_send_io here,
++               * otherwise is will destroy the memory
++               * of the siblings too, which will cause
++               * use after free problems for the others
++               * triggered from ib_drain_qp().
++               */
++              if (wc->status != IB_WC_SUCCESS)
++                      goto skip_free;
++
++              /*
++               * This should not happen!
++               * But we better just close the
++               * connection...
++               */
++              pr_err("unexpected send completion wc->status=%s (%d) 
wc->opcode=%d\n",
++                     ib_wc_status_msg(wc->status), wc->status, wc->opcode);
++              smb_direct_disconnect_rdma_connection(sc);
++              return;
++      }
++
+       /*
+        * Free possible siblings and then the main send_io
+        */
+@@ -956,6 +995,7 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
+       lcredits += 1;
+ 
+       if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
++skip_free:
+               pr_err("Send error. status='%s (%d)', opcode=%d\n",
+                      ib_wc_status_msg(wc->status), wc->status,
+                      wc->opcode);
+@@ -972,19 +1012,37 @@ static void send_done(struct ib_cq *cq, struct ib_wc 
*wc)
+ 
+ static int manage_credits_prior_sending(struct smbdirect_socket *sc)
+ {
++      int missing;
++      int available;
+       int new_credits;
+ 
+       if (atomic_read(&sc->recv_io.credits.count) >= 
sc->recv_io.credits.target)
+               return 0;
+ 
+-      new_credits = atomic_read(&sc->recv_io.posted.count);
+-      if (new_credits == 0)
++      missing = (int)sc->recv_io.credits.target - 
atomic_read(&sc->recv_io.credits.count);
++      available = atomic_xchg(&sc->recv_io.credits.available, 0);
++      new_credits = (u16)min3(U16_MAX, missing, available);
++      if (new_credits <= 0) {
++              /*
++               * If credits are available, but not granted
++               * we need to re-add them again.
++               */
++              if (available)
++                      atomic_add(available, &sc->recv_io.credits.available);
+               return 0;
++      }
+ 
+-      new_credits -= atomic_read(&sc->recv_io.credits.count);
+-      if (new_credits <= 0)
+-              return 0;
++      if (new_credits < available) {
++              /*
++               * Readd the remaining available again.
++               */
++              available -= new_credits;
++              atomic_add(available, &sc->recv_io.credits.available);
++      }
+ 
++      /*
++       * Remember we granted the credits
++       */
+       atomic_add(new_credits, &sc->recv_io.credits.count);
+       return new_credits;
+ }
+@@ -1028,6 +1086,7 @@ static void smb_direct_send_ctx_init(struct 
smbdirect_send_batch *send_ctx,
+       send_ctx->wr_cnt = 0;
+       send_ctx->need_invalidate_rkey = need_invalidate_rkey;
+       send_ctx->remote_key = remote_key;
++      send_ctx->credit = 0;
+ }
+ 
+ static int smb_direct_flush_send_list(struct smbdirect_socket *sc,
+@@ -1035,10 +1094,10 @@ static int smb_direct_flush_send_list(struct 
smbdirect_socket *sc,
+                                     bool is_last)
+ {
+       struct smbdirect_send_io *first, *last;
+-      int ret;
++      int ret = 0;
+ 
+       if (list_empty(&send_ctx->msg_list))
+-              return 0;
++              goto release_credit;
+ 
+       first = list_first_entry(&send_ctx->msg_list,
+                                struct smbdirect_send_io,
+@@ -1080,6 +1139,13 @@ static int smb_direct_flush_send_list(struct 
smbdirect_socket *sc,
+               smb_direct_free_sendmsg(sc, last);
+       }
+ 
++release_credit:
++      if (is_last && !ret && send_ctx->credit) {
++              atomic_add(send_ctx->credit, &sc->send_io.bcredits.count);
++              send_ctx->credit = 0;
++              wake_up(&sc->send_io.bcredits.wait_queue);
++      }
++
+       return ret;
+ }
+ 
+@@ -1105,6 +1171,25 @@ static int wait_for_credits(struct smbdirect_socket *sc,
+       } while (true);
+ }
+ 
++static int wait_for_send_bcredit(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *send_ctx)
++{
++      int ret;
++
++      if (send_ctx->credit)
++              return 0;
++
++      ret = wait_for_credits(sc,
++                             &sc->send_io.bcredits.wait_queue,
++                             &sc->send_io.bcredits.count,
++                             1);
++      if (ret)
++              return ret;
++
++      send_ctx->credit = 1;
++      return 0;
++}
++
+ static int wait_for_send_lcredit(struct smbdirect_socket *sc,
+                                struct smbdirect_send_batch *send_ctx)
+ {
+@@ -1154,6 +1239,7 @@ static int calc_rw_credits(struct smbdirect_socket *sc,
+ 
+ static int smb_direct_create_header(struct smbdirect_socket *sc,
+                                   int size, int remaining_data_length,
++                                  int new_credits,
+                                   struct smbdirect_send_io **sendmsg_out)
+ {
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+@@ -1169,7 +1255,7 @@ static int smb_direct_create_header(struct 
smbdirect_socket *sc,
+       /* Fill in the packet header */
+       packet = (struct smbdirect_data_transfer *)sendmsg->packet;
+       packet->credits_requested = cpu_to_le16(sp->send_credit_target);
+-      packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc));
++      packet->credits_granted = cpu_to_le16(new_credits);
+ 
+       packet->flags = 0;
+       if (manage_keep_alive_before_sending(sc))
+@@ -1306,6 +1392,17 @@ static int smb_direct_post_send_data(struct 
smbdirect_socket *sc,
+       struct smbdirect_send_io *msg;
+       int data_length;
+       struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1];
++      struct smbdirect_send_batch _send_ctx;
++      int new_credits;
++
++      if (!send_ctx) {
++              smb_direct_send_ctx_init(&_send_ctx, false, 0);
++              send_ctx = &_send_ctx;
++      }
++
++      ret = wait_for_send_bcredit(sc, send_ctx);
++      if (ret)
++              goto bcredit_failed;
+ 
+       ret = wait_for_send_lcredit(sc, send_ctx);
+       if (ret)
+@@ -1315,12 +1412,29 @@ static int smb_direct_post_send_data(struct 
smbdirect_socket *sc,
+       if (ret)
+               goto credit_failed;
+ 
++      new_credits = manage_credits_prior_sending(sc);
++      if (new_credits == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0 &&
++          atomic_read(&sc->recv_io.credits.count) == 0) {
++              queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
++              ret = wait_event_interruptible(sc->send_io.credits.wait_queue,
++                                             
atomic_read(&sc->send_io.credits.count) >= 1 ||
++                                             
atomic_read(&sc->recv_io.credits.available) >= 1 ||
++                                             sc->status != 
SMBDIRECT_SOCKET_CONNECTED);
++              if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
++                      ret = -ENOTCONN;
++              if (ret < 0)
++                      goto credit_failed;
++
++              new_credits = manage_credits_prior_sending(sc);
++      }
++
+       data_length = 0;
+       for (i = 0; i < niov; i++)
+               data_length += iov[i].iov_len;
+ 
+       ret = smb_direct_create_header(sc, data_length, remaining_data_length,
+-                                     &msg);
++                                     new_credits, &msg);
+       if (ret)
+               goto header_failed;
+ 
+@@ -1358,6 +1472,13 @@ static int smb_direct_post_send_data(struct 
smbdirect_socket *sc,
+       ret = post_sendmsg(sc, send_ctx, msg);
+       if (ret)
+               goto err;
++
++      if (send_ctx == &_send_ctx) {
++              ret = smb_direct_flush_send_list(sc, send_ctx, true);
++              if (ret)
++                      goto err;
++      }
++
+       return 0;
+ err:
+       smb_direct_free_sendmsg(sc, msg);
+@@ -1366,6 +1487,9 @@ header_failed:
+ credit_failed:
+       atomic_inc(&sc->send_io.lcredits.count);
+ lcredit_failed:
++      atomic_add(send_ctx->credit, &sc->send_io.bcredits.count);
++      send_ctx->credit = 0;
++bcredit_failed:
+       return ret;
+ }
+ 
+@@ -1827,6 +1951,7 @@ static int smb_direct_send_negotiate_response(struct 
smbdirect_socket *sc,
+               resp->max_fragmented_size =
+                               cpu_to_le32(sp->max_fragmented_recv_size);
+ 
++              atomic_set(&sc->send_io.bcredits.count, 1);
+               sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
+               sc->status = SMBDIRECT_SOCKET_CONNECTED;
+       }
+diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
+index d2e391c29464e7..48cbb04ad41a2f 100644
+--- a/fs/smb/server/transport_tcp.c
++++ b/fs/smb/server/transport_tcp.c
+@@ -41,6 +41,7 @@ static const struct ksmbd_transport_ops 
ksmbd_tcp_transport_ops;
+ 
+ static void tcp_stop_kthread(struct task_struct *kthread);
+ static struct interface *alloc_iface(char *ifname);
++static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
+ 
+ #define KSMBD_TRANS(t)        (&(t)->transport)
+ #define TCP_TRANS(t)  ((struct tcp_transport *)container_of(t, \
+@@ -216,7 +217,7 @@ static int ksmbd_tcp_new_connection(struct socket 
*client_sk)
+       if (IS_ERR(handler)) {
+               pr_err("cannot start conn thread\n");
+               rc = PTR_ERR(handler);
+-              free_transport(t);
++              ksmbd_tcp_disconnect(KSMBD_TRANS(t));
+       }
+       return rc;
+ }
+diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
+index cd6f0ff382a7c8..acade92c5fce1a 100644
+--- a/fs/xfs/scrub/btree.c
++++ b/fs/xfs/scrub/btree.c
+@@ -370,12 +370,15 @@ xchk_btree_check_block_owner(
+ {
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
++      bool                    is_bnobt, is_rmapbt;
+       bool                    init_sa;
+       int                     error = 0;
+ 
+       if (!bs->cur)
+               return 0;
+ 
++      is_bnobt = xfs_btree_is_bno(bs->cur->bc_ops);
++      is_rmapbt = xfs_btree_is_rmap(bs->cur->bc_ops);
+       agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
+       agbno = xfs_daddr_to_agbno(bs->cur->bc_mp, daddr);
+ 
+@@ -398,11 +401,11 @@ xchk_btree_check_block_owner(
+        * have to nullify it (to shut down further block owner checks) if
+        * self-xref encounters problems.
+        */
+-      if (!bs->sc->sa.bno_cur && xfs_btree_is_bno(bs->cur->bc_ops))
++      if (!bs->sc->sa.bno_cur && is_bnobt)
+               bs->cur = NULL;
+ 
+       xchk_xref_is_only_owned_by(bs->sc, agbno, 1, bs->oinfo);
+-      if (!bs->sc->sa.rmap_cur && xfs_btree_is_rmap(bs->cur->bc_ops))
++      if (!bs->sc->sa.rmap_cur && is_rmapbt)
+               bs->cur = NULL;
+ 
+ out_free:
+diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
+index 56b6a82579597a..49a9c914b4e97d 100644
+--- a/io_uring/io-wq.c
++++ b/io_uring/io-wq.c
+@@ -34,6 +34,7 @@ enum {
+ 
+ enum {
+       IO_WQ_BIT_EXIT          = 0,    /* wq exiting */
++      IO_WQ_BIT_EXIT_ON_IDLE  = 1,    /* allow all workers to exit on idle */
+ };
+ 
+ enum {
+@@ -706,9 +707,13 @@ static int io_wq_worker(void *data)
+               raw_spin_lock(&acct->workers_lock);
+               /*
+                * Last sleep timed out. Exit if we're not the last worker,
+-               * or if someone modified our affinity.
++               * or if someone modified our affinity. If wq is marked
++               * idle-exit, drop the worker as well. This is used to avoid
++               * keeping io-wq workers around for tasks that no longer have
++               * any active io_uring instances.
+                */
+-              if (last_timeout && (exit_mask || acct->nr_workers > 1)) {
++              if ((last_timeout && (exit_mask || acct->nr_workers > 1)) ||
++                  test_bit(IO_WQ_BIT_EXIT_ON_IDLE, &wq->state)) {
+                       acct->nr_workers--;
+                       raw_spin_unlock(&acct->workers_lock);
+                       __set_current_state(TASK_RUNNING);
+@@ -965,6 +970,24 @@ static bool io_wq_worker_wake(struct io_worker *worker, 
void *data)
+       return false;
+ }
+ 
++void io_wq_set_exit_on_idle(struct io_wq *wq, bool enable)
++{
++      if (!wq->task)
++              return;
++
++      if (!enable) {
++              clear_bit(IO_WQ_BIT_EXIT_ON_IDLE, &wq->state);
++              return;
++      }
++
++      if (test_and_set_bit(IO_WQ_BIT_EXIT_ON_IDLE, &wq->state))
++              return;
++
++      rcu_read_lock();
++      io_wq_for_each_worker(wq, io_wq_worker_wake, NULL);
++      rcu_read_unlock();
++}
++
+ static void io_run_cancel(struct io_wq_work *work, struct io_wq *wq)
+ {
+       do {
+diff --git a/io_uring/io-wq.h b/io_uring/io-wq.h
+index 774abab54732ef..94b14742b70328 100644
+--- a/io_uring/io-wq.h
++++ b/io_uring/io-wq.h
+@@ -41,6 +41,7 @@ struct io_wq_data {
+ struct io_wq *io_wq_create(unsigned bounded, struct io_wq_data *data);
+ void io_wq_exit_start(struct io_wq *wq);
+ void io_wq_put_and_exit(struct io_wq *wq);
++void io_wq_set_exit_on_idle(struct io_wq *wq, bool enable);
+ 
+ void io_wq_enqueue(struct io_wq *wq, struct io_wq_work *work);
+ void io_wq_hash_work(struct io_wq_work *work, void *val);
+diff --git a/io_uring/tctx.c b/io_uring/tctx.c
+index 5b66755579c08f..03c278aa58120f 100644
+--- a/io_uring/tctx.c
++++ b/io_uring/tctx.c
+@@ -122,6 +122,14 @@ int __io_uring_add_tctx_node(struct io_ring_ctx *ctx)
+                               return ret;
+               }
+       }
++
++      /*
++       * Re-activate io-wq keepalive on any new io_uring usage. The wq may 
have
++       * been marked for idle-exit when the task temporarily had no active
++       * io_uring instances.
++       */
++      if (tctx->io_wq)
++              io_wq_set_exit_on_idle(tctx->io_wq, false);
+       if (!xa_load(&tctx->xa, (unsigned long)ctx)) {
+               node = kmalloc(sizeof(*node), GFP_KERNEL);
+               if (!node)
+@@ -183,6 +191,9 @@ __cold void io_uring_del_tctx_node(unsigned long index)
+       if (tctx->last == node->ctx)
+               tctx->last = NULL;
+       kfree(node);
++
++      if (xa_empty(&tctx->xa) && tctx->io_wq)
++              io_wq_set_exit_on_idle(tctx->io_wq, true);
+ }
+ 
+ __cold void io_uring_clean_tctx(struct io_uring_task *tctx)
+diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c
+index 5fcbc1312c6971..d6fba746030195 100644
+--- a/sound/hda/codecs/conexant.c
++++ b/sound/hda/codecs/conexant.c
+@@ -1081,6 +1081,7 @@ static const struct hda_quirk cxt5066_fixups[] = {
+       SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
+       SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", 
CXT_FIXUP_MUTE_LED_GPIO),
+       SND_PCI_QUIRK(0x103c, 0x8231, "HP ProBook 450 G4", 
CXT_FIXUP_MUTE_LED_GPIO),
++      SND_PCI_QUIRK(0x103c, 0x826b, "HP ZBook Studio G4", 
CXT_FIXUP_MUTE_LED_GPIO),
+       SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK),
+       SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", 
CXT_FIXUP_HP_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", 
CXT_FIXUP_HP_MIC_NO_PRESENCE),

Reply via email to