]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Aug 2026 12:11:05 +0000 (14:11 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Aug 2026 12:11:05 +0000 (14:11 +0200)
added patches:
media-chips-media-wave5-support-cbp-profile.patch
media-qcom-camss-csid-340-fix-unused-variables.patch
media-qcom-camss-fix-rdi-streaming-for-csid-340.patch
media-uapi-rkisp-correct-name-version-enum.patch
usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch
usb-typec-ucsi-split-connector-lock-classes.patch
userfaultfd-prevent-registration-of-special-vmas.patch
wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch

queue-6.18/media-chips-media-wave5-support-cbp-profile.patch [new file with mode: 0644]
queue-6.18/media-qcom-camss-csid-340-fix-unused-variables.patch [new file with mode: 0644]
queue-6.18/media-qcom-camss-fix-rdi-streaming-for-csid-340.patch [new file with mode: 0644]
queue-6.18/media-uapi-rkisp-correct-name-version-enum.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch [new file with mode: 0644]
queue-6.18/usb-typec-ucsi-split-connector-lock-classes.patch [new file with mode: 0644]
queue-6.18/userfaultfd-prevent-registration-of-special-vmas.patch [new file with mode: 0644]
queue-6.18/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch [new file with mode: 0644]

diff --git a/queue-6.18/media-chips-media-wave5-support-cbp-profile.patch b/queue-6.18/media-chips-media-wave5-support-cbp-profile.patch
new file mode 100644 (file)
index 0000000..520828d
--- /dev/null
@@ -0,0 +1,82 @@
+From stable+bounces-295111-greg=kroah.com@vger.kernel.org Tue Aug  4 13:01:09 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue,  4 Aug 2026 06:47:12 -0400
+Subject: media: chips-media: wave5: Support CBP profile
+To: stable@vger.kernel.org
+Cc: Jackson Lee <jackson.lee@chipsnmedia.com>, Nas Chung <nas.chung@chipsnmedia.com>, Brandon Brnich <b-brnich@ti.com>, Nicolas Dufresne <nicolas.dufresne@collabora.com>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260804104712.2825343-1-sashal@kernel.org>
+
+From: Jackson Lee <jackson.lee@chipsnmedia.com>
+
+[ Upstream commit f8505f9d6b223a26854003bfdba1a0772457886d ]
+
+Constrained Baseline Profile (CBP) and Baseline Profile (BP) have been
+treated as the same.
+Introduce the ability to differentiate between the two.
+
+Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jackson Lee <jackson.lee@chipsnmedia.com>
+Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
+Tested-by: Brandon Brnich <b-brnich@ti.com>
+Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/platform/chips-media/wave5/wave5-hw.c      |    3 +++
+ drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c |    5 ++++-
+ drivers/media/platform/chips-media/wave5/wave5-vpuapi.h  |    1 +
+ 3 files changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/platform/chips-media/wave5/wave5-hw.c
++++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c
+@@ -1762,6 +1762,9 @@ int wave5_vpu_enc_init_seq(struct vpu_in
+                       (p_param->skip_intra_trans << 25) |
+                       (p_param->strong_intra_smooth_enable << 27) |
+                       (p_param->en_still_picture << 30);
++      else if (inst->std == W_AVC_ENC)
++              reg_val |= (p_param->constraint_set1_flag << 29);
++
+       vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val);
+       reg_val = (p_param->lossless_enable) |
+--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
++++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
+@@ -915,6 +915,8 @@ static int wave5_vpu_enc_s_ctrl(struct v
+               case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+                       inst->enc_param.profile = H264_PROFILE_BP;
+                       inst->bit_depth = 8;
++                      if (ctrl->val == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE)
++                              inst->enc_param.constraint_set1_flag = 1;
+                       break;
+               case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+                       inst->enc_param.profile = H264_PROFILE_MP;
+@@ -1190,6 +1192,7 @@ static int wave5_set_enc_openparam(struc
+                       open_param->wave_param.intra_period = input.avc_idr_period;
+               }
+       } else {
++              open_param->wave_param.constraint_set1_flag = input.constraint_set1_flag;
+               open_param->wave_param.avc_idr_period = input.avc_idr_period;
+       }
+       open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode;
+@@ -1662,7 +1665,7 @@ static int wave5_vpu_open_enc(struct fil
+                         -6, 6, 1, 0);
+       v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+                         V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+-                        0, 1, 1, 1);
++                        0, 1, 1, 0);
+       v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+                         V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION,
+                         0, 1, 1, 0);
+--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
++++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
+@@ -570,6 +570,7 @@ struct enc_wave_param {
+       u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */
+       u32 mb_level_rc_enable: 1; /* enable MB-level rate control */
+       u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */
++      u32 constraint_set1_flag: 1; /* enable CBP */
+ };
+ struct enc_open_param {
diff --git a/queue-6.18/media-qcom-camss-csid-340-fix-unused-variables.patch b/queue-6.18/media-qcom-camss-csid-340-fix-unused-variables.patch
new file mode 100644 (file)
index 0000000..32e6bc1
--- /dev/null
@@ -0,0 +1,71 @@
+From stable+bounces-295295-greg=kroah.com@vger.kernel.org Wed Aug  5 02:10:21 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue,  4 Aug 2026 20:08:32 -0400
+Subject: media: qcom: camss: csid-340: Fix unused variables
+To: stable@vger.kernel.org
+Cc: Loic Poulain <loic.poulain@oss.qualcomm.com>, Bryan O'Donoghue <bryan.odonoghue@linaro.org>, Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>, Bryan O'Donoghue <bod@kernel.org>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260805000833.3902619-1-sashal@kernel.org>
+
+From: Loic Poulain <loic.poulain@oss.qualcomm.com>
+
+[ Upstream commit 0b61ce8dc4a71ea7baa19275c655677c612dde6d ]
+
+The CSID driver has some unused variables and function parameters
+that are no longer needed (due to refactoring). Clean up those
+unused elements:
+
+- Remove the `vc` parameter from `__csid_configure_rx()`.
+- Drop the unused `lane_cnt` variable.
+- Adjust call to `__csid_configure_rx()` accordingly.
+
+Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+Reviewed-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
+Signed-off-by: Bryan O'Donoghue <bod@kernel.org>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Stable-dep-of: cc1c35619c98 ("media: qcom: camss: Fix RDI streaming for CSID 340")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/platform/qcom/camss/camss-csid-340.c |   10 +++-------
+ 1 file changed, 3 insertions(+), 7 deletions(-)
+
+--- a/drivers/media/platform/qcom/camss/camss-csid-340.c
++++ b/drivers/media/platform/qcom/camss/camss-csid-340.c
+@@ -55,8 +55,7 @@
+ #define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY          0
+ #define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY                1
+-static void __csid_configure_rx(struct csid_device *csid,
+-                              struct csid_phy_config *phy, int vc)
++static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy)
+ {
+       u32 val;
+@@ -81,13 +80,9 @@ static void __csid_configure_rdi_stream(
+       const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+                                                                  csid->res->formats->nformats,
+                                                                  input_format->code);
+-      u8 lane_cnt = csid->phy.lane_cnt;
+       u8 dt_id;
+       u32 val;
+-      if (!lane_cnt)
+-              lane_cnt = 4;
+-
+       /*
+        * DT_ID is a two bit bitfield that is concatenated with
+        * the four least significant bits of the five bit VC
+@@ -120,10 +115,11 @@ static void csid_configure_stream(struct
+ {
+       int i;
++      __csid_configure_rx(csid, &csid->phy);
++
+       for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+               if (csid->phy.en_vc & BIT(i)) {
+                       __csid_configure_rdi_stream(csid, enable, i);
+-                      __csid_configure_rx(csid, &csid->phy, i);
+                       __csid_ctrl_rdi(csid, enable, i);
+               }
+       }
diff --git a/queue-6.18/media-qcom-camss-fix-rdi-streaming-for-csid-340.patch b/queue-6.18/media-qcom-camss-fix-rdi-streaming-for-csid-340.patch
new file mode 100644 (file)
index 0000000..e2c063a
--- /dev/null
@@ -0,0 +1,91 @@
+From stable+bounces-295296-greg=kroah.com@vger.kernel.org Wed Aug  5 02:10:26 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue,  4 Aug 2026 20:08:33 -0400
+Subject: media: qcom: camss: Fix RDI streaming for CSID 340
+To: stable@vger.kernel.org
+Cc: Bryan O'Donoghue <bryan.odonoghue@linaro.org>, Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>, Loic Poulain <loic.poulain@oss.qualcomm.com>, Bryan O'Donoghue <bod@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260805000833.3902619-2-sashal@kernel.org>
+
+From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+
+[ Upstream commit cc1c35619c9895f918f9e388e3caa6ab2ff2fbe7 ]
+
+Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we
+have replicated throughout CAMSS where we use the VC number to populate
+both the VC fields and port fields of the CSID means that in practice only
+VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works.
+
+Fix that for CSID 340 by separating VC and port. Fix to VC zero as a bugfix
+we will look to properly populate the VC field with follow on patches
+later.
+
+Fixes: f0fc808a466a ("media: qcom: camss: Add CSID 340 support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+Reviewed-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
+Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Signed-off-by: Bryan O'Donoghue <bod@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/platform/qcom/camss/camss-csid-340.c |   18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/platform/qcom/camss/camss-csid-340.c
++++ b/drivers/media/platform/qcom/camss/camss-csid-340.c
+@@ -74,9 +74,9 @@ static void __csid_ctrl_rdi(struct csid_
+       writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi));
+ }
+-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
++static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
+ {
+-      struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
++      struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
+       const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+                                                                  csid->res->formats->nformats,
+                                                                  input_format->code);
+@@ -88,14 +88,14 @@ static void __csid_configure_rdi_stream(
+        * the four least significant bits of the five bit VC
+        * bitfield to generate an internal CID value.
+        *
+-       * CSID_RDI_CFG0(vc)
++       * CSID_RDI_CFG0(port)
+        * DT_ID : 28:27
+        * VC    : 26:22
+        * DT    : 21:16
+        *
+        * CID   : VC 3:0 << 2 | DT_ID 1:0
+        */
+-      dt_id = vc & 0x03;
++      dt_id = port & 0x03;
+       val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */
+       val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type);
+@@ -105,10 +105,11 @@ static void __csid_configure_rdi_stream(
+       if (enable)
+               val |= CSID_RDI_CFG0_ENABLE;
+-      dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x vc=%u)\n",
+-              csid->id, enable ? "enable" : "disable", format->data_type, vc);
++      dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n",
++              csid->id, enable ? "enable" : "disable", format->data_type,
++              port, vc);
+-      writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
++      writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));
+ }
+ static void csid_configure_stream(struct csid_device *csid, u8 enable)
+@@ -117,9 +118,10 @@ static void csid_configure_stream(struct
+       __csid_configure_rx(csid, &csid->phy);
++      /* Loop through all enabled ports and configure a stream for each */
+       for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+               if (csid->phy.en_vc & BIT(i)) {
+-                      __csid_configure_rdi_stream(csid, enable, i);
++                      __csid_configure_rdi_stream(csid, enable, i, 0);
+                       __csid_ctrl_rdi(csid, enable, i);
+               }
+       }
diff --git a/queue-6.18/media-uapi-rkisp-correct-name-version-enum.patch b/queue-6.18/media-uapi-rkisp-correct-name-version-enum.patch
new file mode 100644 (file)
index 0000000..1f66260
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-296805-greg=kroah.com@vger.kernel.org Thu Aug  6 14:46:50 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu,  6 Aug 2026 08:46:34 -0400
+Subject: media: uapi: rkisp: Correct name version enum
+To: stable@vger.kernel.org
+Cc: "Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>, "Laurent Pinchart" <laurent.pinchart@ideasonboard.com>, "Hans Verkuil" <hverkuil+cisco@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260806124634.299943-1-sashal@kernel.org>
+
+From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+
+[ Upstream commit c4c01c4fd4a3916ffdfb35ad9f511c48e289f51c ]
+
+The name of the enum to hold the mapping of parameter buffer versions
+have a typo in the name, correct it. While this is a uAPI header the
+impact should be minimal as the enum is only used as a collection for
+the one version number supported.
+
+Fixes: e9d05e9d5db1 ("media: uapi: rkisp1-config: Add extensible params format")
+Cc: stable@vger.kernel.org
+Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Link: https://patch.msgid.link/20260501190339.3449193-1-niklas.soderlund+renesas@ragnatech.se
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+[ Adjusted context to keep the tree's `RKISP1_EXT_PARAM_BUFFER_V1 = 1` initializer instead of upstream's `V4L2_ISP_PARAMS_VERSION_V1`, which does not exist in this tree. ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/uapi/linux/rkisp1-config.h |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/include/uapi/linux/rkisp1-config.h
++++ b/include/uapi/linux/rkisp1-config.h
+@@ -1583,11 +1583,11 @@ struct rkisp1_ext_params_wdr_config {
+       sizeof(struct rkisp1_ext_params_wdr_config))
+ /**
+- * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version
++ * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version
+  *
+  * @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters
+  */
+-enum rksip1_ext_param_buffer_version {
++enum rkisp1_ext_param_buffer_version {
+       RKISP1_EXT_PARAM_BUFFER_V1 = 1,
+ };
+@@ -1667,7 +1667,7 @@ enum rksip1_ext_param_buffer_version {
+  *    +---------------------------------------------------------------------+
+  *
+  * @version: The RkISP1 extensible parameters buffer version, see
+- *         :c:type:`rksip1_ext_param_buffer_version`
++ *         :c:type:`rkisp1_ext_param_buffer_version`
+  * @data_size: The RkISP1 configuration data effective size, excluding this
+  *           header
+  * @data: The RkISP1 extensible configuration data blocks
index 57cb4f230241f7ec6fcb2a73c2c3f54e83eff08d..8ffe76d83c3f169ead386b5c3b2925d5009b21fb 100644 (file)
@@ -362,3 +362,11 @@ net-handshake-take-a-long-lived-file-reference-at-su.patch
 net-handshake-hand-off-the-pinned-file-reference-to-.patch
 net-handshake-close-the-submit-side-sock_hold-race.patch
 net-handshake-drain-pending-requests-at-net-namespac.patch
+usb-typec-ucsi-split-connector-lock-classes.patch
+usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch
+media-chips-media-wave5-support-cbp-profile.patch
+media-qcom-camss-csid-340-fix-unused-variables.patch
+media-qcom-camss-fix-rdi-streaming-for-csid-340.patch
+media-uapi-rkisp-correct-name-version-enum.patch
+wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch
+userfaultfd-prevent-registration-of-special-vmas.patch
diff --git a/queue-6.18/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch b/queue-6.18/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch
new file mode 100644 (file)
index 0000000..97f04b0
--- /dev/null
@@ -0,0 +1,173 @@
+From sashal@kernel.org Thu Jul 30 21:29:55 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Jul 2026 15:29:52 -0400
+Subject: usb: typec: ucsi: Fix race condition and ordering in port unregistration
+To: stable@vger.kernel.org
+Cc: Andrei Kuchynski <akuchynski@chromium.org>, stable <stable@kernel.org>, Benson Leung <bleung@chromium.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260730192952.3124595-2-sashal@kernel.org>
+
+From: Andrei Kuchynski <akuchynski@chromium.org>
+
+[ Upstream commit 7aa7d4bf9d3fa9a6a47b640ad103ab433b7ff261 ]
+
+A synchronization issue exists during port unregistration where pending
+partner work items can race against workqueue destruction, leading to
+use-after-free conditions:
+
+  cros_ec_ucsi cros_ec_ucsi.3.auto: error -ETIMEDOUT: PPM init failed
+  BUG: kernel NULL pointer dereference, address: 0000000000000000
+  RIP: 0010:__queue_work+0x83/0x4a0
+  Call Trace:
+    <IRQ>
+    __cfi_delayed_work_timer_fn+0x10/0x10
+    run_timer_softirq+0x3b6/0xbd0
+    sched_clock_cpu+0xc/0x110
+    irq_exit_rcu+0x18d/0x330
+    fred_sysvec_apic_timer_interrupt+0x5e/0x80
+
+Fix this by ensuring strict ordering and proper serialization during
+teardown:
+
+1. Move ucsi_unregister_partner() to the beginning of the teardown
+sequence and protect it under the connector mutex lock.
+2. Ensure all pending partner tasks are explicitly flushed and finished
+before the workqueue is destroyed.
+3. Switch from mod_delayed_work() to a cancel_delayed_work() and
+queue_delayed_work() sequence. This guarantees that items currently marked
+as pending won't be scheduled an additional time, preventing a double
+release of resources which leads to the following crash:
+
+  Oops: general protection fault, probably for non-canonical address
+    0xdead000000000122: 0000 [#1] SMP NOPTI
+  Workqueue: cros_ec_ucsi.3.auto-con2 ucsi_poll_worker
+  RIP: 0010:ucsi_poll_worker+0x65/0x1e0
+  Call Trace:
+  <TASK>
+    process_scheduled_works+0x218/0x6d0
+    worker_thread+0x188/0x3f0
+    __cfi_worker_thread+0x10/0x10
+    kthread+0x226/0x2a0
+
+To ensure these rules are applied identically across both the normal
+teardown and the ucsi_init() error paths, consolidate the cleanup logic
+into a new helper, ucsi_unregister_port().
+
+Cc: stable <stable@kernel.org>
+Fixes: b9aa02ca39a4 ("usb: typec: ucsi: Add polling mechanism for partner tasks like alt mode checking")
+Fixes: b13abcb7ddd8 ("usb: typec: ucsi: Fix NULL pointer access")
+Fixes: fac4b8633fd6 ("usb: ucsi: Ensure connector delayed work items are flushed")
+Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
+Reviewed-by: Benson Leung <bleung@chromium.org>
+Link: https://patch.msgid.link/20260707141736.1635698-1-akuchynski@chromium.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c |   82 +++++++++++++++++++-----------------------
+ 1 file changed, 39 insertions(+), 43 deletions(-)
+
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -1891,6 +1891,42 @@ out_unlock:
+       return ret;
+ }
++static void ucsi_unregister_port(struct ucsi_connector *con)
++{
++      struct ucsi_work *uwork;
++
++      if (con->wq) {
++              mutex_lock(&con->lock);
++              ucsi_unregister_partner(con);
++              /*
++               * queue delayed items immediately so they can execute
++               * and free themselves before the wq is destroyed
++               */
++              list_for_each_entry(uwork, &con->partner_tasks, node) {
++                      if (cancel_delayed_work(&uwork->work))
++                              queue_delayed_work(con->wq, &uwork->work, 0);
++              }
++              mutex_unlock(&con->lock);
++
++              destroy_workqueue(con->wq);
++              con->wq = NULL;
++      } else {
++              ucsi_unregister_partner(con);
++      }
++
++      ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
++      ucsi_unregister_port_psy(con);
++
++      usb_power_delivery_unregister_capabilities(con->port_sink_caps);
++      con->port_sink_caps = NULL;
++      usb_power_delivery_unregister_capabilities(con->port_source_caps);
++      con->port_source_caps = NULL;
++      usb_power_delivery_unregister(con->pd);
++      con->pd = NULL;
++      typec_unregister_port(con->port);
++      con->port = NULL;
++}
++
+ static u64 ucsi_get_supported_notifications(struct ucsi *ucsi)
+ {
+       u16 features = ucsi->cap.features;
+@@ -2017,22 +2053,8 @@ err_unregister:
+       for (i = 0; i < ucsi->cap.num_connectors; i++)
+               lockdep_unregister_key(&connector[i].lock_key);
+-      for (con = connector; con->port; con++) {
+-              if (con->wq)
+-                      destroy_workqueue(con->wq);
+-              ucsi_unregister_partner(con);
+-              ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
+-              ucsi_unregister_port_psy(con);
+-
+-              usb_power_delivery_unregister_capabilities(con->port_sink_caps);
+-              con->port_sink_caps = NULL;
+-              usb_power_delivery_unregister_capabilities(con->port_source_caps);
+-              con->port_source_caps = NULL;
+-              usb_power_delivery_unregister(con->pd);
+-              con->pd = NULL;
+-              typec_unregister_port(con->port);
+-              con->port = NULL;
+-      }
++      for (con = connector; con->port; con++)
++              ucsi_unregister_port(con);
+       kfree(connector);
+ err_reset:
+       memset(&ucsi->cap, 0, sizeof(ucsi->cap));
+@@ -2260,33 +2282,7 @@ void ucsi_unregister(struct ucsi *ucsi)
+       for (i = 0; i < ucsi->cap.num_connectors; i++) {
+               cancel_work_sync(&ucsi->connector[i].work);
+-
+-              if (ucsi->connector[i].wq) {
+-                      struct ucsi_work *uwork;
+-
+-                      mutex_lock(&ucsi->connector[i].lock);
+-                      /*
+-                       * queue delayed items immediately so they can execute
+-                       * and free themselves before the wq is destroyed
+-                       */
+-                      list_for_each_entry(uwork, &ucsi->connector[i].partner_tasks, node)
+-                              mod_delayed_work(ucsi->connector[i].wq, &uwork->work, 0);
+-                      mutex_unlock(&ucsi->connector[i].lock);
+-                      destroy_workqueue(ucsi->connector[i].wq);
+-              }
+-
+-              ucsi_unregister_partner(&ucsi->connector[i]);
+-              ucsi_unregister_altmodes(&ucsi->connector[i],
+-                                       UCSI_RECIPIENT_CON);
+-              ucsi_unregister_port_psy(&ucsi->connector[i]);
+-
+-              usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps);
+-              ucsi->connector[i].port_sink_caps = NULL;
+-              usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps);
+-              ucsi->connector[i].port_source_caps = NULL;
+-              usb_power_delivery_unregister(ucsi->connector[i].pd);
+-              ucsi->connector[i].pd = NULL;
+-              typec_unregister_port(ucsi->connector[i].port);
++              ucsi_unregister_port(&ucsi->connector[i]);
+               lockdep_unregister_key(&ucsi->connector[i].lock_key);
+       }
diff --git a/queue-6.18/usb-typec-ucsi-split-connector-lock-classes.patch b/queue-6.18/usb-typec-ucsi-split-connector-lock-classes.patch
new file mode 100644 (file)
index 0000000..2320b4a
--- /dev/null
@@ -0,0 +1,124 @@
+From sashal@kernel.org Thu Jul 30 21:29:55 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Jul 2026 15:29:51 -0400
+Subject: usb: typec: ucsi: split connector lock classes
+To: stable@vger.kernel.org
+Cc: Sergey Senozhatsky <senozhatsky@chromium.org>, Heikki Krogerus <heikki.krogerus@linux.intel.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260730192952.3124595-1-sashal@kernel.org>
+
+From: Sergey Senozhatsky <senozhatsky@chromium.org>
+
+[ Upstream commit 8c22256bbafad3dc5fdbe9f684d045b67ff06a68 ]
+
+Lockdep detects a possible recursive locking scenario during
+ucsi init:
+
+[    5.418616] ============================================
+[    5.418634] WARNING: possible recursive locking detected
+[    5.418706] --------------------------------------------
+[    5.418725] kworker/4:1/82 is trying to acquire lock:
+[    5.418759] ffff888119a34648 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi]
+[    5.418801]
+               but task is already holding lock:
+[    5.418835] ffff888119a34080 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi]
+[    5.418884]
+               other info that might help us debug this:
+[    5.418904]  Possible unsafe locking scenario:
+
+[    5.418937]        CPU0
+[    5.418956]        ----
+[    5.418991]   lock(&con->lock);
+[    5.419013]   lock(&con->lock);
+[    5.419033]
+                *** DEADLOCK ***
+
+[    5.419387] Call Trace:
+[    5.419406]  <TASK>
+[    5.419425]  dump_stack_lvl+0x61/0xa0
+[    5.419448]  print_deadlock_bug+0x4a6/0x650
+[    5.419483]  __lock_acquire+0x62b6/0x7f50
+[    5.419507]  lock_acquire+0x11b/0x390
+[    5.419654]  __mutex_lock+0xbc/0xcd0
+[    5.419741]  ucsi_init_work+0x1a78/0x2eb0
+[    5.419785]  ? worker_thread+0xf53/0x2bc0
+[    5.419819]  worker_thread+0xff4/0x2bc0
+[    5.419842]  kthread+0x2a7/0x330
+[    5.419863]  ? __pfx_worker_thread+0x10/0x10
+[    5.419896]  ? __pfx_kthread+0x10/0x10
+[    5.419916]  ret_from_fork+0x38/0x70
+[    5.419936]  ? __pfx_kthread+0x10/0x10
+[    5.419969]  ret_from_fork_asm+0x1b/0x30
+[    5.419991]  </TASK>
+[    5.420009] ---[ end trace 0000000000000000 ]---
+
+The problem is that all connector locks belong to the same
+lockdep lock class, so the following loop:
+
+       for (i = 0; i < ucsi->cap.num_connectors; i++)
+               ucsi_register_port(connector[i])
+                       mutex_lock(&connector[i]->lock)
+
+looks like a recursive acquire of the same mutex.  Put each connector
+lock into a dedicated lock class so that lockdep doesn't see it as a
+possible recursion.
+
+Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260515060042.136083-1-senozhatsky@chromium.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c |    8 ++++++++
+ drivers/usb/typec/ucsi/ucsi.h |    1 +
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -1741,6 +1741,7 @@ static int ucsi_register_port(struct ucs
+       INIT_WORK(&con->work, ucsi_handle_connector_change);
+       init_completion(&con->complete);
+       mutex_init(&con->lock);
++      lockdep_set_class(&con->lock, &con->lock_key);
+       INIT_LIST_HEAD(&con->partner_tasks);
+       con->ucsi = ucsi;
+@@ -1981,6 +1982,9 @@ static int ucsi_init(struct ucsi *ucsi)
+               goto err_reset;
+       }
++      for (i = 0; i < ucsi->cap.num_connectors; i++)
++              lockdep_register_key(&connector[i].lock_key);
++
+       /* Register all connectors */
+       for (i = 0; i < ucsi->cap.num_connectors; i++) {
+               connector[i].num = i + 1;
+@@ -2010,6 +2014,9 @@ static int ucsi_init(struct ucsi *ucsi)
+       return 0;
+ err_unregister:
++      for (i = 0; i < ucsi->cap.num_connectors; i++)
++              lockdep_unregister_key(&connector[i].lock_key);
++
+       for (con = connector; con->port; con++) {
+               if (con->wq)
+                       destroy_workqueue(con->wq);
+@@ -2280,6 +2287,7 @@ void ucsi_unregister(struct ucsi *ucsi)
+               usb_power_delivery_unregister(ucsi->connector[i].pd);
+               ucsi->connector[i].pd = NULL;
+               typec_unregister_port(ucsi->connector[i].port);
++              lockdep_unregister_key(&ucsi->connector[i].lock_key);
+       }
+       kfree(ucsi->connector);
+--- a/drivers/usb/typec/ucsi/ucsi.h
++++ b/drivers/usb/typec/ucsi/ucsi.h
+@@ -502,6 +502,7 @@ struct ucsi_connector {
+       struct ucsi *ucsi;
+       struct mutex lock; /* port lock */
++      struct lock_class_key lock_key;
+       struct work_struct work;
+       struct completion complete;
+       struct workqueue_struct *wq;
diff --git a/queue-6.18/userfaultfd-prevent-registration-of-special-vmas.patch b/queue-6.18/userfaultfd-prevent-registration-of-special-vmas.patch
new file mode 100644 (file)
index 0000000..8267933
--- /dev/null
@@ -0,0 +1,62 @@
+From stable+bounces-297128-greg=kroah.com@vger.kernel.org Fri Aug  7 05:36:00 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu,  6 Aug 2026 23:34:59 -0400
+Subject: userfaultfd: prevent registration of special VMAs
+To: stable@vger.kernel.org
+Cc: "Mike Rapoport (Microsoft)" <rppt@kernel.org>, vova tokarev <vladimirelitokarev@gmail.com>, "David Hildenbrand (Arm)" <david@kernel.org>, Lorenzo Stoakes <ljs@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>, Linus Torvalds <torvalds@linuxfoundation.org>, Oleg Nesterov <oleg@redhat.com>, Peter Xu <peterx@redhat.com>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260807033459.1725973-1-sashal@kernel.org>
+
+From: "Mike Rapoport (Microsoft)" <rppt@kernel.org>
+
+[ Upstream commit 3c58f641e813c3c71039f8fd4d4e2a3aab713288 ]
+
+Vova Tokarev says:
+
+  userfaultfd allows registration on shadow stack VMAs.  With userfaultfd
+  access, you can register on the shadow stack, discard a page ... and
+  inject a page with chosen return addresses via UFFDIO_COPY.
+
+Update vma_can_userfault() to reject VM_SHADOW_STACK.
+
+While on it, also reject VM_SPECIAL so that if a driver would implement
+vm_uffd_ops, it wouldn't be possible to register special VMAs with
+userfaultfd.
+
+Since VM_SPECIAL includes VM_DONTEXPAND which is set but hugetlb, exclude
+hugetlb VMAs from the check for VM_SPECIAL.
+
+Link: https://lore.kernel.org/20260618095017.2553004-1-rppt@kernel.org
+Fixes: 54007f818206 ("mm: Introduce VM_SHADOW_STACK for shadow stack memory")
+Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Reported-by: vova tokarev <vladimirelitokarev@gmail.com>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Reviewed-by: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Cc: Christian Brauner <brauner@kernel.org>
+Cc: Jan Kara <jack@suse.cz>
+Cc: Linus Torvalds <torvalds@linuxfoundation.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Oleg Nesterov <oleg@redhat.com>
+Cc: Peter Xu <peterx@redhat.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/userfaultfd_k.h |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/include/linux/userfaultfd_k.h
++++ b/include/linux/userfaultfd_k.h
+@@ -214,7 +214,10 @@ static inline bool vma_can_userfault(str
+ {
+       vm_flags &= __VM_UFFD_FLAGS;
+-      if (vma->vm_flags & VM_DROPPABLE)
++      if (vma->vm_flags & (VM_DROPPABLE | VM_SHADOW_STACK))
++              return false;
++
++      if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL))
+               return false;
+       if ((vm_flags & VM_UFFD_MINOR) &&
diff --git a/queue-6.18/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch b/queue-6.18/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch
new file mode 100644 (file)
index 0000000..af217ad
--- /dev/null
@@ -0,0 +1,292 @@
+From stable+bounces-296817-greg=kroah.com@vger.kernel.org Thu Aug  6 15:18:34 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu,  6 Aug 2026 09:17:40 -0400
+Subject: wifi: brcmfmac: drain bus_reset work on device removal
+To: stable@vger.kernel.org
+Cc: Fan Wu <fanwu01@zju.edu.cn>, Arend van Spriel <arend.vanspriel@broadcom.com>, Johannes Berg <johannes.berg@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806131740.402812-1-sashal@kernel.org>
+
+From: Fan Wu <fanwu01@zju.edu.cn>
+
+[ Upstream commit 43b25879f004c98defa2776bedc6ca4763c51945 ]
+
+brcmf_fw_crashed() and the debugfs "reset" entry both schedule
+drvr->bus_reset, whose callback recovers drvr through container_of()
+and dereferences it.  The removal path frees drvr (brcmf_free ->
+wiphy_free) without draining the work, so a bus_reset callback pending
+or running during removal can outlive drvr.
+
+Cancellation cannot live in brcmf_detach() or brcmf_free(): the work
+callback reaches teardown through the bus .reset op (PCIe
+brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset ->
+brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for
+the running work and deadlock.
+
+Add a per-bus mutex (bus_reset_lock) and route all arming through
+brcmf_bus_schedule_reset(), which under the lock skips when the bus is
+marked removing.  Each bus remove entry calls
+brcmf_bus_cancel_reset_work(), which under the same lock sets removing
+and cancels the work.  Holding the mutex across cancel_work_sync() makes
+the set-removing + drain step atomic.  Every producer reaches the arming
+path from process context -- the PCIe firmware-halt notification runs in
+the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail
+path runs from the data workqueue -- so the mutex is taken only in
+sleepable contexts.  Where applicable the remove entry first stops the
+firmware-crash producer: on PCIe mask the mailbox and synchronize_irq;
+on SDIO unregister the bus interrupt and cancel the data worker, which
+also reports firmware halts through brcmf_fw_crashed().  The mutex is
+initialized at bus allocation.  The SDIO suspend power-off path frees
+drvr through the same brcmf_sdiod_remove() and takes the same lock;
+resume re-allows the work only on a successful re-probe.
+
+Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire
+before brcmf_attach() wires up drvr, and it dereferences drvr
+(bphy_err/brcmf_dev_coredump) before reaching the arming gate.
+
+The bus_reset work is shared across buses, so the drain is applied to
+every remove path: PCIe (the .reset op introduced by the Fixes commit),
+SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the
+debugfs "reset" entry).  cancel_work_sync() drains a running or pending
+bus_reset work item before removal frees drvr, and patch 1/2 makes the
+scratch-buffer release safe when reset teardown has already released
+those DMA buffers.
+
+This patch fixes the lifetime of the bus_reset work item itself.  It does
+not attempt to address the separate, pre-existing lifetime of the
+asynchronous firmware completion started by the PCIe reset path.  That
+callback needs its own lifetime/ownership protocol and is being tracked
+separately.
+
+This issue was found by an in-house static analysis tool.
+
+Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash")
+Cc: stable@vger.kernel.org
+Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
+Assisted-by: Codex:gpt-5.6
+Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+Link: https://patch.msgid.link/20260718024353.3147201-3-fanwu01@zju.edu.cn
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c |   13 +++
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h    |    6 +
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c   |   46 ++++++++++++--
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c   |    6 +
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c   |    6 +
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h   |    1 
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c    |    3 
+ 7 files changed, 77 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+@@ -1070,6 +1070,7 @@ static int brcmf_ops_sdio_probe(struct s
+       bus_if = kzalloc(sizeof(*bus_if), GFP_KERNEL);
+       if (!bus_if)
+               return -ENOMEM;
++      mutex_init(&bus_if->bus_reset_lock);
+       sdiodev = kzalloc(sizeof(*sdiodev), GFP_KERNEL);
+       if (!sdiodev) {
+               kfree(bus_if);
+@@ -1131,6 +1132,14 @@ static void brcmf_ops_sdio_remove(struct
+               if (func->num != 1)
+                       return;
++              /* Drain bus_reset before the shared brcmf_sdiod_remove()
++               * teardown, which the SDIO reset callback also reaches.  The
++               * data worker can arm bus_reset via brcmf_fw_crashed(); cancel
++               * it first.
++               */
++              brcmf_sdio_cancel_datawork(sdiodev->bus);
++              brcmf_bus_cancel_reset_work(bus_if);
++
+               /* only proceed with rest of cleanup if func 1 */
+               brcmf_sdiod_remove(sdiodev);
+@@ -1205,6 +1214,8 @@ static int brcmf_ops_sdio_suspend(struct
+       } else {
+               /* power will be cut so remove device, probe again in resume */
+               brcmf_sdiod_intr_unregister(sdiodev);
++              brcmf_sdio_cancel_datawork(sdiodev->bus);
++              brcmf_bus_cancel_reset_work(bus_if);
+               ret = brcmf_sdiod_remove(sdiodev);
+               if (ret)
+                       brcmf_err("Failed to remove device on suspend\n");
+@@ -1230,6 +1241,8 @@ static int brcmf_ops_sdio_resume(struct
+               ret = brcmf_sdiod_probe(sdiodev);
+               if (ret)
+                       brcmf_err("Failed to probe device on resume\n");
++              else
++                      brcmf_bus_allow_reset_work(bus_if);
+       } else {
+               if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
+                       disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+@@ -9,6 +9,7 @@
+ #include <linux/kernel.h>
+ #include <linux/firmware.h>
+ #include <linux/device.h>
++#include <linux/mutex.h>
+ #include "debug.h"
+ /* IDs of the 6 default common rings of msgbuf protocol */
+@@ -179,6 +180,8 @@ struct brcmf_bus {
+       enum brcmf_fwvendor fwvid;
+       bool always_use_fws_queue;
+       bool wowl_supported;
++      bool removing;          /* device removal in progress; quiesce async work */
++      struct mutex bus_reset_lock;
+       const struct brcmf_bus_ops *ops;
+       struct brcmf_bus_msgbuf *msgbuf;
+@@ -186,6 +189,9 @@ struct brcmf_bus {
+       struct list_head list;
+ };
++void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if);
++void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if);
++
+ /*
+  * callback wrappers
+  */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -1167,6 +1167,35 @@ static int brcmf_revinfo_read(struct seq
+       return 0;
+ }
++/*
++ * Serialize arming from debugfs reset and brcmf_fw_crashed() against
++ * teardown.  The remove path sets ->removing and drains the work while
++ * holding bus_reset_lock, so a racing armer is either drained or skips it.
++ */
++static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if)
++{
++      mutex_lock(&bus_if->bus_reset_lock);
++      if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing)
++              schedule_work(&bus_if->drvr->bus_reset);
++      mutex_unlock(&bus_if->bus_reset_lock);
++}
++
++void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if)
++{
++      mutex_lock(&bus_if->bus_reset_lock);
++      bus_if->removing = true;
++      if (bus_if->drvr)
++              cancel_work_sync(&bus_if->drvr->bus_reset);
++      mutex_unlock(&bus_if->bus_reset_lock);
++}
++
++void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if)
++{
++      mutex_lock(&bus_if->bus_reset_lock);
++      bus_if->removing = false;
++      mutex_unlock(&bus_if->bus_reset_lock);
++}
++
+ static void brcmf_core_bus_reset(struct work_struct *work)
+ {
+       struct brcmf_pub *drvr = container_of(work, struct brcmf_pub,
+@@ -1187,7 +1216,7 @@ static ssize_t bus_reset_write(struct fi
+       if (value != 1)
+               return -EINVAL;
+-      schedule_work(&drvr->bus_reset);
++      brcmf_bus_schedule_reset(drvr->bus_if);
+       return count;
+ }
+@@ -1417,14 +1446,23 @@ void brcmf_dev_coredump(struct device *d
+ void brcmf_fw_crashed(struct device *dev)
+ {
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+-      struct brcmf_pub *drvr = bus_if->drvr;
++      struct brcmf_pub *drvr;
++
++      /* May fire before brcmf_attach() wires up drvr, or after removal
++       * has cleared it; guard the derefs below (and the arming gate in
++       * brcmf_bus_schedule_reset() already checks drvr/->removing).
++       */
++      if (!bus_if)
++              return;
++      drvr = bus_if->drvr;
++      if (!drvr)
++              return;
+       bphy_err(drvr, "Firmware has halted or crashed\n");
+       brcmf_dev_coredump(dev);
+-      if (drvr->bus_reset.func)
+-              schedule_work(&drvr->bus_reset);
++      brcmf_bus_schedule_reset(bus_if);
+ }
+ void brcmf_detach(struct device *dev)
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+@@ -2504,6 +2504,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
+               ret = -ENOMEM;
+               goto fail;
+       }
++      mutex_init(&bus->bus_reset_lock);
+       bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL);
+       if (!bus->msgbuf) {
+               ret = -ENOMEM;
+@@ -2599,6 +2600,11 @@ brcmf_pcie_remove(struct pci_dev *pdev)
+       if (devinfo->ci)
+               brcmf_pcie_intr_disable(devinfo);
++      if (devinfo->irq_allocated)
++              synchronize_irq(pdev->irq);
++
++      brcmf_bus_cancel_reset_work(bus);
++
+       brcmf_detach(&pdev->dev);
+       brcmf_free(&pdev->dev);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+@@ -4560,6 +4560,12 @@ fail:
+       return ret;
+ }
++void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus)
++{
++      if (bus)
++              cancel_work_sync(&bus->datawork);
++}
++
+ /* Detach and free everything */
+ void brcmf_sdio_remove(struct brcmf_sdio *bus)
+ {
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+@@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdio
+ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
+ void brcmf_sdio_remove(struct brcmf_sdio *bus);
+ void brcmf_sdio_isr(struct brcmf_sdio *bus, bool in_isr);
++void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus);
+ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active);
+ void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+@@ -1260,6 +1260,7 @@ static int brcmf_usb_probe_cb(struct brc
+               ret = -ENOMEM;
+               goto fail;
+       }
++      mutex_init(&bus->bus_reset_lock);
+       bus->dev = dev;
+       bus_pub->bus = bus;
+@@ -1329,6 +1330,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usb
+               return;
+       brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo);
++      brcmf_bus_cancel_reset_work(devinfo->bus_pub.bus);
++
+       brcmf_detach(devinfo->dev);
+       brcmf_free(devinfo->dev);
+       kfree(devinfo->bus_pub.bus);