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),
