diff options
439 files changed, 4729 insertions, 1652 deletions
@@ -66,6 +66,11 @@ Alex Hung <alexhung@gmail.com> <alex.hung@canonical.com> Alex Shi <alexs@kernel.org> <alex.shi@intel.com> Alex Shi <alexs@kernel.org> <alex.shi@linaro.org> Alex Shi <alexs@kernel.org> <alex.shi@linux.alibaba.com> +Alice Mikityanska <alice.kernel@fastmail.im> <maxtram95@gmail.com> +Alice Mikityanska <alice.kernel@fastmail.im> <maximmi@mellanox.com> +Alice Mikityanska <alice.kernel@fastmail.im> <maximmi@nvidia.com> +Alice Mikityanska <alice.kernel@fastmail.im> <maxim@isovalent.com> +Alice Mikityanska <alice.kernel@fastmail.im> <alice@isovalent.com> Aloka Dixit <quic_alokad@quicinc.com> <alokad@codeaurora.org> Al Viro <viro@ftp.linux.org.uk> Al Viro <viro@zenIV.linux.org.uk> @@ -540,6 +545,7 @@ Maciej W. Rozycki <macro@mips.com> <macro@imgtec.com> Maciej W. Rozycki <macro@orcam.me.uk> <macro@linux-mips.org> Maharaja Kennadyrajan <quic_mkenna@quicinc.com> <mkenna@codeaurora.org> Maheshwar Ajja <quic_majja@quicinc.com> <majja@codeaurora.org> +MaÃra Canal <mairacanal@riseup.net> <maira.canal@usp.br> Malathi Gottam <quic_mgottam@quicinc.com> <mgottam@codeaurora.org> Manikanta Pubbisetty <quic_mpubbise@quicinc.com> <mpubbise@codeaurora.org> Manivannan Sadhasivam <mani@kernel.org> <manivannanece23@gmail.com> @@ -585,8 +591,6 @@ Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@osg.samsung.com> Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@redhat.com> Mauro Carvalho Chehab <mchehab@kernel.org> <m.chehab@samsung.com> Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@s-opensource.com> -Maxim Mikityanskiy <maxtram95@gmail.com> <maximmi@mellanox.com> -Maxim Mikityanskiy <maxtram95@gmail.com> <maximmi@nvidia.com> Maxime Ripard <mripard@kernel.org> <maxime@cerno.tech> Maxime Ripard <mripard@kernel.org> <maxime.ripard@bootlin.com> Maxime Ripard <mripard@kernel.org> <maxime.ripard@free-electrons.com> @@ -595,8 +599,8 @@ Mayuresh Janorkar <mayur@ti.com> Md Sadre Alam <quic_mdalam@quicinc.com> <mdalam@codeaurora.org> Miaoqing Pan <quic_miaoqing@quicinc.com> <miaoqing@codeaurora.org> Michael Buesch <m@bues.ch> -Michal Grzeschik <mgr@kernel.org> <m.grzeschik@pengutronix.de> -Michal Grzeschik <mgr@kernel.org> <mgr@pengutronix.de> +Michael Grzeschik <mgr@kernel.org> <m.grzeschik@pengutronix.de> +Michael Grzeschik <mgr@kernel.org> <mgr@pengutronix.de> Michael Riesch <michael.riesch@collabora.com> <michael.riesch@wolfvision.net> Michal Simek <michal.simek@amd.com> <michal.simek@xilinx.com> Michel Dänzer <michel@tungstengraphics.com> @@ -817,6 +821,7 @@ Simon Wunderlich <sw@simonwunderlich.de> <simon.wunderlich@s2003.tu-chemnitz.de> Simon Wunderlich <sw@simonwunderlich.de> <simon.wunderlich@saxnet.de> Simon Wunderlich <sw@simonwunderlich.de> <simon@open-mesh.com> Simon Wunderlich <sw@simonwunderlich.de> <siwu@hrz.tu-chemnitz.de> +SJ Park <sj@kernel.org> Sricharan Ramabadhran <quic_srichara@quicinc.com> <sricharan@codeaurora.org> Srinivas Kandagatla <srini@kernel.org> <srinivas.kandagatla@st.com> Srinivas Kandagatla <srini@kernel.org> <srinivas.kandagatla@linaro.org> diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index b73e6bc28ea5..4fdec63a47d4 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -1,26 +1,26 @@ what: /sys/kernel/mm/damon/ Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Interface for Data Access MONitoring (DAMON). Contains files for controlling DAMON. For more details on DAMON itself, please refer to Documentation/admin-guide/mm/damon/index.rst. What: /sys/kernel/mm/damon/admin/ Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Interface for privileged users of DAMON. Contains files for controlling DAMON that aimed to be used by privileged users. What: /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON worker thread (kdamond) named '0' to 'N-1' under the kdamonds/ directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/state Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing 'on' or 'off' to this file makes the kdamond starts or stops, respectively. Reading the file returns the keywords based on the current status. Writing 'commit' to this file @@ -40,33 +40,33 @@ Description: Writing 'on' or 'off' to this file makes the kdamond starts or What: /sys/kernel/mm/damon/admin/kdamonds/<K>/pid Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the pid of the kdamond if it is running. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/refresh_ms Date: Jul 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the time interval for automatic DAMON status file contents update. Writing '0' disables the update. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/nr_contexts Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON context named '0' to 'N-1' under the contexts/ directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/avail_operations Date: Apr 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the available monitoring operations sets on the currently running kernel. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/operations Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a keyword for a monitoring operations set ('vaddr' for virtual address spaces monitoring, 'fvaddr' for fixed virtual address ranges monitoring, and 'paddr' for the physical address @@ -79,42 +79,42 @@ Description: Writing a keyword for a monitoring operations set ('vaddr' for What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/addr_unit Date: Aug 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing an integer to this file sets the 'address unit' parameter of the given operations set of the context. Reading the file returns the last-written 'address unit' value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/pause Date: Mar 2026 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a boolean keyword to this file sets the 'pause' request parameter for the context. Reading the file returns the last-written 'pause' value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/sample_us Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the sampling interval of the DAMON context in microseconds as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/aggr_us Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the aggregation interval of the DAMON context in microseconds as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/update_us Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the update interval of the DAMON context in microseconds as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/access_bp Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the monitoring intervals auto-tuning target DAMON-observed access events ratio within the given time interval (aggrs in same directory), in bp @@ -122,7 +122,7 @@ Description: Writing a value to this file sets the monitoring intervals What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/aggrs Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the time interval to achieve the monitoring intervals auto-tuning target DAMON-observed access events ratio (access_bp in same directory) within. @@ -130,14 +130,14 @@ Description: Writing a value to this file sets the time interval to achieve What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/min_sample_us Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the minimum value of auto-tuned sampling interval in microseconds. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/max_sample_us Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the maximum value of auto-tuned sampling interval in microseconds. Reading this file returns the value. @@ -145,42 +145,42 @@ Description: Writing a value to this file sets the maximum value of What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/nr_regions/min WDate: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the minimum number of monitoring regions of the DAMON context as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/nr_regions/max Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the maximum number of monitoring regions of the DAMON context as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/nr_targets Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON target of the context named '0' to 'N-1' under the contexts/ directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/pid_target Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the pid of the target process if the context is for virtual address spaces monitoring, respectively. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/obsolete_target Date: Oct 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the obsoleteness of the matching parameters commit destination target. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/regions/nr_regions Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for setting each DAMON target memory region of the context named '0' to 'N-1' under the regions/ directory. In @@ -190,181 +190,181 @@ Description: Writing a number 'N' to this file creates the number of What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/regions/<R>/start Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the start address of the monitoring region. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/targets/<T>/regions/<R>/end Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the end address of the monitoring region. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/nr_schemes Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON-based operation scheme of the context named '0' to 'N-1' under the schemes/ directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/action Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the action of the scheme. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/target_nid Date: Jun 2024 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Action's target NUMA node id. Supported by only relevant actions. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/apply_interval_us Date: Sep 2023 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a value to this file sets the action apply interval of the scheme in microseconds. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/sz/min Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the minimum size of the scheme's target regions in bytes. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/sz/max Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the maximum size of the scheme's target regions in bytes. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/nr_accesses/min Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the manimum 'nr_accesses' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/nr_accesses/max Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the maximum 'nr_accesses' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/age/min Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the minimum 'age' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/access_pattern/age/max Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the maximum 'age' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/ms Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the time quota of the scheme in milliseconds. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/bytes Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the size quota of the scheme in bytes. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/effective_bytes Date: Feb 2024 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading from this file gets the effective size quota of the scheme in bytes, which adjusted for the time quota and goals. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/reset_interval_ms Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the quotas charge reset interval of the scheme in milliseconds. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/nr_goals Date: Nov 2023 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for setting automatic tuning of the scheme's aggressiveness named '0' to 'N-1' under the goals/ directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/target_metric Date: Feb 2024 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the quota auto-tuning goal metric. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/target_value Date: Nov 2023 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the target value of the goal metric. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/current_value Date: Nov 2023 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the current value of the goal metric. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/nid Date: Apr 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the nid parameter of the goal. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goals/<G>/path Date: Oct 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the path parameter of the goal. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/goal_tuner Date: Mar 2026 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the goal-based effective quota auto-tuning algorithm to use. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/fail_charge_num Date: Mar 2026 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the action-failed memory quota charging ratio numerator. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/fail_charge_denom Date: Mar 2026 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the action-failed memory quota charging ratio denominator. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/weights/sz_permil Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the under-quota limit regions prioritization weight for 'size' in permil. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/weights/nr_accesses_permil Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the under-quota limit regions prioritization weight for 'nr_accesses' in permil. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/quotas/weights/age_permil Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the under-quota limit regions prioritization weight for 'age' in permil. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/metric Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the metric of the watermarks for the scheme. The writable/readable keywords for this file are 'none' for disabling the watermarks @@ -373,44 +373,44 @@ Description: Writing to and reading from this file sets and gets the metric What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/interval_us Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the metric check interval of the watermarks for the scheme in microseconds. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/high Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the high watermark of the scheme in permil. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/mid Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the mid watermark of the scheme in permil. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/watermarks/low Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the low watermark of the scheme in permil. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Directory for DAMON core layer-handled DAMOS filters. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/nr_filters Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for setting filters of the scheme named '0' to 'N-1' under the core_filters/ directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/type Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the type of the memory of the interest. 'anon' for anonymous pages, 'memcg' for specific memory cgroup, 'young' for young pages, @@ -419,62 +419,62 @@ Description: Writing to and reading from this file sets and gets the type of What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/memcg_path Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: If 'memcg' is written to the 'type' file, writing to and reading from this file sets and gets the path to the memory cgroup of the interest. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/addr_start Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: If 'addr' is written to the 'type' file, writing to or reading from this file sets or gets the start address of the address range for the filter. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/addr_end Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: If 'addr' is written to the 'type' file, writing to or reading from this file sets or gets the end address of the address range for the filter. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/min Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: If 'hugepage_size' is written to the 'type' file, writing to or reading from this file sets or gets the minimum size of the hugepage for the filter. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/max Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: If 'hugepage_size' is written to the 'type' file, writing to or reading from this file sets or gets the maximum size of the hugepage for the filter. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/damon_target_idx Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: If 'target' is written to the 'type' file, writing to or reading from this file sets or gets the index of the DAMON monitoring target of the interest. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/matching Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing 'Y' or 'N' to this file sets whether the filter is for the memory of the 'type', or all except the 'type'. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters/<F>/allow Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing 'Y' or 'N' to this file sets whether to allow or reject applying the scheme's action to the memory that satisfies the 'type' and the 'matching' of the directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/ops_filters Date: Feb 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Directory for DAMON operations set layer-handled DAMOS filters. Files under this directory works same to those of /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/core_filters @@ -482,7 +482,7 @@ Description: Directory for DAMON operations set layer-handled DAMOS filters. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/filters Date: Dec 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Directory for DAMOS filters. Files under this directory works same to those of /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/{core,ops}_filters @@ -491,14 +491,14 @@ Description: Directory for DAMOS filters. Files under this directory works What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/dests/nr_dests Date: Jul 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number 'N' to this file creates the number of directories for setting action destinations of the scheme named '0' to 'N-1' under the dests/ directory. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/dests/<D>/id Date: Jul 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the id of the DAMOS action destination. For DAMOS_MIGRATE_{HOT,COLD} actions, the destination node's node id can be written and @@ -506,98 +506,98 @@ Description: Writing to and reading from this file sets and gets the id of What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/dests/<D>/weight Date: Jul 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing to and reading from this file sets and gets the weight of the DAMOS action destination to select as the destination of each action among the destinations. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/nr_tried Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the number of regions that the action of the scheme has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/sz_tried Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the total size of regions that the action of the scheme has tried to be applied in bytes. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/nr_applied Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the number of regions that the action of the scheme has successfully applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/sz_applied Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the total size of regions that the action of the scheme has successfully applied in bytes. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/sz_ops_filter_passed Date: Dec 2024 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the total size of memory that passed DAMON operations layer-handled filters of the scheme in bytes. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/qt_exceeds Date: Mar 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the number of the exceed events of the scheme's quotas. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/nr_snapshots Date: Dec 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the total number of DAMON snapshots that the scheme has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/stats/max_nr_snapshots Date: Dec 2025 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Writing a number to this file sets the upper limit of nr_snapshots that deactivates the scheme when the limit is reached or exceeded. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/total_bytes Date: Jul 2023 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the total amount of memory that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/start Date: Oct 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the start address of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/end Date: Oct 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the end address of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/nr_accesses Date: Oct 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the 'nr_accesses' of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/age Date: Oct 2022 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the 'age' of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/schemes/<S>/tried_regions/<R>/sz_filter_passed Date: Dec 2024 -Contact: SeongJae Park <sj@kernel.org> +Contact: SJ Park <sj@kernel.org> Description: Reading this file returns the size of the memory in the region that passed DAMON operations layer-handled filters of the scheme in bytes. diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index 892979da098e..68c26a70bb64 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1585,31 +1585,31 @@ attribute-sets: type: u32 - name: mode - type: flag + type: u8 - name: guard - type: flag + type: u8 - name: protect - type: flag + type: u8 - name: fast-leave - type: flag + type: u8 - name: learning - type: flag + type: u8 - name: unicast-flood - type: flag + type: u8 - name: proxyarp - type: flag + type: u8 - name: learning-sync - type: flag + type: u8 - name: proxyarp-wifi - type: flag + type: u8 - name: root-id type: binary @@ -1656,34 +1656,34 @@ attribute-sets: type: pad - name: mcast-flood - type: flag + type: u8 - name: mcast-to-ucast - type: flag + type: u8 - name: vlan-tunnel - type: flag + type: u8 - name: bcast-flood - type: flag + type: u8 - name: group-fwd-mask type: u16 - name: neigh-suppress - type: flag + type: u8 - name: isolated - type: flag + type: u8 - name: backup-port type: u32 - name: mrp-ring-open - type: flag + type: u8 - name: mrp-in-open - type: flag + type: u8 - name: mcast-eht-hosts-limit type: u32 @@ -1692,10 +1692,10 @@ attribute-sets: type: u32 - name: locked - type: flag + type: u8 - name: mab - type: flag + type: u8 - name: mcast-n-groups type: u32 @@ -1704,7 +1704,7 @@ attribute-sets: type: u32 - name: neigh-vlan-suppress - type: flag + type: u8 - name: backup-nhid type: u32 diff --git a/Documentation/watchdog/watchdog-parameters.rst b/Documentation/watchdog/watchdog-parameters.rst index ec5f67c060cb..502cb6adbeda 100644 --- a/Documentation/watchdog/watchdog-parameters.rst +++ b/Documentation/watchdog/watchdog-parameters.rst @@ -59,7 +59,7 @@ advantechwdt: alim1535_wdt: timeout: - Watchdog timeout in seconds. (0 < timeout < 18000, default=60 + Watchdog timeout in seconds. (0 < timeout < 18000, default=60) nowayout: Watchdog cannot be stopped once started (default=kernel config parameter) @@ -68,7 +68,7 @@ alim1535_wdt: alim7101_wdt: timeout: - Watchdog timeout in seconds. (1<=timeout<=3600, default=30 + Watchdog timeout in seconds. (1<=timeout<=3600, default=30) use_gpio: Use the gpio watchdog (required by old cobalt boards). default=0/off/no diff --git a/MAINTAINERS b/MAINTAINERS index a674e36529f7..ff93f3954fd1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6478,7 +6478,7 @@ L: linux-cifs@vger.kernel.org L: samba-technical@lists.samba.org (moderated for non-subscribers) S: Supported W: https://wiki.samba.org/index.php/LinuxCIFS -T: git https://git.samba.org/sfrench/cifs-2.6.git +T: git https://git.samba.org/sfrench/cifs-2.6.git for-next F: Documentation/admin-guide/cifs/ F: fs/smb/client/ F: fs/smb/common/ @@ -7125,7 +7125,7 @@ W: https://docs.dasharo.com/ F: drivers/platform/x86/dasharo-acpi.c DAMON -M: SeongJae Park <sj@kernel.org> +M: SJ Park <sj@kernel.org> L: damon@lists.linux.dev L: linux-mm@kvack.org S: Maintained @@ -14142,7 +14142,7 @@ R: Sergey Senozhatsky <senozhatsky@chromium.org> R: Tom Talpey <tom@talpey.com> L: linux-cifs@vger.kernel.org S: Maintained -T: git https://git.samba.org/ksmbd.git +T: git https://git.samba.org/ksmbd.git ksmbd-for-next F: Documentation/filesystems/smb/ksmbd.rst F: fs/smb/common/ F: fs/smb/server/ @@ -17257,6 +17257,7 @@ R: Ryan Roberts <ryan.roberts@arm.com> R: Dev Jain <dev.jain@arm.com> R: Barry Song <baohua@kernel.org> R: Lance Yang <lance.yang@linux.dev> +R: Usama Arif <usama.arif@linux.dev> L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org @@ -19108,6 +19109,7 @@ F: drivers/nfc/ F: include/net/nfc/ F: include/uapi/linux/nfc.h F: net/nfc/ +F: tools/testing/selftests/nci/ NFC VIRTUAL NCI DEVICE DRIVER M: Bongsu Jeon <bongsu.jeon@samsung.com> @@ -25972,8 +25974,9 @@ F: Documentation/devicetree/bindings/phy/st,stm32mp25-combophy.yaml F: drivers/phy/st/phy-stm32-combophy.c STMMAC ETHERNET DRIVER +M: Maxime Chevallier <maxime.chevallier@bootlin.com> L: netdev@vger.kernel.org -S: Orphan +S: Maintained F: Documentation/networking/device_drivers/ethernet/stmicro/ F: drivers/net/ethernet/stmicro/stmmac/ @@ -27929,7 +27932,6 @@ F: drivers/usb/isp1760/* USB LAN78XX ETHERNET DRIVER M: Thangaraj Samynathan <Thangaraj.S@microchip.com> -M: Rengarajan Sundararajan <Rengarajan.S@microchip.com> M: UNGLinuxDriver@microchip.com L: netdev@vger.kernel.org S: Maintained diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 81c17320a588..f816f5d77f1a 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -131,7 +131,7 @@ * Annoyingly, the negative levels for Address size faults aren't laid out * contiguously (or in the desired order) */ -#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x25 : 0x2C) +#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x29 : 0x2C) #define ESR_ELx_FSC_ADDRSZ_L(n) ((n) < 0 ? ESR_ELx_FSC_ADDRSZ_nL(n) : \ (ESR_ELx_FSC_ADDRSZ + (n))) diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h index 65555284446e..7075a3bd2c61 100644 --- a/arch/arm64/include/asm/fixmap.h +++ b/arch/arm64/include/asm/fixmap.h @@ -78,8 +78,12 @@ enum fixed_addresses { /* * Temporary boot-time mappings, used by early_ioremap(), * before ioremap() is functional. + * + * Reserve one extra page so a 256K mapping may start at any + * offset within a page. early_ioremap() maps the page-aligned + * physical range, so the initial offset can consume an extra page. */ -#define NR_FIX_BTMAPS (SZ_256K / PAGE_SIZE) +#define NR_FIX_BTMAPS ((SZ_256K / PAGE_SIZE) + 1) #define FIX_BTMAPS_SLOTS 7 #define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS) diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h index f2782560647b..35ce2c94040e 100644 --- a/arch/arm64/include/asm/kprobes.h +++ b/arch/arm64/include/asm/kprobes.h @@ -26,6 +26,12 @@ struct prev_kprobe { struct kprobe *kp; unsigned int status; + + /* + * The original DAIF state of the outer kprobe, saved here before + * a nested kprobe overwrites kcb->saved_irqflag during reentry. + */ + unsigned long saved_irqflag; }; /* per-cpu kprobe control block */ diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c index 43a0361a8bf0..4e0efad5caf2 100644 --- a/arch/arm64/kernel/probes/kprobes.c +++ b/arch/arm64/kernel/probes/kprobes.c @@ -174,12 +174,27 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) { kcb->prev_kprobe.kp = kprobe_running(); kcb->prev_kprobe.status = kcb->kprobe_status; + + /* + * Save the outer kprobe's original DAIF flags before the nested + * kprobe calls kprobes_save_local_irqflag() and overwrites + * kcb->saved_irqflag. Without this, the outer kprobe will restore + * the wrong DAIF state and leave interrupts permanently masked. + */ + kcb->prev_kprobe.saved_irqflag = kcb->saved_irqflag; } static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) { __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; + + /* + * Restore the outer kprobe's saved_irqflag so that when its + * single-step completes, kprobes_restore_local_irqflag() uses + * the correct original DAIF value. + */ + kcb->saved_irqflag = kcb->prev_kprobe.saved_irqflag; } static void __kprobes set_current_kprobe(struct kprobe *p) @@ -240,10 +255,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p, switch (kcb->kprobe_status) { case KPROBE_HIT_SSDONE: case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SS: + /* + * A probe can be hit while another kprobe is preparing or + * executing its XOL single-step instruction. This is still a + * recoverable one-level reentry, so handle it in the same way as + * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE. + */ kprobes_inc_nmissed_count(p); setup_singlestep(p, regs, kcb, 1); break; - case KPROBE_HIT_SS: case KPROBE_REENTER: pr_warn("Failed to recover from reentered kprobes.\n"); dump_kprobe(p); @@ -282,10 +303,32 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr) struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + /* + * Simulated kprobes execute in the debug trap context and have no + * XOL slot. Any page fault taken while a simulated kprobe is in + * progress cannot have been caused by kprobe single-stepping and + * must be left alone for the normal page fault handler, including + * fixup_exception. + */ + if (cur && !cur->ainsn.xol_insn) + return 0; + switch (kcb->kprobe_status) { case KPROBE_HIT_SS: case KPROBE_REENTER: /* + * A page fault taken while in KPROBE_HIT_SS or + * KPROBE_REENTER state is only attributable to kprobe + * single-stepping if the faulting PC points to the + * current kprobe's XOL instruction. If the fault occurred + * elsewhere (e.g. in perf or tracing code invoked from the + * debug exception path), leave it for the normal page fault + * handler to process. + */ + if (instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn) + break; + + /* * We are here because the instruction being single * stepped caused a page fault. We reset the current * kprobe and the ip points back to the probe address diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 85e23388f9bb..0b52557652be 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -76,6 +76,8 @@ static void data_abort_decode(unsigned long esr) pr_alert(" SF = %lu, AR = %lu\n", (esr & ESR_ELx_SF) >> ESR_ELx_SF_SHIFT, (esr & ESR_ELx_AR) >> ESR_ELx_AR_SHIFT); + pr_alert(" Xs = %llu\n", + (iss2 & ESR_ELx_Xs_MASK) >> ESR_ELx_Xs_SHIFT); } else { pr_alert(" ISV = 0, ISS = 0x%08lx, ISS2 = 0x%08lx\n", esr & ESR_ELx_ISS_MASK, iss2); @@ -87,11 +89,10 @@ static void data_abort_decode(unsigned long esr) (iss2 & ESR_ELx_TnD) >> ESR_ELx_TnD_SHIFT, (iss2 & ESR_ELx_TagAccess) >> ESR_ELx_TagAccess_SHIFT); - pr_alert(" GCS = %ld, Overlay = %lu, DirtyBit = %lu, Xs = %llu\n", + pr_alert(" GCS = %ld, Overlay = %lu, DirtyBit = %lu\n", (iss2 & ESR_ELx_GCS) >> ESR_ELx_GCS_SHIFT, (iss2 & ESR_ELx_Overlay) >> ESR_ELx_Overlay_SHIFT, - (iss2 & ESR_ELx_DirtyBit) >> ESR_ELx_DirtyBit_SHIFT, - (iss2 & ESR_ELx_Xs_MASK) >> ESR_ELx_Xs_SHIFT); + (iss2 & ESR_ELx_DirtyBit) >> ESR_ELx_DirtyBit_SHIFT); } static void mem_abort_decode(unsigned long esr) diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index 30772a909aea..8e799c1fe0aa 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -87,7 +87,7 @@ static int find_num_contig(struct mm_struct *mm, unsigned long addr, p4dp = p4d_offset(pgdp, addr); pudp = pud_offset(p4dp, addr); pmdp = pmd_offset(pudp, addr); - if ((pte_t *)pmdp == ptep) { + if ((pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) { *pgsize = PMD_SIZE; return CONT_PMDS; } diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index a25d8beacc83..18a8b0d3714e 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -2194,7 +2194,7 @@ static int prevent_memory_remove_notifier(struct notifier_block *nb, } } - if (!can_unmap_without_split(pfn, arg->nr_pages)) + if (!can_unmap_without_split(arg->start_pfn, arg->nr_pages)) return NOTIFY_BAD; return NOTIFY_OK; diff --git a/arch/m68k/include/asm/page_mm.h b/arch/m68k/include/asm/page_mm.h index ed782609ca41..0971a0651d49 100644 --- a/arch/m68k/include/asm/page_mm.h +++ b/arch/m68k/include/asm/page_mm.h @@ -55,10 +55,12 @@ static inline void clear_page(void *page) #define clear_user_page(addr, vaddr, page) \ do { clear_page(addr); \ flush_dcache_page(page); \ + (void)(vaddr); \ } while (0) #define copy_user_page(to, from, vaddr, page) \ do { copy_page(to, from); \ flush_dcache_page(page); \ + (void)(vaddr); \ } while (0) extern unsigned long m68k_memoffset; diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 60017ceec9d2..e2d5808169e4 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -209,13 +209,13 @@ struct kvm_vcpu_arch { /* * VCPU interrupts * - * We have a lockless approach for tracking pending VCPU interrupts - * implemented using atomic bitops. The irqs_pending bitmap represent - * pending interrupts whereas irqs_pending_mask represent bits changed - * in irqs_pending. Our approach is modeled around multiple producer - * and single consumer problem where the consumer is the VCPU itself. + * The irqs_pending bitmap represents pending interrupts whereas + * irqs_pending_mask represents bits changed in irqs_pending. Updates + * to these bitmaps are serialized so vcpu interrupt sync/flush cannot + * drop a newly injected interrupt while syncing guest-visible HVIP. */ #define KVM_RISCV_VCPU_NR_IRQS 64 + raw_spinlock_t irqs_pending_lock; DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS); DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index bafb009c5ce5..9a653b4ad40a 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -53,12 +53,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu) struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; unsigned long mask, val; + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (!kvm_riscv_aia_available()) return; - if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) { - mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0); - val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask; + mask = vcpu->arch.irqs_pending_mask[1]; + if (mask) { + vcpu->arch.irqs_pending_mask[1] = 0; + val = vcpu->arch.irqs_pending[1] & mask; csr->hviph &= ~mask; csr->hviph |= val; @@ -69,6 +72,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (kvm_riscv_aia_available()) csr->vsieh = ncsr_read(CSR_VSIEH); } @@ -77,13 +82,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { unsigned long seip; +#ifdef CONFIG_32BIT + unsigned long flags; + bool pending; +#endif if (!kvm_riscv_aia_available()) return false; #ifdef CONFIG_32BIT - if (READ_ONCE(vcpu->arch.irqs_pending[1]) & - (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask))) + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + pending = vcpu->arch.irqs_pending[1] & + (vcpu->arch.aia_context.guest_csr.vsieh & + upper_32_bits(mask)); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + + if (pending) return true; #endif @@ -207,6 +221,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long); +#ifdef CONFIG_32BIT + unsigned long flags; +#endif if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA)) return -ENOENT; @@ -219,8 +236,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, ((unsigned long *)csr)[reg_num] = val; #ifdef CONFIG_32BIT - if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0); + if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + vcpu->arch.irqs_pending_mask[1] = 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, + flags); + } #endif } diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index c4c3b79567f1..b0474fcf065a 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -5,11 +5,13 @@ */ #include <linux/bitops.h> +#include <linux/cpufeature.h> #include <linux/errno.h> #include <linux/kvm_host.h> #include <linux/module.h> #include <linux/pgtable.h> #include <asm/kvm_gstage.h> +#include <asm/hwcap.h> #ifdef CONFIG_64BIT unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 3; @@ -171,8 +173,10 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, } if (pte_val(*ptep) != pte_val(map->pte)) { + bool was_invalid = !pte_val(*ptep); set_pte(ptep, map->pte); - if (gstage_pte_leaf(ptep)) + if (gstage_pte_leaf(ptep) && + !(was_invalid && riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC))) gstage_tlb_flush(gstage, current_level, map->addr); } diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 082f9b261733..8a0aa5e0e216 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -41,6 +41,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, pgprot_t prot; unsigned long pfn; phys_addr_t addr, end; + unsigned long pgd_levels = kvm->arch.pgd_levels; struct kvm_mmu_memory_cache pcache = { .gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0, .gfp_zero = __GFP_ZERO, @@ -63,7 +64,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, if (!writable) map.pte = pte_wrprotect(map.pte); - ret = kvm_mmu_topup_memory_cache(&pcache, kvm->arch.pgd_levels); + ret = __kvm_mmu_topup_memory_cache(&pcache, pgd_levels, pgd_levels); if (ret) goto out; diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index cf6e231e76e2..977e36ab83d3 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu, static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) { + unsigned long flags; bool loaded; /** @@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) kvm_riscv_vcpu_aia_reset(vcpu); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS); bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); kvm_riscv_vcpu_pmu_reset(vcpu); @@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) /* Setup VCPU hfence queue */ spin_lock_init(&vcpu->arch.hfence_lock); + raw_spin_lock_init(&vcpu->arch.irqs_pending_lock); spin_lock_init(&vcpu->arch.reset_state.lock); @@ -352,10 +356,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu) { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; unsigned long mask, val; + unsigned long flags; - if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) { - mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0); - val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask; + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + + mask = vcpu->arch.irqs_pending_mask[0]; + if (mask) { + vcpu->arch.irqs_pending_mask[0] = 0; + val = vcpu->arch.irqs_pending[0] & mask; csr->hvip &= ~mask; csr->hvip |= val; @@ -363,11 +371,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu) /* Flush AIA high interrupts */ kvm_riscv_vcpu_aia_flush_interrupts(vcpu); + + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); } void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) { unsigned long hvip; + unsigned long flags; struct kvm_vcpu_arch *v = &vcpu->arch; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; @@ -376,34 +387,41 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) /* Sync-up HVIP.VSSIP bit changes does by Guest */ hvip = ncsr_read(CSR_HVIP); + + raw_spin_lock_irqsave(&v->irqs_pending_lock, flags); + if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) { if (hvip & (1UL << IRQ_VS_SOFT)) { - if (!test_and_set_bit(IRQ_VS_SOFT, - v->irqs_pending_mask)) - set_bit(IRQ_VS_SOFT, v->irqs_pending); + if (!__test_and_set_bit(IRQ_VS_SOFT, + v->irqs_pending_mask)) + __set_bit(IRQ_VS_SOFT, v->irqs_pending); } else { - if (!test_and_set_bit(IRQ_VS_SOFT, - v->irqs_pending_mask)) - clear_bit(IRQ_VS_SOFT, v->irqs_pending); + if (!__test_and_set_bit(IRQ_VS_SOFT, + v->irqs_pending_mask)) + __clear_bit(IRQ_VS_SOFT, v->irqs_pending); } } /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */ if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) { if (!(hvip & (1UL << IRQ_PMU_OVF)) && - !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) - clear_bit(IRQ_PMU_OVF, v->irqs_pending); + !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) + __clear_bit(IRQ_PMU_OVF, v->irqs_pending); } /* Sync-up AIA high interrupts */ kvm_riscv_vcpu_aia_sync_interrupts(vcpu); + raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags); + /* Sync-up timer CSRs */ kvm_riscv_vcpu_timer_sync(vcpu); } int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) { + unsigned long flags; + /* * We only allow VS-mode software, timer, and external * interrupts when irq is one of the local interrupts @@ -416,9 +434,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) irq != IRQ_PMU_OVF) return -EINVAL; - set_bit(irq, vcpu->arch.irqs_pending); - smp_mb__before_atomic(); - set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + __set_bit(irq, vcpu->arch.irqs_pending); + __set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); kvm_vcpu_kick(vcpu); @@ -427,6 +446,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) { + unsigned long flags; + /* * We only allow VS-mode software, timer, counter overflow and external * interrupts when irq is one of the local interrupts @@ -439,26 +460,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) irq != IRQ_PMU_OVF) return -EINVAL; - clear_bit(irq, vcpu->arch.irqs_pending); - smp_mb__before_atomic(); - set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + __clear_bit(irq, vcpu->arch.irqs_pending); + __set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); return 0; } bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { + unsigned long flags; unsigned long ie; + bool ret; + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK) << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask; ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK & (unsigned long)mask; - if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie) - return true; + ret = vcpu->arch.irqs_pending[0] & ie; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); /* Check AIA high interrupts */ - return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + if (!ret) + ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + + return ret; } void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu) diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c index 0bb0c51e3c89..6c8530b9f29e 100644 --- a/arch/riscv/kvm/vcpu_exit.c +++ b/arch/riscv/kvm/vcpu_exit.c @@ -38,6 +38,25 @@ static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run, return kvm_riscv_vcpu_mmio_store(vcpu, run, fault_addr, trap->htinst); + case EXC_INST_GUEST_PAGE_FAULT: { + /* + * No memslot backs this GPA and an instruction fetch + * cannot be emulated as MMIO. On bare metal a fetch + * from an unbacked physical address raises an + * instruction access fault, so reflect that back to + * the guest. + */ + struct kvm_cpu_trap inst_trap = { + .sepc = trap->sepc, + .scause = EXC_INST_ACCESS, + .stval = trap->stval, + .htval = 0, + .htinst = 0, + }; + + kvm_riscv_vcpu_trap_redirect(vcpu, &inst_trap); + return 1; + } default: return -EOPNOTSUPP; }; diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index bb920e8923c9..99b9107b1ac1 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -50,19 +50,13 @@ static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu, reg_val = vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK; break; case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - if (kvm_riscv_isa_check_host(ZICBOM)) - return -ENOENT; - reg_val = riscv_cbom_block_size; + reg_val = (kvm_riscv_isa_check_host(ZICBOM)) ? 0 : riscv_cbom_block_size; break; case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - if (kvm_riscv_isa_check_host(ZICBOZ)) - return -ENOENT; - reg_val = riscv_cboz_block_size; + reg_val = (kvm_riscv_isa_check_host(ZICBOZ)) ? 0 : riscv_cboz_block_size; break; case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size): - if (kvm_riscv_isa_check_host(ZICBOP)) - return -ENOENT; - reg_val = riscv_cbop_block_size; + reg_val = (kvm_riscv_isa_check_host(ZICBOP)) ? 0 : riscv_cbop_block_size; break; case KVM_REG_RISCV_CONFIG_REG(mvendorid): reg_val = vcpu->arch.mvendorid; @@ -144,21 +138,15 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, } break; case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - if (kvm_riscv_isa_check_host(ZICBOM)) - return -ENOENT; - if (reg_val != riscv_cbom_block_size) + if (reg_val && reg_val != riscv_cbom_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - if (kvm_riscv_isa_check_host(ZICBOZ)) - return -ENOENT; - if (reg_val != riscv_cboz_block_size) + if (reg_val && reg_val != riscv_cboz_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size): - if (kvm_riscv_isa_check_host(ZICBOP)) - return -ENOENT; - if (reg_val != riscv_cbop_block_size) + if (reg_val && reg_val != riscv_cbop_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(mvendorid): @@ -298,6 +286,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long); + unsigned long flags; if (reg_num >= regs_max) return -ENOENT; @@ -311,8 +300,11 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, ((unsigned long *)csr)[reg_num] = reg_val; - if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0); + if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + vcpu->arch.irqs_pending_mask[0] = 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + } return 0; } @@ -614,20 +606,6 @@ static int copy_config_reg_indices(const struct kvm_vcpu *vcpu, u64 size; u64 reg; - /* - * Avoid reporting config reg if the corresponding extension - * was not available. - */ - if (i == KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) && - kvm_riscv_isa_check_host(ZICBOM)) - continue; - else if (i == KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) && - kvm_riscv_isa_check_host(ZICBOZ)) - continue; - else if (i == KVM_REG_RISCV_CONFIG_REG(zicbop_block_size) && - kvm_riscv_isa_check_host(ZICBOP)) - continue; - size = IS_ENABLED(CONFIG_32BIT) ? KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; reg = KVM_REG_RISCV | size | KVM_REG_RISCV_CONFIG | i; diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index bb46dcbfb24d..2025b664961c 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -586,7 +586,7 @@ int kvm_riscv_vcpu_pmu_ctr_start(struct kvm_vcpu *vcpu, unsigned long ctr_base, } } /* Start the counters that have been configured and requested by the guest */ - for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) { + for_each_set_bit(i, &ctr_mask, BITS_PER_LONG) { pmc_index = array_index_nospec(i + ctr_base, RISCV_KVM_MAX_COUNTERS); if (!test_bit(pmc_index, kvpmu->pmc_in_use)) @@ -658,7 +658,7 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base, } /* Stop the counters that have been configured and requested by the guest */ - for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) { + for_each_set_bit(i, &ctr_mask, BITS_PER_LONG) { pmc_index = array_index_nospec(i + ctr_base, RISCV_KVM_MAX_COUNTERS); if (!test_bit(pmc_index, kvpmu->pmc_in_use)) diff --git a/arch/riscv/kvm/vcpu_sbi_fwft.c b/arch/riscv/kvm/vcpu_sbi_fwft.c index ab39ac464ffd..1342adb3180c 100644 --- a/arch/riscv/kvm/vcpu_sbi_fwft.c +++ b/arch/riscv/kvm/vcpu_sbi_fwft.c @@ -327,9 +327,11 @@ static int kvm_sbi_fwft_set(struct kvm_vcpu *vcpu, u32 feature, if (conf->flags & SBI_FWFT_SET_FLAG_LOCK) return SBI_ERR_DENIED_LOCKED; - conf->flags = flags; + ret = conf->feature->set(vcpu, conf, false, value); + if (ret == SBI_SUCCESS) + conf->flags = flags; - return conf->feature->set(vcpu, conf, false, value); + return ret; } static int kvm_sbi_fwft_get(struct kvm_vcpu *vcpu, unsigned long feature, diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c index 62d2fb77bb9b..3708616e2c32 100644 --- a/arch/riscv/kvm/vcpu_vector.c +++ b/arch/riscv/kvm/vcpu_vector.c @@ -10,6 +10,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/kvm_host.h> +#include <linux/nospec.h> #include <linux/uaccess.h> #include <asm/cpufeature.h> #include <asm/kvm_isa.h> @@ -129,11 +130,20 @@ static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu, return -ENOENT; } } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) { + unsigned long reg_offset; + if (reg_size != vlenb) return -EINVAL; WARN_ON(!cntx->vector.datap); - *reg_addr = cntx->vector.datap + - (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb; + /* + * The reg_num is derived from the userspace-provided ONE_REG + * id. Sanitize it with array_index_nospec() to prevent + * speculative out-of-bounds access to the vector register + * buffer (32 vector registers: v0..v31). + */ + reg_offset = array_index_nospec( + reg_num - KVM_REG_RISCV_VECTOR_REG(0), 32); + *reg_addr = cntx->vector.datap + reg_offset * vlenb; } else { return -ENOENT; } diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index bd0df61d1907..2b989bebd220 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -65,6 +65,7 @@ ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); EXPORT_SYMBOL(s390_epoch_delta_notifier); unsigned char ptff_function_mask[16]; +EXPORT_SYMBOL(ptff_function_mask); static unsigned long lpar_offset; static unsigned long initial_leap_seconds; diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 38bba9a1114c..48b019114c19 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn) if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; - if (!from_timer_fn && apic->apicv_active) { + if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) { WARN_ON(kvm_get_running_vcpu() != vcpu); kvm_apic_inject_pending_timer_irqs(apic); return; diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 234d0a95abf5..22cf222d3033 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2642,6 +2642,7 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, */ if (tdp_enabled && invalid_list && child->role.guest_mode && + !child->root_count && !atomic_long_read(&child->parent_ptes.val)) return kvm_mmu_prepare_zap_page(kvm, child, invalid_list); @@ -4852,16 +4853,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (r != RET_PF_CONTINUE) return r; - r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); - if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; - r = make_mmu_pages_available(vcpu); if (r) goto out_unlock; + if (is_page_fault_stale(vcpu, fault)) { + r = RET_PF_RETRY; + goto out_unlock; + } + r = direct_map(vcpu, fault); out_unlock: @@ -7574,7 +7576,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) static void mmu_destroy_caches(void) { kmem_cache_destroy(pte_list_desc_cache); + pte_list_desc_cache = NULL; kmem_cache_destroy(mmu_page_header_cache); + mmu_page_header_cache = NULL; } static void kvm_wake_nx_recovery_thread(struct kvm *kvm) diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index df3ae0c7ec2c..1ba840a73b7a 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -864,15 +864,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault } #endif - r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); - if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; - r = make_mmu_pages_available(vcpu); if (r) goto out_unlock; + + if (is_page_fault_stale(vcpu, fault)) { + r = RET_PF_RETRY; + goto out_unlock; + } + r = FNAME(fetch)(vcpu, fault, &walker); out_unlock: diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 220d42ebc82e..ddf6df7bee93 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -336,6 +336,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu) static void free_nested(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); + struct vmcs *shadow_vmcs; if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01)) vmx_switch_vmcs(vcpu, &vmx->vmcs01); @@ -353,9 +354,15 @@ static void free_nested(struct kvm_vcpu *vcpu) vmx->nested.current_vmptr = INVALID_GPA; if (enable_shadow_vmcs) { vmx_disable_shadow_vmcs(vmx); - vmcs_clear(vmx->vmcs01.shadow_vmcs); - free_vmcs(vmx->vmcs01.shadow_vmcs); + + /* + * Keep the pointer visible until after VMCLEAR, so migration + * can clear an active shadow VMCS on the old CPU. + */ + shadow_vmcs = vmx->vmcs01.shadow_vmcs; + vmcs_clear(shadow_vmcs); vmx->vmcs01.shadow_vmcs = NULL; + free_vmcs(shadow_vmcs); } kfree(vmx->nested.cached_vmcs12); vmx->nested.cached_vmcs12 = NULL; diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 101f324ee178..4b3a62aa8798 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -43,20 +43,22 @@ struct aie2_ctx_health { static inline void aie2_tdr_signal(struct amdxdna_dev *xdna) { - WRITE_ONCE(xdna->dev_handle->tdr_status, AIE2_TDR_SIGNALED); + WRITE_ONCE(xdna->dev_handle->last_signal_ts, jiffies); } static bool aie2_tdr_detect(struct amdxdna_dev *xdna) { struct amdxdna_dev_hdl *ndev = xdna->dev_handle; + unsigned long last = READ_ONCE(ndev->last_signal_ts); - if (READ_ONCE(ndev->tdr_status) == AIE2_TDR_WAIT) { - XDNA_ERR(xdna, "TDR timeout detected"); - return true; - } + if (!tdr_timeout_ms) + return false; + + if (!time_after(jiffies, last + msecs_to_jiffies(tdr_timeout_ms))) + return false; - WRITE_ONCE(ndev->tdr_status, AIE2_TDR_WAIT); - return false; + XDNA_ERR(xdna, "TDR timeout detected"); + return true; } static void aie2_cmd_release(struct kref *ref) @@ -434,6 +436,12 @@ out: mmput(job->mm); fence = ERR_PTR(ret); } else { + /* + * Command is successfully posted to hardware, update the + * tdr timestamp. The total pending commands are limited. + * So there will not be a case that driver keeps posting + * commands without getting any hardware respond. + */ aie2_tdr_signal(hwctx->client->xdna); } trace_xdna_job(sched_job, hwctx->name, "sent to device", @@ -658,7 +666,9 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) const struct drm_sched_init_args args = { .ops = &sched_ops, .credit_limit = HWCTX_MAX_CMDS, - .timeout = msecs_to_jiffies(tdr_timeout_ms), + .timeout = tdr_timeout_ms ? + msecs_to_jiffies(tdr_timeout_ms) : + MAX_SCHEDULE_TIMEOUT, .name = "amdxdna_js", .dev = xdna->ddev.dev, }; diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index 22f66c7f534d..daec1f6b4907 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -420,6 +420,7 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) goto stop_fw; } + WRITE_ONCE(ndev->last_signal_ts, jiffies); ndev->dev_status = AIE2_DEV_START; return 0; diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index 77648cc548b6..ea1dac106400 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -143,11 +143,6 @@ struct aie2_exec_msg_ops { u32 (*get_chain_msg_op)(u32 cmd_op); }; -enum aie2_tdr_status { - AIE2_TDR_WAIT, - AIE2_TDR_SIGNALED, -}; - struct amdxdna_dev_hdl { struct aie_device aie; const struct amdxdna_dev_priv *priv; @@ -179,7 +174,7 @@ struct amdxdna_dev_hdl { u32 hwctx_num; struct amdxdna_async_error last_async_err; - enum aie2_tdr_status tdr_status; + unsigned long last_signal_ts; }; struct aie2_hw_ops { diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 3401883e207f..d50fed64d4d9 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -192,9 +192,17 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, return len; } -static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, +static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm) +{ + u32 storage = fm->precision >> 14; + + return !ethosu_is_u65(edev) && storage == 2; +} + +static u64 feat_matrix_length(struct ethosu_device *edev, + struct ethosu_validated_cmdstream_info *info, struct feat_matrix *fm, - u32 x, u32 y, u32 c) + u32 x, u32 y, u32 c, bool ofm) { u32 element_size, storage = fm->precision >> 14; int tile = 0; @@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, if (fm->region < 0) return U64_MAX; + if (feat_matrix_chained(edev, fm)) + return 0; + switch (storage) { case 0: if (x >= fm->width0 + 1) { @@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, tile = 1; } break; + default: + return U64_MAX; } if (fm->base[tile] == U64_MAX) return U64_MAX; @@ -231,10 +244,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, switch ((fm->precision >> 6) & 0x3) { // format case 0: //nhwc: - addr += x * fm->stride_x + c; + element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); + addr += x * fm->stride_x + c * element_size; break; case 1: //nhcwb16: - element_size = BIT((fm->precision >> 1) & 0x3); + element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size; break; @@ -250,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev, u16 op, struct cmd_state *st, bool ifm, bool ifm2, bool weight, bool scale) { + struct ethosu_device *edev = to_ethosu_device(ddev); u64 len; if (ifm) { @@ -267,8 +282,8 @@ static int calc_sizes(struct drm_device *ddev, if (ifm_height < 0 || ifm_width < 0) return -EINVAL; - len = feat_matrix_length(info, &st->ifm, ifm_width, - ifm_height, st->ifm.depth); + len = feat_matrix_length(edev, info, &st->ifm, ifm_width, + ifm_height, st->ifm.depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); if (len == U64_MAX) @@ -276,8 +291,8 @@ static int calc_sizes(struct drm_device *ddev, } if (ifm2) { - len = feat_matrix_length(info, &st->ifm2, st->ifm.depth, - 0, st->ofm.depth); + len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth, + 0, st->ofm.depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); if (len == U64_MAX) @@ -308,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev, st->scale[0].base + st->scale[0].length); } - len = feat_matrix_length(info, &st->ofm, st->ofm.width, - st->ofm.height[2], st->ofm.depth); + len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, + st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) return -EINVAL; - info->output_region[st->ofm.region] = true; + if (!feat_matrix_chained(edev, &st->ofm)) + info->output_region[st->ofm.region] = true; return 0; } @@ -324,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, u16 op, struct cmd_state *st, bool ifm, bool ifm2) { + struct ethosu_device *edev = to_ethosu_device(ddev); u32 height, width, depth; u64 len; @@ -332,8 +349,8 @@ static int calc_sizes_elemwise(struct drm_device *ddev, width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width; depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth; - len = feat_matrix_length(info, &st->ifm, width, - height, depth); + len = feat_matrix_length(edev, info, &st->ifm, width, + height, depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); if (len == U64_MAX) @@ -345,21 +362,22 @@ static int calc_sizes_elemwise(struct drm_device *ddev, width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width; depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth; - len = feat_matrix_length(info, &st->ifm2, width, - height, depth); + len = feat_matrix_length(edev, info, &st->ifm2, width, + height, depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); if (len == U64_MAX) return -EINVAL; } - len = feat_matrix_length(info, &st->ofm, st->ofm.width, - st->ofm.height[2], st->ofm.depth); + len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, + st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) return -EINVAL; - info->output_region[st->ofm.region] = true; + if (!feat_matrix_chained(edev, &st->ofm)) + info->output_region[st->ofm.region] = true; return 0; } diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ac90d81aa294..1f1c2810f6ee 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1950,10 +1950,15 @@ static int rbd_object_map_update_finish(struct rbd_obj_request *obj_req, bool has_current_state; void *p; - if (osd_req->r_result) + if (osd_req->r_result < 0) return osd_req->r_result; /* + * Writes aren't allowed to return a data payload. + */ + WARN_ON_ONCE(osd_req->r_result > 0); + + /* * Nothing to do for a snapshot object map. */ if (osd_req->r_num_ops == 1) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 08c0a99a62c5..8f7ed469cac6 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2782,7 +2782,9 @@ static int btusb_setup_realtek(struct hci_dev *hdev) static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb) { - if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) { + if (skb->len >= HCI_EVENT_HDR_SIZE + 1 && + skb->data[0] == HCI_VENDOR_PKT && + skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) { struct rtk_dev_coredump_hdr hdr = { .code = RTK_DEVCOREDUMP_CODE_MEMDUMP, }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 9c564cd5edee..eaf19ec843e8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3324,7 +3324,8 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, acrtc = get_crtc_by_otg_inst( adev, state->stream_status[i].primary_otg_inst); - if (acrtc && state->stream_status[i].plane_count != 0) { + if (acrtc && state->stream_status[i].plane_count != 0 && + amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; if (rc) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index b43cd68cde67..95d3da3c4199 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -280,10 +280,19 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) * is enabled. On DCE, vupdate is only needed in VRR mode. */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { - rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable); + if (enable) { + rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc); + } } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) { if (enable) { - /* vblank irq on -> Only need vupdate irq in vrr mode */ + /* vblank irq on -> Only need vupdate irq in vrr mode + * Not ref-counted since we need explicit enable/disable + * for DCE VRR handling + */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true); } else { diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 9a06ff7d2608..18ddef94da47 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -781,7 +781,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range, const struct dev_pagemap *other = NULL; int err, i; - pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); + pfns = kvcalloc(npages, sizeof(*pfns), GFP_KERNEL); if (!pfns) return DRM_GPUSVM_SCAN_UNPOPULATED; @@ -1753,8 +1753,10 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm, return -EFAULT; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); - if (!pfns) + if (!pfns) { + mmput(mm); return -ENOMEM; + } hmm_range.hmm_pfns = pfns; while (!time_after(jiffies, timeout)) { diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 15c78eca180b..892b325fa99b 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -727,8 +727,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, } err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst); - if (err) - goto err_aborted_migration; + if (err) { + npages = 0; + goto err_finalize; + } own_pages = 0; @@ -807,8 +809,11 @@ next: msecs_to_jiffies(mdetails->timeslice_ms); err_finalize: - if (err) + if (err) { drm_pagemap_migration_unlock_put_pages(npages, migrate.dst); + for (i = npages; i < npages_in_range(start, end); ++i) + migrate.dst[i] = 0; + } err_aborted_migration: migrate_vma_pages(&migrate); diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c index 2a7916ca5907..a780643f25aa 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c @@ -98,6 +98,7 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; u32 temp; + int ret; i2c_dev->status = I2C_STAT_INIT; i2c_dev->msg = pmsg; @@ -109,9 +110,14 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) HDMI_WRITE(HDMI_HI2CHCR, temp); HDMI_READ(HDMI_HI2CHCR); - while (i2c_dev->status != I2C_TRANSACTION_DONE) - wait_for_completion_interruptible_timeout(&i2c_dev->complete, + while (i2c_dev->status != I2C_TRANSACTION_DONE) { + ret = wait_for_completion_interruptible_timeout(&i2c_dev->complete, 10 * HZ); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + } return 0; } @@ -130,7 +136,7 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, { struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - int i; + int i, ret = 0; mutex_lock(&i2c_dev->i2c_lock); @@ -142,9 +148,11 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, for (i = 0; i < num; i++) { if (pmsg->len && pmsg->buf) { if (pmsg->flags & I2C_M_RD) - xfer_read(adap, pmsg); + ret = xfer_read(adap, pmsg); else - xfer_write(adap, pmsg); + ret = xfer_write(adap, pmsg); + if (ret) + break; } pmsg++; /* next message */ } @@ -154,6 +162,9 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, mutex_unlock(&i2c_dev->i2c_lock); + if (ret) + return ret; + return i; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index 7a6c07f6aaeb..266e042e0023 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -615,12 +615,7 @@ check_if_vesa_backlight_possible(struct intel_dp *intel_dp) int ret; u8 bit_min, bit_max; - /* - * Since we only support Fully AUX Based VESA Backlight interface make sure - * backlight enable is possible via AUX along with backlight adjustment - */ - if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP && - intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) + if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) return false; ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min); diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c index 396d349fb6ce..ceb78694cd98 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -747,6 +747,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, pvr_gem_object_get(pvr_obj); + mutex_lock(&vm_ctx->lock); err = drm_gpuvm_exec_lock(&vm_exec); if (err) goto err_cleanup; @@ -756,6 +757,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, drm_gpuvm_exec_unlock(&vm_exec); err_cleanup: + mutex_unlock(&vm_ctx->lock); pvr_vm_bind_op_fini(&bind_op); return err; diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 7450b27622a2..3eac4d0f88bc 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -232,6 +232,7 @@ config DRM_PANEL_HIMAX_HX83121A depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER select DRM_KMS_HELPER help @@ -332,6 +333,7 @@ config DRM_PANEL_ILITEK_ILI9882T depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER help Say Y if you want to enable support for panels based on the @@ -954,6 +956,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER help Say Y or M here if you want to enable support for the diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 0652cdb57d11..6c8d02712566 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -2562,9 +2562,12 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) "Couldn't get our power regulator\n"); ctx->iovcc = devm_regulator_get_optional(&dsi->dev, "iovcc"); - if (IS_ERR(ctx->iovcc)) - return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc), + if (IS_ERR(ctx->iovcc)) { + if (PTR_ERR(ctx->iovcc) != -ENODEV) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc), "Couldn't get our iovcc regulator\n"); + ctx->iovcc = NULL; + } ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset)) diff --git a/drivers/gpu/drm/tests/drm_gem_shmem_test.c b/drivers/gpu/drm/tests/drm_gem_shmem_test.c index 44a190109249..5e69ff1d1ad6 100644 --- a/drivers/gpu/drm/tests/drm_gem_shmem_test.c +++ b/drivers/gpu/drm/tests/drm_gem_shmem_test.c @@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test) sg_init_one(sgt->sgl, buf, TEST_SIZE); /* - * Set the DMA mask to 64-bits and map the sgtables - * otherwise drm_gem_shmem_free will cause a warning - * on debug kernels. + * Map the sgtables otherwise drm_gem_shmem_free will cause a warning on + * debug kernels. */ - ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64)); - KUNIT_ASSERT_EQ(test, ret, 0); - ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -352,12 +348,20 @@ static int drm_gem_shmem_test_init(struct kunit *test) { struct device *dev; struct drm_device *drm_dev; + int ret; /* Allocate a parent device */ dev = drm_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); /* + * Set the DMA mask to 64-bits to avoid intermittent failures calling + * drm_gem_shmem_get_pages_sgt(). + */ + ret = dma_set_mask(dev, DMA_BIT_MASK(64)); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* * The DRM core will automatically initialize the GEM core and create * a DRM Memory Manager object which provides an address space pool * for GEM objects allocation. diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c index cdd35af49892..ef71b9957961 100644 --- a/drivers/gpu/drm/tiny/appletbdrm.c +++ b/drivers/gpu/drm/tiny/appletbdrm.c @@ -315,33 +315,16 @@ static const u32 appletbdrm_primary_plane_formats[] = { DRM_FORMAT_XRGB8888, /* emulated */ }; -static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, - struct drm_atomic_commit *state) +static int appletbdrm_primary_plane_helper_begin_fb_access(struct drm_plane *plane, + struct drm_plane_state *new_plane_state) { - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); - struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); - struct drm_crtc *new_crtc = new_plane_state->crtc; - struct drm_crtc_state *new_crtc_state = NULL; struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state); + size_t frames_size = 0; struct drm_atomic_helper_damage_iter iter; struct drm_rect damage; - size_t frames_size = 0; size_t request_size; - int ret; - - if (new_crtc) - new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); - ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, - DRM_PLANE_NO_SCALING, - DRM_PLANE_NO_SCALING, - false, false); - if (ret) - return ret; - else if (!new_plane_state->visible) - return 0; - - drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state); + drm_atomic_helper_damage_iter_init(&iter, plane->state, new_plane_state); drm_atomic_for_each_plane_damage(&iter, &damage) { frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage)); } @@ -366,6 +349,29 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, appletbdrm_state->request_size = request_size; appletbdrm_state->frames_size = frames_size; + return drm_gem_begin_shadow_fb_access(plane, new_plane_state); +} + +static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_commit *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + int ret; + + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + return 0; } @@ -468,7 +474,7 @@ end_fb_cpu_access: } static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, - struct drm_atomic_commit *old_state) + struct drm_atomic_commit *old_state) { struct appletbdrm_device *adev = drm_to_adev(plane->dev); struct drm_device *drm = plane->dev; @@ -552,7 +558,8 @@ static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane, } static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = { - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .begin_fb_access = appletbdrm_primary_plane_helper_begin_fb_access, + .end_fb_access = drm_gem_end_shadow_fb_access, .atomic_check = appletbdrm_primary_plane_helper_atomic_check, .atomic_update = appletbdrm_primary_plane_helper_atomic_update, .atomic_disable = appletbdrm_primary_plane_helper_atomic_disable, diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c index 81df4cb5606b..3c067aadc52d 100644 --- a/drivers/gpu/drm/ttm/ttm_backup.c +++ b/drivers/gpu/drm/ttm/ttm_backup.c @@ -6,9 +6,10 @@ #include <drm/ttm/ttm_backup.h> #include <linux/export.h> -#include <linux/page-flags.h> #include <linux/swap.h> +#include "ttm_pool_internal.h" + /* * Need to map shmem indices to handle since a handle value * of 0 means error, following the swp_entry_t convention. @@ -68,17 +69,23 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, } /** - * ttm_backup_backup_page() - Backup a page + * ttm_backup_backup_folio() - Backup a folio * @backup: The struct backup pointer to use. - * @page: The page to back up. - * @writeback: Whether to perform immediate writeback of the page. + * @folio: The folio to back up. + * @order: The allocation order of @folio. Since TTM allocates higher-order + * pages without __GFP_COMP, folio_nr_pages(@folio) would always + * return 1; the caller must pass the true order explicitly. + * @writeback: Whether to perform immediate writeback of the folio's pages. * This may have performance implications. - * @idx: A unique integer for each page and each struct backup. + * @idx: A unique integer for the first page of the folio and each struct backup. * This allows the backup implementation to avoid managing * its address space separately. - * @page_gfp: The gfp value used when the page was allocated. - * This is used for accounting purposes. + * @folio_gfp: The gfp value used when the folio was allocated. + * Currently unused. * @alloc_gfp: The gfp to be used when allocating memory. + * @nr_pages_backed: Output. On a successful return, set to the number of + * pages actually backed up, which may be less than (1 << @order) + * if an -ENOMEM was encountered mid-folio. * * Context: If called from reclaim context, the caller needs to * assert that the shrinker gfp has __GFP_FS set, to avoid @@ -87,53 +94,87 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, * that the shrinker gfp has __GFP_IO set, since without it, * we're not allowed to start backup IO. * - * Return: A handle on success. Negative error code on failure. - * - * Note: This function could be extended to back up a folio and - * implementations would then split the folio internally if needed. - * Drawback is that the caller would then have to keep track of - * the folio size- and usage. + * Return: A handle for the first backed-up page on success (handles for + * subsequent pages follow sequentially). -ENOMEM if no pages could be backed + * up. Any other negative error code if a non-ENOMEM failure occurred; in that + * case any pages backed up so far are truncated before returning. */ s64 -ttm_backup_backup_page(struct file *backup, struct page *page, - bool writeback, pgoff_t idx, gfp_t page_gfp, - gfp_t alloc_gfp) +ttm_backup_backup_folio(struct file *backup, struct folio *folio, + unsigned int order, bool writeback, pgoff_t idx, + gfp_t folio_gfp, gfp_t alloc_gfp, + pgoff_t *nr_pages_backed) { struct address_space *mapping = backup->f_mapping; - unsigned long handle = 0; + int nr_pages = 1 << order; struct folio *to_folio; - int ret; + int ret, i; - to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp); - if (IS_ERR(to_folio)) - return PTR_ERR(to_folio); + *nr_pages_backed = 0; - folio_mark_accessed(to_folio); - folio_lock(to_folio); - folio_mark_dirty(to_folio); - copy_highpage(folio_file_page(to_folio, idx), page); - handle = ttm_backup_shmem_idx_to_handle(idx); + for (i = 0; i < nr_pages; ) { + int to_nr, j; - if (writeback && !folio_mapped(to_folio) && - folio_clear_dirty_for_io(to_folio)) { - folio_set_reclaim(to_folio); - ret = shmem_writeout(to_folio, NULL, NULL); - if (!folio_test_writeback(to_folio)) - folio_clear_reclaim(to_folio); /* - * If writeout succeeds, it unlocks the folio. errors - * are otherwise dropped, since writeout is only best - * effort here. + * Only inject past the first subpage so *nr_pages_backed is + * always > 0 here, matching a genuine mid-compound -ENOMEM + * and driving the caller's reactive split fallback instead + * of an early, no-progress failure. */ - if (ret) + if (IS_ENABLED(CONFIG_FAULT_INJECTION) && i && + ttm_backup_fault_inject_folio()) + to_folio = ERR_PTR(-ENOMEM); + else + to_folio = shmem_read_folio_gfp(mapping, idx + i, alloc_gfp); + if (IS_ERR(to_folio)) { + int err = PTR_ERR(to_folio); + + if (err == -ENOMEM && *nr_pages_backed) + return ttm_backup_shmem_idx_to_handle(idx); + + if (*nr_pages_backed) { + shmem_truncate_range(file_inode(backup), + (loff_t)idx << PAGE_SHIFT, + ((loff_t)(idx + i) << PAGE_SHIFT) - 1); + /* + * The pages just truncated are no longer + * backed up; don't let the caller mistake + * them for valid handles. + */ + *nr_pages_backed = 0; + } + return err; + } + + to_nr = min_t(int, nr_pages - i, + folio_next_index(to_folio) - (idx + i)); + + folio_mark_accessed(to_folio); + folio_lock(to_folio); + folio_mark_dirty(to_folio); + + for (j = 0; j < to_nr; j++) + copy_highpage(folio_file_page(to_folio, idx + i + j), + folio_page(folio, i + j)); + + if (writeback && !folio_mapped(to_folio) && + folio_clear_dirty_for_io(to_folio)) { + folio_set_reclaim(to_folio); + ret = shmem_writeout(to_folio, NULL, NULL); + if (!folio_test_writeback(to_folio)) + folio_clear_reclaim(to_folio); + if (ret == AOP_WRITEPAGE_ACTIVATE) + folio_unlock(to_folio); + } else { folio_unlock(to_folio); - } else { - folio_unlock(to_folio); - } + } - folio_put(to_folio); + folio_put(to_folio); + i += to_nr; + *nr_pages_backed = i; + } - return handle; + return ttm_backup_shmem_idx_to_handle(idx); } /** diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 46983e7de7a3..e7648ad3e615 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -53,8 +53,23 @@ #ifdef CONFIG_FAULT_INJECTION #include <linux/fault-inject.h> static DECLARE_FAULT_ATTR(backup_fault_inject); + +/* + * Exposed to ttm_backup.c so a mid-compound subpage can be made to fail + * with -ENOMEM, exercising the reactive split-and-retry fallback in + * ttm_pool_backup() for high-order backups. + */ +bool ttm_backup_fault_inject_folio(void) +{ + return should_fail(&backup_fault_inject, 1); +} #else #define should_fail(...) false + +bool ttm_backup_fault_inject_folio(void) +{ + return false; +} #endif /** @@ -487,7 +502,7 @@ static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p) /** * DOC: Partial backup and restoration of a struct ttm_tt. * - * Swapout using ttm_backup_backup_page() and swapin using + * Swapout using ttm_backup_backup_folio() and swapin using * ttm_backup_copy_page() may fail. * The former most likely due to lack of swap-space or memory, the latter due * to lack of memory or because of signal interruption during waits. @@ -1036,12 +1051,12 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, { struct file *backup = tt->backup; struct page *page; - unsigned long handle; gfp_t alloc_gfp; gfp_t gfp; int ret = 0; pgoff_t shrunken = 0; - pgoff_t i, num_pages; + pgoff_t i, j, num_pages, npages; + pgoff_t nr_backed; if (WARN_ON(ttm_tt_is_backed_up(tt))) return -EINVAL; @@ -1119,9 +1134,11 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (IS_ENABLED(CONFIG_FAULT_INJECTION) && should_fail(&backup_fault_inject, 1)) num_pages = DIV_ROUND_UP(num_pages, 2); - for (i = 0; i < num_pages; ++i) { - s64 shandle; + for (i = 0; i < num_pages; i += npages) { + unsigned int order; + s64 handle; + npages = 1; page = tt->pages[i]; if (unlikely(!page)) continue; @@ -1130,19 +1147,61 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (unlikely(ttm_backup_page_ptr_is_handle(page))) continue; - ttm_pool_split_for_swap(pool, page); + order = ttm_pool_page_order(pool, page); + npages = 1UL << order; + + /* + * We don't allow dipping kernel reserves for high order backup + */ + if (order) + alloc_gfp |= __GFP_NOMEMALLOC; + else + alloc_gfp &= ~__GFP_NOMEMALLOC; + + /* + * Back up the compound atomically at its native order. If + * fault injection truncated num_pages mid-compound, skip + * the partial tail rather than splitting. + */ + if (unlikely(i + npages > num_pages)) + break; - shandle = ttm_backup_backup_page(backup, page, flags->writeback, i, - gfp, alloc_gfp); - if (shandle < 0) { - /* We allow partially shrunken tts */ - ret = shandle; + handle = ttm_backup_backup_folio(backup, page_folio(page), + order, flags->writeback, i, + gfp, alloc_gfp, + &nr_backed); + /* + * Zero progress on this compound (whether order 0 or a + * high-order compound that failed before backing up even + * its first subpage) is unrecoverable: bail out rather than + * looping forever with npages == nr_backed == 0 below. + */ + if (unlikely(handle < 0 && !nr_backed)) { + ret = handle; break; } - handle = shandle; - tt->pages[i] = ttm_backup_handle_to_page_ptr(handle); - __free_pages_gpu_account(page, 0, false); - shrunken++; + + for (j = 0; j < nr_backed; j++) + tt->pages[i + j] = ttm_backup_handle_to_page_ptr(handle + j); + + shrunken += nr_backed; + + if (unlikely(nr_backed < npages)) { + /* + * Partial OOM backup: split the compound and free the + * subpages whose content is now in shmem. Continue the + * loop from the first un-backed order-0 page. + */ + ttm_pool_split_for_swap(pool, page); + for (j = 0; j < nr_backed; j++) + __free_pages_gpu_account(page + j, 0, false); + npages = nr_backed; + continue; + } + + /* Fully backed up: free at native order. */ + page->private = 0; + __free_pages_gpu_account(page, order, false); } return shrunken ? shrunken : ret; diff --git a/drivers/gpu/drm/ttm/ttm_pool_internal.h b/drivers/gpu/drm/ttm/ttm_pool_internal.h index 24c179fd69d1..cbb17a2129fe 100644 --- a/drivers/gpu/drm/ttm/ttm_pool_internal.h +++ b/drivers/gpu/drm/ttm/ttm_pool_internal.h @@ -22,4 +22,12 @@ static inline unsigned int ttm_pool_beneficial_order(struct ttm_pool *pool) return pool->alloc_flags & 0xff; } +/* + * Implemented in ttm_pool.c, used by ttm_backup.c. Returns true if a fault + * should be injected mid-compound to test the reactive split-and-retry + * fallback in ttm_pool_backup(). Always returns false when + * CONFIG_FAULT_INJECTION is disabled. + */ +bool ttm_backup_fault_inject_folio(void); + #endif diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 4ebe175a8c6b..423bcfe331a0 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -571,6 +571,8 @@ extern bool super_pages; void v3d_init_hw_state(struct v3d_dev *v3d); int v3d_gem_init(struct drm_device *dev); void v3d_gem_destroy(struct drm_device *dev); +void v3d_idle_axi(struct v3d_dev *v3d, int core); +void v3d_idle_gca(struct v3d_dev *v3d); void v3d_reset_sms(struct v3d_dev *v3d); void v3d_reset(struct v3d_dev *v3d); void v3d_invalidate_caches(struct v3d_dev *v3d); diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index c43d9af41374..23a0db9575a5 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -36,9 +36,21 @@ v3d_init_core(struct v3d_dev *v3d, int core) V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0); } -static void +void v3d_idle_axi(struct v3d_dev *v3d, int core) { + if (v3d->ver >= V3D_GEN_71) { + V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); + + if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) & + (V3D_GMP_STATUS_RD_COUNT_MASK | + V3D_GMP_STATUS_WR_COUNT_MASK | + V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) { + drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n"); + } + return; + } + V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) & @@ -49,7 +61,7 @@ v3d_idle_axi(struct v3d_dev *v3d, int core) } } -static void +void v3d_idle_gca(struct v3d_dev *v3d) { if (v3d->ver >= V3D_GEN_41) diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c index f7df6393d38f..c53146316079 100644 --- a/drivers/gpu/drm/v3d/v3d_power.c +++ b/drivers/gpu/drm/v3d/v3d_power.c @@ -54,8 +54,15 @@ int v3d_power_suspend(struct device *dev) v3d_clean_caches(v3d); + /* Wait until V3D has no active or pending AXI transactions. */ + v3d_idle_axi(v3d, 0); + v3d_idle_gca(v3d); + ret = v3d_suspend_sms(v3d); if (ret) { + /* Staying active: undo the GMP STOP_REQ from v3d_idle_axi(). */ + V3D_WRITE(V3D_GMP_CFG(v3d->ver), + V3D_READ(V3D_GMP_CFG(v3d->ver)) & ~V3D_GMP_CFG_STOP_REQ); v3d_irq_enable(v3d); return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 2161761b1f22..49ea2ed0996b 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -732,9 +732,13 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct { struct vc4_bo *bo = to_vc4_bo(obj); - if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { - DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); - return -EINVAL; + if (bo->validated_shader) { + if (vma->vm_flags & VM_WRITE) { + DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); + return -EINVAL; + } + + vm_flags_clear(vma, VM_MAYWRITE); } mutex_lock(&bo->madv_lock); @@ -1044,7 +1048,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused) struct vc4_dev *vc4 = to_vc4_dev(dev); int i; - timer_delete(&vc4->bo_cache.time_timer); + timer_shutdown_sync(&vc4->bo_cache.time_timer); cancel_work_sync(&vc4->bo_cache.time_work); vc4_bo_cache_purge(dev); diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 184d51ea3fa5..e715147d091c 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -1752,7 +1752,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) static void vc4_hvs_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); + struct drm_device *drm = data; struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_hvs *hvs = vc4->hvs; struct drm_mm_node *node, *next; diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index d31b906cb8e7..f32410420d3e 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -494,7 +494,7 @@ err_put_runtime_pm: static void vc4_v3d_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); + struct drm_device *drm = data; struct vc4_dev *vc4 = to_vc4_dev(drm); vc4_irq_uninstall(drm); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index b2d3927b5567..bd0563741e89 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -78,7 +78,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res, static int vmw_gb_surface_destroy(struct vmw_resource *res); static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv); static int @@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data, */ static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv) { @@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct drm_device *dev, req->base.svga3d_flags); /* array_size must be null for non-GL3 host. */ - if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) { - VMW_DEBUG_USER("SM4 surface not supported.\n"); - return -EINVAL; + if (req->base.array_size > 0) { + if (has_sm5_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else if (has_sm4_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else { + VMW_DEBUG_USER("SM4+ surface not supported.\n"); + return -EINVAL; + } } if (!has_sm4_1_context(dev_priv)) { diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 706783863d07..f05f23221c1b 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -95,18 +95,21 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c) struct platform_device *pdev; struct fwnode_handle *fwnode; int ret; + u32 id; fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL); if (IS_ERR(fwnode)) return PTR_ERR(fwnode); + id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci); + /* * Not using platform_device_register_full() here because we don't have * a handle to the platform_device before it returns. xe_i2c_notifier() * uses that handle, but it may be called before * platform_device_register_full() is done. */ - pdev = platform_device_alloc(adapter_name, pci_dev_id(pci)); + pdev = platform_device_alloc(adapter_name, id); if (!pdev) { ret = -ENOMEM; goto err_fwnode_remove; diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 7d28290e7d1c..be787f331768 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -1313,6 +1313,7 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, * content. * @src_bo: The buffer object @src is currently bound to. * @read_write : Creates BB commands for CCS read/write. + * @bound: Device is bound * * Directly clearing the BB lacks atomicity and can lead to undefined * behavior if the vCPU is halted mid-operation during the clearing @@ -1325,7 +1326,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, * Returns: None. */ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, - enum xe_sriov_vf_ccs_rw_ctxs read_write) + enum xe_sriov_vf_ccs_rw_ctxs read_write, + bool bound) { struct xe_mem_pool_node *bb = src_bo->bb_ccs[read_write]; struct xe_device *xe = xe_bo_device(src_bo); @@ -1339,13 +1341,15 @@ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, bb_pool = ctx->mem.ccs_bb_pool; scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) { - xe_mem_pool_swap_shadow_locked(bb_pool); + if (bound) { + xe_mem_pool_swap_shadow_locked(bb_pool); - cs = xe_mem_pool_node_cpu_addr(bb); - memset(cs, MI_NOOP, bb->sa_node.size); - xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + cs = xe_mem_pool_node_cpu_addr(bb); + memset(cs, MI_NOOP, bb->sa_node.size); + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); - xe_mem_pool_sync_shadow_locked(bb); + xe_mem_pool_sync_shadow_locked(bb); + } xe_mem_pool_free_node(bb); src_bo->bb_ccs[read_write] = NULL; } diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index 78e5b63f3ebe..c3a268b01768 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -142,7 +142,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, enum xe_sriov_vf_ccs_rw_ctxs read_write); void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, - enum xe_sriov_vf_ccs_rw_ctxs read_write); + enum xe_sriov_vf_ccs_rw_ctxs read_write, + bool bound); struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate); struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate); diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 6787564629c6..a8c831fbee3b 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -3,6 +3,8 @@ * Copyright © 2025 Intel Corporation */ +#include <drm/drm_drv.h> + #include "instructions/xe_mi_commands.h" #include "instructions/xe_gpu_commands.h" #include "xe_bb.h" @@ -446,7 +448,7 @@ err_unwind: */ for_each_ccs_rw_ctx(ctx_id) { if (bo->bb_ccs[ctx_id]) - xe_migrate_ccs_rw_copy_clear(bo, ctx_id); + xe_migrate_ccs_rw_copy_clear(bo, ctx_id, true); } return err; } @@ -466,19 +468,27 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo) struct xe_device *xe = xe_bo_device(bo); enum xe_sriov_vf_ccs_rw_ctxs ctx_id; struct xe_mem_pool_node *bb; + bool bound; + int idx; xe_assert(xe, IS_VF_CCS_READY(xe)); if (!xe_bo_has_valid_ccs_bb(bo)) return 0; + bound = drm_dev_enter(&xe->drm, &idx); + for_each_ccs_rw_ctx(ctx_id) { bb = bo->bb_ccs[ctx_id]; if (!bb) continue; - xe_migrate_ccs_rw_copy_clear(bo, ctx_id); + xe_migrate_ccs_rw_copy_clear(bo, ctx_id, bound); } + + if (bound) + drm_dev_exit(idx); + return 0; } diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 32ded13491ca..67819deb45e3 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1809,10 +1809,10 @@ err_close: return ERR_PTR(err); err_svm_fini: - if (flags & XE_VM_FLAG_FAULT_MODE) { - vm->size = 0; /* close the vm */ - xe_svm_fini(vm); - } + vm->size = 0; /* close the vm */ + if (flags & XE_VM_FLAG_FAULT_MODE) + xe_svm_close(vm); + xe_svm_fini(vm); err_no_resv: mutex_destroy(&vm->snap_mutex); for_each_tile(tile, xe, id) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index 246fe1843142..0474768a38aa 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -332,6 +332,20 @@ static int xe_vm_invalidate_madvise_range(struct xe_vm *vm, u64 start, u64 end) return err; } +/** + * madvise_range_needs_invalidation() - Check whether madvise needs invalidation + * @args: madvise ioctl arguments + * + * Purgeable state updates only touch VMA/BO metadata. PTEs stay valid and are + * zapped only if the BO is later purged. + * + * Return: true when the update needs PTE invalidation. + */ +static bool madvise_range_needs_invalidation(const struct drm_xe_madvise *args) +{ + return args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE; +} + static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madvise *args) { if (XE_IOCTL_DBG(xe, !args)) @@ -708,8 +722,9 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args, &details); - err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, - madvise_range.addr + args->range); + if (madvise_range_needs_invalidation(args)) + err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, + madvise_range.addr + args->range); if (madvise_range.has_svm_userptr_vmas) xe_svm_notifier_unlock(vm); diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 29a23484cbe7..01a022b3ee82 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -1009,7 +1009,7 @@ struct ec_sensors_data { /* sorted list of unique register banks */ u8 banks[ASUS_EC_MAX_BANK + 1]; /* in jiffies */ - unsigned long last_updated; + u64 next_update; struct lock_data lock_data; /* number of board EC sensors */ u8 nr_sensors; @@ -1189,7 +1189,7 @@ static int asus_ec_block_read(const struct device *dev, } for (ireg = 0; ireg < ec->nr_registers; ireg++) { reg_bank = register_bank(ec->registers[ireg]); - if (reg_bank < bank) { + if (reg_bank != bank) { continue; } ec_read(register_index(ec->registers[ireg]), @@ -1278,13 +1278,12 @@ static int get_cached_value_or_update(const struct device *dev, int sensor_index, struct ec_sensors_data *state, s32 *value) { - if (time_after(jiffies, state->last_updated + HZ)) { + if (time_after64(get_jiffies_64(), state->next_update)) { if (update_ec_sensors(dev, state)) { dev_err(dev, "update_ec_sensors() failure\n"); return -EIO; } - - state->last_updated = jiffies; + state->next_update = get_jiffies_64() + HZ; } *value = state->sensors[sensor_index].cached_value; @@ -1402,6 +1401,7 @@ static int asus_ec_probe(struct platform_device *pdev) if (!ec_data) return -ENOMEM; + ec_data->next_update = INITIAL_JIFFIES; dev_set_drvdata(dev, ec_data); ec_data->board_info = pboard_info; @@ -1495,9 +1495,11 @@ static int asus_ec_probe(struct platform_device *pdev) if (!nr_count[type]) continue; - asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev, - nr_count[type], type, - hwmon_attributes[type]); + status = asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); + if (status) + return status; *ptr_asus_ec_ci++ = asus_ec_hwmon_chan++; } diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index b6e508e43fa1..8354a002f4c5 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -645,6 +645,7 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) out_hw_close: hid_hw_close(hdev); + hid_device_io_stop(hdev); out_hw_stop: hid_hw_stop(hdev); return ret; diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index 76f3e1da68d0..ce958cdaef58 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -822,6 +822,7 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id fail_and_close: hid_hw_close(hdev); + hid_device_io_stop(hdev); fail_and_stop: hid_hw_stop(hdev); return ret; diff --git a/drivers/hwmon/gigabyte_waterforce.c b/drivers/hwmon/gigabyte_waterforce.c index 27487e215bdd..4eea05f8b569 100644 --- a/drivers/hwmon/gigabyte_waterforce.c +++ b/drivers/hwmon/gigabyte_waterforce.c @@ -371,13 +371,15 @@ static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id if (IS_ERR(priv->hwmon_dev)) { ret = PTR_ERR(priv->hwmon_dev); hid_err(hdev, "hwmon registration failed with %d\n", ret); - goto fail_and_close; + goto fail_and_io_stop; } waterforce_debugfs_init(priv); return 0; +fail_and_io_stop: + hid_device_io_stop(hdev); fail_and_close: hid_hw_close(hdev); fail_and_stop: diff --git a/drivers/hwmon/nzxt-kraken3.c b/drivers/hwmon/nzxt-kraken3.c index d00409bcab93..05525406c5fb 100644 --- a/drivers/hwmon/nzxt-kraken3.c +++ b/drivers/hwmon/nzxt-kraken3.c @@ -948,7 +948,7 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id ret = kraken3_init_device(hdev); if (ret < 0) { hid_err(hdev, "device init failed with %d\n", ret); - goto fail_and_close; + goto fail_and_stop_io; } ret = kraken3_get_fw_ver(hdev); @@ -960,13 +960,15 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id if (IS_ERR(priv->hwmon_dev)) { ret = PTR_ERR(priv->hwmon_dev); hid_err(hdev, "hwmon registration failed with %d\n", ret); - goto fail_and_close; + goto fail_and_stop_io; } kraken3_debugfs_init(priv, device_name); return 0; +fail_and_stop_io: + hid_device_io_stop(hdev); fail_and_close: hid_hw_close(hdev); fail_and_stop: diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index 58ef9fa0184b..e2316c46629d 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -768,7 +768,7 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev, out_hw_close: hid_hw_close(hdev); - + hid_device_io_stop(hdev); out_hw_stop: hid_hw_stop(hdev); return ret; diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index e18e80e832fd..175208d712b0 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -1052,32 +1052,49 @@ static int occ_setup_sensor_attrs(struct occ *occ) } /* only need to do this once at startup, as OCC won't change sensors on us */ -static void occ_parse_poll_response(struct occ *occ) +static int occ_parse_poll_response(struct occ *occ) { unsigned int i, old_offset, offset = 0, size = 0; + u16 data_length; struct occ_sensor *sensor; - struct occ_sensors *sensors = &occ->sensors; + struct occ_sensors parsed = {}; + struct occ_sensors *sensors = &parsed; struct occ_response *resp = &occ->resp; struct occ_poll_response *poll = (struct occ_poll_response *)&resp->data[0]; struct occ_poll_response_header *header = &poll->header; struct occ_sensor_data_block *block = &poll->block; + data_length = get_unaligned_be16(&resp->data_length); + if (data_length < sizeof(*header) || data_length > OCC_RESP_DATA_BYTES) { + dev_err(occ->bus_dev, "invalid OCC poll response length %u\n", + data_length); + return -EMSGSIZE; + } + dev_info(occ->bus_dev, "OCC found, code level: %.16s\n", header->occ_code_level); for (i = 0; i < header->num_sensor_data_blocks; ++i) { block = (struct occ_sensor_data_block *)((u8 *)block + offset); + if (size + sizeof(*header) + sizeof(block->header) > + data_length) { + dev_err(occ->bus_dev, + "truncated OCC sensor block header\n"); + return -EMSGSIZE; + } + old_offset = offset; offset = (block->header.num_sensors * block->header.sensor_length) + sizeof(block->header); - size += offset; /* validate all the length/size fields */ - if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) { - dev_warn(occ->bus_dev, "exceeded response buffer\n"); - return; + if (size + sizeof(*header) + offset > data_length) { + dev_err(occ->bus_dev, + "exceeded OCC poll response length\n"); + return -EMSGSIZE; } + size += offset; dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n", old_offset, offset - 1, block->header.eye_catcher, @@ -1107,6 +1124,9 @@ static void occ_parse_poll_response(struct occ *occ) dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size, sizeof(*header), size + sizeof(*header)); + occ->sensors = parsed; + + return 0; } int occ_active(struct occ *occ, bool active) @@ -1138,10 +1158,12 @@ int occ_active(struct occ *occ, bool active) goto unlock; } - occ->active = true; occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; - occ_parse_poll_response(occ); + rc = occ_parse_poll_response(occ); + if (rc) + goto unlock; + occ->active = true; rc = occ_setup_sensor_attrs(occ); if (rc) { dev_err(occ->bus_dev, diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 74876d2207fb..e56057e9273c 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -88,6 +88,33 @@ static int max34440_read_word_data(struct i2c_client *client, int page, ret = pmbus_read_word_data(client, page, phase, data->iout_oc_warn_limit); break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_MFR_MAX_TEMP_1: + /* + * MAX34451/ADPM family do not support VIN/IIN limit registers, + * manufacturer-specific min/max registers, or undercurrent/ + * undertemperature fault limits. Accessing these triggers CML + * error and asserts ALERT. + */ + if (data->id == max34451 || data->id == adpm12160 || + data->id == adpm12200 || data->id == adpm12250) + return -ENXIO; + ret = -ENODATA; + break; case PMBUS_VIRT_READ_VOUT_MIN: ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_VOUT_MIN); @@ -244,6 +271,51 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) return ret; } +static int max34451_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); + + switch (reg) { + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* + * MAX34451/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Accessing them triggers CML + * error and asserts ALERT. + */ + if (data->id == max34451 || data->id == adpm12160 || + data->id == adpm12200 || data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: + return -ENODATA; + } +} + +static int max34451_write_byte_data(struct i2c_client *client, int page, + int reg, u8 byte) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); + + switch (reg) { + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* + * MAX34451/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Writing to them triggers CML + * error and asserts ALERT. + */ + if (data->id == max34451 || data->id == adpm12160 || + data->id == adpm12200 || data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: + return -ENODATA; + } +} + static int max34451_set_supported_funcs(struct i2c_client *client, struct max34440_data *data) { @@ -363,7 +435,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT, .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, }, [adpm12200] = { @@ -399,7 +473,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, .func[14] = PMBUS_HAVE_IOUT, .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, }, [adpm12250] = { @@ -433,7 +509,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, .func[14] = PMBUS_HAVE_IOUT, .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, }, [max34440] = { @@ -581,7 +659,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c index 71a7ca2196ad..311c8bc93160 100644 --- a/drivers/infiniband/hw/bng_re/bng_dev.c +++ b/drivers/infiniband/hw/bng_re/bng_dev.c @@ -113,7 +113,7 @@ static void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg, } static int bng_re_net_ring_free(struct bng_re_dev *rdev, - u16 fw_ring_id, int type) + u32 fw_ring_id, int type) { struct bnge_auxr_dev *aux_dev = rdev->aux_dev; struct hwrm_ring_free_input req = {}; @@ -123,7 +123,7 @@ static int bng_re_net_ring_free(struct bng_re_dev *rdev, bng_re_init_hwrm_hdr((void *)&req, HWRM_RING_FREE); req.ring_type = type; - req.ring_id = cpu_to_le16(fw_ring_id); + req.ring_id = cpu_to_le32(fw_ring_id); bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT); rc = bnge_send_msg(aux_dev, &fw_msg); @@ -161,7 +161,7 @@ static int bng_re_net_ring_alloc(struct bng_re_dev *rdev, sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT); rc = bnge_send_msg(aux_dev, &fw_msg); if (!rc) - *fw_ring_id = le16_to_cpu(resp.ring_id); + *fw_ring_id = (u16)le32_to_cpu(resp.ring_id); return rc; } diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index e93bcb5eef70..e7d7b4cb9337 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -3864,6 +3864,12 @@ not_found: return 1; found: + if (early_acpihid_map_size == EARLY_MAP_SIZE) { + pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n", + str); + return 1; + } + p = acpiid; hid = strsep(&p, ":"); uid = p; diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 563f9c2672d5..29dc18d3d22e 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -1450,11 +1450,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu) int ret; u64 data; - if (!iommu->need_sync) - return 0; - raw_spin_lock_irqsave(&iommu->lock, flags); + if (!iommu->need_sync) { + /* + * No command has been queued since the last completion-wait. + * A concurrent CPU may have already queued that CWAIT and + * cleared need_sync; need_sync == false only means a covering + * CWAIT is queued, not that all prior commands have completed. + * Wait for the last allocated sequence number so that any + * command queued before this call (possibly on another CPU) + * is guaranteed to have completed before returning. + */ + data = iommu->cmd_sem_val; + raw_spin_unlock_irqrestore(&iommu->lock, flags); + return wait_on_sem(iommu, data); + } + data = get_cmdsem_val(iommu); build_completion_wait(&cmd, iommu, data); @@ -1464,9 +1476,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu) if (ret) return ret; - ret = wait_on_sem(iommu, data); - - return ret; + return wait_on_sem(iommu, data); } static void domain_flush_complete(struct protection_domain *domain) diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c index 5b902598e68a..63b53b29e029 100644 --- a/drivers/iommu/amd/nested.c +++ b/drivers/iommu/amd/nested.c @@ -59,7 +59,9 @@ static int validate_gdte_nested(struct iommu_hwpt_amd_guest *gdte) return 0; } -static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long index) +static void *gdom_info_load_or_alloc_locked(struct xarray *xa, + unsigned long index, + unsigned long *flags) { struct guest_domain_mapping_info *elm, *res; @@ -67,13 +69,13 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long ind if (elm) return elm; - xa_unlock(xa); + xa_unlock_irqrestore(xa, *flags); elm = kzalloc_obj(struct guest_domain_mapping_info); - xa_lock(xa); + xa_lock_irqsave(xa, *flags); if (!elm) return ERR_PTR(-ENOMEM); - res = __xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL); + res = __xa_cmpxchg(xa, index, NULL, elm, GFP_ATOMIC); if (xa_is_err(res)) res = ERR_PTR(xa_err(res)); @@ -95,6 +97,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, const struct iommu_user_data *user_data) { int ret; + unsigned long irqflags; struct nested_domain *ndom; struct guest_domain_mapping_info *gdom_info; struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core); @@ -136,11 +139,12 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, * keep track of the gDomID mapping. When the S2 is changed, the INVALIDATE_IOMMU_PAGES * command must be issued for each hDomID in the xarray. */ - xa_lock(&aviommu->gdomid_array); + xa_lock_irqsave(&aviommu->gdomid_array, irqflags); - gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, ndom->gdom_id); + gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, + ndom->gdom_id, &irqflags); if (IS_ERR(gdom_info)) { - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); ret = PTR_ERR(gdom_info); goto out_err; } @@ -148,7 +152,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, /* Check if gDomID exist */ if (refcount_inc_not_zero(&gdom_info->users)) { ndom->gdom_info = gdom_info; - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); pr_debug("%s: Found gdom_id=%#x, hdom_id=%#x\n", __func__, ndom->gdom_id, gdom_info->hdom_id); @@ -161,7 +165,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, if (gdom_info->hdom_id <= 0) { __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC); - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); ret = -ENOSPC; goto out_err_gdom_info; } @@ -169,7 +173,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, ndom->gdom_info = gdom_info; refcount_set(&gdom_info->users, 1); - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); pr_debug("%s: Allocate gdom_id=%#x, hdom_id=%#x\n", __func__, ndom->gdom_id, gdom_info->hdom_id); @@ -257,14 +261,15 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev, static void nested_domain_free(struct iommu_domain *dom) { + unsigned long irqflags; struct guest_domain_mapping_info *curr; - struct nested_domain *ndom = to_ndomain(dom); + struct nested_domain *ndom __free(kfree) = to_ndomain(dom); struct amd_iommu_viommu *aviommu = ndom->viommu; - xa_lock(&aviommu->gdomid_array); + xa_lock_irqsave(&aviommu->gdomid_array, irqflags); if (!refcount_dec_and_test(&ndom->gdom_info->users)) { - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); return; } @@ -275,7 +280,7 @@ static void nested_domain_free(struct iommu_domain *dom) curr = __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id, ndom->gdom_info, NULL, GFP_ATOMIC); - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); if (WARN_ON(!curr || xa_err(curr))) return; @@ -285,7 +290,6 @@ static void nested_domain_free(struct iommu_domain *dom) amd_iommu_pdom_id_free(ndom->gdom_info->hdom_id); kfree(curr); - kfree(ndom); } static const struct iommu_domain_ops nested_domain_ops = { diff --git a/drivers/iommu/intel/perf.c b/drivers/iommu/intel/perf.c index 02168f2f20a4..bec98dcbb9ec 100644 --- a/drivers/iommu/intel/perf.c +++ b/drivers/iommu/intel/perf.c @@ -63,7 +63,7 @@ void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type) return; spin_lock_irqsave(&latency_lock, flags); - memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM); + memset(&lstat[type], 0, sizeof(*lstat)); spin_unlock_irqrestore(&latency_lock, flags); } diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index fea10acd4f02..726f7b6d0bff 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -27,7 +27,7 @@ void intel_svm_check(struct intel_iommu *iommu) { - if (!pasid_supported(iommu)) + if (!pasid_supported(iommu) || !ecap_smpwc(iommu->ecap)) return; if (cpu_feature_enabled(X86_FEATURE_GBPAGES) && diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 951dd10e192b..182a41d59a75 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -1211,7 +1211,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) data = true; } v6 = false; - group.ip4 = iph->daddr; + group.ip4 = ip_hdr(skb)->daddr; #if IS_ENABLED(CONFIG_IPV6) } else if (iph->version == 6) { ip6h = ipv6_hdr(skb); @@ -1235,7 +1235,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) data = true; } v6 = true; - group.ip6 = ip6h->daddr; + group.ip6 = ipv6_hdr(skb)->daddr; #endif } else { dev->stats.tx_errors++; @@ -1278,12 +1278,12 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) hlist_for_each_entry_rcu(gnode, &tunnel->groups[hash], node) { if (!v6) { - if (gnode->group_addr.ip4 == iph->daddr) + if (gnode->group_addr.ip4 == group.ip4) goto found; #if IS_ENABLED(CONFIG_IPV6) } else { if (ipv6_addr_equal(&gnode->group_addr.ip6, - &ip6h->daddr)) + &group.ip6)) goto found; #endif } @@ -2000,14 +2000,18 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb, struct igmpv3_report *ihrv3 = igmpv3_report_hdr(skb); int len = skb_transport_offset(skb) + sizeof(*ihrv3); void *zero_grec = (void *)&igmpv3_zero_grec; - struct iphdr *iph = ip_hdr(skb); struct amt_group_node *gnode; union amt_addr group, host; struct igmpv3_grec *grec; + __be32 saddr; u16 nsrcs; + u16 ngrec; int i; - for (i = 0; i < ntohs(ihrv3->ngrec); i++) { + saddr = ip_hdr(skb)->saddr; + ngrec = ntohs(ihrv3->ngrec); + + for (i = 0; i < ngrec; i++) { len += sizeof(*grec); if (!ip_mc_may_pull(skb, len)) break; @@ -2019,10 +2023,13 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb, if (!ip_mc_may_pull(skb, len)) break; + grec = (void *)(skb->data + len - sizeof(*grec) - + nsrcs * sizeof(__be32)); + memset(&group, 0, sizeof(union amt_addr)); group.ip4 = grec->grec_mca; memset(&host, 0, sizeof(union amt_addr)); - host.ip4 = iph->saddr; + host.ip4 = saddr; gnode = amt_lookup_group(tunnel, &group, &host, false); if (!gnode) { gnode = amt_add_group(amt, tunnel, &group, &host, @@ -2162,14 +2169,18 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb, struct mld2_report *mld2r = (struct mld2_report *)icmp6_hdr(skb); int len = skb_transport_offset(skb) + sizeof(*mld2r); void *zero_grec = (void *)&mldv2_zero_grec; - struct ipv6hdr *ip6h = ipv6_hdr(skb); struct amt_group_node *gnode; union amt_addr group, host; struct mld2_grec *grec; + struct in6_addr saddr; u16 nsrcs; + u16 ngrec; int i; - for (i = 0; i < ntohs(mld2r->mld2r_ngrec); i++) { + saddr = ipv6_hdr(skb)->saddr; + ngrec = ntohs(mld2r->mld2r_ngrec); + + for (i = 0; i < ngrec; i++) { len += sizeof(*grec); if (!ipv6_mc_may_pull(skb, len)) break; @@ -2181,10 +2192,13 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb, if (!ipv6_mc_may_pull(skb, len)) break; + grec = (void *)(skb->data + len - sizeof(*grec) - + nsrcs * sizeof(struct in6_addr)); + memset(&group, 0, sizeof(union amt_addr)); group.ip6 = grec->grec_mca; memset(&host, 0, sizeof(union amt_addr)); - host.ip6 = ip6h->saddr; + host.ip6 = saddr; gnode = amt_lookup_group(tunnel, &group, &host, true); if (!gnode) { gnode = amt_add_group(amt, tunnel, &group, &host, @@ -2305,7 +2319,9 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) skb_push(skb, sizeof(*eth)); skb_reset_mac_header(skb); skb_pull(skb, sizeof(*eth)); - eth = eth_hdr(skb); + + if (skb_cow_head(skb, 0)) + return true; if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2315,6 +2331,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) if (!ipv4_is_multicast(iph->daddr)) return true; skb->protocol = htons(ETH_P_IP); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IP); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) @@ -2328,6 +2345,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) if (!ipv6_addr_is_multicast(&ip6h->daddr)) return true; skb->protocol = htons(ETH_P_IPV6); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IPV6); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif @@ -2351,10 +2369,12 @@ static bool amt_membership_query_handler(struct amt_dev *amt, struct sk_buff *skb) { struct amt_header_membership_query *amtmq; - struct igmpv3_query *ihv3; struct ethhdr *eth, *oeth; + struct igmpv3_query *ihv3; + u8 h_source[ETH_ALEN]; struct iphdr *iph; int hdr_size, len; + u64 response_mac; hdr_size = sizeof(*amtmq) + sizeof(struct udphdr); if (!pskb_may_pull(skb, hdr_size)) @@ -2367,6 +2387,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt, if (amtmq->nonce != amt->nonce) return true; + response_mac = amtmq->response_mac; + hdr_size -= sizeof(*eth); if (iptunnel_pull_header(skb, hdr_size, htons(ETH_P_TEB), false)) return true; @@ -2376,6 +2398,9 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_pull(skb, sizeof(*eth)); skb_reset_network_header(skb); eth = eth_hdr(skb); + ether_addr_copy(h_source, oeth->h_source); + if (skb_cow_head(skb, 0)) + return true; if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2388,6 +2413,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, sizeof(*ihv3))) return true; + iph = ip_hdr(skb); if (!ipv4_is_multicast(iph->daddr)) return true; @@ -2395,10 +2421,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_transport_header(skb); skb_push(skb, sizeof(*iph) + AMT_IPHDR_OPTS); WRITE_ONCE(amt->ready4, true); - amt->mac = amtmq->response_mac; + amt->mac = response_mac; amt->req_cnt = 0; amt->qi = ihv3->qqic; skb->protocol = htons(ETH_P_IP); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IP); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) @@ -2421,10 +2448,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_transport_header(skb); skb_push(skb, sizeof(*ip6h) + AMT_IP6HDR_OPTS); WRITE_ONCE(amt->ready6, true); - amt->mac = amtmq->response_mac; + amt->mac = response_mac; amt->req_cnt = 0; amt->qi = mld2q->mld2q_qqic; skb->protocol = htons(ETH_P_IPV6); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IPV6); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif @@ -2432,7 +2460,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, return true; } - ether_addr_copy(eth->h_source, oeth->h_source); + ether_addr_copy(eth->h_source, h_source); skb->pkt_type = PACKET_MULTICAST; skb->ip_summed = CHECKSUM_NONE; len = skb->len; @@ -2455,8 +2483,11 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) struct ethhdr *eth; struct iphdr *iph; int len, hdr_size; + u64 response_mac; + __be32 saddr; + __be32 nonce; - iph = ip_hdr(skb); + saddr = ip_hdr(skb)->saddr; hdr_size = sizeof(*amtmu) + sizeof(struct udphdr); if (!pskb_may_pull(skb, hdr_size)) @@ -2466,15 +2497,18 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) if (amtmu->reserved || amtmu->version) return true; + nonce = amtmu->nonce; + response_mac = amtmu->response_mac; + if (iptunnel_pull_header(skb, hdr_size, skb->protocol, false)) return true; skb_reset_network_header(skb); list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) { - if (tunnel->ip4 == iph->saddr) { - if ((amtmu->nonce == tunnel->nonce && - amtmu->response_mac == tunnel->mac)) { + if (tunnel->ip4 == saddr) { + if ((nonce == tunnel->nonce && + response_mac == tunnel->mac)) { mod_delayed_work(amt_wq, &tunnel->gc_wq, msecs_to_jiffies(amt_gmi(amt)) * 3); @@ -2492,6 +2526,9 @@ report: if (!pskb_may_pull(skb, sizeof(*iph))) return true; + if (skb_cow_head(skb, 0)) + return true; + iph = ip_hdr(skb); if (iph->version == 4) { if (ip_mc_check_igmp(skb)) { @@ -2508,6 +2545,7 @@ report: eth = eth_hdr(skb); skb->protocol = htons(ETH_P_IP); eth->h_proto = htons(ETH_P_IP); + iph = ip_hdr(skb); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) } else if (iph->version == 6) { @@ -2527,6 +2565,7 @@ report: eth = eth_hdr(skb); skb->protocol = htons(ETH_P_IPV6); eth->h_proto = htons(ETH_P_IPV6); + ip6h = ipv6_hdr(skb); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif } else { @@ -2772,7 +2811,7 @@ drop: static int amt_rcv(struct sock *sk, struct sk_buff *skb) { struct amt_dev *amt; - struct iphdr *iph; + __be32 saddr; int type; bool err; @@ -2785,7 +2824,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) } skb->dev = amt->dev; - iph = ip_hdr(skb); + saddr = ip_hdr(skb)->saddr; type = amt_parse_type(skb); if (type == -1) { err = true; @@ -2795,7 +2834,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) if (amt->mode == AMT_MODE_GATEWAY) { switch (type) { case AMT_MSG_ADVERTISEMENT: - if (iph->saddr != amt->discovery_ip) { + if (saddr != amt->discovery_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; @@ -2807,7 +2846,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) } goto out; case AMT_MSG_MULTICAST_DATA: - if (iph->saddr != amt->remote_ip) { + if (saddr != amt->remote_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; @@ -2818,7 +2857,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) else goto out; case AMT_MSG_MEMBERSHIP_QUERY: - if (iph->saddr != amt->remote_ip) { + if (saddr != amt->remote_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; @@ -2995,9 +3034,15 @@ static int amt_dev_open(struct net_device *dev) amt->event_idx = 0; amt->nr_events = 0; + enable_delayed_work(&amt->discovery_wq); + enable_delayed_work(&amt->req_wq); + err = amt_socket_create(amt); - if (err) + if (err) { + disable_delayed_work(&amt->req_wq); + disable_delayed_work(&amt->discovery_wq); return err; + } amt->req_cnt = 0; amt->remote_ip = 0; @@ -3023,8 +3068,8 @@ static int amt_dev_stop(struct net_device *dev) struct sock *sk; int i; - cancel_delayed_work_sync(&amt->req_wq); - cancel_delayed_work_sync(&amt->discovery_wq); + disable_delayed_work_sync(&amt->req_wq); + disable_delayed_work_sync(&amt->discovery_wq); cancel_delayed_work_sync(&amt->secret_wq); /* shutdown */ @@ -3278,6 +3323,8 @@ static int amt_newlink(struct net_device *dev, INIT_DELAYED_WORK(&amt->req_wq, amt_req_work); INIT_DELAYED_WORK(&amt->secret_wq, amt_secret_work); INIT_WORK(&amt->event_wq, amt_event_work); + disable_delayed_work(&amt->req_wq); + disable_delayed_work(&amt->discovery_wq); INIT_LIST_HEAD(&amt->tunnel_list); return 0; err: diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e044fc733b8c..522eab060f9e 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3455,7 +3455,8 @@ static void bond_send_validate(struct bonding *bond, struct slave *slave) { bond_arp_send_all(bond, slave); #if IS_ENABLED(CONFIG_IPV6) - bond_ns_send_all(bond, slave); + if (likely(ipv6_mod_enabled())) + bond_ns_send_all(bond, slave); #endif } diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 59001fd4b6f7..79418e682f71 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1686,11 +1686,14 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma) } } -static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev) +static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) { struct airoha_gdm_port *port = dev->port; struct airoha_eth *eth = dev->eth; u32 val, i = 0; + u64 data; + + spin_lock(&port->stats_lock); /* Read relevant MIB for GDM with multiple port attached */ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX) @@ -1701,152 +1704,188 @@ static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev) u64_stats_update_begin(&dev->stats.syncp); - /* TX */ + /* TX - 64-bit H+L registers: hw accumulates the total, read directly. + * Use local variable to prevent readers from seeing intermediate values. + * Clamp to prevent regression from torn reads between H and L. + */ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); - dev->stats.tx_ok_pkts += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); - dev->stats.tx_ok_pkts += val; + data += val; + dev->stats.tx_ok_pkts = max(data, dev->stats.tx_ok_pkts); val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); - dev->stats.tx_ok_bytes += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); - dev->stats.tx_ok_bytes += val; + data += val; + dev->stats.tx_ok_bytes = max(data, dev->stats.tx_ok_bytes); + /* TX - 32-bit registers: accumulate delta to handle wrap-around. */ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); - dev->stats.tx_drops += val; + dev->stats.tx_drops += (u32)(val - dev->stats.mib_prev.tx_drops); + dev->stats.mib_prev.tx_drops = val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); - dev->stats.tx_broadcast += val; + dev->stats.tx_broadcast += (u32)(val - dev->stats.mib_prev.tx_broadcast); + dev->stats.mib_prev.tx_broadcast = val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); - dev->stats.tx_multicast += val; + dev->stats.tx_multicast += (u32)(val - dev->stats.mib_prev.tx_multicast); + dev->stats.mib_prev.tx_multicast = val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); - dev->stats.tx_len[i] += val; + dev->stats.mib_prev.tx_runt64 += + (u32)(val - dev->stats.mib_prev.tx_runt); + dev->stats.mib_prev.tx_runt = val; + /* tx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */ + data = dev->stats.mib_prev.tx_runt64; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data += (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); - dev->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += (u32)(val - dev->stats.mib_prev.tx_long); + dev->stats.mib_prev.tx_long = val; /* RX */ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); - dev->stats.rx_ok_pkts += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); - dev->stats.rx_ok_pkts += val; + data += val; + dev->stats.rx_ok_pkts = max(data, dev->stats.rx_ok_pkts); val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); - dev->stats.rx_ok_bytes += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); - dev->stats.rx_ok_bytes += val; + data += val; + dev->stats.rx_ok_bytes = max(data, dev->stats.rx_ok_bytes); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); - dev->stats.rx_drops += val; + dev->stats.rx_drops += (u32)(val - dev->stats.mib_prev.rx_drops); + dev->stats.mib_prev.rx_drops = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); - dev->stats.rx_broadcast += val; + dev->stats.rx_broadcast += (u32)(val - dev->stats.mib_prev.rx_broadcast); + dev->stats.mib_prev.rx_broadcast = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); - dev->stats.rx_multicast += val; + dev->stats.rx_multicast += (u32)(val - dev->stats.mib_prev.rx_multicast); + dev->stats.mib_prev.rx_multicast = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); - dev->stats.rx_errors += val; + dev->stats.rx_errors += (u32)(val - dev->stats.mib_prev.rx_errors); + dev->stats.mib_prev.rx_errors = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); - dev->stats.rx_crc_error += val; + dev->stats.rx_crc_error += (u32)(val - dev->stats.mib_prev.rx_crc_error); + dev->stats.mib_prev.rx_crc_error = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); - dev->stats.rx_over_errors += val; + dev->stats.rx_over_errors += (u32)(val - dev->stats.mib_prev.rx_over_errors); + dev->stats.mib_prev.rx_over_errors = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); - dev->stats.rx_fragment += val; + dev->stats.rx_fragment += (u32)(val - dev->stats.mib_prev.rx_fragment); + dev->stats.mib_prev.rx_fragment = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); - dev->stats.rx_jabber += val; + dev->stats.rx_jabber += (u32)(val - dev->stats.mib_prev.rx_jabber); + dev->stats.mib_prev.rx_jabber = val; i = 0; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); - dev->stats.rx_len[i] += val; + dev->stats.mib_prev.rx_runt64 += + (u32)(val - dev->stats.mib_prev.rx_runt); + dev->stats.mib_prev.rx_runt = val; + /* rx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */ + data = dev->stats.mib_prev.rx_runt64; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data += (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); - dev->stats.rx_len[i++] += val; + dev->stats.rx_len[i] += (u32)(val - dev->stats.mib_prev.rx_long); + dev->stats.mib_prev.rx_long = val; u64_stats_update_end(&dev->stats.syncp); -} - -static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) -{ - struct airoha_gdm_port *port = dev->port; - int i; - - spin_lock(&port->stats_lock); - - for (i = 0; i < ARRAY_SIZE(port->devs); i++) { - if (port->devs[i]) - airoha_dev_get_hw_stats(port->devs[i]); - } - - /* Reset MIB counters */ - airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id), - FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); spin_unlock(&port->stats_lock); } @@ -2504,8 +2543,7 @@ static int airoha_tc_setup_qdisc_ets(struct net_device *dev, if (opt->parent == TC_H_ROOT) return -EINVAL; - channel = TC_H_MAJ(opt->handle) >> 16; - channel = channel % AIROHA_NUM_QOS_CHANNELS; + channel = TC_H_MIN(opt->parent) % AIROHA_NUM_QOS_CHANNELS; switch (opt->command) { case TC_ETS_REPLACE: diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index f6d01a8e8da1..fe934f9ffe8a 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -245,6 +245,33 @@ struct airoha_hw_stats { u64 rx_fragment; u64 rx_jabber; u64 rx_len[7]; + + struct { + /* Previous HW register values for 32-bit counter delta + * tracking. Storing the last seen value and accumulating + * (u32)(curr - prev) into the 64-bit software counter + * handles wrap-around transparently via unsigned arithmetic. + * tx_runt64/rx_runt64 hold the running sum of runt deltas. + * These fields are never reported to userspace. + */ + u32 tx_drops; + u32 tx_broadcast; + u32 tx_multicast; + u32 tx_runt; + u32 tx_long; + u64 tx_runt64; + u32 rx_drops; + u32 rx_broadcast; + u32 rx_multicast; + u32 rx_errors; + u32 rx_crc_error; + u32 rx_over_errors; + u32 rx_fragment; + u32 rx_jabber; + u32 rx_runt; + u32 rx_long; + u64 rx_runt64; + } mib_prev; }; enum { diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c index 870d61fdd9c6..b679bed952de 100644 --- a/drivers/net/ethernet/airoha/airoha_npu.c +++ b/drivers/net/ethernet/airoha/airoha_npu.c @@ -168,7 +168,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, dma_addr_t dma_addr; int ret; - dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE); + dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL); ret = dma_mapping_error(npu->dev, dma_addr); if (ret) return ret; @@ -191,7 +191,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, spin_unlock_bh(&npu->cores[core].lock); - dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE); + dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL); return ret; } diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index e7c78293002a..f6396925722d 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1659,6 +1659,7 @@ void airoha_ppe_deinit(struct airoha_eth *eth) npu = rcu_replace_pointer(eth->npu, NULL, lockdep_is_held(&flow_offload_mutex)); if (npu) { + synchronize_rcu(); npu->ops.ppe_deinit(npu); airoha_npu_put(npu); } diff --git a/drivers/net/ethernet/amd/pds_core/adminq.c b/drivers/net/ethernet/amd/pds_core/adminq.c index 097bb092bdb8..eadb4b604fbe 100644 --- a/drivers/net/ethernet/amd/pds_core/adminq.c +++ b/drivers/net/ethernet/amd/pds_core/adminq.c @@ -18,7 +18,13 @@ static int pdsc_process_notifyq(struct pdsc_qcq *qcq) comp = cq_info->comp; eid = le64_to_cpu(comp->event.eid); while (eid > pdsc->last_eid) { - u16 ecode = le16_to_cpu(comp->event.ecode); + u16 ecode; + + /* Order the payload read after the event id, the field the + * driver uses to detect a new completion. + */ + dma_rmb(); + ecode = le16_to_cpu(comp->event.ecode); switch (ecode) { case PDS_EVENT_LINK_CHANGE: @@ -101,6 +107,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq) spin_lock_irqsave(&pdsc->adminq_lock, irqflags); comp = cq->info[cq->tail_idx].comp; while (pdsc_color_match(comp->color, cq->done_color)) { + /* Order the payload reads after the color bit, the field the + * driver uses to detect a new completion. + */ + dma_rmb(); q_info = &q->info[q->tail_idx]; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); diff --git a/drivers/net/ethernet/amd/pds_core/auxbus.c b/drivers/net/ethernet/amd/pds_core/auxbus.c index 73b3481220b1..3acafe10a6d5 100644 --- a/drivers/net/ethernet/amd/pds_core/auxbus.c +++ b/drivers/net/ethernet/amd/pds_core/auxbus.c @@ -177,17 +177,21 @@ void pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf, { struct pds_auxiliary_dev *padev; - if (!*pd_ptr) - return; - mutex_lock(&pf->config_lock); + /* A concurrent del may have already torn this device down and + * cleared it. + */ padev = *pd_ptr; + if (!padev) + goto out_unlock; + pds_client_unregister(pf, padev->client_id); auxiliary_device_delete(&padev->aux_dev); auxiliary_device_uninit(&padev->aux_dev); *pd_ptr = NULL; +out_unlock: mutex_unlock(&pf->config_lock); } @@ -210,6 +214,13 @@ int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf, mutex_lock(&pf->config_lock); + /* Nothing to do if the aux device is already present. This also + * guards against a second add overwriting *pd_ptr and leaking the + * first, symmetric with the check in pdsc_auxbus_dev_del(). + */ + if (*pd_ptr) + goto out_unlock; + mask = BIT_ULL(PDSC_S_FW_DEAD) | BIT_ULL(PDSC_S_STOPPING_DRIVER); if (cf->state & mask) { diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 38a2446571af..04ec2569c61c 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -110,7 +110,6 @@ static void pdsc_qcq_intr_free(struct pdsc *pdsc, struct pdsc_qcq *qcq) return; pdsc_intr_free(pdsc, qcq->intx); - qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED; } static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq) @@ -145,6 +144,12 @@ void pdsc_qcq_free(struct pdsc *pdsc, struct pdsc_qcq *qcq) pdsc_qcq_intr_free(pdsc, qcq); + /* Drain any work queued by ISR before it was freed above */ + if (qcq->work.func) + cancel_work_sync(&qcq->work); + + qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED; + if (qcq->q_base) dma_free_coherent(dev, qcq->q_size, qcq->q_base, qcq->q_base_pa); @@ -304,8 +309,11 @@ err_out: static void pdsc_core_uninit(struct pdsc *pdsc) { - pdsc_qcq_free(pdsc, &pdsc->notifyqcq); + /* Free adminqcq first: its work accesses notifyqcq, so we must + * disable its IRQ and drain its work before freeing notifyqcq. + */ pdsc_qcq_free(pdsc, &pdsc->adminqcq); + pdsc_qcq_free(pdsc, &pdsc->notifyqcq); if (pdsc->kern_dbpage) { iounmap(pdsc->kern_dbpage); @@ -479,8 +487,6 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing) { if (!pdsc->pdev->is_virtfn) pdsc_devcmd_reset(pdsc); - if (pdsc->adminqcq.work.func) - cancel_work_sync(&pdsc->adminqcq.work); pci_clear_master(pdsc->pdev); @@ -533,6 +539,7 @@ static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc) dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n", __func__); cpu_relax(); + cond_resched(); } } @@ -606,9 +613,10 @@ void pdsc_pci_reset_thread(struct work_struct *work) struct pdsc *pdsc = container_of(work, struct pdsc, pci_reset_work); struct pci_dev *pdev = pdsc->pdev; - pci_dev_get(pdev); - pci_reset_function(pdev); - pci_dev_put(pdev); + /* Use try variant to avoid deadlock with pdsc_remove(). + * If lock is contended, the watchdog timer will retry. + */ + pci_try_reset_function(pdev); } static void pdsc_check_pci_health(struct pdsc *pdsc) diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index 2ea97e1c5939..8adae7b18898 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -90,6 +90,12 @@ int pdsc_dl_flash_update(struct devlink *dl, { struct pdsc *pdsc = devlink_priv(dl); + if (params->component) { + NL_SET_ERR_MSG_MOD(extack, + "Component update not supported by this device"); + return -EOPNOTSUPP; + } + return pdsc_firmware_update(pdsc, params->fw, extack); } diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 22db78343eb0..8d94a4d70395 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -238,6 +238,10 @@ static int pdsc_init_pf(struct pdsc *pdsc) /* General workqueue and timer, but don't start timer yet */ snprintf(wq_name, sizeof(wq_name), "%s.%d", PDS_CORE_DRV_NAME, pdsc->uid); pdsc->wq = create_singlethread_workqueue(wq_name); + if (!pdsc->wq) { + err = -ENOMEM; + goto err_out_unmap_bars; + } INIT_WORK(&pdsc->health_work, pdsc_health_thread); INIT_WORK(&pdsc->pci_reset_work, pdsc_pci_reset_thread); timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0); @@ -253,7 +257,7 @@ static int pdsc_init_pf(struct pdsc *pdsc) err = pdsc_setup(pdsc, PDSC_SETUP_INIT); if (err) { mutex_unlock(&pdsc->config_lock); - goto err_out_unmap_bars; + goto err_out_shutdown_timer; } err = pdsc_start(pdsc); @@ -305,13 +309,14 @@ err_out_stop: pdsc_stop(pdsc); err_out_teardown: pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); -err_out_unmap_bars: +err_out_shutdown_timer: timer_shutdown_sync(&pdsc->wdtimer); if (pdsc->wq) destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); pci_free_irq_vectors(pdsc->pdev); +err_out_unmap_bars: pdsc_unmap_bars(pdsc); err_out_release_regions: pci_release_regions(pdsc->pdev); @@ -435,8 +440,6 @@ static void pdsc_remove(struct pci_dev *pdev) pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev); timer_shutdown_sync(&pdsc->wdtimer); - if (pdsc->wq) - destroy_workqueue(pdsc->wq); mutex_lock(&pdsc->config_lock); set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state); @@ -444,6 +447,9 @@ static void pdsc_remove(struct pci_dev *pdev) pdsc_stop(pdsc); pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); mutex_unlock(&pdsc->config_lock); + + if (pdsc->wq) + destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index fa0df6181207..12770af031eb 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -267,9 +267,14 @@ static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable, XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg); - reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL); - reg |= XGBE_VEND2_MAC_AUTO_SW; - XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg); + if (pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) { + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL); + if (enable) + reg |= XGBE_VEND2_MAC_AUTO_SW; + else + reg &= ~XGBE_VEND2_MAC_AUTO_SW; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg); + } } static void xgbe_an37_restart(struct xgbe_prv_data *pdata) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h index f21cff651fd4..4479ccd071f5 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge.h @@ -36,6 +36,7 @@ struct bnge_pf_info { }; #define INVALID_HW_RING_ID ((u16)-1) +#define INVALID_HW_RING_ID_32BIT (U32_MAX) enum { BNGE_FW_CAP_SHORT_CMD = BIT_ULL(0), diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c index 1c9cfec1b633..651c5e783516 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c @@ -1283,7 +1283,7 @@ int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn) int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring, - u32 ring_type, int cmpl_ring_id) + u32 ring_type, u32 cmpl_ring_id) { struct hwrm_ring_free_input *req; struct bnge_dev *bd = bn->bd; @@ -1295,7 +1295,7 @@ int hwrm_ring_free_send_msg(struct bnge_net *bn, req->cmpl_ring = cpu_to_le16(cmpl_ring_id); req->ring_type = ring_type; - req->ring_id = cpu_to_le16(ring->fw_ring_id); + req->ring_id = cpu_to_le32(ring->fw_ring_id); bnge_hwrm_req_hold(bd, req); rc = bnge_hwrm_req_send(bd, req); @@ -1317,7 +1317,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn, struct hwrm_ring_alloc_output *resp; struct hwrm_ring_alloc_input *req; struct bnge_dev *bd = bn->bd; - u16 ring_id, flags = 0; + u32 ring_id, flags = 0; int rc; rc = bnge_hwrm_req_init(bd, req, HWRM_RING_ALLOC); @@ -1401,7 +1401,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn, resp = bnge_hwrm_req_hold(bd, req); rc = bnge_hwrm_req_send(bd, req); - ring_id = le16_to_cpu(resp->ring_id); + ring_id = le32_to_cpu(resp->ring_id); bnge_hwrm_req_drop(bd, req); exit: diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h index 3501de7a89b9..bf452e390d5b 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h @@ -50,7 +50,7 @@ int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd, void bnge_hwrm_stat_ctx_free(struct bnge_net *bn); int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn); int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring, - u32 ring_type, int cmpl_ring_id); + u32 ring_type, u32 cmpl_ring_id); int hwrm_ring_alloc_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring, u32 ring_type, u32 map_index); diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index 70768193004c..6f7ef506d4e1 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -1327,12 +1327,12 @@ err_free_core: return rc; } -u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr) +u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr) { return rxr->rx_cpr->ring_struct.fw_ring_id; } -u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr) +u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr) { return txr->tx_cpr->ring_struct.fw_ring_id; } @@ -1375,12 +1375,12 @@ static void bnge_init_nq_tree(struct bnge_net *bn) struct bnge_nq_ring_info *nqr = &bn->bnapi[i]->nq_ring; struct bnge_ring_struct *ring = &nqr->ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; for (j = 0; j < nqr->cp_ring_count; j++) { struct bnge_cp_ring_info *cpr = &nqr->cp_ring_arr[j]; ring = &cpr->ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } } } @@ -1637,7 +1637,7 @@ static void bnge_init_one_rx_ring_rxbd(struct bnge_net *bn, ring = &rxr->rx_ring_struct; bnge_init_rxbd_pages(ring, type); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn, @@ -1647,7 +1647,7 @@ static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn, u32 type; ring = &rxr->rx_agg_ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; if (bnge_is_agg_reqd(bn->bd)) { type = ((u32)BNGE_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP; @@ -1708,7 +1708,7 @@ static void bnge_init_tx_rings(struct bnge_net *bn) struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; struct bnge_ring_struct *ring = &txr->tx_ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_TX, &txr->bnapi->napi); @@ -1867,7 +1867,7 @@ static int bnge_hwrm_rx_agg_ring_alloc(struct bnge_net *bn, ring->fw_ring_id); bnge_db_write(bn->bd, &rxr->rx_agg_db, rxr->rx_agg_prod); bnge_db_write(bn->bd, &rxr->rx_db, rxr->rx_prod); - bn->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id; + bn->grp_info[grp_idx].agg_fw_ring_id = (u16)ring->fw_ring_id; return 0; } @@ -1886,7 +1886,7 @@ static int bnge_hwrm_rx_ring_alloc(struct bnge_net *bn, return rc; bnge_set_db(bn, &rxr->rx_db, type, map_idx, ring->fw_ring_id); - bn->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id; + bn->grp_info[map_idx].rx_fw_ring_id = (u16)ring->fw_ring_id; return 0; } @@ -1916,7 +1916,7 @@ static int bnge_hwrm_ring_alloc(struct bnge_net *bn) bnge_set_db(bn, &nqr->nq_db, type, map_idx, ring->fw_ring_id); bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons); enable_irq(vector); - bn->grp_info[i].nq_fw_ring_id = ring->fw_ring_id; + bn->grp_info[i].nq_fw_ring_id = (u16)ring->fw_ring_id; if (!i) { rc = bnge_hwrm_set_async_event_cr(bd, ring->fw_ring_id); @@ -1986,15 +1986,13 @@ void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic) tbl_size = bnge_get_rxfh_indir_size(bd); for (i = 0; i < tbl_size; i++) { - u16 ring_id, j; + u32 j; j = bd->rss_indir_tbl[i]; rxr = &bn->rx_ring[j]; - ring_id = rxr->rx_ring_struct.fw_ring_id; - *ring_tbl++ = cpu_to_le16(ring_id); - ring_id = bnge_cp_ring_for_rx(rxr); - *ring_tbl++ = cpu_to_le16(ring_id); + *ring_tbl++ = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id); + *ring_tbl++ = cpu_to_le16(bnge_cp_ring_for_rx(rxr)); } } @@ -2285,7 +2283,7 @@ static void bnge_disable_int(struct bnge_net *bn) nqr = &bnapi->nq_ring; ring = &nqr->ring_struct; - if (ring->fw_ring_id != INVALID_HW_RING_ID) + if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons); } } @@ -2401,7 +2399,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn, u32 grp_idx = rxr->bnapi->index; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = bnge_cp_ring_for_rx(rxr); @@ -2409,7 +2407,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn, RING_FREE_REQ_RING_TYPE_RX, close_path ? cmpl_ring_id : INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[grp_idx].rx_fw_ring_id = INVALID_HW_RING_ID; } @@ -2421,14 +2419,14 @@ static void bnge_hwrm_rx_agg_ring_free(struct bnge_net *bn, u32 grp_idx = rxr->bnapi->index; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = bnge_cp_ring_for_rx(rxr); hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_RX_AGG, close_path ? cmpl_ring_id : INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[grp_idx].agg_fw_ring_id = INVALID_HW_RING_ID; } @@ -2439,14 +2437,14 @@ static void bnge_hwrm_tx_ring_free(struct bnge_net *bn, struct bnge_ring_struct *ring = &txr->tx_ring_struct; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = close_path ? bnge_cp_ring_for_tx(txr) : INVALID_HW_RING_ID; hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_TX, cmpl_ring_id); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_hwrm_cp_ring_free(struct bnge_net *bn, @@ -2455,12 +2453,12 @@ static void bnge_hwrm_cp_ring_free(struct bnge_net *bn, struct bnge_ring_struct *ring; ring = &cpr->ring_struct; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_L2_CMPL, INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) @@ -2496,11 +2494,11 @@ static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) bnge_hwrm_cp_ring_free(bn, &nqr->cp_ring_arr[j]); ring = &nqr->ring_struct; - if (ring->fw_ring_id != INVALID_HW_RING_ID) { + if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) { hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_NQ, INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[i].nq_fw_ring_id = INVALID_HW_RING_ID; } } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h index f4636b5b0cf3..d177919c2e11 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h @@ -630,8 +630,8 @@ struct bnge_l2_filter { refcount_t refcnt; }; -u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr); -u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr); +u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr); +u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr); void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic); int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr, u16 prod, gfp_t gfp); diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h index 341c7f81ed09..bb0c79a1ee60 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h @@ -184,7 +184,7 @@ struct bnge_ctx_mem_info { struct bnge_ring_struct { struct bnge_ring_mem_info ring_mem; - u16 fw_ring_id; + u32 fw_ring_id; union { u16 grp_idx; u16 map_idx; /* Used by NQs */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 9335703768a9..764d2a09668f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4732,6 +4732,7 @@ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) dpaa2_mac_disconnect(mac); dpaa2_mac_close(mac); + put_device(&mac->mc_dev->dev); kfree(mac); } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 858ba844ac51..dd4f60031d0c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1560,6 +1560,7 @@ static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) dpaa2_mac_disconnect(mac); dpaa2_mac_close(mac); + put_device(&mac->mc_dev->dev); kfree(mac); } diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index fe35703c509e..b8d70c0ecb6c 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -900,22 +900,28 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, { struct mac_device *mac_dev = fman_config_to_mac(config); struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs; - u32 tmp; + u32 ecntrl, maccfg2; + + maccfg2 = ioread32be(®s->maccfg2); + maccfg2 &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); switch (state->interface) { case PHY_INTERFACE_MODE_RMII: - tmp = DTSEC_ECNTRL_RMM; + ecntrl = DTSEC_ECNTRL_RMM; + maccfg2 |= MACCFG2_NIBBLE_MODE; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; + ecntrl = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; + maccfg2 |= MACCFG2_BYTE_MODE; break; case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: - tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; + ecntrl = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; + maccfg2 |= MACCFG2_BYTE_MODE; break; default: dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n", @@ -923,7 +929,8 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, return; } - iowrite32be(tmp, ®s->ecntrl); + iowrite32be(ecntrl, ®s->ecntrl); + iowrite32be(maccfg2, ®s->maccfg2); } static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy, diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 1d66d3834f7e..c280ff35ee77 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -13,6 +13,7 @@ #include <linux/netdevice.h> #include <linux/net_tstamp.h> #include <linux/pci.h> +#include <linux/timer.h> #include <linux/ptp_clock_kernel.h> #include <linux/u64_stats_sync.h> #include <net/page_pool/helpers.h> @@ -41,6 +42,7 @@ /* Interval to schedule a stats report update, 20000ms. */ #define GVE_STATS_REPORT_TIMER_PERIOD 20000 +#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */ /* Numbers of NIC tx/rx stats in stats report. */ #define NIC_TX_STATS_REPORT_NUM 0 @@ -341,6 +343,7 @@ struct gve_rx_ring { struct xdp_rxq_info xdp_rxq; struct xsk_buff_pool *xsk_pool; struct page_frag_cache page_cache; /* Page cache to allocate XDP frames */ + struct timer_list starvation_timer; /* for queue starvation recovery */ }; /* A TX desc ring entry */ diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 02cba280d81a..8271f731a91f 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -18,6 +18,16 @@ #include <net/tcp.h> #include <net/xdp_sock_drv.h> +static void gve_rx_starvation_timer(struct timer_list *t) +{ + struct gve_rx_ring *rx = timer_container_of(rx, t, starvation_timer); + struct gve_priv *priv = rx->gve; + struct gve_notify_block *block; + + block = &priv->ntfy_blocks[rx->ntfy_id]; + napi_schedule(&block->napi); +} + static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx) { struct device *hdev = &priv->pdev->dev; @@ -120,6 +130,7 @@ void gve_rx_stop_ring_dqo(struct gve_priv *priv, int idx) if (rx->dqo.page_pool) page_pool_disable_direct_recycling(rx->dqo.page_pool); + timer_shutdown_sync(&rx->starvation_timer); gve_remove_napi(priv, ntfy_idx); gve_rx_remove_from_block(priv, idx); gve_rx_reset_ring_dqo(priv, idx); @@ -208,8 +219,10 @@ static int gve_rx_alloc_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx, void gve_rx_start_ring_dqo(struct gve_priv *priv, int idx) { int ntfy_idx = gve_rx_idx_to_ntfy(priv, idx); + struct gve_rx_ring *rx = &priv->rx[idx]; gve_rx_add_to_block(priv, idx); + timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0); gve_add_napi(priv, ntfy_idx, gve_napi_poll_dqo); } @@ -365,6 +378,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq; struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq; struct gve_priv *priv = rx->gve; + u32 num_bufs_avail_to_hw; u32 num_avail_slots; u32 num_full_slots; u32 num_posted = 0; @@ -400,6 +414,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) } rx->fill_cnt += num_posted; + + /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors + * visible to the hardware, the hardware is in danger of starving + * and cannot trigger interrupts. + * + * We use a threshold of 32 because a single maximum-sized RSC + * packet can consume up to 19 descriptors in the Rx path. Lower + * thresholds (e.g., 8 or 16) would be unsafe as they could cause + * the device to drop/stall on a maximum-sized RSC packet. + * + * Start the timer to periodically reschedule NAPI and recover. + */ + num_bufs_avail_to_hw = + ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) - + bufq->head) & bufq->mask; + + if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) { + mod_timer(&rx->starvation_timer, + jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS)); + } } static void gve_rx_skb_csum(struct sk_buff *skb, diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index 18376bcc718a..fc2c47dcfaab 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -594,7 +594,11 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) skb = build_skb(buf, priv->rx_buf_size); if (unlikely(!skb)) { net_dbg_ratelimited("build_skb failed\n"); - goto refill; + /* Retain the slot; return budget so NAPI retries this + * buffer. Refill would overwrite rx_buf[]/rx_phys[] + * and leak them. + */ + return budget; } dma_unmap_single(priv->dev, priv->rx_phys[priv->rx_head], @@ -622,14 +626,15 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) rx++; } -refill: buf = netdev_alloc_frag(priv->rx_buf_size); if (!buf) goto done; phys = dma_map_single(priv->dev, buf, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(priv->dev, phys)) + if (dma_mapping_error(priv->dev, phys)) { + skb_free_frag(buf); goto done; + } priv->rx_buf[priv->rx_head] = buf; priv->rx_phys[priv->rx_head] = phys; hip04_set_recv_desc(priv, phys); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 52ea97c818b8..d9ab94910a2a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -104,8 +104,6 @@ struct hinic_dev { u16 num_rss; u16 rss_limit; struct hinic_rss_type rss_type; - u8 *rss_hkey_user; - s32 *rss_indir_user; struct hinic_intr_coal_info *rx_intr_coalesce; struct hinic_intr_coal_info *tx_intr_coalesce; struct hinic_sriov_info sriov_info; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index a8b129ce1b7e..f75e8563f23a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -1064,17 +1064,6 @@ static int __set_rss_rxfh(struct net_device *netdev, int err; if (indir) { - if (!nic_dev->rss_indir_user) { - nic_dev->rss_indir_user = - kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, - GFP_KERNEL); - if (!nic_dev->rss_indir_user) - return -ENOMEM; - } - - memcpy(nic_dev->rss_indir_user, indir, - sizeof(u32) * HINIC_RSS_INDIR_SIZE); - err = hinic_rss_set_indir_tbl(nic_dev, nic_dev->rss_tmpl_idx, indir); if (err) @@ -1082,16 +1071,6 @@ static int __set_rss_rxfh(struct net_device *netdev, } if (key) { - if (!nic_dev->rss_hkey_user) { - nic_dev->rss_hkey_user = - kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL); - - if (!nic_dev->rss_hkey_user) - return -ENOMEM; - } - - memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE); - err = hinic_rss_set_template_tbl(nic_dev, nic_dev->rss_tmpl_idx, key); if (err) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index f72bb1aa4067..fc91b6665f90 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -767,6 +767,9 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring) struct ice_vsi *vsi = ring->vsi; struct ice_pf *pf = vsi->back; + if (vsi->type != ICE_VSI_PF) + return false; + return test_bit(ring->q_index, pf->txtime_txqs); } diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index c30e27bbfe6e..b069e6c514fb 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -512,9 +512,6 @@ int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf) struct ice_repr *repr; int err; - if (!ice_is_eswitch_mode_switchdev(pf)) - return 0; - repr = ice_repr_create_vf(vf); if (IS_ERR(repr)) return PTR_ERR(repr); diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c index 310e8fe2925c..08a17ded0ad5 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.c +++ b/drivers/net/ethernet/intel/ice/ice_lag.c @@ -2623,7 +2623,7 @@ int ice_init_lag(struct ice_pf *pf) goto free_lport_res; /* associate recipes to profiles */ - for (n = 0; n < ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER; n++) { + for (n = 0; n < ICE_MAX_NUM_PROFILES; n++) { err = ice_aq_get_recipe_to_profile(&pf->hw, n, &recipe_bits, NULL); if (err) diff --git a/drivers/net/ethernet/intel/ice/ice_parser.c b/drivers/net/ethernet/intel/ice/ice_parser.c index f8e69630fb72..3ede4c1a5a8a 100644 --- a/drivers/net/ethernet/intel/ice/ice_parser.c +++ b/drivers/net/ethernet/intel/ice/ice_parser.c @@ -2368,6 +2368,9 @@ int ice_parser_profile_init(struct ice_parser_result *rslt, u16 proto_off = 0; u16 off; + if (rslt->ptype >= ICE_FLOW_PTYPE_MAX) + return -EINVAL; + memset(prof, 0, sizeof(*prof)); set_bit(rslt->ptype, prof->ptypes); if (blk == ICE_BLK_SW) { diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index ec3d89d8d4d3..eaec36ab6ae3 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -346,7 +346,7 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) return 0; } - return ice_ptp_extend_32b_ts(pf->ptp.cached_phc_time, + return ice_ptp_extend_32b_ts(READ_ONCE(pf->ptp.cached_phc_time), (in_tstamp >> 8) & mask); } @@ -3037,6 +3037,11 @@ void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) struct ice_ptp *ptp = &pf->ptp; int err; + if (ptp->state == ICE_PTP_UNINIT) { + dev_dbg(ice_pf_to_dev(pf), "PTP was not initialized, skipping rebuild\n"); + return; + } + if (ptp->state == ICE_PTP_READY) { ice_ptp_prepare_for_reset(pf, reset_type); } else if (ptp->state != ICE_PTP_RESETTING) { diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 7e00e091756d..e04de0215596 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -484,12 +484,14 @@ static int ice_start_vfs(struct ice_pf *pf) goto teardown; } - retval = ice_eswitch_attach_vf(pf, vf); - if (retval) { - dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d", - vf->vf_id, retval); - ice_vf_vsi_release(vf); - goto teardown; + if (ice_is_eswitch_mode_switchdev(pf)) { + retval = ice_eswitch_attach_vf(pf, vf); + if (retval) { + dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d", + vf->vf_id, retval); + ice_vf_vsi_release(vf); + goto teardown; + } } set_bit(ICE_VF_STATE_INIT, vf->vf_states); diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index 4ca1a0602307..c04c5856dad6 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -1654,7 +1654,7 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) ret = ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, &frag_off); if (ret < 0) - return -1; + goto checksum_sw_fb; } /* define outer transport */ @@ -1673,11 +1673,7 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) l4.hdr = skb_inner_network_header(skb); break; default: - if (first->tx_flags & ICE_TX_FLAGS_TSO) - return -1; - - skb_checksum_help(skb); - return 0; + goto checksum_sw_fb; } /* compute outer L3 header size */ @@ -1736,7 +1732,7 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, &frag_off); } else { - return -1; + goto checksum_sw_fb; } /* compute inner L3 header size */ @@ -1789,15 +1785,17 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) break; default: - if (first->tx_flags & ICE_TX_FLAGS_TSO) - return -1; - skb_checksum_help(skb); - return 0; + goto checksum_sw_fb; } off->td_cmd |= cmd; off->td_offset |= offset; return 1; + +checksum_sw_fb: + if (first->tx_flags & ICE_TX_FLAGS_TSO) + return -1; + return skb_checksum_help(skb); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 27e4acb1620f..9052e71e9c99 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -812,7 +812,8 @@ void ice_reset_all_vfs(struct ice_pf *pf) } ice_vf_post_vsi_rebuild(vf); - ice_eswitch_attach_vf(pf, vf); + if (ice_is_eswitch_mode_switchdev(pf)) + ice_eswitch_attach_vf(pf, vf); mutex_unlock(&vf->cfg_lock); } diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c index be66f9b2e101..dc5ad784f456 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c @@ -3555,7 +3555,6 @@ restart: pci_sriov_set_totalvfs(adapter->pdev, idpf_get_max_vfs(adapter)); num_max_vports = idpf_get_max_vports(adapter); - adapter->max_vports = num_max_vports; adapter->vports = kzalloc_objs(*adapter->vports, num_max_vports); if (!adapter->vports) return -ENOMEM; @@ -3576,6 +3575,12 @@ restart: goto err_netdev_alloc; } + /* Set max_vports only after vports, netdevs and vport_config buffers + * are allocated to make sure max_vport bound loops don't end up + * crashing, following allocation errors on init. + */ + adapter->max_vports = num_max_vports; + /* Start the mailbox task before requesting vectors. This will ensure * vector information response from mailbox is handled */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 5dd0591fed99..99d78fc5a2c4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -272,6 +272,7 @@ exit: if (allocated) { pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC; pfvf->flags |= OTX2_FLAG_NTUPLE_SUPPORT; + pfvf->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT; } if (allocated != count) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 40162b08014d..0b46ec29e64e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -30,6 +30,7 @@ #define OTX2_UNSUPP_LSE_DEPTH GENMASK(6, 4) #define MCAST_INVALID_GRP (-1U) +#define RATE_MANTISSA_BITS 8 static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, u32 *burst_exp, u32 *burst_mantissa) @@ -66,28 +67,30 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp, u32 *mantissa, u32 *div_exp) { - u64 tmp; - /* Rate calculation by hardware * * PIR_ADD = ((256 + mantissa) << exp) / 256 * rate = (2 * PIR_ADD) / ( 1 << div_exp) * The resultant rate is in Mbps. + * + * Use div_exp = 0 and compute exp/mantissa for maxrate / 2; the + * leading factor of two yields the full rate. Rates below 2 Mbps + * are floored to the smallest step (exp = 0, mantissa = 0). */ - /* 2Mbps to 100Gbps can be expressed with div_exp = 0. - * Setting this to '0' will ease the calculation of - * exponent and mantissa. - */ *div_exp = 0; - if (maxrate) { - *exp = ilog2(maxrate) ? ilog2(maxrate) - 1 : 0; - tmp = maxrate - rounddown_pow_of_two(maxrate); - if (maxrate < MAX_RATE_MANTISSA) - *mantissa = tmp * 2; - else - *mantissa = tmp / (1ULL << (*exp - 7)); + maxrate = maxrate / 2; + if (!maxrate) { + /* Rates below 2 Mbps map to the smallest step */ + *exp = 0; + *mantissa = 0; + } else { + *exp = ilog2(maxrate); + /* Clear MSB and derive fractional bits */ + maxrate &= ~BIT(*exp); + *mantissa = (maxrate << RATE_MANTISSA_BITS) >> *exp; + } } else { /* Instead of disabling rate limiting, set all values to max */ *exp = MAX_RATE_EXPONENT; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 4b86df6d5b9e..00e706e1ede1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -173,6 +173,13 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, } memcpy(ets->tc_tsa, priv->dcbx.tc_tsa, sizeof(ets->tc_tsa)); + /* Report 0 for non ETS TSA */ + for (i = 0; i < ets->ets_cap; i++) { + if (ets->tc_tx_bw[i] == MLX5E_MAX_BW_ALLOC && + priv->dcbx.tc_tsa[i] != IEEE_8021QAZ_TSA_ETS) + ets->tc_tx_bw[i] = 0; + } + return err; } @@ -317,6 +324,14 @@ static int mlx5e_dbcnl_validate_ets(struct net_device *netdev, } } + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_CB_SHAPER) { + netdev_err(netdev, + "Failed to validate ETS: CB Shaper is not supported\n"); + return -EOPNOTSUPP; + } + } + /* Validate Bandwidth Sum */ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index aa8610cedaa8..c1acb9012d3f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -211,11 +211,11 @@ static void mlx5e_disable_async_events(struct mlx5e_priv *priv) static int mlx5e_devcom_event_mpv(int event, void *my_data, void *event_data) { - struct mlx5e_priv *slave_priv = my_data; + struct mlx5e_priv *master_priv = event_data; switch (event) { case MPV_DEVCOM_MASTER_UP: - mlx5_devcom_comp_set_ready(slave_priv->devcom, true); + mlx5_devcom_comp_set_ready(master_priv->devcom, true); break; case MPV_DEVCOM_MASTER_DOWN: /* no need for comp set ready false since we unregister after diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c index ba5cce706ea2..9693c74e9b16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c @@ -71,7 +71,7 @@ int esw_egress_acl_vlan_create(struct mlx5_eswitch *esw, flow_act.action = flow_action; vport->egress.allowed_vlan = mlx5_add_flow_rules(vport->egress.acl, spec, - &flow_act, fwd_dest, 0); + &flow_act, fwd_dest, fwd_dest ? 1 : 0); if (IS_ERR(vport->egress.allowed_vlan)) { err = PTR_ERR(vport->egress.allowed_vlan); esw_warn(esw->dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index ddbe9ca8971d..9f682f6bdb50 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -314,7 +314,7 @@ static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num, return -EIO; } - ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); + ptr = MLX5_ADDR_OF(mcia_reg, out, dwords); *module_id = ptr[0]; @@ -399,7 +399,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev, return -EIO; } - ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); + ptr = MLX5_ADDR_OF(mcia_reg, out, dwords); memcpy(data, ptr, size); return size; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c index 6d5833479d12..237300b82b91 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c @@ -96,6 +96,9 @@ static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res) res->mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET, addr, key); + if (!res->mutex) + return -ENOMEM; + res->cpp_id = NFP_CPP_ID(entry.region.cpp_target, entry.region.cpp_action, entry.region.cpp_token); diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h index 9bd6872474c1..03b12d83f6e9 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase.h +++ b/drivers/net/ethernet/realtek/rtase/rtase.h @@ -192,6 +192,12 @@ enum rtase_sw_flag_content { RTASE_SWF_MSIX_ENABLED = BIT(2), }; +enum rtase_parse_result { + RTASE_PARSE_OK, + RTASE_PARSE_SKIP, + RTASE_PARSE_DROP, +}; + #define RSVD_MASK 0x3FFFC000 struct rtase_tx_desc { @@ -363,4 +369,6 @@ struct rtase_private { #define RTASE_MSS_MASK GENMASK(28, 18) +#define RTASE_MIN_PAD_LEN 47 + #endif /* RTASE_H */ diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index 255667775f0e..4168ad9e48ea 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -61,6 +61,7 @@ #include <linux/pci.h> #include <linux/pm_runtime.h> #include <linux/prefetch.h> +#include <linux/ptp_classify.h> #include <linux/rtnetlink.h> #include <linux/tcp.h> #include <asm/irq.h> @@ -1252,6 +1253,199 @@ static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev) return csum_cmd; } +static enum rtase_parse_result rtase_get_l3_proto(struct sk_buff *skb, + __be16 *proto, + u32 *network_offset) +{ + struct vlan_hdr *vh, _vh; + struct ethhdr *eh, _eh; + u32 offset = ETH_HLEN; + + eh = skb_header_pointer(skb, 0, sizeof(_eh), &_eh); + if (!eh) + return RTASE_PARSE_DROP; + + *proto = eh->h_proto; + + while (eth_type_vlan(*proto)) { + vh = skb_header_pointer(skb, offset, sizeof(_vh), &_vh); + if (!vh) + return RTASE_PARSE_DROP; + + *proto = vh->h_vlan_encapsulated_proto; + offset += VLAN_HLEN; + } + + *network_offset = offset; + + return RTASE_PARSE_OK; +} + +static bool rtase_pad_to_transport_len(struct sk_buff *skb, + u32 transport_offset, + u32 pad_to_len) +{ + u32 trans_data_len; + u32 pad_len; + + trans_data_len = skb->len - transport_offset; + if (trans_data_len >= pad_to_len) + return true; + + if (skb_is_nonlinear(skb)) { + if (skb_linearize(skb)) + return false; + } + + pad_len = pad_to_len - trans_data_len; + if (__skb_put_padto(skb, skb->len + pad_len, false)) + return false; + + return true; +} + +static enum rtase_parse_result rtase_get_transport_offset(struct sk_buff *skb, + u32 *transport_offset, + u8 *transport_proto, + u32 *pad_to_len) +{ + enum rtase_parse_result ret; + struct ipv6hdr *i6h, _i6h; + struct iphdr *ih, _ih; + bool non_first_frag; + __be16 proto; + u32 offset; + u32 no; + + ret = rtase_get_l3_proto(skb, &proto, &no); + if (ret != RTASE_PARSE_OK) + return ret; + + switch (proto) { + case htons(ETH_P_IP): + ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih); + if (!ih) + return RTASE_PARSE_DROP; + + if (ih->ihl < 5) + return RTASE_PARSE_DROP; + + offset = no + ih->ihl * 4; + if (offset > skb->len) + return RTASE_PARSE_DROP; + + non_first_frag = ntohs(ih->frag_off) & IP_OFFSET; + + if (ih->protocol == IPPROTO_TCP) { + if (skb->len - offset < sizeof(struct tcphdr)) { + if (non_first_frag) { + *transport_offset = offset; + *transport_proto = IPPROTO_TCP; + *pad_to_len = sizeof(struct tcphdr); + + return RTASE_PARSE_OK; + } + + return RTASE_PARSE_DROP; + } + + return RTASE_PARSE_SKIP; + } + + if (ih->protocol != IPPROTO_UDP) + return RTASE_PARSE_SKIP; + + *transport_offset = offset; + *transport_proto = IPPROTO_UDP; + + if (skb->len - offset < sizeof(struct udphdr)) { + if (non_first_frag) { + *pad_to_len = sizeof(struct udphdr); + + return RTASE_PARSE_OK; + } + + return RTASE_PARSE_DROP; + } + + return RTASE_PARSE_OK; + + case htons(ETH_P_IPV6): + i6h = skb_header_pointer(skb, no, sizeof(_i6h), &_i6h); + if (!i6h) + return RTASE_PARSE_DROP; + + offset = no + sizeof(*i6h); + + if (i6h->nexthdr == IPPROTO_TCP) { + if (skb->len - offset < sizeof(struct tcphdr)) + return RTASE_PARSE_DROP; + + return RTASE_PARSE_SKIP; + } + + if (i6h->nexthdr != IPPROTO_UDP) + return RTASE_PARSE_SKIP; + + if (skb->len - offset < sizeof(struct udphdr)) + return RTASE_PARSE_DROP; + + *transport_offset = offset; + *transport_proto = IPPROTO_UDP; + + return RTASE_PARSE_OK; + + default: + return RTASE_PARSE_SKIP; + } +} + +static bool rtase_skb_pad(struct sk_buff *skb) +{ + enum rtase_parse_result ret; + u32 transport_offset; + __be16 *dest, _dest; + u32 trans_data_len; + u32 pad_to_len = 0; + u8 transport_proto; + u16 dest_port; + + ret = rtase_get_transport_offset(skb, &transport_offset, + &transport_proto, &pad_to_len); + if (ret == RTASE_PARSE_SKIP) { + return true; + } else if (ret == RTASE_PARSE_DROP) { + netdev_dbg(skb->dev, "drop malformed packet\n"); + return false; + } + + if (pad_to_len && + !rtase_pad_to_transport_len(skb, transport_offset, pad_to_len)) + return false; + + if (transport_proto != IPPROTO_UDP) + return true; + + trans_data_len = skb->len - transport_offset; + if (trans_data_len < offsetof(struct udphdr, len) || + trans_data_len >= RTASE_MIN_PAD_LEN) + return true; + + dest = skb_header_pointer(skb, + transport_offset + + offsetof(struct udphdr, dest), + sizeof(_dest), &_dest); + if (!dest) + return true; + + dest_port = ntohs(*dest); + if (dest_port != PTP_EV_PORT && dest_port != PTP_GEN_PORT) + return true; + + return rtase_pad_to_transport_len(skb, transport_offset, + RTASE_MIN_PAD_LEN); +} + static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb, u32 opts1, u32 opts2) { @@ -1365,6 +1559,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, opts2 |= rtase_tx_csum(skb, dev); } + if (!rtase_skb_pad(skb)) + goto err_dma_0; + frags = rtase_xmit_frags(ring, skb, opts1, opts2); if (unlikely(frags < 0)) goto err_dma_0; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index b8d467ba6d72..4d207f41a43b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -525,6 +525,32 @@ static int intel_set_reg_access(const struct pmc_serdes_regs *regs, int max_regs return ret; } +/* + * Return true if the SerDes lane rate must change to serve @interface. + * If the current rate cannot be determined, reconfigure as before. + */ +static bool intel_serdes_needs_reconfig(struct stmmac_priv *priv, + struct intel_priv_data *intel_priv, + phy_interface_t interface) +{ + u32 cur_rate, want_rate; + int data; + + if (!intel_priv->mdio_adhoc_addr) + return true; + + data = mdiobus_read(priv->mii, intel_priv->mdio_adhoc_addr, + SERDES_GCR0); + if (data < 0) + return true; + + cur_rate = (data & SERDES_RATE_MASK) >> SERDES_RATE_PCIE_SHIFT; + want_rate = interface == PHY_INTERFACE_MODE_2500BASEX ? + SERDES_RATE_PCIE_GEN2 : SERDES_RATE_PCIE_GEN1; + + return cur_rate != want_rate; +} + static int intel_mac_finish(struct net_device *ndev, void *intel_data, unsigned int mode, @@ -536,6 +562,11 @@ static int intel_mac_finish(struct net_device *ndev, int max_regs = 0; int ret = 0; + if (!intel_serdes_needs_reconfig(priv, intel_priv, interface)) { + priv->plat->phy_interface = interface; + return 0; + } + ret = intel_tsn_lane_is_available(ndev, intel_priv); if (ret < 0) { netdev_info(priv->dev, "No TSN lane available to set the registers.\n"); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 829a23bdad01..23ffe1adcd0d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -106,6 +106,17 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); } +static void dwmac4_dma_deinit_channel(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; + u32 value; + + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + value &= ~DMA_CHAN_INTR_DEFAULT_MASK; + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); +} + static void dwmac410_dma_init_channel(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, u32 chan) @@ -125,6 +136,17 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); } +static void dwmac410_dma_deinit_channel(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; + u32 value; + + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + value &= ~DMA_CHAN_INTR_DEFAULT_MASK_4_10; + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); +} + static void dwmac4_dma_init(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg) { @@ -548,6 +570,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = { .reset = dwmac4_dma_reset, .init = dwmac4_dma_init, .init_chan = dwmac4_dma_init_channel, + .deinit_chan = dwmac4_dma_deinit_channel, .init_rx_chan = dwmac4_dma_init_rx_chan, .init_tx_chan = dwmac4_dma_init_tx_chan, .axi = dwmac4_dma_axi, @@ -577,6 +600,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = { .reset = dwmac4_dma_reset, .init = dwmac4_dma_init, .init_chan = dwmac410_dma_init_channel, + .deinit_chan = dwmac410_dma_deinit_channel, .init_rx_chan = dwmac4_dma_init_rx_chan, .init_tx_chan = dwmac4_dma_init_tx_chan, .axi = dwmac4_dma_axi, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index f02b434bbd50..52054f31376d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -1370,36 +1370,40 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no, value &= ~XGMAC_L4PEN0; } - value &= ~(XGMAC_L4SPM0 | XGMAC_L4SPIM0); - value &= ~(XGMAC_L4DPM0 | XGMAC_L4DPIM0); if (sa) { value |= XGMAC_L4SPM0; if (inv) value |= XGMAC_L4SPIM0; + else + value &= ~XGMAC_L4SPIM0; } else { value |= XGMAC_L4DPM0; if (inv) value |= XGMAC_L4DPIM0; + else + value &= ~XGMAC_L4DPIM0; } ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, value); if (ret) return ret; - if (sa) { - value = FIELD_PREP(XGMAC_L4SP0, match); + ret = dwxgmac2_filter_read(hw, filter_no, XGMAC_L4_ADDR, &value); + if (ret) + return ret; - ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); - if (ret) - return ret; + if (sa) { + value &= ~XGMAC_L4SP0; + value |= FIELD_PREP(XGMAC_L4SP0, match); } else { - value = FIELD_PREP(XGMAC_L4DP0, match); - - ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); - if (ret) - return ret; + value &= ~XGMAC_L4DP0; + value |= FIELD_PREP(XGMAC_L4DP0, match); } + ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); + if (ret) + return ret; + if (!en) return dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, 0); diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index e6317b94fff7..04dafec021b4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -170,6 +170,8 @@ struct stmmac_dma_ops { void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg); void (*init_chan)(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, u32 chan); + void (*deinit_chan)(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan); void (*init_rx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, dma_addr_t phy, u32 chan); @@ -235,6 +237,8 @@ struct stmmac_dma_ops { stmmac_do_void_callback(__priv, dma, init, __args) #define stmmac_init_chan(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, init_chan, __priv, __args) +#define stmmac_deinit_chan(__priv, __args...) \ + stmmac_do_void_callback(__priv, dma, deinit_chan, __priv, __args) #define stmmac_init_rx_chan(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args) #define stmmac_init_tx_chan(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 2a0d7eff88d3..151c77713025 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1083,63 +1083,45 @@ static void stmmac_mac_link_up(struct phylink_config *config, old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl = old_ctrl & ~priv->hw->link.speed_mask; - if (interface == PHY_INTERFACE_MODE_USXGMII) { - switch (speed) { - case SPEED_10000: - ctrl |= priv->hw->link.xgmii.speed10000; - break; - case SPEED_5000: - ctrl |= priv->hw->link.xgmii.speed5000; - break; - case SPEED_2500: + switch (speed) { + case SPEED_100000: + ctrl |= priv->hw->link.xlgmii.speed100000; + break; + case SPEED_50000: + ctrl |= priv->hw->link.xlgmii.speed50000; + break; + case SPEED_40000: + ctrl |= priv->hw->link.xlgmii.speed40000; + break; + case SPEED_25000: + ctrl |= priv->hw->link.xlgmii.speed25000; + break; + case SPEED_10000: + ctrl |= priv->hw->link.xgmii.speed10000; + break; + case SPEED_5000: + ctrl |= priv->hw->link.xgmii.speed5000; + break; + case SPEED_2500: + if (interface == PHY_INTERFACE_MODE_USXGMII) ctrl |= priv->hw->link.xgmii.speed2500; - break; - default: - return; - } - } else if (interface == PHY_INTERFACE_MODE_XLGMII) { - switch (speed) { - case SPEED_100000: - ctrl |= priv->hw->link.xlgmii.speed100000; - break; - case SPEED_50000: - ctrl |= priv->hw->link.xlgmii.speed50000; - break; - case SPEED_40000: - ctrl |= priv->hw->link.xlgmii.speed40000; - break; - case SPEED_25000: - ctrl |= priv->hw->link.xlgmii.speed25000; - break; - case SPEED_10000: - ctrl |= priv->hw->link.xgmii.speed10000; - break; - case SPEED_2500: - ctrl |= priv->hw->link.speed2500; - break; - case SPEED_1000: - ctrl |= priv->hw->link.speed1000; - break; - default: - return; - } - } else { - switch (speed) { - case SPEED_2500: + else ctrl |= priv->hw->link.speed2500; - break; - case SPEED_1000: - ctrl |= priv->hw->link.speed1000; - break; - case SPEED_100: - ctrl |= priv->hw->link.speed100; - break; - case SPEED_10: - ctrl |= priv->hw->link.speed10; - break; - default: - return; - } + break; + case SPEED_1000: + ctrl |= priv->hw->link.speed1000; + break; + case SPEED_100: + ctrl |= priv->hw->link.speed100; + break; + case SPEED_10: + ctrl |= priv->hw->link.speed10; + break; + default: + netdev_err(priv->dev, + "unsupported speed %s on %s, leaving the MAC disabled\n", + phy_speed_to_str(speed), phy_modes(interface)); + return; } if (priv->plat->fix_mac_speed) @@ -2560,6 +2542,7 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv) { u8 rx_channels_count = priv->plat->rx_queues_to_use; u8 tx_channels_count = priv->plat->tx_queues_to_use; + u8 dma_csr_ch = max(rx_channels_count, tx_channels_count); u8 chan; for (chan = 0; chan < rx_channels_count; chan++) @@ -2567,6 +2550,9 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv) for (chan = 0; chan < tx_channels_count; chan++) stmmac_stop_tx_dma(priv, chan); + + for (chan = 0; chan < dma_csr_ch; chan++) + stmmac_deinit_chan(priv, priv->ioaddr, chan); } /** diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index d78652718599..14cabe76e53e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -446,6 +446,7 @@ static int tc_parse_flow_actions(struct stmmac_priv *priv, } #define ETHER_TYPE_FULL_MASK cpu_to_be16(~0) +#define IP_PROTO_FULL_MASK 0xFF static int tc_add_basic_flow(struct stmmac_priv *priv, struct flow_cls_offload *cls, @@ -461,6 +462,37 @@ static int tc_add_basic_flow(struct stmmac_priv *priv, flow_rule_match_basic(rule, &match); + /* Both network proto and transport proto not present in the key */ + if (!match.mask || !(match.mask->n_proto || match.mask->ip_proto)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "filter must specify network or transport protocol"); + return -EOPNOTSUPP; + } + + /* If the proto is present in the key and is not full mask */ + if ((match.mask->n_proto && match.mask->n_proto != ETHER_TYPE_FULL_MASK) || + (match.mask->ip_proto && match.mask->ip_proto != IP_PROTO_FULL_MASK)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only full protocol mask is supported"); + return -EOPNOTSUPP; + } + + /* Network proto is present in the key and is not IPv4 */ + if (match.mask->n_proto && match.key->n_proto != cpu_to_be16(ETH_P_IP)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only IPv4 network protocol is supported"); + return -EOPNOTSUPP; + } + + /* Transport proto is present in the key and is not TCP or UDP */ + if (match.mask->ip_proto && + match.key->ip_proto != IPPROTO_TCP && + match.key->ip_proto != IPPROTO_UDP) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only TCP and UDP transport protocols are supported"); + return -EOPNOTSUPP; + } + entry->ip_proto = match.key->ip_proto; return 0; } @@ -598,6 +630,8 @@ static int tc_add_flow(struct stmmac_priv *priv, ret = tc_flow_parsers[i].fn(priv, cls, entry); if (!ret) entry->in_use = true; + else if (ret == -EOPNOTSUPP) + return ret; } if (!entry->in_use) @@ -627,6 +661,7 @@ static int tc_del_flow(struct stmmac_priv *priv, entry->in_use = false; entry->cookie = 0; entry->is_l4 = false; + entry->action = 0; return ret; } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index affea1a364ef..26d0cfc58ee2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -96,11 +96,13 @@ int txgbe_read_eeprom_hostif(struct wx *wx, dword_len = round_up(length, 4) >> 2; for (i = 0; i < dword_len; i++) { + u32 copy_len = min_t(u32, 4, length - i * 4); + value = rd32a(wx, WX_FW2SW_MBOX, i + offset); le32_to_cpus(&value); - memcpy(data, &value, 4); - data += 4; + memcpy(data, &value, copy_len); + data += copy_len; } return 0; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 20c5a295c6c2..c277863baf67 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -945,6 +945,7 @@ static void txgbe_remove(struct pci_dev *pdev) netdev = wx->netdev; wx_disable_sriov(wx); unregister_netdev(netdev); + txgbe_fdir_filter_exit(wx); timer_shutdown_sync(&wx->service_timer); cancel_work_sync(&wx->service_task); diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 396e1a113cd4..72023ebd0e1b 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -43,8 +43,17 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); #define GENEVE_OPT_GRO_HINT_LEN 1 struct geneve_opt_gro_hint { +#if defined(__LITTLE_ENDIAN_BITFIELD) u8 inner_proto_id:2, - nested_is_v6:1; + nested_is_v6:1, + rsvd:5; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 rsvd:5, + nested_is_v6:1, + inner_proto_id:2; +#else +#error "Please fix <asm/byteorder.h>" +#endif u8 nested_nh_offset; u8 nested_tp_offset; u8 nested_hdr_len; @@ -577,6 +586,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, struct iphdr *iph; struct udphdr *uh; __be16 p; + int err; hint_off = geneve_sk_gro_hint_off(sk, *geneveh, &p, &len); if (!hint_off) @@ -601,12 +611,20 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, !geneve_opt_gro_hint_validate(skb->data, gro_hint))) return -EINVAL; - ipv6h = (void *)skb->data + gro_hint->nested_nh_offset; - iph = (struct iphdr *)ipv6h; total_len = skb->len - gro_hint->nested_nh_offset; if (total_len >= GRO_LEGACY_MAX_SIZE) return -E2BIG; + err = skb_ensure_writable(skb, gro_hint->nested_tp_offset + sizeof(*uh)); + if (unlikely(err)) + return err; + + *geneveh = geneve_hdr(skb); + gro_hint = geneve_opt_gro_hint(*geneveh, hint_off); + + ipv6h = (void *)skb->data + gro_hint->nested_nh_offset; + iph = (struct iphdr *)ipv6h; + /* * After stripping the outer encap, the packet still carries a * tunnel encapsulation: the nested one. @@ -2376,6 +2394,9 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], struct geneve_config cfg; int err; + if (!rtnl_dev_link_net_capable(dev, geneve->net)) + return -EPERM; + /* If the geneve device is configured for metadata (or externally * controlled, for example, OVS), then nothing can be changed. */ diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index a60ef32b35b8..9a12cc53da00 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -669,8 +669,9 @@ static int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) return -1; /* pull GTP and UDP headers */ - skb_pull_data(skb, - sizeof(struct gtp1_header_long) + sizeof(struct udphdr)); + if (!skb_pull_data(skb, sizeof(struct gtp1_header_long) + + sizeof(struct udphdr))) + return -1; gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet)); memset(gtp_pkt, 0, sizeof(struct gtp1u_packet)); @@ -826,13 +827,17 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) if (!pskb_may_pull(skb, hdrlen)) return -1; + gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); + + if (gtp1->flags & GTP1_F_EXTHDR && + gtp_parse_exthdrs(skb, &hdrlen) < 0) + return -1; + if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) { netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n"); return -1; } - gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); - pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid), gtp_proto_to_family(inner_proto)); if (!pctx) { @@ -840,10 +845,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) return 1; } - if (gtp1->flags & GTP1_F_EXTHDR && - gtp_parse_exthdrs(skb, &hdrlen) < 0) - return -1; - return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto); } diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c index 6d2bbae7477b..88d9e36cd4a2 100644 --- a/drivers/net/mctp/mctp-i3c.c +++ b/drivers/net/mctp/mctp-i3c.c @@ -731,18 +731,21 @@ static __init int mctp_i3c_mod_init(void) int rc; rc = i3c_register_notifier(&mctp_i3c_notifier); - if (rc < 0) { - i3c_driver_unregister(&mctp_i3c_driver); + if (rc < 0) return rc; - } i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL); rc = i3c_driver_register(&mctp_i3c_driver); if (rc < 0) - return rc; + goto err_unregister_notifier; return 0; + +err_unregister_notifier: + i3c_unregister_notifier(&mctp_i3c_notifier); + mctp_i3c_bus_remove_all(); + return rc; } static __exit void mctp_i3c_mod_exit(void) diff --git a/drivers/net/mctp/mctp-serial.c b/drivers/net/mctp/mctp-serial.c index 26c9a33fd636..a5070ffa9a95 100644 --- a/drivers/net/mctp/mctp-serial.c +++ b/drivers/net/mctp/mctp-serial.c @@ -318,7 +318,7 @@ static void mctp_serial_push_header(struct mctp_serial *dev, u8 c) } else { dev->rxlen = c; dev->rxpos = 0; - dev->rxstate = STATE_DATA; + dev->rxstate = c > 0 ? STATE_DATA : STATE_TRAILER; dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c); } break; diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index a6b777a9c2d9..9a66d693039a 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -142,7 +142,7 @@ void ovpn_decrypt_post(void *data, int ret) } /* keep track of last received authenticated packet for keepalive */ - WRITE_ONCE(peer->last_recv, ktime_get_real_seconds()); + WRITE_ONCE(peer->last_recv, ktime_get_boottime_seconds()); rcu_read_lock(); sock = rcu_dereference(peer->sock); @@ -294,7 +294,7 @@ void ovpn_encrypt_post(void *data, int ret) ovpn_peer_stats_increment_tx(&peer->link_stats, orig_len); /* keep track of last sent packet for keepalive */ - WRITE_ONCE(peer->last_sent, ktime_get_real_seconds()); + WRITE_ONCE(peer->last_sent, ktime_get_boottime_seconds()); /* skb passed down the stack - don't free it */ skb = NULL; err_unlock: diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a09d61296425..a21d02ac715e 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -26,11 +26,12 @@ static void unlock_ovpn(struct ovpn_priv *ovpn, struct llist_head *release_list) __releases(&ovpn->lock) { - struct ovpn_peer *peer; + struct ovpn_peer *peer, *next; spin_unlock_bh(&ovpn->lock); - llist_for_each_entry(peer, release_list->first, release_entry) { + llist_for_each_entry_safe(peer, next, release_list->first, + release_entry) { ovpn_socket_release(peer); ovpn_peer_put(peer); } @@ -44,7 +45,7 @@ static void unlock_ovpn(struct ovpn_priv *ovpn, */ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout) { - time64_t now = ktime_get_real_seconds(); + time64_t now = ktime_get_boottime_seconds(); netdev_dbg(peer->ovpn->dev, "scheduling keepalive for peer %u: interval=%u timeout=%u\n", @@ -1167,7 +1168,6 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk, ovpn_sock = rcu_access_pointer(peer->sock); if (!ovpn_sock || ovpn_sock->sk != sk) { spin_unlock_bh(&ovpn->lock); - ovpn_peer_put(peer); return; } } @@ -1285,8 +1285,10 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer, netdev_dbg(peer->ovpn->dev, "sending keepalive to peer %u\n", peer->id); - if (schedule_work(&peer->keepalive_work)) - ovpn_peer_hold(peer); + if (WARN_ON(!ovpn_peer_hold(peer))) + return 0; + if (!schedule_work(&peer->keepalive_work)) + ovpn_peer_put(peer); } if (next_run1 < next_run2) @@ -1357,7 +1359,7 @@ void ovpn_peer_keepalive_work(struct work_struct *work) { struct ovpn_priv *ovpn = container_of(work, struct ovpn_priv, keepalive_work.work); - time64_t next_run = 0, now = ktime_get_real_seconds(); + time64_t next_run = 0, now = ktime_get_boottime_seconds(); LLIST_HEAD(release_list); spin_lock_bh(&ovpn->lock); diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 433bd07a4f1b..0af14055c39a 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -151,7 +151,8 @@ err: /* take reference for deferred peer deletion. should never fail */ if (WARN_ON(!ovpn_peer_hold(peer))) goto err_nopeer; - schedule_work(&peer->tcp.defer_del_work); + if (!schedule_work(&peer->tcp.defer_del_work)) + ovpn_peer_put(peer); ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); err_nopeer: kfree_skb(skb); @@ -283,7 +284,8 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) * stream therefore we abort the connection */ ovpn_peer_hold(peer); - schedule_work(&peer->tcp.defer_del_work); + if (!schedule_work(&peer->tcp.defer_del_work)) + ovpn_peer_put(peer); /* we bail out immediately and keep tx_in_progress set * to true. This way we prevent more TX attempts diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index e69fa2f0a0e8..0337e2bcc012 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -1058,6 +1058,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, /* Reset link_state */ state->link = false; + state->an_complete = false; state->speed = SPEED_UNKNOWN; state->duplex = DUPLEX_UNKNOWN; state->pause = 0; @@ -1069,6 +1070,8 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, if (ret < 0) return ret; + state->an_complete = ret & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR; + if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { int speed_value; @@ -1086,34 +1089,13 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, state->duplex = DUPLEX_FULL; else state->duplex = DUPLEX_HALF; - } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { - int speed, duplex; - - state->link = true; - - speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMCR); - if (speed < 0) - return speed; - - speed &= BMCR_SPEED100 | BMCR_SPEED1000; - if (speed == BMCR_SPEED1000) - state->speed = SPEED_1000; - else if (speed == BMCR_SPEED100) - state->speed = SPEED_100; - else if (speed == 0) - state->speed = SPEED_10; - - duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE); - if (duplex < 0) - return duplex; - if (duplex & ADVERTISE_1000XFULL) - state->duplex = DUPLEX_FULL; - else if (duplex & ADVERTISE_1000XHALF) - state->duplex = DUPLEX_HALF; + return 0; + } + /* Clear AN complete status or interrupt */ + if (state->an_complete) xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); - } return 0; } diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 7a578b5aa2ed..f71cffa88406 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -753,7 +753,7 @@ static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes) err = phy_modify(phydev, MII_M1111_PHY_EXT_SR, MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr); if (err < 0) - return extsr; + return err; return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR, BMCR_ANENABLE, bmcr); diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 717c1d3aa953..cacc4c3a37d2 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -810,7 +810,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCSMRU: if (get_user(val, p)) break; + ppp_recv_lock(ppp); ppp->mru = val; + ppp_recv_unlock(ppp); err = 0; break; @@ -831,7 +833,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCGFLAGS: + ppp_lock(ppp); val = ppp->flags | ppp->xstate | ppp->rstate; + ppp_unlock(ppp); if (put_user(val, p)) break; err = 0; @@ -855,7 +859,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCSDEBUG: if (get_user(val, p)) break; - ppp->debug = val; + WRITE_ONCE(ppp->debug, val); err = 0; break; @@ -866,16 +870,16 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCGIDLE32: - idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle32.recv_idle = (jiffies - ppp->last_recv) / HZ; - if (copy_to_user(argp, &idle32, sizeof(idle32))) + idle32.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ; + idle32.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ; + if (copy_to_user(argp, &idle32, sizeof(idle32))) break; err = 0; break; case PPPIOCGIDLE64: - idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle64.recv_idle = (jiffies - ppp->last_recv) / HZ; + idle64.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ; + idle64.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ; if (copy_to_user(argp, &idle64, sizeof(idle64))) break; err = 0; @@ -916,7 +920,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_to_user(argp, &npi, sizeof(npi))) break; } else { - ppp->npmode[i] = npi.mode; + WRITE_ONCE(ppp->npmode[i], npi.mode); /* we may be able to transmit more packets now (??) */ netif_wake_queue(ppp->dev); } @@ -1454,7 +1458,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) goto outf; /* Drop, accept or reject the packet */ - switch (ppp->npmode[npi]) { + switch (READ_ONCE(ppp->npmode[npi])) { case NPMODE_PASS: break; case NPMODE_QUEUE: @@ -1482,7 +1486,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) outf: kfree_skb(skb); - ++dev->stats.tx_dropped; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } @@ -1532,11 +1536,11 @@ ppp_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr, static void ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) { - stats64->rx_errors = dev->stats.rx_errors; - stats64->tx_errors = dev->stats.tx_errors; - stats64->rx_dropped = dev->stats.rx_dropped; - stats64->tx_dropped = dev->stats.tx_dropped; - stats64->rx_length_errors = dev->stats.rx_length_errors; + stats64->rx_errors = DEV_STATS_READ(dev, rx_errors); + stats64->tx_errors = DEV_STATS_READ(dev, tx_errors); + stats64->rx_dropped = DEV_STATS_READ(dev, rx_dropped); + stats64->tx_dropped = DEV_STATS_READ(dev, tx_dropped); + stats64->rx_length_errors = DEV_STATS_READ(dev, rx_length_errors); dev_fetch_sw_netstats(stats64, dev->tstats); } @@ -1790,7 +1794,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_OUTBOUND_TAG); if (ppp->pass_filter && bpf_prog_run(ppp->pass_filter, skb) == 0) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: outbound frame " "not passed\n"); @@ -1800,11 +1804,11 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && bpf_prog_run(ppp->active_filter, skb) == 0)) - ppp->last_xmit = jiffies; + WRITE_ONCE(ppp->last_xmit, jiffies); skb_pull(skb, 2); #else /* for data packets, record the time */ - ppp->last_xmit = jiffies; + WRITE_ONCE(ppp->last_xmit, jiffies); #endif /* CONFIG_PPP_FILTER */ } @@ -1889,7 +1893,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) drop: kfree_skb(skb); - ++ppp->dev->stats.tx_errors; + DEV_STATS_INC(ppp->dev, tx_errors); return 1; } @@ -2154,9 +2158,9 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) noskb: spin_unlock(&pch->downl); err_linearize: - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); - ++ppp->dev->stats.tx_errors; + DEV_STATS_INC(ppp->dev, tx_errors); ++ppp->nxseq; return 1; /* abandon the frame */ } @@ -2329,7 +2333,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) if (!ppp_decompress_proto(skb)) { kfree_skb(skb); if (ppp) { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); ppp_receive_error(ppp); } goto done; @@ -2391,7 +2395,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) static void ppp_receive_error(struct ppp *ppp) { - ++ppp->dev->stats.rx_errors; + DEV_STATS_INC(ppp->dev, rx_errors); if (ppp->vj) slhc_toss(ppp->vj); } @@ -2502,7 +2506,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_INBOUND_TAG); if (ppp->pass_filter && bpf_prog_run(ppp->pass_filter, skb) == 0) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: inbound frame " "not passed\n"); @@ -2511,14 +2515,14 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) } if (!(ppp->active_filter && bpf_prog_run(ppp->active_filter, skb) == 0)) - ppp->last_recv = jiffies; + WRITE_ONCE(ppp->last_recv, jiffies); __skb_pull(skb, 2); } else #endif /* CONFIG_PPP_FILTER */ - ppp->last_recv = jiffies; + WRITE_ONCE(ppp->last_recv, jiffies); if ((ppp->dev->flags & IFF_UP) == 0 || - ppp->npmode[npi] != NPMODE_PASS) { + READ_ONCE(ppp->npmode[npi]) != NPMODE_PASS) { kfree_skb(skb); } else { /* chop off protocol */ @@ -2658,7 +2662,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) */ if (seq_before(seq, ppp->nextseq)) { kfree_skb(skb); - ++ppp->dev->stats.rx_dropped; + DEV_STATS_INC(ppp->dev, rx_dropped); ppp_receive_error(ppp); return; } @@ -2694,7 +2698,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) if (pskb_may_pull(skb, 2)) ppp_receive_nonmp_frame(ppp, skb); else { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); kfree_skb(skb); ppp_receive_error(ppp); } @@ -2771,7 +2775,7 @@ ppp_mp_reconstruct(struct ppp *ppp) seq = seq_before(minseq, PPP_MP_CB(p)->sequence)? minseq + 1: PPP_MP_CB(p)->sequence; - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "lost frag %u..%u\n", oldseq, seq-1); @@ -2800,7 +2804,7 @@ ppp_mp_reconstruct(struct ppp *ppp) if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) && (PPP_MP_CB(head)->BEbits & B)) { if (len > ppp->mrru + 2) { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); netdev_printk(KERN_DEBUG, ppp->dev, "PPP: reconstructed packet" " is too long (%d)\n", len); @@ -2820,7 +2824,7 @@ ppp_mp_reconstruct(struct ppp *ppp) struct sk_buff *tmp2; skb_queue_reverse_walk_from_safe(list, p, tmp2) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "discarding frag %u\n", PPP_MP_CB(p)->sequence); @@ -2842,7 +2846,7 @@ ppp_mp_reconstruct(struct ppp *ppp) skb_queue_walk_safe(list, p, tmp) { if (p == head) break; - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "discarding frag %u\n", PPP_MP_CB(p)->sequence); @@ -2850,12 +2854,12 @@ ppp_mp_reconstruct(struct ppp *ppp) kfree_skb(p); } - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, " missed pkts %u..%u\n", ppp->nextseq, PPP_MP_CB(head)->sequence-1); - ++ppp->dev->stats.rx_dropped; + DEV_STATS_INC(ppp->dev, rx_dropped); ppp_receive_error(ppp); } @@ -3161,7 +3165,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (!ppp->rc_state) break; if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len, - ppp->file.index, 0, ppp->mru, ppp->debug)) { + ppp->file.index, 0, ppp->mru, + READ_ONCE(ppp->debug))) { ppp->rstate |= SC_DECOMP_RUN; ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR); } @@ -3170,7 +3175,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (!ppp->xc_state) break; if (ppp->xcomp->comp_init(ppp->xc_state, dp, len, - ppp->file.index, 0, ppp->debug)) + ppp->file.index, 0, + READ_ONCE(ppp->debug))) ppp->xstate |= SC_COMP_RUN; } break; @@ -3322,8 +3328,8 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) st->p.ppp_opackets += tx_packets; st->p.ppp_obytes += tx_bytes; } - st->p.ppp_ierrors = ppp->dev->stats.rx_errors; - st->p.ppp_oerrors = ppp->dev->stats.tx_errors; + st->p.ppp_ierrors = DEV_STATS_READ(ppp->dev, rx_errors); + st->p.ppp_oerrors = DEV_STATS_READ(ppp->dev, tx_errors); if (!vj) return; st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 4a018acb5262..6874a1a8edaf 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -825,6 +825,7 @@ static int pppoe_sendmsg(struct socket *sock, struct msghdr *m, dev_hard_header(skb, dev, ETH_P_PPP_SES, po->pppoe_pa.remote, NULL, total_len); + ph = pppoe_hdr(skb); memcpy(ph, &hdr, sizeof(struct pppoe_hdr)); ph->length = htons(total_len); diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 820e1a8fc956..faae711cf793 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -693,6 +693,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) return; + spin_lock_bh(&sl->lock); + /* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { @@ -708,6 +710,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, #endif slip_unesc(sl, *cp++); } + + spin_unlock_bh(&sl->lock); } /************************************ diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 40522afc0532..f8df83f9965d 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1530,7 +1530,11 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, struct ipv6hdr *ipv6; struct tcphdr *tcp; } hdr; - BUG_ON(gdesc->rcd.tcp == 0); + + /* v4/v6/tcp then describe the inner header, which we can't locate. */ + if ((le32_to_cpu(gdesc->dword[0]) & (1UL << VMXNET3_RCD_HDR_INNER_SHIFT)) || + gdesc->rcd.tcp == 0) + return 0; maplen = skb_headlen(skb); if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen)) @@ -1544,15 +1548,21 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, hdr.eth = eth_hdr(skb); if (gdesc->rcd.v4) { - BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) && - hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP)); + if (hdr.eth->h_proto != htons(ETH_P_IP) && + hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP)) + return 0; + hdr.ptr += hlen; - BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP); + if (hdr.ipv4->protocol != IPPROTO_TCP) + return 0; + hlen = hdr.ipv4->ihl << 2; hdr.ptr += hdr.ipv4->ihl << 2; } else if (gdesc->rcd.v6) { - BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6) && - hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6)); + if (hdr.eth->h_proto != htons(ETH_P_IPV6) && + hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6)) + return 0; + hdr.ptr += hlen; /* Use an estimated value, since we also need to handle * TSO case. diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 67c367cc5662..d834a4865aec 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4421,6 +4421,9 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], struct vxlan_rdst *dst; int err; + if (!rtnl_dev_link_net_capable(dev, vxlan->net)) + return -EPERM; + dst = &vxlan->default_dst; err = vxlan_nl2conf(tb, data, dev, &conf, true, extack); if (err) diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 055a4969f593..af7a0d7f95a5 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -42,6 +42,7 @@ struct vxlan_mdb_remote { }; #define VXLAN_SGRP_F_DELETE BIT(0) +#define VXLAN_SGRP_F_NEW BIT(1) struct vxlan_mdb_src_entry { struct hlist_node node; @@ -844,6 +845,7 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg, ent = vxlan_mdb_remote_src_entry_add(remote, &src->addr); if (!ent) return -ENOMEM; + ent->flags |= VXLAN_SGRP_F_NEW; } else if (!(cfg->nlflags & NLM_F_REPLACE)) { NL_SET_ERR_MSG_MOD(extack, "Source entry already exists"); return -EEXIST; @@ -853,15 +855,16 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg, if (err) goto err_src_del; - /* Clear flags in case source entry was marked for deletion as part of - * replace flow. + /* Clear the deletion mark so the entry survives the replace sweep. + * The new mark is retained until the whole operation succeeds. */ - ent->flags = 0; + ent->flags &= ~VXLAN_SGRP_F_DELETE; return 0; err_src_del: - vxlan_mdb_remote_src_entry_del(ent); + if (ent->flags & VXLAN_SGRP_F_NEW) + vxlan_mdb_remote_src_entry_del(ent); return err; } @@ -889,11 +892,19 @@ static int vxlan_mdb_remote_srcs_add(const struct vxlan_mdb_config *cfg, goto err_src_del; } + hlist_for_each_entry(ent, &remote->src_list, node) + ent->flags &= ~VXLAN_SGRP_F_NEW; + return 0; err_src_del: - hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) - vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, ent); + hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) { + if (ent->flags & VXLAN_SGRP_F_NEW) + vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, + ent); + else + ent->flags &= ~VXLAN_SGRP_F_DELETE; + } return err; } @@ -1069,7 +1080,7 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg, err = vxlan_mdb_remote_srcs_add(cfg, remote, extack); if (err) - goto err_clear_delete; + return err; hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) { if (ent->flags & VXLAN_SGRP_F_DELETE) @@ -1078,11 +1089,6 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg, } return 0; - -err_clear_delete: - hlist_for_each_entry(ent, &remote->src_list, node) - ent->flags &= ~VXLAN_SGRP_F_DELETE; - return err; } static int vxlan_mdb_remote_replace(const struct vxlan_mdb_config *cfg, diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index d4da88c77112..065c00c12cc1 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -514,7 +514,8 @@ static void wanxl_pci_remove_one(struct pci_dev *pdev) if (card->irq) free_irq(card->irq, card); - wanxl_reset(card); + if (card->plx) + wanxl_reset(card); for (i = 0; i < RX_QUEUE_LENGTH; i++) if (card->rx_skbs[i]) { diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index faac359aa9ac..b3f1b7186721 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -706,6 +706,7 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt, if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); + __skb_queue_purge(list); return -EIO; } } @@ -770,6 +771,7 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt, if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); + __skb_queue_purge(list); return -EIO; } } diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index f566d699d074..1e1dea485760 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -996,6 +996,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) ret = ath11k_ahb_setup_msa_resources(ab); if (ret) { ath11k_err(ab, "failed to setup msa resources\n"); + of_node_put(node); return ret; } diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 9e90d8e3f155..8e2abc7b8383 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4618,6 +4618,9 @@ static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar, msdu_details = &msdu_link->msdu_link[0]; for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (!i && FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu_details[i].buf_addr_info.info0) == 0) + break; if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR, msdu_details[i].buf_addr_info.info0) == 0) { msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 35bb9e7a63a2..a163168f3617 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -199,6 +199,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -208,6 +210,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 410a7ee076a0..7dc07339b957 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -3294,9 +3294,14 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); - ath11k_core_qmi_firmware_ready(ab); - set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); - + if (!test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) { + set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); + break; + } + set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); + } break; case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE: break; diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index 47d009a0d61f..a12073afc307 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -274,11 +274,14 @@ int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp) void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp) { - mutex_lock(&dp->link_peer_rhash_tbl_lock); + guard(mutex)(&dp->link_peer_rhash_tbl_lock); + + if (!dp->rhead_peer_addr) + return; + rhashtable_destroy(dp->rhead_peer_addr); kfree(dp->rhead_peer_addr); dp->rhead_peer_addr = NULL; - mutex_unlock(&dp->link_peer_rhash_tbl_lock); } static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp, @@ -570,6 +573,8 @@ int ath12k_dp_link_peer_assign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_hw, peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); rcu_assign_pointer(dp_peer->link_peers[peer->link_id], peer); + WRITE_ONCE(dp_peer->link_peers_map, + READ_ONCE(dp_peer->link_peers_map) | BIT(peer->link_id)); rcu_assign_pointer(dp_hw->dp_peers[peerid_index], dp_peer); @@ -632,6 +637,8 @@ void ath12k_dp_link_peer_unassign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_ peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL); + WRITE_ONCE(dp_peer->link_peers_map, + READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id)); rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL); diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index f5067e66f1e1..7c9709bf717b 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -140,6 +140,7 @@ struct ath12k_dp_peer { /* Info used in MMIC verification of * RX fragments */ struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; + unsigned long link_peers_map; struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS]; struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 06e74124e57e..8fa0e90b4531 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1384,10 +1384,9 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc pubsta = peer ? peer->sta : NULL; - if (pubsta && pubsta->valid_links) { - status->link_valid = 1; - status->link_id = peer->hw_links[rxcb->hw_link_id]; - } + status->link_valid = 0; + if (pubsta && pubsta->valid_links) + ath12k_hw_set_rx_link_id(dp->hw_params, peer, rxcb, status); ath12k_dbg(dp->ab, ATH12K_DBG_DATA, "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index d135b2936378..86fb8b719613 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -13,6 +13,10 @@ #include "wmi.h" #include "hal.h" +struct ath12k_dp_peer; +struct ath12k_skb_rxcb; +struct ieee80211_rx_status; + /* Target configuration defines */ /* Num VDEVS per radio */ @@ -243,6 +247,9 @@ struct ath12k_hw_ops { bool (*dp_srng_is_tx_comp_ring)(int ring_num); bool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif, struct ieee80211_mgmt *mgmt); + void (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); }; static inline @@ -273,6 +280,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw, return 0; } +static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw, + struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status) +{ + if (hw->hw_ops->set_rx_link_id) + hw->hw_ops->set_rx_link_id(dp_peer, rxcb, status); +} + struct ath12k_fw_ie { __le32 id; __le32 len; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index af354bef5c0d..51c4df32e716 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1238,9 +1238,13 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar) /* cleanup dp peer */ spin_lock_bh(&dp_hw->peer_lock); dp_peer = peer->dp_peer; - peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); - rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL); - rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL); + if (dp_peer) { + peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); + rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL); + WRITE_ONCE(dp_peer->link_peers_map, + READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id)); + rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL); + } spin_unlock_bh(&dp_hw->peer_lock); ath12k_dp_link_peer_rhash_delete(dp, peer); diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index d9a22d6afbb0..fee4129ea405 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -188,6 +188,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -197,6 +199,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index c222bdaa333c..2681a047d4d5 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -453,6 +453,9 @@ err_free: void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab) { + if (!ab->rhead_sta_addr) + return; + rhashtable_destroy(ab->rhead_sta_addr); kfree(ab->rhead_sta_addr); ab->rhead_sta_addr = NULL; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index cb9dd8cb28b6..95d87dd67872 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -5,6 +5,7 @@ */ #include "dp_rx.h" +#include "../dp_peer.h" #include "../dp_tx.h" #include "../peer.h" #include "hal_qcn9274.h" @@ -2315,3 +2316,35 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, return tlv_tag == HAL_RX_MPDU_START; } + +void +ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status) +{ + status->link_valid = 1; + status->link_id = dp_peer->hw_links[rxcb->hw_link_id]; +} + +void +ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status) +{ + struct ath12k_dp_link_peer *link_peer; + unsigned long links_map; + int i; + + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), + "ath12k set rx link id called without rcu lock"); + + links_map = READ_ONCE(dp_peer->link_peers_map); + for_each_set_bit(i, &links_map, ATH12K_NUM_MAX_LINKS) { + link_peer = rcu_dereference(dp_peer->link_peers[i]); + if (link_peer && link_peer->peer_id == rxcb->peer_id) { + status->link_valid = 1; + status->link_id = link_peer->link_id; + return; + } + } +} diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h index 8aa79faf567f..1d3a4788a2dd 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h @@ -57,4 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, struct hal_rx_desc *rx_desc); int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab, struct ath12k_dp_rx_tid_rxq *rx_tid); +void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); +void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); #endif diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 3d59fa452ec0..d9fdd2fc8298 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -158,6 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274, + .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274, }; static const struct ath12k_hw_ops wcn7850_ops = { @@ -168,6 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850, + .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850, }; static const struct ath12k_hw_ops qcc2072_ops = { @@ -178,6 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850, + .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850, }; #define ATH12K_TX_RING_MASK_0 0x1 diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 97fdac7237e2..d81825413906 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1723,13 +1723,15 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no, rxtid = &aggr_conn->rx_tid[tid]; - if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) - ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", - __func__, win_sz, tid); - if (rxtid->aggr) aggr_delete_tid_state(aggr_conn, tid); + if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", + __func__, win_sz, tid); + return; + } + rxtid->seq_next = seq_no; hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q); rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL); @@ -1828,7 +1830,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn) return; if (aggr_conn->timer_scheduled) { - timer_delete(&aggr_conn->timer); + timer_delete_sync(&aggr_conn->timer); aggr_conn->timer_scheduled = false; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 72611a2ceb9d..2b0c5038ae04 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -484,6 +484,18 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) evt = (struct wmi_tx_complete_event *) datap; + if (len < sizeof(*evt)) { + ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d\n", + len); + return -EINVAL; + } + + if (len < sizeof(*evt) + evt->num_msg * sizeof(struct tx_complete_msg_v1)) { + ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d for %u msgs\n", + len, evt->num_msg); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n", evt->num_msg, evt->msg_len, evt->msg_type); @@ -862,6 +874,14 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, ev = (struct wmi_connect_event *) datap; + if (len < sizeof(*ev) + ev->beacon_ie_len + + ev->assoc_req_len + ev->assoc_resp_len) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "connect event: IE lengths %u+%u+%u exceed buffer %d\n", + ev->beacon_ie_len, ev->assoc_req_len, + ev->assoc_resp_len, len); + return -EINVAL; + } if (vif->nw_type == AP_NETWORK) { /* AP mode start/STA connected event */ struct net_device *dev = vif->ndev; diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 515267f48d80..47f904e7e652 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1215,15 +1215,10 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev, ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, &hif_dev->udev->dev, GFP_KERNEL, hif_dev, ath9k_hif_usb_firmware_cb); - if (ret) { + if (ret) dev_err(&hif_dev->udev->dev, "ath9k_htc: Async request for firmware %s failed\n", hif_dev->fw_name); - return ret; - } - - dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", - hif_dev->fw_name); return ret; } diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 6833430130f4..0383d5c9698b 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -150,7 +150,8 @@ static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) spin_lock(&ar->cmd_lock); if (ar->readbuf) { if (len >= 4) - memcpy(ar->readbuf, buffer + 4, len - 4); + memcpy(ar->readbuf, buffer + 4, + min_t(u32, len - 4, ar->readlen)); ar->readbuf = NULL; } @@ -917,7 +918,9 @@ static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len) } } - skb_put_data(ar->rx_failover, tbuf, tlen); + skb_put_data(ar->rx_failover, tbuf, + min_t(unsigned int, tlen, + ar->rx_failover_missing)); ar->rx_failover_missing -= tlen; if (ar->rx_failover_missing <= 0) { diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 59caf1e4b158..06aaf281655b 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -692,7 +692,7 @@ void carl9170_tx_process_status(struct ar9170 *ar, unsigned int i; for (i = 0; i < cmd->hdr.ext; i++) { - if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) { + if (WARN_ON(i >= (cmd->hdr.len / 2))) { print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE, (void *) cmd, cmd->hdr.len + 4); break; diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 32e3e09e7680..d9c2809be4ba 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -1521,13 +1521,16 @@ static inline int at76_guess_freq(struct at76_priv *priv) if (ieee80211_is_probe_resp(hdr->frame_control)) { el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; } else if (ieee80211_is_beacon(hdr->frame_control)) { el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; } else { goto exit; } + + if (len < el_off) + goto exit; + + el = priv->rx_skb->data + el_off; len -= el_off; el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index d24b80e492e0..869c4872d399 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -911,6 +911,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) return ret; } switch (sdiodev->func2->device) { + case SDIO_DEVICE_ID_BROADCOM_43752: case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373: f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE; break; @@ -1069,6 +1070,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, bus_if = kzalloc_obj(*bus_if); if (!bus_if) return -ENOMEM; + mutex_init(&bus_if->bus_reset_lock); sdiodev = kzalloc_obj(*sdiodev); if (!sdiodev) { kfree(bus_if); @@ -1130,6 +1132,14 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) 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); @@ -1204,6 +1214,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev) } 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"); @@ -1229,6 +1241,8 @@ static int brcmf_ops_sdio_resume(struct device *dev) 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); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index fe31051a9e11..9371c1489948 100644 --- 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 */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 0b55d445895f..89f61710a210 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2146,7 +2146,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) sme->crypto.akm_suites[0]); return -EINVAL; } - } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_1X_SHA256)) { switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA2_AUTH_UNSPECIFIED; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index ec170647800d..dad6f4563d14 100644 --- 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_file *s, void *data) 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 file *file, const char __user *user_buf, 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 *dev) 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) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 13662aa4b4ea..55f4d7b970f2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1383,16 +1383,20 @@ fail: static void brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) { - if (devinfo->shared.scratch) + if (devinfo->shared.scratch) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_SCRATCH_BUF_LEN, devinfo->shared.scratch, devinfo->shared.scratch_dmahandle); - if (devinfo->shared.ringupd) + devinfo->shared.scratch = NULL; + } + if (devinfo->shared.ringupd) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_RINGUPD_BUF_LEN, devinfo->shared.ringupd, devinfo->shared.ringupd_dmahandle); + devinfo->shared.ringupd = NULL; + } } static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) @@ -2499,6 +2503,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = -ENOMEM; goto fail; } + mutex_init(&bus->bus_reset_lock); bus->msgbuf = kzalloc_obj(*bus->msgbuf); if (!bus->msgbuf) { ret = -ENOMEM; @@ -2594,6 +2599,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); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index b725c64e5b5c..9f7ed1d293a0 100644 --- 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) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 80180d5c6c87..b93d153a8963 100644 --- 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_dev *sdiodev); 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); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 0b52f968b907..b41949a9bdc8 100644 --- 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 brcmf_usbdev_info *devinfo, 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_usbdev_info *devinfo) 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); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c index 7a04cb120b1b..6080f5f23e69 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c @@ -15,6 +15,7 @@ .non_shared_ant = ANT_B, \ .vht_mu_mimo_supported = true, \ .uhb_supported = true, \ + .unii9_supported = true, \ .eht_supported = true, \ .uhr_supported = true, \ .num_rbds = IWL_NUM_RBDS_EHT, \ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index bf0f851a9075..430a7642e01a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -865,6 +865,11 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) min_size, max_size, &tbl_rev); if (!IS_ERR(wifi_pkg)) { + if (tbl_rev < 0 || + tbl_rev >= BITS_PER_BYTE * + sizeof(rev_data[idx].revisions)) + continue; + if (!(BIT(tbl_rev) & rev_data[idx].revisions)) continue; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 443a9a416325..0172c0747a47 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -205,6 +205,7 @@ struct iwl_nvm_get_info_phy { #define IWL_NUM_CHANNELS_V1 51 #define IWL_NUM_CHANNELS_V2 110 +#define IWL_NUM_CHANNELS_V3 115 /** * struct iwl_nvm_get_info_regulatory_v1 - regulatory information @@ -219,12 +220,12 @@ struct iwl_nvm_get_info_regulatory_v1 { } __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */ /** - * struct iwl_nvm_get_info_regulatory - regulatory information + * struct iwl_nvm_get_info_regulatory_v2 - regulatory information * @lar_enabled: is LAR enabled * @n_channels: number of valid channels in the array * @channel_profile: regulatory data of this channel */ -struct iwl_nvm_get_info_regulatory { +struct iwl_nvm_get_info_regulatory_v2 { __le32 lar_enabled; __le32 n_channels; __le32 channel_profile[IWL_NUM_CHANNELS_V2]; @@ -245,6 +246,32 @@ struct iwl_nvm_get_info_rsp_v3 { } __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_3 */ /** + * struct iwl_nvm_get_info_rsp_v4 - response to get NVM data + * @general: general NVM data + * @mac_sku: data relating to MAC sku + * @phy_sku: data relating to PHY sku + * @regulatory: regulatory data + */ +struct iwl_nvm_get_info_rsp_v4 { + struct iwl_nvm_get_info_general general; + struct iwl_nvm_get_info_sku mac_sku; + struct iwl_nvm_get_info_phy phy_sku; + struct iwl_nvm_get_info_regulatory_v2 regulatory; +} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */ + +/** + * struct iwl_nvm_get_info_regulatory - regulatory information + * @lar_enabled: is LAR enabled + * @n_channels: number of valid channels in the array + * @channel_profile: regulatory data of this channel + */ +struct iwl_nvm_get_info_regulatory { + __le32 lar_enabled; + __le32 n_channels; + __le32 channel_profile[IWL_NUM_CHANNELS_V3]; +} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_3 */ + +/** * struct iwl_nvm_get_info_rsp - response to get NVM data * @general: general NVM data * @mac_sku: data relating to MAC sku @@ -256,7 +283,7 @@ struct iwl_nvm_get_info_rsp { struct iwl_nvm_get_info_sku mac_sku; struct iwl_nvm_get_info_phy phy_sku; struct iwl_nvm_get_info_regulatory regulatory; -} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */ +} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_5 */ /** * struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 08f4cc3ea1c3..ee78371082b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1319,7 +1319,7 @@ struct iwl_scan_offload_match_info { u8 resume_while_scanning; u8 self_recovery; __le16 reserved; - struct iwl_scan_offload_profile_match matches[]; + struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES_V2]; } __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_3 and * SCAN_OFFLOAD_MATCH_INFO_NOTIFICATION_S_VER_1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index afff8d51ca95..ec0ff58ab312 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2020-2025 Intel Corporation + * Copyright(c) 2020-2026 Intel Corporation */ #include "iwl-drv.h" @@ -12,6 +12,7 @@ #include "fw/api/alive.h" #include "fw/uefi.h" #include "fw/img.h" +#include "fw/dbg.h" #define IWL_PNVM_REDUCED_CAP_BIT BIT(25) @@ -26,6 +27,12 @@ static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_trans *trans = (struct iwl_trans *)data; struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data; + if (IWL_FW_CHECK(trans, + iwl_rx_packet_payload_len(pkt) < sizeof(*pnvm_ntf), + "Bad notif len: %d\n", + iwl_rx_packet_payload_len(pkt))) + return true; + IWL_DEBUG_FW(trans, "PNVM complete notification received with status 0x%0x\n", le32_to_cpu(pnvm_ntf->status)); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 8d9ff36e30f5..1d6d38ee55b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -389,7 +389,7 @@ bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc) if (*size >= IWL_WTAS_BLACK_LIST_MAX) return false; - list[*size++] = mcc; + list[(*size)++] = mcc; return true; } IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c index 344ddde85b18..20ed26a1bb00 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021, 2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2021, 2025-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -14,9 +14,17 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt, { struct iwl_shared_mem_cfg *mem_cfg = (void *)pkt->data; int i, lmac; - int lmac_num = le32_to_cpu(mem_cfg->lmac_num); - u8 api_ver = iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP, - SHARED_MEM_CFG_CMD, 0); + int lmac_num; + u8 api_ver; + + if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < + offsetofend(struct iwl_shared_mem_cfg, lmac_smem[1]), + "bad shared mem notification size\n")) + return; + + lmac_num = le32_to_cpu(mem_cfg->lmac_num); + api_ver = iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP, + SHARED_MEM_CFG_CMD, 0); /* Note: notification has 3 entries, but we only expect 2 */ if (IWL_FW_CHECK(fwrt, lmac_num > ARRAY_SIZE(fwrt->smem_cfg.lmac), @@ -30,7 +38,7 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt, if (api_ver >= 4 && !IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < sizeof(*mem_cfg), - "bad shared mem notification size\n")) { + "bad shared mem notification size (v4)\n")) { fwrt->smem_cfg.rxfifo2_control_size = le32_to_cpu(mem_cfg->rxfifo2_control_size); } @@ -53,6 +61,11 @@ static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt, struct iwl_shared_mem_cfg_v2 *mem_cfg = (void *)pkt->data; int i; + if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < + offsetof(struct iwl_shared_mem_cfg_v2, rxfifo_addr), + "bad shared mem notification size\n")) + return; + fwrt->smem_cfg.num_lmacs = 1; fwrt->smem_cfg.num_txfifo_entries = ARRAY_SIZE(mem_cfg->txfifo_size); @@ -67,6 +80,11 @@ static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt, /* new API has more data, from rxfifo_addr field and on */ if (fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { + if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < + sizeof(*mem_cfg), + "bad shared mem notification size (extend)\n")) + return; + BUILD_BUG_ON(sizeof(fwrt->smem_cfg.internal_txfifo_size) != sizeof(mem_cfg->internal_txfifo_size)); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 2ef0a7a920ad..014f22c42f41 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -700,7 +700,7 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) return -EINVAL; parse_table: - if (!(BIT(data->revision) & valid_rev)) { + if (data->revision >= 32 || !(BIT(data->revision) & valid_rev)) { ret = -EINVAL; IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 6a3539ad7331..3ac7f000ede4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -415,6 +415,7 @@ struct iwl_mac_cfg { * @vht_mu_mimo_supported: VHT MU-MIMO support * @nvm_type: see &enum iwl_nvm_type * @uhb_supported: ultra high band channels supported + * @unii9_supported: UNII-9 channels supported * @eht_supported: EHT supported * @uhr_supported: UHR supported * @num_rbds: number of receive buffer descriptors to use @@ -450,6 +451,7 @@ struct iwl_rf_cfg { lp_xtal_workaround:1, vht_mu_mimo_supported:1, uhb_supported:1, + unii9_supported:1, eht_supported:1, uhr_supported:1; u8 valid_tx_ant; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index d021b24d04d6..b1a55909f0d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -475,6 +475,7 @@ static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data, { const struct iwl_ucode_tlv *tlv; u32 tlv_len; + size_t aligned_tlv_len; while (len >= sizeof(*tlv)) { len -= sizeof(*tlv); @@ -487,8 +488,16 @@ static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data, len, tlv_len); return -EINVAL; } - len -= ALIGN(tlv_len, 4); - data += sizeof(*tlv) + ALIGN(tlv_len, 4); + + aligned_tlv_len = ALIGN(tlv_len, 4); + if (len < aligned_tlv_len) { + IWL_ERR(trans, "invalid aligned TLV len: %zd/%zu\n", + len, aligned_tlv_len); + return -EINVAL; + } + + len -= aligned_tlv_len; + data += sizeof(*tlv) + aligned_tlv_len; iwl_dbg_tlv_alloc(trans, tlv, true); } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 842586d4fc5c..7f1515dfc5bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -492,7 +492,7 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces, * Gets uCode section from tlv. */ static int iwl_store_ucode_sec(struct fw_img_parsing *img, - const void *data, int size) + const void *data, size_t size) { struct fw_sec *sec; const struct fw_sec_parsing *sec_parse; @@ -501,6 +501,9 @@ static int iwl_store_ucode_sec(struct fw_img_parsing *img, if (WARN_ON(!img || !data)) return -EINVAL; + if (size < sizeof(sec_parse->offset)) + return -EINVAL; + sec_parse = (const struct fw_sec_parsing *)data; alloc_size = sizeof(*img->sec) * (img->sec_counter + 1); @@ -804,6 +807,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, u32 build, paging_mem_size; int num_of_cpus; bool usniffer_req = false; + size_t aligned_tlv_len; if (len < sizeof(*ucode)) { IWL_ERR(drv, "uCode has invalid length: %zd\n", len); @@ -852,8 +856,16 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, len, tlv_len); return -EINVAL; } - len -= ALIGN(tlv_len, 4); - data += sizeof(*tlv) + ALIGN(tlv_len, 4); + + aligned_tlv_len = ALIGN(tlv_len, 4); + if (len < aligned_tlv_len) { + IWL_ERR(drv, "invalid aligned TLV len: %zd/%zu\n", + len, aligned_tlv_len); + return -EINVAL; + } + + len -= aligned_tlv_len; + data += sizeof(*tlv) + aligned_tlv_len; switch (tlv_type) { case IWL_UCODE_TLV_INST: diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index d47b4ae2f486..bd5353fdde81 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -85,16 +85,7 @@ static const u16 iwl_nvm_channels[] = { 149, 153, 157, 161, 165 }; -static const u16 iwl_ext_nvm_channels[] = { - /* 2.4 GHz */ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - /* 5 GHz */ - 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, - 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, - 149, 153, 157, 161, 165, 169, 173, 177, 181 -}; - -static const u16 iwl_uhb_nvm_channels[] = { +static const u16 iwl_unii9_nvm_channels[] = { /* 2.4 GHz */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 5 GHz */ @@ -105,12 +96,16 @@ static const u16 iwl_uhb_nvm_channels[] = { 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, - 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233 + 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233, + + /* UNII-9 */ + 237, 241, 245, 249, 253 }; #define IWL_NVM_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) -#define IWL_NVM_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels) -#define IWL_NVM_NUM_CHANNELS_UHB ARRAY_SIZE(iwl_uhb_nvm_channels) +#define IWL_NVM_NUM_CHANNELS_EXT 51 +#define IWL_NVM_NUM_CHANNELS_UHB 110 +#define IWL_NVM_NUM_CHANNELS_UNII9 ARRAY_SIZE(iwl_unii9_nvm_channels) #define NUM_2GHZ_CHANNELS 14 #define NUM_5GHZ_CHANNELS 37 #define FIRST_2GHZ_HT_MINUS 5 @@ -351,12 +346,15 @@ static int iwl_init_channel_map(struct iwl_trans *trans, int num_of_ch; const u16 *nvm_chan; - if (cfg->uhb_supported) { + if (cfg->unii9_supported) { + num_of_ch = IWL_NVM_NUM_CHANNELS_UNII9; + nvm_chan = iwl_unii9_nvm_channels; + } else if (cfg->uhb_supported) { num_of_ch = IWL_NVM_NUM_CHANNELS_UHB; - nvm_chan = iwl_uhb_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else if (cfg->nvm_type == IWL_NVM_EXT) { num_of_ch = IWL_NVM_NUM_CHANNELS_EXT; - nvm_chan = iwl_ext_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else { num_of_ch = IWL_NVM_NUM_CHANNELS; nvm_chan = iwl_nvm_channels; @@ -1441,7 +1439,9 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, u8 rx_chains = fw->valid_rx_ant; u8 tx_chains = fw->valid_rx_ant; - if (cfg->uhb_supported) + if (cfg->unii9_supported) + data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9); + else if (cfg->uhb_supported) data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB); else data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_EXT); @@ -1506,7 +1506,9 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, u16 lar_config; const __le16 *ch_section; - if (cfg->uhb_supported) + if (cfg->unii9_supported) + data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9); + else if (cfg->uhb_supported) data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB); else if (cfg->nvm_type != IWL_NVM_EXT) data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS); @@ -1727,12 +1729,15 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans, int max_num_ch; struct iwl_reg_capa reg_capa; - if (cfg->uhb_supported) { + if (cfg->unii9_supported) { + max_num_ch = IWL_NVM_NUM_CHANNELS_UNII9; + nvm_chan = iwl_unii9_nvm_channels; + } else if (cfg->uhb_supported) { max_num_ch = IWL_NVM_NUM_CHANNELS_UHB; - nvm_chan = iwl_uhb_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else if (cfg->nvm_type == IWL_NVM_EXT) { max_num_ch = IWL_NVM_NUM_CHANNELS_EXT; - nvm_chan = iwl_ext_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else { max_num_ch = IWL_NVM_NUM_CHANNELS; nvm_chan = iwl_nvm_channels; @@ -2087,13 +2092,26 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, struct iwl_nvm_get_info_rsp_v3 *rsp_v3; bool v4 = fw_has_api(&fw->ucode_capa, IWL_UCODE_TLV_API_REGULATORY_NVM_INFO); - size_t rsp_size = v4 ? sizeof(*rsp) : sizeof(*rsp_v3); + size_t rsp_size; void *channel_profile; ret = iwl_trans_send_cmd(trans, &hcmd); if (ret) return ERR_PTR(ret); + switch (iwl_fw_lookup_notif_ver(fw, REGULATORY_AND_NVM_GROUP, + NVM_GET_INFO, 0)) { + case 5: + rsp_size = sizeof(struct iwl_nvm_get_info_rsp); + break; + case 4: + rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v4); + break; + default: + rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v3); + break; + } + if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != rsp_size, "Invalid payload len in NVM response from FW %d", iwl_rx_packet_payload_len(hcmd.resp_pkt))) { @@ -2107,7 +2125,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, if (empty_otp) IWL_INFO(trans, "OTP is empty\n"); - nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V2); + nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V3); if (!nvm) { ret = -ENOMEM; goto out; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index e3627ad0321c..a464ebdec57f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "agg.h" #include "sta.h" @@ -222,6 +222,11 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, if (baid == IWL_RX_REORDER_DATA_INVALID_BAID) return IWL_MLD_PASS_SKB; + if (IWL_FW_CHECK(mld, baid >= ARRAY_SIZE(mld->fw_id_to_ba), + "Got out-of-range BAID %u in reorder_data=0x%x\n", + baid, reorder)) + return IWL_MLD_PASS_SKB; + /* no sta yet */ if (WARN_ONCE(!sta, "Got valid BAID without a valid station assigned\n")) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 17286b3341c0..9ac17be30400 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1708,7 +1708,7 @@ static void iwl_mld_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data, ies->len); - if (!elem || elem->datalen < 10 || + if (!elem || elem->datalen < 11 || !(elem->data[10] & WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) { *tolerated = false; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c index 8502129abe49..830c251f43af 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -18,9 +18,15 @@ static struct iwl_mcc_update_resp_v8 * iwl_mld_copy_mcc_resp(const struct iwl_rx_packet *pkt) { const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data; - int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); struct iwl_mcc_update_resp_v8 *resp_cp; - int notif_len = struct_size(resp_cp, channels, n_channels); + int n_channels; + int notif_len; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*mcc_resp_v8)) + return ERR_PTR(-EINVAL); + + n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); + notif_len = struct_size(resp_cp, channels, n_channels); if (iwl_rx_packet_payload_len(pkt) != notif_len) return ERR_PTR(-EINVAL); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c index f8a8c35066be..e445b1d7d4b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifdef CONFIG_THERMAL #include <linux/sort.h> @@ -272,6 +272,7 @@ static void iwl_mld_thermal_zone_register(struct iwl_mld *mld) if (ret) { IWL_DEBUG_TEMP(mld, "Failed to enable thermal zone\n"); thermal_zone_device_unregister(mld->tzone); + mld->tzone = NULL; } } @@ -385,10 +386,8 @@ static void iwl_mld_thermal_zone_unregister(struct iwl_mld *mld) return; IWL_DEBUG_TEMP(mld, "Thermal zone device unregister\n"); - if (mld->tzone) { - thermal_zone_device_unregister(mld->tzone); - mld->tzone = NULL; - } + thermal_zone_device_unregister(mld->tzone); + mld->tzone = NULL; } static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index 2185dade95f8..2df6643a5aa8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -1120,6 +1120,10 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, ssn = le32_to_cpup((__le32 *)agg_status + tx_resp->frame_count) & 0xFFFF; + if (IWL_FW_CHECK(mld, txq_id >= ARRAY_SIZE(mld->fw_id_to_txq), + "Invalid txq id %d\n", txq_id)) + return; + __skb_queue_head_init(&skbs); /* we can free until ssn % q.n_bd not inclusive */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 9a74f60c9185..6b11fa32ea5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1509,6 +1509,10 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, if (WARN_ON_ONCE(truncated < 0)) truncated = 0; + /* this would be a firmware bug */ + if (WARN_ON_ONCE(pktsize < sizeof(*hdr))) + return; + if (ieee80211_is_data(hdr->frame_control)) { int hdrlen = ieee80211_hdrlen(hdr->frame_control); int ivlen = 0, icvlen = 4; /* also FCS */ @@ -1517,10 +1521,6 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, if (!pkt) goto report; - skb_put_data(pkt, pktdata, hdrlen); - pktdata += hdrlen; - pktsize -= hdrlen; - if (ieee80211_has_protected(hdr->frame_control)) { /* * This is unlocked and using gtk_i(c)vlen, @@ -1539,13 +1539,24 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, /* if truncated, FCS/ICV is (partially) gone */ if (truncated >= icvlen) { - icvlen = 0; truncated -= icvlen; + icvlen = 0; } else { icvlen -= truncated; truncated = 0; } + if (IWL_FW_CHECK(mvm, + pktsize <= hdrlen + ivlen + icvlen, + "pktsize is too small %d\n", + pktsize)) { + kfree_skb(pkt); + return; + } + + skb_put_data(pkt, pktdata, hdrlen); + pktdata += hdrlen; + pktsize -= hdrlen; pktsize -= ivlen + icvlen; pktdata += ivlen; @@ -2123,16 +2134,16 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, struct iwl_wowlan_status_data *status, u32 len) { - if (IWL_FW_CHECK(mvm, data->num_mlo_link_keys, - "MLO is not supported, shouldn't receive MLO keys\n")) - return; - if (len < sizeof(*data)) { IWL_ERR(mvm, "Invalid WoWLAN info notification!\n"); status = NULL; return; } + if (IWL_FW_CHECK(mvm, data->num_mlo_link_keys, + "MLO is not supported, shouldn't receive MLO keys\n")) + return; + if (mvm->fast_resume) return; @@ -2448,13 +2459,15 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { - query_len = sizeof(struct iwl_scan_offload_match_info); matches_len = sizeof(struct iwl_scan_offload_profile_match) * max_profiles; + query_len = offsetof(struct iwl_scan_offload_match_info, + matches) + matches_len; } else { - query_len = sizeof(struct iwl_scan_offload_profiles_query_v1); matches_len = sizeof(struct iwl_scan_offload_profile_match_v1) * max_profiles; + query_len = sizeof(struct iwl_scan_offload_profiles_query_v1) + + matches_len; } len = iwl_rx_packet_payload_len(cmd.resp_pkt); @@ -2756,7 +2769,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm, struct iwl_wowlan_status_data *status, u32 len) { - u32 data_size, packet_len = le32_to_cpu(notif->wake_packet_length); + u32 data_size, packet_len; if (len < sizeof(*notif)) { IWL_ERR(mvm, "Invalid WoWLAN wake packet notification!\n"); @@ -2775,6 +2788,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm, return -EIO; } + packet_len = le32_to_cpu(notif->wake_packet_length); data_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, wake_packet); /* data_size got the padding from the notification, remove it. */ @@ -2807,7 +2821,8 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm, if (IS_ERR_OR_NULL(vif)) return; - if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) { + if (len < offsetof(struct iwl_scan_offload_match_info, matches) + + matches_len) { IWL_ERR(mvm, "Invalid scan match info notification\n"); return; } @@ -2942,6 +2957,11 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { struct iwl_d3_end_notif *notif = (void *)pkt->data; + if (len < sizeof(*notif)) { + IWL_ERR(mvm, "Invalid D3 end notification size\n"); + break; + } + d3_data->d3_end_flags = __le32_to_cpu(notif->flags); d3_data->notif_received |= IWL_D3_NOTIF_D3_END_NOTIF; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index 3a14ca5e512a..8d7f60dcd027 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -646,7 +646,8 @@ static void iter(struct ieee80211_hw *hw, WARN_ON(*target->cipher == IWL_LOCATION_CIPHER_INVALID); } -static void +/* The tk buffer is assumed to be exactly TK_11AZ_LEN bytes */ +static int iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u8 *bssid, u8 *cipher, u8 *hltk, u8 *tk, u8 *rx_pn, u8 *tx_pn, __le32 *flags) @@ -656,12 +657,12 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (mvmvif->ftm_unprotected) - return; + return 0; #endif if (!(le32_to_cpu(*flags) & (IWL_INITIATOR_AP_FLAGS_NON_TB | IWL_INITIATOR_AP_FLAGS_TB))) - return; + return 0; lockdep_assert_held(&mvm->mutex); @@ -679,14 +680,31 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (vif->cfg.assoc && !memcmp(vif->bss_conf.bssid, bssid, ETH_ALEN)) { struct iwl_mvm_ftm_iter_data target; + u32 key_len; target.bssid = bssid; target.cipher = cipher; target.tk = NULL; ieee80211_iter_keys(mvm->hw, vif, iter, &target); - if (!WARN_ON(!target.tk)) - memcpy(tk, target.tk, TK_11AZ_LEN); + if (WARN_ON(!target.tk)) + return -EINVAL; + + switch (*target.cipher) { + case IWL_LOCATION_CIPHER_CCMP_128: + case IWL_LOCATION_CIPHER_GCMP_128: + key_len = WLAN_KEY_LEN_CCMP; + break; + case IWL_LOCATION_CIPHER_GCMP_256: + key_len = WLAN_KEY_LEN_GCMP_256; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + memset(tk, 0, TK_11AZ_LEN); + memcpy(tk, target.tk, key_len); } else { memcpy(tk, entry->tk, sizeof(entry->tk)); } @@ -695,8 +713,10 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, memcpy(tx_pn, entry->tx_pn, sizeof(entry->tx_pn)); FTM_SET_FLAG(SECURED); - return; + return 0; } + + return 0; } static int @@ -708,12 +728,11 @@ iwl_mvm_ftm_put_target_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (err) return err; - iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, - &target->cipher, target->hltk, - target->tk, target->rx_pn, - target->tx_pn, - &target->initiator_ap_flags); - return err; + return iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, + &target->cipher, target->hltk, + target->tk, target->rx_pn, + target->tx_pn, + &target->initiator_ap_flags); } static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm, @@ -881,11 +900,13 @@ iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags); iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id, &target->initiator_ap_flags); - iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, - &target->cipher, target->hltk, - target->tk, target->rx_pn, - target->tx_pn, - &target->initiator_ap_flags); + ret = iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, + &target->cipher, target->hltk, + target->tk, target->rx_pn, + target->tx_pn, + &target->initiator_ap_flags); + if (ret) + return ret; i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 : IWL_MVM_FTM_I2R_MAX_STS; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 6e507d6dcdd2..fa523be91d8a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -964,12 +964,22 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) return ret; } + if (IWL_FW_CHECK(mvm, + iwl_rx_packet_payload_len(cmd.resp_pkt) != + sizeof(*resp), + "Wrong size for iwl_geo_tx_power_profiles_resp: %d\n", + iwl_rx_packet_payload_len(cmd.resp_pkt))) { + ret = -EIO; + goto out; + } + resp = (void *)cmd.resp_pkt->data; ret = le32_to_cpu(resp->profile_idx); if (WARN_ON(ret > BIOS_GEO_MAX_PROFILE_NUM)) ret = -EIO; +out: iwl_free_resp(&cmd); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index d6a8624b1ae5..b2c5be22c293 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1493,49 +1493,54 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); - struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; - struct iwl_extended_beacon_notif_v5 *beacon_v5 = (void *)pkt->data; struct ieee80211_vif *csa_vif; struct ieee80211_vif *tx_blocked_vif; struct agg_tx_status *agg_status; + u32 beacon_gp2; u16 status; lockdep_assert_held(&mvm->mutex); - mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); - if (!iwl_mvm_is_short_beacon_notif_supported(mvm)) { + struct iwl_extended_beacon_notif_v5 *beacon = (void *)pkt->data; struct iwl_tx_resp *beacon_notify_hdr = - &beacon_v5->beacon_notify_hdr; + &beacon->beacon_notify_hdr; - if (unlikely(pkt_len < sizeof(*beacon_v5))) + if (unlikely(pkt_len < sizeof(*beacon))) return; - mvm->ibss_manager = beacon_v5->ibss_mgr_status != 0; + beacon_gp2 = le32_to_cpu(beacon->gp2); + + mvm->ibss_manager = beacon->ibss_mgr_status != 0; agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr); status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK; IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%016llX gp2:0x%X rate:%d\n", status, beacon_notify_hdr->failure_frame, le64_to_cpu(beacon->tsf), - mvm->ap_last_beacon_gp2, + beacon_gp2, le32_to_cpu(beacon_notify_hdr->initial_rate)); } else { + const struct iwl_extended_beacon_notif *beacon = + (void *)pkt->data; + if (unlikely(pkt_len < sizeof(*beacon))) return; + beacon_gp2 = le32_to_cpu(beacon->gp2); + mvm->ibss_manager = beacon->ibss_mgr_status != 0; status = le32_to_cpu(beacon->status) & TX_STATUS_MSK; IWL_DEBUG_RX(mvm, "beacon status %#x tsf:0x%016llX gp2:0x%X\n", status, le64_to_cpu(beacon->tsf), - mvm->ap_last_beacon_gp2); + beacon_gp2); } csa_vif = rcu_dereference_protected(mvm->csa_vif, lockdep_is_held(&mvm->mutex)); if (unlikely(csa_vif && csa_vif->bss_conf.csa_active)) - iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2, + iwl_mvm_csa_count_down(mvm, csa_vif, beacon_gp2, (status == TX_STATUS_SUCCESS)); tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 74bd4038fd56..3e73a6195fd9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -625,9 +625,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_sched_scan_reqs = 1; hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; hw->wiphy->max_match_sets = iwl_umac_scan_get_max_profiles(mvm->fw); - /* we create the 802.11 header and zero length SSID IE. */ - hw->wiphy->max_sched_scan_ie_len = - SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; + hw->wiphy->max_sched_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS; hw->wiphy->max_sched_scan_plan_interval = U16_MAX; @@ -3061,7 +3059,6 @@ void iwl_mvm_stop_ap_ibss_common(struct iwl_mvm *mvm, } mvmvif->ap_ibss_active = false; - mvm->ap_last_beacon_gp2 = 0; if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) { iwl_mvm_vif_set_low_latency(mvmvif, false, @@ -3490,7 +3487,7 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data, ies->len); - if (!elem || elem->datalen < 10 || + if (!elem || elem->datalen < 11 || !(elem->data[10] & WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) { data->tolerated = false; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 683cac56822c..31912f4d0175 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1168,9 +1168,6 @@ struct iwl_mvm { struct ieee80211_vif __rcu *csa_tx_blocked_vif; u8 csa_tx_block_bcn_timeout; - /* system time of last beacon (for AP/GO interface) */ - u32 ap_last_beacon_gp2; - /* indicates that we transmitted the last beacon */ bool ibss_manager; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 953218f1e025..f76e57399c1f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -416,6 +416,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, int ret, resp_ver; u32 status; int resp_len, n_channels; + unsigned int pkt_len; u16 mcc; if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) @@ -431,6 +432,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, return ERR_PTR(ret); pkt = cmd.resp_pkt; + pkt_len = iwl_rx_packet_payload_len(pkt); resp_ver = iwl_fw_lookup_notif_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, MCC_UPDATE_CMD, 0); @@ -439,9 +441,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, if (resp_ver >= 8) { struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v8), + "MCC v8 response too short: %u\n", pkt_len)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } + n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); - if (iwl_rx_packet_payload_len(pkt) != - struct_size(mcc_resp_v8, channels, n_channels)) { + if (IWL_FW_CHECK(mvm, + pkt_len != + struct_size(mcc_resp_v8, channels, n_channels), + "invalid MCC v8 response size: %u (n_channels=%d)\n", + pkt_len, n_channels)) { resp_cp = ERR_PTR(-EINVAL); goto exit; } @@ -464,9 +475,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT)) { struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v4), + "MCC v4 response too short: %u\n", pkt_len)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } + n_channels = __le32_to_cpu(mcc_resp_v4->n_channels); - if (iwl_rx_packet_payload_len(pkt) != - struct_size(mcc_resp_v4, channels, n_channels)) { + if (IWL_FW_CHECK(mvm, + pkt_len != + struct_size(mcc_resp_v4, channels, n_channels), + "invalid MCC v4 response size: %u (n_channels=%d)\n", + pkt_len, n_channels)) { resp_cp = ERR_PTR(-EINVAL); goto exit; } @@ -489,9 +509,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, } else { struct iwl_mcc_update_resp_v3 *mcc_resp_v3 = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v3), + "MCC v3 response too short: %u\n", pkt_len)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } + n_channels = __le32_to_cpu(mcc_resp_v3->n_channels); - if (iwl_rx_packet_payload_len(pkt) != - struct_size(mcc_resp_v3, channels, n_channels)) { + if (IWL_FW_CHECK(mvm, + pkt_len != + struct_size(mcc_resp_v3, channels, n_channels), + "invalid MCC v3 response size: %u (n_channels=%d)\n", + pkt_len, n_channels)) { resp_cp = ERR_PTR(-EINVAL); goto exit; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index f39eb48864eb..49dcb1388007 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2021 - 2023, 2025 Intel Corporation + * Copyright (C) 2021 - 2023, 2025-2026 Intel Corporation */ #include "mvm.h" @@ -121,6 +121,8 @@ iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm *mvm, u32 *gp2, u64 *sys_time) *sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 | le32_to_cpu(resp->platform_timestamp_lo); + iwl_free_resp(&cmd); + return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 89ac4c6b3e54..e2382be8edd7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "rs.h" #include "fw-api.h" @@ -423,9 +423,14 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_lq_sta_rs_fw *lq_sta; u32 flags; + notif = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, notif->sta_id >= mvm->fw->ucode_capa.num_stations, + "Invalid sta id (%d) in TLC notification\n", + notif->sta_id)) + return; + rcu_read_lock(); - notif = (void *)pkt->data; link_sta = rcu_dereference(mvm->fw_id_to_link_sta[notif->sta_id]); sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]); if (IS_ERR_OR_NULL(sta) || !link_sta) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 269c4b45de80..ab1eb2eb0c3c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -1227,6 +1227,11 @@ void iwl_mvm_window_status_notif(struct iwl_mvm *mvm, /* get the station */ sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK) >> BA_WINDOW_STATUS_STA_ID_POS; + if (IWL_FW_CHECK(mvm, + sta_id >= mvm->fw->ucode_capa.num_stations, + "Invalid sta id (%d) in BA window status notification\n", + sta_id)) + continue; sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); if (IS_ERR_OR_NULL(sta)) continue; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 79829f775c89..3831b3c27e0f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -3144,8 +3144,6 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, if (ret) return ret; - iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - /* for 6 GHZ band only PSC channels need to be added */ for (i = 0; i < params.n_channels; i++) { struct ieee80211_channel *channel = params.channels[i]; @@ -3179,6 +3177,8 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, goto out; } + iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); + uid = iwl_mvm_build_scan_cmd(mvm, vif, &hcmd, ¶ms, type); if (uid < 0) { ret = uid; @@ -3217,6 +3217,10 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, mvm->mei_scan_filter.is_mei_limited_scan = false; + if (IWL_FW_CHECK(mvm, uid >= ARRAY_SIZE(mvm->scan_uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + IWL_DEBUG_SCAN(mvm, "Scan completed: uid=%u type=%u, status=%s, EBS=%s\n", uid, mvm->scan_uid_status[uid], diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 1692b6e75f57..93b46c9e2333 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -951,6 +951,11 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, struct ieee80211_vif *vif; struct iwl_mvm_vif *mvmvif; + if (IWL_FW_CHECK(mvm, id >= ARRAY_SIZE(mvm->vif_id_to_mac), + "Invalid mac_link_id (%d) in session protect notif\n", + id)) + return; + rcu_read_lock(); /* note we use link ID == MAC ID */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index dc69c71faa76..d3c2fe830477 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1544,6 +1544,17 @@ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm, return val & 0xFFF; } +static inline size_t iwl_mvm_tx_resp_min_len(struct iwl_mvm *mvm, + struct iwl_tx_resp *tx_resp) +{ + struct agg_tx_status *agg_status = + iwl_mvm_get_agg_status(mvm, tx_resp); + + /* The aggregate response ends with a trailing SCD SSN __le32 word. */ + return (u8 *)(agg_status + tx_resp->frame_count) - (u8 *)tx_resp + + sizeof(__le32); +} + static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -1847,6 +1858,9 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, int queue = SEQ_TO_QUEUE(sequence); struct ieee80211_sta *sta; + if (WARN_ON_ONCE(iwl_mvm_has_new_tx_api(mvm))) + return; + if (WARN_ON_ONCE(queue < IWL_MVM_DQA_MIN_DATA_QUEUE && (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE))) return; @@ -1881,6 +1895,26 @@ void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_tx_resp *tx_resp = (void *)pkt->data; + size_t min_len; + + if (IWL_FW_CHECK(mvm, !tx_resp->frame_count, + "invalid TX_CMD frame_count %u\n", + tx_resp->frame_count)) + return; + + if (IWL_FW_CHECK(mvm, + iwl_mvm_has_new_tx_api(mvm) && + tx_resp->frame_count != 1, + "invalid TX_CMD frame_count %u for new TX API\n", + tx_resp->frame_count)) + return; + + min_len = iwl_mvm_tx_resp_min_len(mvm, tx_resp); + if (IWL_FW_CHECK(mvm, iwl_rx_packet_payload_len(pkt) < min_len, + "invalid TX_CMD len %u (frame_count %u, min %zu)\n", + iwl_rx_packet_payload_len(pkt), tx_resp->frame_count, + min_len)) + return; if (tx_resp->frame_count == 1) iwl_mvm_rx_tx_cmd_single(mvm, pkt); @@ -2100,8 +2134,14 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) if (tid == IWL_MGMT_TID) tid = IWL_MAX_TID_COUNT; + if (IWL_FW_CHECK(mvm, tid >= + ARRAY_SIZE(mvmsta->tid_data), + "invalid TID %d in compressed BA\n", + tid)) + continue; + if (mvmsta) - mvmsta->tid_data[i].lq_color = lq_color; + mvmsta->tid_data[tid].lq_color = lq_color; iwl_mvm_tx_reclaim(mvm, sta_id, tid, (int)(le16_to_cpu(ba_tfd->q_num)), @@ -2121,6 +2161,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ba_notif = (void *)pkt->data; sta_id = ba_notif->sta_id; tid = ba_notif->tid; + if (IWL_FW_CHECK(mvm, tid >= ARRAY_SIZE(mvmsta->tid_data), + "invalid TID %d in BA notif\n", tid)) + return; /* "flow" corresponds to Tx queue */ txq = le16_to_cpu(ba_notif->scd_flow); /* "ssn" is start of block-ack Tx window, corresponds to index diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index d5eb895144ef..7f886200d693 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "iwl-trans.h" #include "iwl-fh.h" @@ -99,6 +99,11 @@ int iwl_pcie_init_fw_sec(struct iwl_trans *trans, /* add 2 due to separators */ paging_cnt = iwl_pcie_get_num_sections(fw, lmac_cnt + umac_cnt + 2); + if (WARN_ON(lmac_cnt > ARRAY_SIZE(ctxt_dram->lmac_img) || + umac_cnt > ARRAY_SIZE(ctxt_dram->umac_img) || + paging_cnt > ARRAY_SIZE(ctxt_dram->virtual_img))) + return -EINVAL; + dram->fw = kzalloc_objs(*dram->fw, umac_cnt + lmac_cnt); if (!dram->fw) return -ENOMEM; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c index fe263cdc2e4f..4631e11f2a96 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2024 Intel Corporation + * Copyright (C) 2003-2014, 2018-2024, 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1238,11 +1238,16 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) } } kfree(trans_pcie->rx_pool); + trans_pcie->rx_pool = NULL; kfree(trans_pcie->global_table); + trans_pcie->global_table = NULL; kfree(trans_pcie->rxq); + trans_pcie->rxq = NULL; - if (trans_pcie->alloc_page) + if (trans_pcie->alloc_page) { __free_pages(trans_pcie->alloc_page, trans_pcie->rx_page_order); + trans_pcie->alloc_page = NULL; + } } static void iwl_pcie_rx_move_to_allocator(struct iwl_rxq *rxq, diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index e9e896606912..93561116959a 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -823,7 +823,7 @@ struct chan_band_param_set { struct mwifiex_ie_types_chan_band_list_param_set { struct mwifiex_ie_types_header header; - struct chan_band_param_set chan_band_param[1]; + struct chan_band_param_set chan_band_param[]; } __packed; struct mwifiex_ie_types_rates_param_set { @@ -886,7 +886,7 @@ struct mwifiex_ie_types_wildcard_ssid_params { #define TSF_DATA_SIZE 8 struct mwifiex_ie_types_tsf_timestamp { struct mwifiex_ie_types_header header; - u8 tsf_data[1]; + u8 tsf_data[]; } __packed; struct mwifiex_cf_param_set { @@ -903,8 +903,8 @@ struct mwifiex_ibss_param_set { struct mwifiex_ie_types_ss_param_set { struct mwifiex_ie_types_header header; union { - struct mwifiex_cf_param_set cf_param_set[1]; - struct mwifiex_ibss_param_set ibss_param_set[1]; + DECLARE_FLEX_ARRAY(struct mwifiex_cf_param_set, cf_param_set); + DECLARE_FLEX_ARRAY(struct mwifiex_ibss_param_set, ibss_param_set); } cf_ibss; } __packed; @@ -922,8 +922,8 @@ struct mwifiex_ds_param_set { struct mwifiex_ie_types_phy_param_set { struct mwifiex_ie_types_header header; union { - struct mwifiex_fh_param_set fh_param_set[1]; - struct mwifiex_ds_param_set ds_param_set[1]; + DECLARE_FLEX_ARRAY(struct mwifiex_fh_param_set, fh_param_set); + DECLARE_FLEX_ARRAY(struct mwifiex_ds_param_set, ds_param_set); } fh_ds; } __packed; @@ -1383,7 +1383,7 @@ struct host_cmd_ds_802_11_snmp_mib { __le16 query_type; __le16 oid; __le16 buf_size; - u8 value[1]; + u8 value[]; } __packed; struct mwifiex_rate_scope { @@ -1551,7 +1551,7 @@ struct mwifiex_scan_cmd_config { * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set */ - u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored + u8 tlv_buf[]; /* SSID TLV(s) and ChanList TLVs are stored here */ } __packed; @@ -1683,7 +1683,7 @@ struct host_cmd_ds_802_11_bg_scan_query_rsp { struct mwifiex_ietypes_domain_param_set { struct mwifiex_ie_types_header header; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - struct ieee80211_country_ie_triplet triplet[1]; + struct ieee80211_country_ie_triplet triplet[]; } __packed; struct host_cmd_ds_802_11d_domain_info { diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index b48f7febaf03..259140395d35 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -421,15 +421,15 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); - phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); - memcpy(&phy_tlv->fh_ds.ds_param_set, + phy_tlv->header.len = cpu_to_le16(sizeof(*phy_tlv->fh_ds.ds_param_set)); + memcpy(phy_tlv->fh_ds.ds_param_set, &bss_desc->phy_param_set.ds_param_set.current_chan, - sizeof(phy_tlv->fh_ds.ds_param_set)); + sizeof(*phy_tlv->fh_ds.ds_param_set)); pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); - ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); + ss_tlv->header.len = cpu_to_le16(sizeof(*ss_tlv->cf_ibss.cf_param_set)); pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); /* Get the common rates supported between the driver and the BSS Desc */ diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 623ddde8c8e5..071f7cb305e1 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -108,7 +108,7 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) - - 1 + S_DS_GEN); + + S_DS_GEN); snmp_mib->oid = cpu_to_le16((u16)cmd_oid); if (cmd_action == HostCmd_ACT_GEN_GET) { diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index a6550548d3b4..9460d5352b23 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -196,6 +196,7 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv) struct mwifiex_adapter *adapter = priv->adapter; char rgpower_table_name[30]; char country_code[3]; + int ret; strscpy(country_code, domain_info->country_code, sizeof(country_code)); @@ -214,16 +215,17 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv) adapter->rgpower_data = NULL; } - if ((request_firmware(&adapter->rgpower_data, rgpower_table_name, - adapter->dev))) { + ret = request_firmware_direct(&adapter->rgpower_data, rgpower_table_name, + adapter->dev); + + if (ret) { mwifiex_dbg( adapter, INFO, - "info: %s: failed to request regulatory power table\n", - __func__); - return -EIO; + "info: %s: failed to request regulatory power table: %d\n", + __func__, ret); } - return 0; + return ret; } static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv) diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 845f2a22e071..c71ffe8399e4 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -215,7 +215,7 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, /* follow AP's channel bandwidth */ if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && - bss_desc->bcn_ht_cap && + bss_desc->bcn_ht_oper && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c index 679fdae0f001..ba1bdbbff687 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_event.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -123,11 +123,31 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) len = ETH_ALEN; if (len != -1) { + u16 evt_len = le16_to_cpu(event->len); + sinfo->assoc_req_ies = &event->data[len]; len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control; - sinfo->assoc_req_ies_len = - le16_to_cpu(event->len) - (u16)len; + + /* + * event->len is reported by the device firmware + * and is not otherwise validated. Reject a + * length that underflows the header, or that + * would place the association request IEs + * outside the fixed-size event_body[] buffer the + * event was copied into; otherwise the IE walk + * in mwifiex_set_sta_ht_cap() reads past + * event_body and out of the adapter slab object. + */ + if (evt_len < len || + (u8 *)&event->frame_control + evt_len > + adapter->event_body + MAX_EVENT_SIZE) { + mwifiex_dbg(adapter, ERROR, + "invalid STA assoc event length\n"); + kfree(sinfo); + return -1; + } + sinfo->assoc_req_ies_len = evt_len - (u16)len; } } cfg80211_new_sta(priv->netdev->ieee80211_ptr, event->sta_addr, diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig index 502303622a53..2ca96e0527c0 100644 --- a/drivers/net/wireless/mediatek/mt76/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -38,8 +38,8 @@ config MT792x_USB select MT76_USB config MT76_NPU - bool - depends on MT76_CORE + tristate + depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig" diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile index 1d42adfe8030..cacdd2b13d05 100644 --- a/drivers/net/wireless/mediatek/mt76/Makefile +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -12,7 +12,11 @@ mt76-y := \ mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \ tx.o agg-rx.o mcu.o wed.o scan.o channel.o -mt76-$(CONFIG_MT76_NPU) += npu.o +ifdef CONFIG_MT76_NPU +# CONFIG_MT76_NPU is tristate to simplify dependency tracking, +# but it behaves as a bool symbol here. +mt76-y += npu.o +endif mt76-$(CONFIG_PCI) += pci.o mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index f8c2fe5f2f58..322041859217 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -1189,7 +1189,10 @@ void mt76_dma_cleanup(struct mt76_dev *dev) mt76_for_each_q_rx(dev, i) { struct mt76_queue *q = &dev->q_rx[i]; - netif_napi_del(&dev->napi[i]); + if (!mt76_queue_is_wed_rro(q)) { + napi_disable(&dev->napi[i]); + netif_napi_del(&dev->napi[i]); + } mt76_dma_rx_cleanup(dev, q); page_pool_destroy(q->page_pool); diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index b99d7452800f..afdb73661866 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -181,7 +181,7 @@ mt76_eeprom_override(struct mt76_phy *phy) if (err == -EPROBE_DEFER) return err; - if (err) { + if (!is_valid_ether_addr(phy->macaddr)) { eth_random_addr(phy->macaddr); dev_info(dev->dev, "Invalid MAC address, using random address %pM\n", diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 07955555f84d..3822eb8fd88f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1647,7 +1647,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state); int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len); -#ifdef CONFIG_MT76_NPU +#if IS_ENABLED(CONFIG_MT76_NPU) void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb, u32 info); int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q, @@ -1736,12 +1736,12 @@ static inline int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, static inline bool mt76_npu_device_active(struct mt76_dev *dev) { - return !!rcu_access_pointer(dev->mmio.npu); + return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.npu); } static inline bool mt76_ppe_device_active(struct mt76_dev *dev) { - return !!rcu_access_pointer(dev->mmio.ppe_dev); + return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.ppe_dev); } static inline int mt76_npu_send_msg(struct airoha_npu *npu, int ifindex, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index ce0051468501..aad232c5a6fa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1601,6 +1601,8 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) + return false; mt7615_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: @@ -1634,6 +1636,10 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, dev_kfree_skb(skb); break; case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) { + dev_kfree_skb(skb); + break; + } mt7615_mac_tx_free(dev, skb->data, skb->len); dev_kfree_skb(skb); break; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 6596c9e198f4..58b0b15e4fd6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1458,6 +1458,8 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif, struct bss_info_uni_he *he; cap = mt76_connac_get_he_phy_cap(phy, vif); + if (!cap) + return; he = (struct bss_info_uni_he *)tlv; he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 4a381d351e61..e8fe86f93309 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -595,6 +595,8 @@ mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he)); @@ -1177,13 +1179,12 @@ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy, } static void -mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, - struct mt7915_phy *phy, struct sta_rec_bf *bf) +mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, + const struct ieee80211_sta_he_cap *vc, + struct sta_rec_bf *bf) { struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; - const struct ieee80211_sta_he_cap *vc = - mt76_connac_get_he_phy_cap(phy->mt76, vif); const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); @@ -1242,6 +1243,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_phy *phy = mvif->phy; + const struct ieee80211_sta_he_cap *vc = NULL; int tx_ant = hweight8(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; @@ -1260,6 +1262,12 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, if (!ebf && !dev->ibf) return; + if (sta->deflink.he_cap.has_he && ebf) { + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!vc) + return; + } + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); bf = (struct sta_rec_bf *)tlv; @@ -1268,7 +1276,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, * ht: iBF only, since mac80211 lacks of eBF support */ if (sta->deflink.he_cap.has_he && ebf) - mt7915_mcu_sta_bfer_he(sta, vif, phy, bf); + mt7915_mcu_sta_bfer_he(sta, vc, bf); else if (sta->deflink.vht_cap.vht_supported) mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf); else if (sta->deflink.ht_cap.ht_supported) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 1c2377d0a53d..f7d54472da1b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -570,8 +570,9 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ - mt7921_mac_tx_free(dev, data, len); /* mmio */ + if (!mt76_is_mmio(mdev)) + return false; + mt7921_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: for (rxd += 2; rxd + 8 <= end; rxd += 8) @@ -600,7 +601,10 @@ void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } mt7921_mac_tx_free(dev, skb->data, skb->len); napi_consume_skb(skb, 1); break; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 0641a7131d7c..6b0cd1996ecb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1203,8 +1203,9 @@ bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ - mt7925_mac_tx_free(dev, data, len); /* mmio */ + if (!mt76_is_mmio(mdev)) + return false; + mt7925_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: for (rxd += 4; rxd + 12 <= end; rxd += 12) @@ -1240,7 +1241,10 @@ void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } mt7925_mac_tx_free(dev, skb->data, skb->len); napi_consume_skb(skb, 1); break; @@ -1284,6 +1288,9 @@ mt7925_vif_connect_iter(void *priv, u8 *mac, for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { bss_conf = mt792x_vif_to_bss_conf(vif, i); + if (!bss_conf) + continue; + mconf = mt792x_vif_to_link(mvif, i); mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index a9059866b701..2b6cc8e253c0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1713,6 +1713,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw, mconf = mt792x_vif_to_link(mvif, i); mlink = mt792x_sta_to_link(msta, i); + if (!mlink) + continue; + if (enabled) set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags); else diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index e94fa544ff20..cb265a6fc7ad 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2773,6 +2773,8 @@ mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif); + if (!cap) + return; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig index 5503d03bf62c..5742bce12fbb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig @@ -16,6 +16,6 @@ config MT7996E config MT7996_NPU bool "MT7996 (PCIe) NPU support" depends on MT7996E - depends on NET_AIROHA_NPU=y || MT7996E=NET_AIROHA_NPU + depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU select MT76_NPU default n diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index f119f023bcd5..2e83f4b79c87 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -935,6 +935,8 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); @@ -1855,17 +1857,18 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta, { struct ieee80211_sta_he_cap *pc = &link_sta->he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; - const struct ieee80211_sta_he_cap *vc = - mt76_connac_get_he_phy_cap(phy->mt76, vif); - const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + const struct ieee80211_he_cap_elem *ve; + const struct ieee80211_sta_he_cap *vc; u8 snd_dim, sts; + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); if (!vc) return; bf->tx_mode = MT_PHY_TYPE_HE_SU; + ve = &vc->he_cap_elem; mt7996_mcu_sta_sounding_rate(bf, phy); @@ -1921,14 +1924,18 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta, struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp; - const struct ieee80211_sta_eht_cap *vc = - mt76_connac_get_eht_phy_cap(phy->mt76, vif); - const struct ieee80211_eht_cap_elem_fixed *ve = &vc->eht_cap_elem; u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss, IEEE80211_EHT_MCS_NSS_RX) - 1; + const struct ieee80211_eht_cap_elem_fixed *ve; + const struct ieee80211_sta_eht_cap *vc; u8 snd_dim, sts; + vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif); + if (!vc) + return; + bf->tx_mode = MT_PHY_TYPE_EHT_MU; + ve = &vc->eht_cap_elem; mt7996_mcu_sta_sounding_rate(bf, phy); diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 009c4770a6f9..60fe5f08964f 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -600,6 +600,11 @@ static s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len, u16 ies_len; struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer; + if (buffer_len < sizeof(*res)) { + ret_conn_info->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + return -EINVAL; + } + ret_conn_info->status = le16_to_cpu(res->status_code); if (ret_conn_info->status == WLAN_STATUS_SUCCESS) { ies = &buffer[sizeof(*res)]; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 956ff9b94526..75caa97becc8 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -870,9 +870,9 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 }, [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 }, [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 }, - [HWSIM_ATTR_TX_INFO] = { .type = NLA_BINARY, - .len = IEEE80211_TX_MAX_RATES * - sizeof(struct hwsim_tx_rate)}, + [HWSIM_ATTR_TX_INFO] = + NLA_POLICY_EXACT_LEN(IEEE80211_TX_MAX_RATES * + sizeof(struct hwsim_tx_rate)), [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 }, [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 }, diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 3c9ef826551d..e835779b6f5f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1618,6 +1618,8 @@ static DEVICE_ATTR_RW(charge_control_end_threshold); static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { + int ret, rv; + /* The WMI method does not provide a way to specific a battery, so we * just assume it is the first battery. * Note: On some newer ASUS laptops (Zenbook UM431DA), the primary/first @@ -1635,12 +1637,30 @@ static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_batter /* The charge threshold is only reset when the system is power cycled, * and we can't read the current threshold, however the majority of - * platforms retains it, therefore signal the threshold as unknown - * until user explicitly sets it to a new value. + * platforms retains it. + * + * Setting a negative value would signal the threshold as unknown + * until user explicitly sets it to a new value, however to avoid + * regressing userspace, we initialize it to a value of 100. */ - charge_end_threshold = -1; + charge_end_threshold = 100; + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, charge_end_threshold, &rv); + if (ret) { + pr_err("Failed to reset battery charge threshold\n"); + goto asus_wmi_battery_add_err; + } + + if (rv != 1) { + pr_err("Error in battery charge threshold reset\n"); + ret = -EIO; + goto asus_wmi_battery_add_err; + } return 0; +asus_wmi_battery_add_err: + device_remove_file(&battery->dev, + &dev_attr_charge_control_end_threshold); + return ret; } static int asus_wmi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 3ae4557b32b4..5ab2215fdd7f 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -103,6 +103,12 @@ static void intel_vsec_remove_aux(void *data) auxiliary_device_uninit(data); } +static void intel_vsec_dev_free(struct intel_vsec_device *intel_vsec_dev) +{ + kfree(intel_vsec_dev->acpi_disc); + kfree(intel_vsec_dev); +} + static void intel_vsec_dev_release(struct device *dev) { struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); @@ -111,8 +117,7 @@ static void intel_vsec_dev_release(struct device *dev) ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); - kfree(intel_vsec_dev->acpi_disc); - kfree(intel_vsec_dev); + intel_vsec_dev_free(intel_vsec_dev); } static const struct vsec_feature_dependency * @@ -218,20 +223,22 @@ int intel_vsec_add_aux(struct device *parent, struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; int ret, id; - if (!parent) + if (!parent) { + intel_vsec_dev_free(intel_vsec_dev); return -EINVAL; + } ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, PMT_XA_LIMIT, GFP_KERNEL); if (ret < 0) { - kfree(intel_vsec_dev); + intel_vsec_dev_free(intel_vsec_dev); return ret; } id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); if (id < 0) { xa_erase(&auxdev_array, intel_vsec_dev->id); - kfree(intel_vsec_dev); + intel_vsec_dev_free(intel_vsec_dev); return id; } diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c index 94e952ee6990..5e381c354d74 100644 --- a/drivers/ptp/ptp_netc.c +++ b/drivers/ptp/ptp_netc.c @@ -779,6 +779,7 @@ static void netc_timer_init(struct netc_timer *priv) netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); netc_timer_wr(priv, NETC_TMR_ECTRL, NETC_TMR_DEFAULT_ETTF_THR); + netc_timer_offset_write(priv, 0); ktime_get_real_ts64(&now); ns = timespec64_to_ns(&now); netc_timer_cnt_write(priv, ns); diff --git a/drivers/ptp/ptp_s390.c b/drivers/ptp/ptp_s390.c index 29618eb9bf44..02d624d89a0a 100644 --- a/drivers/ptp/ptp_s390.c +++ b/drivers/ptp/ptp_s390.c @@ -107,6 +107,9 @@ static __init int ptp_s390_init(void) if (IS_ERR(ptp_stcke_clock)) return PTR_ERR(ptp_stcke_clock); + if (!test_facility(28) || !ptff_query(PTFF_QPT)) + return 0; + ptp_qpt_clock = ptp_clock_register(&ptp_s390_qpt_info, NULL); if (IS_ERR(ptp_qpt_clock)) { ptp_clock_unregister(ptp_stcke_clock); @@ -117,7 +120,8 @@ static __init int ptp_s390_init(void) static __exit void ptp_s390_exit(void) { - ptp_clock_unregister(ptp_qpt_clock); + if (ptp_qpt_clock) + ptp_clock_unregister(ptp_qpt_clock); ptp_clock_unregister(ptp_stcke_clock); } diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index b69f99488111..2f09f4b78bd3 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1552,12 +1552,9 @@ static u16 mpam_wa_t241_calc_min_from_max(struct mpam_props *props, static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, struct mpam_config *cfg) { - u32 pri_val = 0; u16 cmax = MPAMCFG_CMAX_CMAX; struct mpam_msc *msc = ris->vmsc->msc; struct mpam_props *rprops = &ris->props; - u16 dspri = GENMASK(rprops->dspri_wd, 0); - u16 intpri = GENMASK(rprops->intpri_wd, 0); mutex_lock(&msc->part_sel_lock); __mpam_part_sel(ris->ris_idx, partid, msc); @@ -1583,9 +1580,9 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, if (mpam_has_feature(mpam_feat_mbw_part, rprops)) { if (mpam_has_feature(mpam_feat_mbw_part, cfg)) - mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); - else mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm); + else + mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); } if (mpam_has_feature(mpam_feat_mbw_min, rprops)) { @@ -1622,16 +1619,25 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, if (mpam_has_feature(mpam_feat_intpri_part, rprops) || mpam_has_feature(mpam_feat_dspri_part, rprops)) { - /* aces high? */ - if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops)) - intpri = 0; - if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops)) - dspri = 0; + u32 pri_val = 0; + + if (mpam_has_feature(mpam_feat_intpri_part, rprops)) { + u16 intpri = GENMASK(rprops->intpri_wd - 1, 0); + + /* aces high? */ + if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops)) + intpri = 0; - if (mpam_has_feature(mpam_feat_intpri_part, rprops)) pri_val |= FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri); - if (mpam_has_feature(mpam_feat_dspri_part, rprops)) + } + if (mpam_has_feature(mpam_feat_dspri_part, rprops)) { + u16 dspri = GENMASK(rprops->dspri_wd - 1, 0); + + if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops)) + dspri = 0; + pri_val |= FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri); + } mpam_write_partsel_reg(msc, PRI, pri_val); } @@ -2606,8 +2612,10 @@ static void __destroy_component_cfg(struct mpam_component *comp) msc = vmsc->msc; if (mpam_mon_sel_lock(msc)) { - list_for_each_entry(ris, &vmsc->ris, vmsc_list) - add_to_garbage(ris->mbwu_state); + list_for_each_entry(ris, &vmsc->ris, vmsc_list) { + if (ris->mbwu_state) + add_to_garbage(ris->mbwu_state); + } mpam_mon_sel_unlock(msc); } } diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 4e71ed679a76..4266a0cb7e3b 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2549,6 +2549,7 @@ static struct usbatm_driver uea_usbatm_driver = { static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *usb = interface_to_usbdev(intf); + bool single_iface = usb->config->desc.bNumInterfaces == 1; int ret; uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", @@ -2557,6 +2558,22 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) le16_to_cpu(usb->descriptor.bcdDevice), chip_name[UEA_CHIP_VERSION(id)]); + /* + * uea_probe() decides between the pre-firmware and post-firmware case + * from the USB id and stores a different object as interface data in + * each case: a struct completion for a pre-firmware device, a struct + * usbatm_data for a post-firmware one. uea_disconnect() instead tells + * the two apart by the number of interfaces (a pre-firmware device + * exposes a single interface, ADI930 has 2 and eagle has 3). A crafted + * device advertising a pre-firmware id together with a multi-interface + * descriptor (or the other way around) makes the two disagree, so that + * usbatm_usb_disconnect() treats the small completion object as a + * struct usbatm_data and reads out of bounds. Reject such inconsistent + * descriptors so both paths make the same decision. + */ + if (UEA_IS_PREFIRM(id) != single_iface) + return -ENODEV; + usb_reset_device(usb); if (UEA_IS_PREFIRM(id)) { diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 77b59f49bddb..3e72b9c6af0c 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -731,10 +731,12 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq, goto err; } - gso = buf + pad - sock_hlen; - - if (!sock_hlen) + if (!sock_hlen) { memset(buf, 0, pad); + gso = buf; + } else { + gso = buf + pad - sock_hlen; + } if ((gso->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && vhost16_to_cpu(vq, gso->csum_start) + diff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c index 4230b817a80b..d66291def0f4 100644 --- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c +++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c @@ -82,8 +82,8 @@ static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size, if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE) return 0; + end = PAGE_ALIGN(phys + size); phys = PAGE_ALIGN_DOWN(phys); - end = phys + PAGE_ALIGN(size); while (phys < end) { const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID; diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 088b3a0e6ce6..581ac799d974 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -1017,10 +1017,6 @@ static int virtballoon_probe(struct virtio_device *vdev) unsigned int capacity; capacity = virtqueue_get_vring_size(vb->reporting_vq); - if (capacity < PAGE_REPORTING_CAPACITY) { - err = -ENOSPC; - goto out_unregister_oom; - } vb->pr_dev_info.order = PAGE_REPORTING_ORDER_UNSPECIFIED; @@ -1041,6 +1037,7 @@ static int virtballoon_probe(struct virtio_device *vdev) vb->pr_dev_info.order = 5; #endif + vb->pr_dev_info.capacity = capacity; err = page_reporting_register(&vb->pr_dev_info); if (err) goto out_unregister_oom; diff --git a/drivers/watchdog/airoha_wdt.c b/drivers/watchdog/airoha_wdt.c index dc8ca11c14d8..4bd333189b87 100644 --- a/drivers/watchdog/airoha_wdt.c +++ b/drivers/watchdog/airoha_wdt.c @@ -147,6 +147,9 @@ static int airoha_wdt_probe(struct platform_device *pdev) /* Watchdog ticks at half the bus rate */ airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2; + if (!airoha_wdt->wdt_freq) + return dev_err_probe(dev, -EINVAL, + "invalid clock frequency\n"); /* Initialize struct watchdog device */ wdog_dev = &airoha_wdt->wdog_dev; diff --git a/drivers/watchdog/ni903x_wdt.c b/drivers/watchdog/ni903x_wdt.c index 8b1b9baa914e..c72a9ee9cb8e 100644 --- a/drivers/watchdog/ni903x_wdt.c +++ b/drivers/watchdog/ni903x_wdt.c @@ -183,9 +183,14 @@ static int ni903x_acpi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct watchdog_device *wdd; struct ni903x_wdt *wdt; + acpi_handle handle; acpi_status status; int ret; + handle = ACPI_HANDLE(dev); + if (!handle) + return -ENODEV; + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; @@ -193,7 +198,7 @@ static int ni903x_acpi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdt); wdt->dev = dev; - status = acpi_walk_resources(ACPI_HANDLE(dev), METHOD_NAME__CRS, + status = acpi_walk_resources(handle, METHOD_NAME__CRS, ni903x_resources, wdt); if (ACPI_FAILURE(status) || wdt->io_base == 0) { dev_err(dev, "failed to get resources\n"); diff --git a/drivers/watchdog/s32g_wdt.c b/drivers/watchdog/s32g_wdt.c index ad55063060af..6422a694fc65 100644 --- a/drivers/watchdog/s32g_wdt.c +++ b/drivers/watchdog/s32g_wdt.c @@ -56,8 +56,7 @@ MODULE_PARM_DESC(early_enable, static const struct watchdog_info s32g_wdt_info = { .identity = "s32g watchdog", - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | - WDIOC_GETTIMEOUT | WDIOC_GETTIMELEFT, + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; static struct s32g_wdt_device *wdd_to_s32g_wdt(struct watchdog_device *wdd) diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c index 19eb2ed2c7cb..02e09b9e396d 100644 --- a/drivers/watchdog/watchdog_pretimeout.c +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -167,6 +167,8 @@ void watchdog_unregister_governor(struct watchdog_governor *gov) } spin_lock_irq(&pretimeout_lock); + if (default_gov == gov) + default_gov = NULL; list_for_each_entry(p, &pretimeout_list, entry) if (p->wdd->gov == gov) p->wdd->gov = default_gov; diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 23c3eeb58dc1..1be632c742bd 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -3054,7 +3054,10 @@ void btrfs_backref_free_node(struct btrfs_backref_cache *cache, if (node) { ASSERT(list_empty(&node->list)); ASSERT(list_empty(&node->lower)); - ASSERT(node->eb == NULL, "node->eb->start=%llu", node->eb->start); + ASSERT(node->eb == NULL, "node->eb->start=%llu level=%d owner=%llu", + node->eb ? node->eb->start : 0, + node->eb ? btrfs_header_level(node->eb) : 0, + node->eb ? btrfs_header_owner(node->eb) : 0); cache->nr_nodes--; btrfs_put_root(node->root); kfree(node); diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index d5d81f9546c3..7fdc6c3fd066 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -476,6 +476,8 @@ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode) if (inode->flags & BTRFS_INODE_NODATACOW || inode->flags & BTRFS_INODE_NODATASUM) return false; + if (btrfs_is_data_reloc_root(inode->root)) + return false; return true; } diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 7d604524e83c..de5785117a47 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2004,7 +2004,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, - -eb->len, + -(s64)eb->len, fs_info->dirty_metadata_batch); ret = true; } else { @@ -3774,7 +3774,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, return; buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY); - percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len, + percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len, fs_info->dirty_metadata_batch); for (int i = 0; i < num_extent_folios(eb); i++) { diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index fce9c5cc0122..6ad7b39ae358 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -866,13 +866,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, goto next; } - flags = em->flags; /* * In case we split the extent map, we want to preserve the * EXTENT_FLAG_LOGGING flag on our extent map, but we don't want * it on the new extent maps. */ - em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING); + flags = em->flags & ~EXTENT_FLAG_LOGGING; + em->flags &= ~EXTENT_FLAG_PINNED; modified = !list_empty(&em->list); /* diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 272598f6ae77..b446c3014b24 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8068,7 +8068,8 @@ static int btrfs_getattr(struct mnt_idmap *idmap, stat->result_mask |= STATX_SUBVOL; spin_lock(&BTRFS_I(inode)->lock); - delalloc_bytes = BTRFS_I(inode)->new_delalloc_bytes; + delalloc_bytes = S_ISREG(inode->i_mode) ? + BTRFS_I(inode)->new_delalloc_bytes : 0; inode_bytes = inode_get_bytes(inode); spin_unlock(&BTRFS_I(inode)->lock); stat->blocks = (ALIGN(inode_bytes, blocksize) + diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 81e87bc39828..68b33f365fda 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2048,6 +2048,7 @@ static int _btrfs_ioctl_get_subvol_info(struct inode *inode, ret = -ENOENT; goto out; } + ret = 0; } out: diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 0d63d117db59..fc5c14b5adad 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -588,6 +588,7 @@ static int __add_reloc_root(struct btrfs_root *root, struct reloc_control *rc) btrfs_err(fs_info, "Duplicate root found for start=%llu while inserting into relocation tree", node->bytenr); + kfree(node); return -EEXIST; } @@ -890,6 +891,13 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr, leaf = path->nodes[0]; fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); + if (unlikely(btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE)) { + btrfs_print_leaf(leaf); + btrfs_err(fs_info, + "unexpected inline file extent item for data reloc inode %llu key offset %llu", + btrfs_ino(BTRFS_I(reloc_inode)), bytenr); + return -EUCLEAN; + } /* * The cluster-boundary key searched above is always written by @@ -1518,6 +1526,17 @@ static int insert_dirty_subvol(struct btrfs_trans_handle *trans, return 0; } +static void clear_reloc_root(struct btrfs_root *root) +{ + root->reloc_root = NULL; + /* + * Need barrier to ensure clear_bit() only happens after + * root->reloc_root = NULL. Pairs with have_reloc_root(). + */ + smp_wmb(); + clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); +} + static int clean_dirty_subvols(struct reloc_control *rc) { struct btrfs_root *root; @@ -1532,13 +1551,7 @@ static int clean_dirty_subvols(struct reloc_control *rc) struct btrfs_root *reloc_root = root->reloc_root; list_del_init(&root->reloc_dirty_list); - root->reloc_root = NULL; - /* - * Need barrier to ensure clear_bit() only happens after - * root->reloc_root = NULL. Pairs with have_reloc_root. - */ - smp_wmb(); - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); + clear_reloc_root(root); if (reloc_root) { /* * btrfs_drop_snapshot drops our ref we hold for @@ -1914,21 +1927,39 @@ again: goto out; } ret = merge_reloc_root(rc, root); - btrfs_put_root(root); if (ret) { - if (list_empty(&reloc_root->root_list)) + /* + * Clear the reloc root since below we will call + * free_reloc_roots(), otherwise we leave + * root->reloc_root pointing to a freed reloc + * root and trigger a use-after-free during + * unmount or elsewhere. + */ + clear_reloc_root(root); + btrfs_put_root(root); + /* + * We are adding the reloc_root to the local + * reloc_roots list, so we add a ref for this + * list which will be dropped below by the call + * to free_reloc_roots(). + */ + if (list_empty(&reloc_root->root_list)) { list_add_tail(&reloc_root->root_list, &reloc_roots); + btrfs_grab_root(reloc_root); + } + /* Now drop the ref for root->reloc_root. */ + btrfs_put_root(reloc_root); goto out; } + btrfs_put_root(root); } else { if (!IS_ERR(root)) { if (root->reloc_root == reloc_root) { - root->reloc_root = NULL; + clear_reloc_root(root); + /* Drop the ref for root->reloc_root. */ btrfs_put_root(reloc_root); } - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, - &root->state); btrfs_put_root(root); } diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c index 56060acac2e9..2a9397be8116 100644 --- a/fs/btrfs/subpage.c +++ b/fs/btrfs/subpage.c @@ -359,6 +359,23 @@ void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info, folio_mark_dirty(folio); } +static void folio_clear_tags(struct folio *folio) +{ + struct address_space *mapping = folio_mapping(folio); + XA_STATE(xas, &mapping->i_pages, folio->index); + unsigned long flags; + + ASSERT(folio_test_locked(folio)); + ASSERT(mapping); + ASSERT(mapping_use_writeback_tags(mapping)); + + xas_lock_irqsave(&xas, flags); + xas_load(&xas); + xas_clear_mark(&xas, PAGECACHE_TAG_DIRTY); + xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE); + xas_unlock_irqrestore(&xas, flags); +} + /* * Extra clear_and_test function for subpage dirty bitmap. * @@ -403,7 +420,6 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info, unsigned int start_bit = subpage_calc_start_bit(fs_info, folio, writeback, start, len); unsigned long flags; - bool keep_write; spin_lock_irqsave(&bfs->lock, flags); bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits); @@ -413,10 +429,14 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info, * folio. Doing so can cause WB_SYNC_ALL writepages() to overlook it, * assume writeback is complete, and exit too early — violating sync * ordering guarantees. + * + * Instead we manually clear the DIRTY and TOWRITE tags after the folio + * is no longer dirty. */ - keep_write = folio_test_dirty(folio); if (!folio_test_writeback(folio)) - __folio_start_writeback(folio, keep_write); + __folio_start_writeback(folio, true); + if (!folio_test_dirty(folio)) + folio_clear_tags(folio); spin_unlock_irqrestore(&bfs->lock, flags); } diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 61bd6d92ff25..ecf33b66610c 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -790,6 +790,9 @@ static int write_folio_nounlock(struct folio *folio, ceph_wbc.truncate_size, true); if (IS_ERR(req)) { folio_redirty_for_writepage(wbc, folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(req); } @@ -809,6 +812,9 @@ static int write_folio_nounlock(struct folio *folio, folio_redirty_for_writepage(wbc, folio); folio_end_writeback(folio); ceph_osdc_put_request(req); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(bounce_page); } } @@ -847,6 +853,9 @@ static int write_folio_nounlock(struct folio *folio, ceph_vinop(inode), folio); folio_redirty_for_writepage(wbc, folio); folio_end_writeback(folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return err; } if (err == -EBLOCKLISTED) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 4b37d9ffdf7f..d7283fb54cec 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -3094,7 +3094,19 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, ret = -ERESTARTSYS; break; } - wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + + /* + * If a cap update is lost after + * mds_wanted was raised, waiting + * forever will never make progress. + * Retry the renew path periodically + * so we can resend synchronously. + */ + if (!wait_woken(&wait, TASK_INTERRUPTIBLE, + CEPH_GET_CAPS_WAIT_TIMEOUT)) { + ret = -EUCLEAN; + break; + } } remove_wait_queue(&ci->i_cap_wq, &wait); @@ -3128,7 +3140,8 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, continue; } if (ret == -EUCLEAN) { - /* session was killed, try renew caps */ + /* session was killed or a waited cap + * request needs a retry */ ret = ceph_renew_caps(inode, flags); if (ret == 0) continue; @@ -4375,6 +4388,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); + ceph_decode_need(&snaptrace, end, snaptrace_len, bad); p = snaptrace + snaptrace_len; if (msg_version >= 2) { diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 27ce9e55e947..ef9e92e362d3 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -546,11 +546,16 @@ more: pr_warn_client(cl, "%p %llx.%llx rde->offset 0x%llx ctx->pos 0x%llx\n", inode, ceph_vinop(inode), rde->offset, ctx->pos); + ceph_mdsc_put_request(dfi->last_readdir); + dfi->last_readdir = NULL; return -EIO; } - if (WARN_ON_ONCE(!rde->inode.in)) + if (WARN_ON_ONCE(!rde->inode.in)) { + ceph_mdsc_put_request(dfi->last_readdir); + dfi->last_readdir = NULL; return -EIO; + } ctx->pos = rde->offset; doutc(cl, "%p %llx.%llx (%d/%d) -> %llx '%.*s' %p\n", inode, diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 71161f2b2151..a4a2a4b6a027 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -314,7 +314,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode) } /* - * try renew caps after session gets killed. + * Retry cap acquisition after a stale session or a lost cap update. */ int ceph_renew_caps(struct inode *inode, int fmode) { @@ -322,14 +322,15 @@ int ceph_renew_caps(struct inode *inode, int fmode) struct ceph_client *cl = mdsc->fsc->client; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_request *req; - int err, flags, wanted; + int err, flags, wanted, issued; spin_lock(&ci->i_ceph_lock); __ceph_touch_fmode(ci, mdsc, fmode); wanted = __ceph_caps_file_wanted(ci); + issued = __ceph_caps_issued(ci, NULL); if (__ceph_is_any_real_caps(ci) && - (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap)) { - int issued = __ceph_caps_issued(ci, NULL); + (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap) && + (issued & wanted) == wanted) { spin_unlock(&ci->i_ceph_lock); doutc(cl, "%p %llx.%llx want %s issued %s updating mds_wanted\n", inode, ceph_vinop(inode), ceph_cap_string(wanted), diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index 15cde055f3da..de07f19b0caa 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -72,6 +72,9 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) struct ceph_ioctl_layout nl; int err; + if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + return -EACCES; + if (copy_from_user(&l, arg, sizeof(l))) return -EFAULT; @@ -142,6 +145,9 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg) int err; struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + return -EACCES; + /* copy and validate */ if (copy_from_user(&l, arg, sizeof(l))) return -EFAULT; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 853bf698b356..3c692ad02c85 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -6,6 +6,7 @@ #include <linux/slab.h> #include <linux/gfp.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/delay.h> #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -4015,6 +4016,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) struct ceph_mds_reply_head *head = msg->front.iov_base; struct ceph_mds_reply_info_parsed *rinfo; /* parsed reply info */ struct ceph_snap_realm *realm; + unsigned int nofs_flags; u64 tid; int err, result; int mds = session->s_mds; @@ -4158,6 +4160,14 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) /* insert trace into our cache */ mutex_lock(&req->r_fill_mutex); + + /* disable fs reclaim while we are using current->journal_info + * for our own purposes, or else shrinkers of other + * filesystems might dereference this pointer as a different + * type + */ + nofs_flags = memalloc_nofs_save(); + current->journal_info = req; err = ceph_fill_trace(mdsc->fsc->sb, req); if (err == 0) { @@ -4166,6 +4176,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) err = ceph_readdir_prepopulate(req, req->r_session); } current->journal_info = NULL; + memalloc_nofs_restore(nofs_flags); mutex_unlock(&req->r_fill_mutex); up_read(&mdsc->snap_rwsem); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 731d6ad04956..0ece4c9e3529 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -77,6 +77,7 @@ struct ceph_fs_client; struct ceph_cap; #define MDS_AUTH_UID_ANY -1 +#define CEPH_GET_CAPS_WAIT_TIMEOUT (5 * HZ) #define CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC 60 #define CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES 5 #define CEPH_CAP_FLUSH_MAX_DUMP_ITERS 5 diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 47324062fee5..66b9c9150fed 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -22,22 +22,14 @@ #include "fscrypt_private.h" -static struct block_device **fscrypt_get_devices(struct super_block *sb, - unsigned int *num_devs) +static unsigned int +fscrypt_get_devices(struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]) { - struct block_device **devs; - - if (sb->s_cop->get_devices) { - devs = sb->s_cop->get_devices(sb, num_devs); - if (devs) - return devs; - } - devs = kmalloc_obj(*devs); - if (!devs) - return ERR_PTR(-ENOMEM); + if (sb->s_cop->get_devices) + return sb->s_cop->get_devices(sb, devs); devs[0] = sb->s_bdev; - *num_devs = 1; - return devs; + return 1; } static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_inode_info *ci) @@ -96,7 +88,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; struct blk_crypto_config crypto_cfg; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; @@ -135,20 +127,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, crypto_cfg.key_type = is_hw_wrapped_key ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; - devs = fscrypt_get_devices(sb, &num_devs); - if (IS_ERR(devs)) - return PTR_ERR(devs); - + num_devs = fscrypt_get_devices(sb, devs); for (i = 0; i < num_devs; i++) { if (!blk_crypto_config_supported(devs[i], &crypto_cfg)) - goto out_free_devs; + return 0; } fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg); ci->ci_inlinecrypt = true; -out_free_devs: - kfree(devs); return 0; } @@ -164,7 +151,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, enum blk_crypto_key_type key_type = is_hw_wrapped ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; struct blk_crypto_key *blk_key; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; int err; @@ -182,17 +169,12 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, } /* Start using blk-crypto on all the filesystem's block devices. */ - devs = fscrypt_get_devices(sb, &num_devs); - if (IS_ERR(devs)) { - err = PTR_ERR(devs); - goto fail; - } + num_devs = fscrypt_get_devices(sb, devs); for (i = 0; i < num_devs; i++) { err = blk_crypto_start_using_key(devs[i], blk_key); if (err) break; } - kfree(devs); if (err) { fscrypt_err(inode, "error %d starting to use blk-crypto", err); goto fail; @@ -210,20 +192,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key) { struct blk_crypto_key *blk_key = prep_key->blk_key; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; if (!blk_key) return; - /* Evict the key from all the filesystem's block devices. */ - devs = fscrypt_get_devices(sb, &num_devs); - if (!IS_ERR(devs)) { - for (i = 0; i < num_devs; i++) - blk_crypto_evict_key(devs[i], blk_key); - kfree(devs); - } + /* + * Evict the key from all the filesystem's block devices. + * This *must* be done before the key is freed. + */ + num_devs = fscrypt_get_devices(sb, devs); + for (i = 0; i < num_devs; i++) + blk_crypto_evict_key(devs[i], blk_key); + kfree_sensitive(blk_key); } diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index e6e527c73f16..7e3a58dc4b56 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -147,13 +147,19 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert, if (memcmp(ci->ci_policy.v1.master_key_descriptor, dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0) continue; + /* The sb is used at eviction time, so it must be the same. */ + if (ci->ci_inode->i_sb != dk->dk_sb) + continue; if (ci->ci_mode != dk->dk_mode) continue; if (!fscrypt_is_key_prepared(&dk->dk_key, ci)) continue; if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize)) continue; - /* using existing tfm with same (descriptor, mode, raw_key) */ + /* + * Use an existing prepared key with the same (descriptor, sb, + * mode, inlinecrypt, raw_key) combination. + */ refcount_inc(&dk->dk_refcount); spin_unlock(&fscrypt_direct_keys_lock); free_direct_key(to_insert); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2b8d96411156..9760e4efeffe 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3749,24 +3749,27 @@ static bool f2fs_has_stable_inodes(struct super_block *sb) return true; } -static struct block_device **f2fs_get_devices(struct super_block *sb, - unsigned int *num_devs) +static unsigned int +f2fs_get_devices(struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]) { struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct block_device **devs; + int ndevs; int i; - if (!f2fs_is_multi_device(sbi)) - return NULL; + static_assert(MAX_DEVICES <= FSCRYPT_MAX_DEVICES); - devs = kmalloc_objs(*devs, sbi->s_ndevs); - if (!devs) - return ERR_PTR(-ENOMEM); + if (!f2fs_is_multi_device(sbi)) { + devs[0] = sb->s_bdev; + return 1; + } + ndevs = sbi->s_ndevs; + if (WARN_ON_ONCE(ndevs > FSCRYPT_MAX_DEVICES)) + ndevs = FSCRYPT_MAX_DEVICES; - for (i = 0; i < sbi->s_ndevs; i++) + for (i = 0; i < ndevs; i++) devs[i] = FDEV(i).bdev; - *num_devs = sbi->s_ndevs; - return devs; + return ndevs; } static const struct fscrypt_operations f2fs_cryptops = { diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 99ed9228a677..2772675bd35a 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -249,13 +249,13 @@ static inline unsigned char fat_checksum(const __u8 *name) return s; } -static inline sector_t fat_clus_to_blknr(struct msdos_sb_info *sbi, int clus) +static inline sector_t fat_clus_to_blknr(const struct msdos_sb_info *sbi, int clus) { return ((sector_t)clus - FAT_START_ENT) * sbi->sec_per_clus + sbi->data_start; } -static inline void fat_get_blknr_offset(struct msdos_sb_info *sbi, +static inline void fat_get_blknr_offset(const struct msdos_sb_info *sbi, loff_t i_pos, sector_t *blknr, int *offset) { *blknr = i_pos >> sbi->dir_per_block_bits; diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c index 4eeed9dca549..9583ce66dca3 100644 --- a/fs/fat/fat_test.c +++ b/fs/fat/fat_test.c @@ -22,7 +22,7 @@ static void fat_checksum_test(struct kunit *test) static void fat_clus_to_blknr_test(struct kunit *test) { - struct msdos_sb_info sbi = { + static const struct msdos_sb_info sbi = { .sec_per_clus = 4, .data_start = 100, }; @@ -34,7 +34,7 @@ static void fat_clus_to_blknr_test(struct kunit *test) static void fat_get_blknr_offset_test(struct kunit *test) { - struct msdos_sb_info sbi = { + static const struct msdos_sb_info sbi = { .dir_per_block = 16, .dir_per_block_bits = 4, }; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 4836887f11ef..386a881b520f 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -901,7 +901,8 @@ static __be32 nlmsvc_proc_granted_msg(struct svc_rqst *rqstp) if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) return rpc_garbage_args; - host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + host = nlmsvc_lookup_host(rqstp, argp->xdrgen.alock.caller_name.data, + argp->xdrgen.alock.caller_name.len); if (!host) return rpc_system_err; diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 66b9104e7ca2..1788d93a2522 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -440,6 +440,7 @@ cifs_alloc_inode(struct super_block *sb) return NULL; cifs_inode->cifsAttrs = ATTR_ARCHIVE; /* default */ cifs_inode->time = 0; + cifs_inode->time_last_write = 0; /* * Until the file is open and we have gotten oplock info back from the * server, can not assume caching of file data or metadata. diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 08e94633a9c1..79e4e84f8985 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1566,6 +1566,7 @@ struct cifsInodeInfo { spinlock_t writers_lock; unsigned int writers; /* Number of writers on this inode */ unsigned long time; /* jiffies of last update of inode */ + unsigned long time_last_write; /* jiffies of last writable close or truncate */ u64 uniqueid; /* server inode number */ u64 createtime; /* creation time on server */ __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */ diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 968740e7c9c3..b279a44be729 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -1423,11 +1423,21 @@ void smb2_deferred_work_close(struct work_struct *work) { struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, deferred.work); + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + spin_lock(&cinode->deferred_lock); cifs_del_deferred_close(cfile); cfile->deferred_close_scheduled = false; - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + spin_unlock(&cinode->deferred_lock); + /* + * Refresh time_last_write immediately before the actual server close + * so the protection window is anchored to the real close time, not + * the earlier userspace close time stored by cifs_close(). + */ + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cinode->time_last_write, jiffies); + } _cifsFileInfo_put(cfile, true, false); } @@ -1457,6 +1467,10 @@ int cifs_close(struct inode *inode, struct file *file) if (file->private_data != NULL) { cfile = file->private_data; file->private_data = NULL; + if (file->f_mode & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cinode->time_last_write, jiffies); + } dclose = kmalloc_obj(struct cifs_deferred_close); if ((cfile->status_file_deleted == false) && (smb2_can_defer_close(inode, dclose))) { @@ -3225,13 +3239,26 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode) bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, bool from_readdir) { + struct cifs_sb_info *cifs_sb; + unsigned long tlw; + if (!cifsInode) return true; + cifs_sb = CIFS_SB(cifsInode); + if (is_inode_writable(cifsInode) || ((cifsInode->oplock & CIFS_CACHE_RW_FLG) != 0 && from_readdir)) { /* This inode is open for write at least once */ - struct cifs_sb_info *cifs_sb = CIFS_SB(cifsInode); + + /* + * Readdir data is unreliable when we have writable handles or + * an exclusive lease -- never allow it to change i_size, even + * on direct-IO mounts where the server's directory metadata + * can still lag behind the actual file state. + */ + if (from_readdir) + return false; if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_DIRECT_IO) { /* since no page cache to corrupt on directio @@ -3243,8 +3270,40 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, return true; return false; - } else - return true; + } + + /* + * No writable handles open. Check whether we are within the attribute + * cache validity window of a recent local modification. + * + * For the close() path: cifs_close() calls smp_store_release() on + * time_last_write before _cifsFileInfo_put() removes the handle under + * open_file_lock. That spin_unlock() is a store-release that pairs + * with the spin_lock() (load-acquire) in is_inode_writable() above, + * so if is_inode_writable() returned false the smp_load_acquire() + * below is guaranteed to observe any time_last_write update from a + * concurrent close(). + * + * For the setattr/truncate paths: those callers use smp_store_release() + * directly; the smp_load_acquire() below pairs with that store. There + * is no shared lock between setattr and readdir, so this relies on + * acquire-release semantics alone. The store propagation latency on + * weakly-ordered architectures (nanoseconds) is negligible relative to + * the acregmax window (seconds) and the readdir RPC round-trip + * (milliseconds), making this a sound design choice in practice. + * + * time_last_write == 0 means the inode has never been written locally; + * skip the window check to avoid false positives near boot time when + * jiffies is still close to INITIAL_JIFFIES on 32-bit systems. + */ + if (from_readdir) { + /* Pairs with smp_store_release() at close and truncate sites. */ + tlw = smp_load_acquire(&cifsInode->time_last_write); + if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax)) + return false; + } + + return true; } void cifs_oplock_break(struct work_struct *work) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index deed04dd9b91..b2806371bfde 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -237,6 +237,8 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) { i_size_write(inode, fattr->cf_eof); inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes); + } else if (from_readdir && i_size_read(inode) != fattr->cf_eof) { + cifs_i->time = 0; } if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) { @@ -3277,6 +3279,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); @@ -3478,6 +3482,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index e4bac2a0b85d..6edebc0807ea 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -497,7 +497,7 @@ cifs_del_deferred_close(struct cifsFileInfo *cfile) void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) { - struct cifsFileInfo *cfile = NULL; + struct cifsFileInfo *cfile = NULL, *failed_cfile = NULL; struct file_list *tmp_list, *tmp_next_list; LIST_HEAD(file_head); @@ -514,8 +514,10 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC); - if (tmp_list == NULL) + if (tmp_list == NULL) { + failed_cfile = cfile; break; + } tmp_list->cfile = cfile; list_add_tail(&tmp_list->list, &file_head); } @@ -523,8 +525,24 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) } spin_unlock(&cifs_inode->open_file_lock); + if (failed_cfile) { + if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(failed_cfile, false, false); + } + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, false, false); + struct cifsFileInfo *cfile = tmp_list->cfile; + + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(cfile, false, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -533,7 +551,7 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) void cifs_close_all_deferred_files(struct cifs_tcon *tcon) { - struct cifsFileInfo *cfile; + struct cifsFileInfo *cfile, *failed_cfile = NULL; struct file_list *tmp_list, *tmp_next_list; LIST_HEAD(file_head); @@ -547,8 +565,10 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC); - if (tmp_list == NULL) + if (tmp_list == NULL) { + failed_cfile = cfile; break; + } tmp_list->cfile = cfile; list_add_tail(&tmp_list->list, &file_head); } @@ -556,8 +576,24 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) } spin_unlock(&tcon->open_file_lock); + if (failed_cfile) { + if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(failed_cfile, true, false); + } + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); + struct cifsFileInfo *cfile = tmp_list->cfile; + + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -604,7 +640,7 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, struct dentry *dentry) { struct file_list *tmp_list, *tmp_next_list; - struct cifsFileInfo *cfile; + struct cifsFileInfo *cfile, *failed_cfile = NULL; LIST_HEAD(file_head); spin_lock(&tcon->open_file_lock); @@ -617,16 +653,34 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC); - if (tmp_list == NULL) + if (tmp_list == NULL) { + failed_cfile = cfile; break; + } tmp_list->cfile = cfile; list_add_tail(&tmp_list->list, &file_head); } } spin_unlock(&tcon->open_file_lock); + if (failed_cfile) { + if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(failed_cfile, true, false); + } + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); + struct cifsFileInfo *cfile = tmp_list->cfile; + + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -682,6 +736,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, int i, rc = 0; char *data_end; struct dfs_referral_level_3 *ref; + unsigned int path_consumed; + size_t search_name_len; if (rsp_size < sizeof(*rsp)) { cifs_dbg(VFS | ONCE, @@ -728,6 +784,7 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, rc = -ENOMEM; goto parse_DFS_referrals_exit; } + search_name_len = strlen(searchName); /* collect necessary data from referrals */ for (i = 0; i < *num_of_nodes; i++) { @@ -736,21 +793,34 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, struct dfs_info3_param *node = (*target_nodes)+i; node->flags = le32_to_cpu(rsp->DFSFlags); + path_consumed = le16_to_cpu(rsp->PathConsumed); if (is_unicode) { - __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, - GFP_KERNEL); - if (tmp == NULL) { + size_t search_name_utf16_len = search_name_len * 2 + 2; + __le16 *tmp; + + if (path_consumed > search_name_utf16_len) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + tmp = kmalloc(search_name_utf16_len, GFP_KERNEL); + if (!tmp) { rc = -ENOMEM; goto parse_DFS_referrals_exit; } - cifsConvertToUTF16((__le16 *) tmp, searchName, + cifsConvertToUTF16((__le16 *)tmp, searchName, PATH_MAX, nls_codepage, remap); - node->path_consumed = cifs_utf16_bytes(tmp, - le16_to_cpu(rsp->PathConsumed), - nls_codepage); + node->path_consumed = cifs_utf16_bytes(tmp, path_consumed, + nls_codepage); kfree(tmp); - } else - node->path_consumed = le16_to_cpu(rsp->PathConsumed); + } else { + if (path_consumed > search_name_len) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + node->path_consumed = path_consumed; + } node->server_type = le16_to_cpu(ref->ServerType); node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index ee5996e6d7d8..32a75afca8f5 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -952,7 +952,7 @@ static bool cifs_dir_emit(struct dir_context *ctx, static int cifs_filldir(char *find_entry, struct file *file, struct dir_context *ctx, char *scratch_buf, unsigned int max_len, - struct cached_fid *cfid) + char *end_of_smb, struct cached_fid *cfid) { struct cifsFileInfo *file_info = file->private_data; struct super_block *sb = file_inode(file)->i_sb; @@ -974,6 +974,11 @@ static int cifs_filldir(char *find_entry, struct file *file, return -EINVAL; } + if (de.name + de.namelen > end_of_smb) { + cifs_dbg(VFS, "search entry name extends past end of SMB\n"); + return -EINVAL; + } + /* skip . and .. since we added them first */ if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode)) return 0; @@ -1194,7 +1199,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) */ *tmp_buf = 0; rc = cifs_filldir(current_entry, file, ctx, - tmp_buf, max_len, cfid); + tmp_buf, max_len, end_of_smb, cfid); if (rc) { if (rc > 0) rc = 0; diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index 5ef919bce52d..f35b6488d810 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -30,6 +30,19 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) u8 *end = (u8 *)err + iov->iov_len; u32 len; + /* + * Per [MS-SMB2] section 2.2.2, a STATUS_STOPPED_ON_SYMLINK response has to + * carry a Symbolic Link Error Response, so ByteCount cannot be zero. Some + * servers (e.g. the macOS built-in SMB server) violate this and return an + * empty error response, with both ErrorContextCount and ByteCount set to + * zero, i.e. without the symlink target. Detect this and return -ENODATA + * so that callers can tell "server did not send the target" apart from a + * malformed response, and retrieve the target with FSCTL_GET_REPARSE_POINT + * instead. + */ + if (!err->ErrorContextCount && !le32_to_cpu(err->ByteCount)) + return ERR_PTR(-ENODATA); + if (err->ErrorContextCount) { struct smb2_error_context_rsp *p; @@ -199,6 +212,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, oparms->path, &data->symlink_target); + /* + * If smb2_parse_symlink_response returned -ENODATA then the + * symlink_target was not sent. Treat this as if the SMB2_open() + * failed with STATUS_IO_REPARSE_TAG_NOT_HANDLED status, which is + * indicated by the -EIO errno. + */ + if (rc == -ENODATA) + rc = -EIO; if (!rc) { memset(&data->fi, 0, sizeof(data->fi)); oparms->create_options |= OPEN_REPARSE_POINT; diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 6c9c229b91f6..213bc298cdf2 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -792,9 +792,19 @@ static int parse_create_response(struct cifs_open_info_data *data, rc = smb2_parse_symlink_response(cifs_sb, iov, full_path, &data->symlink_target); - if (rc) + if (rc != 0 && rc != -ENODATA) return rc; - tag = IO_REPARSE_TAG_SYMLINK; + /* + * -ENODATA means that the response was parsed but did not contain + * the symlink target at all (see symlink_data()). Treat it like + * STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not contain it + * either: leave the tag unset and clear rc, so that the caller + * retrieves the target with SMB2_OP_GET_REPARSE. + */ + if (rc == -ENODATA) + rc = 0; + else + tag = IO_REPARSE_TAG_SYMLINK; reparse_point = true; break; case STATUS_SUCCESS: @@ -987,7 +997,14 @@ int smb2_query_path_info(const unsigned int xid, rc = -EOPNOTSUPP; } - if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) { + /* + * If the symlink was already parsed in create response then it is needed to fix + * its type now (after the second call with OPEN_REPARSE_POINT which filled the + * data->fi.Attributes). If the symlink was not parsed in create response then + * the data->symlink_target was not filled yet and then the type will be fixed + * later after data->symlink_target is filled. + */ + if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) { bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY; rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb); } diff --git a/include/drm/ttm/ttm_backup.h b/include/drm/ttm/ttm_backup.h index 29b9c855af77..49efa713e87c 100644 --- a/include/drm/ttm/ttm_backup.h +++ b/include/drm/ttm/ttm_backup.h @@ -13,9 +13,8 @@ * ttm_backup_handle_to_page_ptr() - Convert handle to struct page pointer * @handle: The handle to convert. * - * Converts an opaque handle received from the - * ttm_backup_backup_page() function to an (invalid) - * struct page pointer suitable for a struct page array. + * Converts an opaque handle received from a ttm_backup_backup_*() + * function to an (invalid) struct page pointer suitable for a struct page array. * * Return: An (invalid) struct page pointer. */ @@ -59,9 +58,10 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, pgoff_t handle, bool intr, gfp_t additional_gfp); s64 -ttm_backup_backup_page(struct file *backup, struct page *page, - bool writeback, pgoff_t idx, gfp_t page_gfp, - gfp_t alloc_gfp); +ttm_backup_backup_folio(struct file *backup, struct folio *folio, + unsigned int order, bool writeback, pgoff_t idx, + gfp_t folio_gfp, gfp_t alloc_gfp, + pgoff_t *nr_pages_backed); void ttm_backup_fini(struct file *backup); diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h index 068ba2e77c5d..6ed9f82e639f 100644 --- a/include/linux/alloc_tag.h +++ b/include/linux/alloc_tag.h @@ -134,6 +134,8 @@ static inline bool mem_alloc_profiling_enabled(void) &mem_alloc_profiling_key); } +bool mem_alloc_profiling_permanently_disabled(void); + static inline struct alloc_tag_counters alloc_tag_read(struct alloc_tag *tag) { struct alloc_tag_counters v = { 0, 0 }; @@ -239,6 +241,7 @@ static inline bool alloc_tag_is_inaccurate(struct alloc_tag *tag) #define DEFINE_ALLOC_TAG(_alloc_tag) static inline bool mem_alloc_profiling_enabled(void) { return false; } +static inline bool mem_alloc_profiling_permanently_disabled(void) { return true; } static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, size_t bytes) {} static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) {} diff --git a/include/linux/bnge/hsi.h b/include/linux/bnge/hsi.h index 8ea13d5407ee..1f7bd96415a5 100644 --- a/include/linux/bnge/hsi.h +++ b/include/linux/bnge/hsi.h @@ -8317,8 +8317,7 @@ struct hwrm_ring_alloc_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - __le16 ring_id; - __le16 logical_ring_id; + __le32 ring_id; u8 push_buffer_index; #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL @@ -8345,10 +8344,10 @@ struct hwrm_ring_free_input { u8 flags; #define RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID 0x1UL #define RING_FREE_REQ_FLAGS_LAST RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID - __le16 ring_id; + __le16 unused_1; __le32 prod_idx; __le32 opaque; - __le32 unused_1; + __le32 ring_id; }; /* hwrm_ring_free_output (size:128b/16B) */ diff --git a/include/linux/damon.h b/include/linux/damon.h index 02ac34537df9..cfbbf8ba28f6 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * DAMON api - * - * Author: SeongJae Park <sj@kernel.org> */ #ifndef _DAMON_H_ diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 54712ec61ffb..f6b235cd72b4 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -57,6 +57,9 @@ struct fscrypt_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +/* Maximum supported number of block devices per filesystem */ +#define FSCRYPT_MAX_DEVICES 8 + #ifdef CONFIG_FS_ENCRYPTION /* Crypto operations for filesystems */ @@ -181,21 +184,20 @@ struct fscrypt_operations { bool (*has_stable_inodes)(struct super_block *sb); /* - * Return an array of pointers to the block devices to which the - * filesystem may write encrypted file contents, NULL if the filesystem - * only has a single such block device, or an ERR_PTR() on error. + * Retrieve the list of block devices to which the filesystem may write + * encrypted file contents. * - * On successful non-NULL return, *num_devs is set to the number of - * devices in the returned array. The caller must free the returned - * array using kfree(). + * This writes the block_device pointers to @devs and returns the count + * (between 1 and FSCRYPT_MAX_DEVICES inclusively). * * If the filesystem can use multiple block devices (other than block * devices that aren't used for encrypted file contents, such as * external journal devices), and wants to support inline encryption, * then it must implement this function. Otherwise it's not needed. */ - struct block_device **(*get_devices)(struct super_block *sb, - unsigned int *num_devs); + unsigned int (*get_devices)( + struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]); }; int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name, diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 695c86ee6d7a..8f18a508320d 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -12215,18 +12215,7 @@ struct mlx5_ifc_mcia_reg_bits { u8 reserved_at_60[0x20]; - u8 dword_0[0x20]; - u8 dword_1[0x20]; - u8 dword_2[0x20]; - u8 dword_3[0x20]; - u8 dword_4[0x20]; - u8 dword_5[0x20]; - u8 dword_6[0x20]; - u8 dword_7[0x20]; - u8 dword_8[0x20]; - u8 dword_9[0x20]; - u8 dword_10[0x20]; - u8 dword_11[0x20]; + u8 dwords[0x400]; }; struct mlx5_ifc_dcbx_param_bits { diff --git a/include/linux/page_reporting.h b/include/linux/page_reporting.h index 9d4ca5c218a0..272b1274efdc 100644 --- a/include/linux/page_reporting.h +++ b/include/linux/page_reporting.h @@ -5,7 +5,6 @@ #include <linux/mmzone.h> #include <linux/scatterlist.h> -/* This value should always be a power of 2, see page_reporting_cycle() */ #define PAGE_REPORTING_CAPACITY 32 #define PAGE_REPORTING_ORDER_UNSPECIFIED -1 @@ -22,6 +21,9 @@ struct page_reporting_dev_info { /* Minimal order of page reporting */ unsigned int order; + + /* Max pages per report batch; 0 (default) means PAGE_REPORTING_CAPACITY */ + unsigned int capacity; }; /* Tear-down and bring-up for page reporting devices */ diff --git a/include/linux/slab.h b/include/linux/slab.h index 51f03f18c9a7..32c9f8ed7ae2 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -58,10 +58,13 @@ enum _slab_flag_bits { #endif _SLAB_OBJECT_POISON, _SLAB_CMPXCHG_DOUBLE, +#ifdef CONFIG_SLAB_OBJ_EXT _SLAB_NO_OBJ_EXT, -#if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT) +#ifdef CONFIG_64BIT _SLAB_OBJ_EXT_IN_OBJ, #endif +#endif + _SLAB_NO_SHEAVES, _SLAB_FLAGS_LAST_BIT }; @@ -239,8 +242,14 @@ enum _slab_flag_bits { #endif #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT /* Objects are short-lived */ -/* Slab created using create_boot_cache */ +/* Slab caches without obj_exts array */ +#ifdef CONFIG_SLAB_OBJ_EXT #define SLAB_NO_OBJ_EXT __SLAB_FLAG_BIT(_SLAB_NO_OBJ_EXT) +#else +#define SLAB_NO_OBJ_EXT __SLAB_FLAG_UNUSED +#endif + +#define SLAB_NO_SHEAVES __SLAB_FLAG_BIT(_SLAB_NO_SHEAVES) #if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT) #define SLAB_OBJ_EXT_IN_OBJ __SLAB_FLAG_BIT(_SLAB_OBJ_EXT_IN_OBJ) @@ -505,8 +514,12 @@ typedef struct { unsigned long v; } kmalloc_token_t; extern unsigned long random_kmalloc_seed; #define __kmalloc_token(...) ((kmalloc_token_t){ .v = _CODE_LOCATION_ }) #elif defined(CONFIG_KMALLOC_PARTITION_TYPED) +#ifdef __CHECKER__ +#define __kmalloc_token(...) ((kmalloc_token_t){ .v = 0 }) +#else /* !__CHECKER__ */ #define __kmalloc_token(...) ((kmalloc_token_t){ .v = __builtin_infer_alloc_token(__VA_ARGS__) }) -#endif +#endif /* __CHECKER__ */ +#endif /* CONFIG_KMALLOC_PARTITION_TYPED */ #define DECL_TOKEN_PARAM(_token) , kmalloc_token_t (_token) #define _PASS_TOKEN_PARAM(_token) , (_token) #define PASS_TOKEN_PARAM(_token) (_token) @@ -700,6 +713,9 @@ enum kmalloc_cache_type { #ifndef CONFIG_MEMCG KMALLOC_CGROUP = KMALLOC_NORMAL, #endif +#ifndef CONFIG_SLAB_OBJ_EXT + KMALLOC_NO_OBJ_EXT = KMALLOC_NORMAL, +#endif KMALLOC_PARTITION_START = KMALLOC_NORMAL, KMALLOC_PARTITION_END = KMALLOC_PARTITION_START + KMALLOC_PARTITION_CACHES_NR, #ifdef CONFIG_SLUB_TINY @@ -713,6 +729,9 @@ enum kmalloc_cache_type { #ifdef CONFIG_MEMCG KMALLOC_CGROUP, #endif +#ifdef CONFIG_SLAB_OBJ_EXT + KMALLOC_NO_OBJ_EXT, +#endif NR_KMALLOC_TYPES }; diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index feb6b3ae5e69..102c278e3584 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -226,6 +226,9 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, u8 bit_rate, u8 data_bits, u8 stop_bits, u8 parity, u8 flow_ctrl_settings, u8 xon_char, u8 xoff_char, u16 param_mask); +int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits, + u8 stop_bits, u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask); /* ---- RFCOMM DLCs (channels) ---- */ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 3bd08d7f39c1..5f5cb36439fe 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -156,8 +156,20 @@ static inline int tcf_classify(struct sk_buff *skb, { return TC_ACT_UNSPEC; } - #endif +static inline int tcf_classify_qdisc(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res, bool compat_mode) +{ + int ret = tcf_classify(skb, NULL, tp, res, compat_mode); + + /* TC_ACT_REDIRECT from qdisc filter chains is not supported. + * Use BPF via tcx or mirred redirect instead. + */ + if (unlikely(ret == TC_ACT_REDIRECT)) + ret = TC_ACT_SHOT; + return ret; +} static inline unsigned long __cls_set_class(unsigned long *clp, unsigned long cl) diff --git a/include/net/tcp.h b/include/net/tcp.h index 6d376ea4d1c0..2c5b889530b5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1974,6 +1974,8 @@ static inline void tcp_fast_path_check(struct sock *sk) bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb, int mib_idx, u32 *last_oow_ack_time); +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req); static inline void tcp_mib_init(struct net *net) { diff --git a/include/trace/events/memory-failure.h b/include/trace/events/memory-failure.h index aa57cc8f896b..7a8ee5d1a44e 100644 --- a/include/trace/events/memory-failure.h +++ b/include/trace/events/memory-failure.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM -#define TRACE_SYSTEM memory_failure +/* + * For historical versions, memory_failure_event is in ras subsystem, + * some user programs depend on it. + */ +#define TRACE_SYSTEM ras #define TRACE_INCLUDE_FILE memory-failure #if !defined(_TRACE_MEMORY_FAILURE_H) || defined(TRACE_HEADER_MULTI_READ) diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 9b576603b3f1..0a13baf3d8d1 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -598,7 +598,7 @@ struct btrfs_ioctl_search_args_v2 { __u64 buf_size; /* in - size of buffer * out - on EOVERFLOW: needed size * to store item */ - __u64 buf[]; /* out - found items */ + __u8 buf[]; /* out - found items */ }; /* With a @src_length of zero, the range from @src_offset->EOF is cloned! */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 99444eae917e..7aa47342dc65 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9189,7 +9189,8 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, return ret; if (check_mem_reg(env, reg, argno, arg->mem_size)) return -EINVAL; - if (!(arg->arg_type & PTR_MAYBE_NULL) && (reg->type & PTR_MAYBE_NULL)) { + if (!(arg->arg_type & PTR_MAYBE_NULL) && + (type_may_be_null(reg->type) || bpf_register_is_null(reg))) { bpf_log(log, "%s is expected to be non-NULL\n", reg_arg_name(env, argno)); return -EINVAL; diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index b79b2a488974..f38b5b18f3f8 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -378,7 +378,7 @@ static const struct luo_ioctl_op luo_session_ioctl_ops[] = { IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd, struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_INCOMING), IOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name, - struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_ALL), + struct liveupdate_session_get_name, name, LUO_IOCTL_ALL), }; static bool luo_ioctl_type_valid(struct luo_session *session, diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index 50518b071414..bcd97cb24ac9 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -172,7 +172,8 @@ static bool eprobe_dyn_event_match(const char *system, const char *event, if (!slash) return false; - if (strncmp(ep->event_system, argv[0], slash - argv[0])) + if (strncmp(ep->event_system, argv[0], slash - argv[0]) || + ep->event_system[slash - argv[0]] != '\0') return false; if (strcmp(ep->event_name, slash + 1)) return false; diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index d17cfee77d9c..506e6037e163 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -188,7 +188,7 @@ void __trace_probe_log_err(int offset, int err_type) lockdep_assert_held(&dyn_event_ops_mutex); - if (!trace_probe_log.argv) + if (!trace_probe_log.argv || !trace_probe_log.argc) return; /* Recalculate the length and allocate buffer */ @@ -2013,7 +2013,7 @@ int traceprobe_update_arg(struct probe_arg *arg) } /* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) +#define LEN_OR_ZERO (len > pos ? len - pos : 0) static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, enum probe_print_type ptype) { @@ -2338,16 +2338,17 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b) bool trace_probe_match_command_args(struct trace_probe *tp, int argc, const char **argv) { - char buf[MAX_ARGSTR_LEN + 1]; int i; if (tp->nr_args < argc) return false; for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "%s=%s", - tp->args[i].name, tp->args[i].comm); - if (strcmp(buf, argv[i])) + int len = strlen(tp->args[i].name); + + if (strncmp(argv[i], tp->args[i].name, len) || + argv[i][len] != '=' || + strcmp(argv[i] + len + 1, tp->args[i].comm)) return false; } return true; diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index d9be1cf5187d..e5b218176c5a 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -26,6 +26,15 @@ static bool mem_profiling_support = true; static bool mem_profiling_support; #endif +/* + * Memory allocation profiling is permanently disabled and cannot be enabled. + * Must be called after setup_early_mem_profiling(). + */ +bool mem_alloc_profiling_permanently_disabled(void) +{ + return !mem_profiling_support; +} + static struct codetag_type *alloc_tag_cttype; #ifdef CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 40cfb38ac919..d459bef245f4 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -878,6 +878,7 @@ int rhashtable_walk_start_check(struct rhashtable_iter *iter) iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht); iter->slot = 0; iter->skip = 0; + iter->p = NULL; return -EAGAIN; } diff --git a/mm/damon/core.c b/mm/damon/core.c index 7e4b9affc5b0..cff932b3317d 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Data Access Monitor - * - * Author: SeongJae Park <sj@kernel.org> */ #define pr_fmt(fmt) "damon: " fmt @@ -356,8 +354,21 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, { struct damon_region *r, *next; unsigned int i; + unsigned long last_end; int err; + for (i = 0; i < nr_ranges; i++) { + unsigned long start, end; + + start = ALIGN_DOWN(ranges[i].start, min_region_sz); + end = ALIGN(ranges[i].end, min_region_sz); + if (start >= end) + return -EINVAL; + if (i > 0 && last_end > start) + return -EINVAL; + last_end = end; + } + /* Remove regions which are not in the new ranges */ damon_for_each_region_safe(r, next, t) { for (i = 0; i < nr_ranges; i++) { diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 8298c6001fd0..32f41491b726 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON-based LRU-lists Sorting - * - * Author: SeongJae Park <sj@kernel.org> */ #define pr_fmt(fmt) "damon-lru-sort: " fmt diff --git a/mm/damon/modules-common.c b/mm/damon/modules-common.c index 86d58f8c4f63..f87fa46a95a0 100644 --- a/mm/damon/modules-common.c +++ b/mm/damon/modules-common.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Common Code for DAMON Modules - * - * Author: SeongJae Park <sj@kernel.org> */ #include <linux/damon.h> diff --git a/mm/damon/modules-common.h b/mm/damon/modules-common.h index f103ad556368..6fd45490e45b 100644 --- a/mm/damon/modules-common.h +++ b/mm/damon/modules-common.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Common Code for DAMON Modules - * - * Author: SeongJae Park <sj@kernel.org> */ #include <linux/moduleparam.h> diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index d1842e2b00ef..6bdd1cfd3863 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Common Code for Data Access Monitoring - * - * Author: SeongJae Park <sj@kernel.org> */ #include <linux/migrate.h> diff --git a/mm/damon/ops-common.h b/mm/damon/ops-common.h index 5efa5b5970de..38d295488fa1 100644 --- a/mm/damon/ops-common.h +++ b/mm/damon/ops-common.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Common Code for Data Access Monitoring - * - * Author: SeongJae Park <sj@kernel.org> */ #include <linux/damon.h> diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index d0598f5f2688..5c2da45f988c 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON Code for The Physical Address Space - * - * Author: SeongJae Park <sj@kernel.org> */ #define pr_fmt(fmt) "damon-pa: " fmt diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index ce4499cf4b8b..11b70d0a9a6f 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON-based page reclamation - * - * Author: SeongJae Park <sj@kernel.org> */ #define pr_fmt(fmt) "damon-reclaim: " fmt diff --git a/mm/damon/sysfs-common.c b/mm/damon/sysfs-common.c index bdc6ae2639e4..c59d7bf7a73a 100644 --- a/mm/damon/sysfs-common.c +++ b/mm/damon/sysfs-common.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Common Code for DAMON Sysfs Interface - * - * Author: SeongJae Park <sj@kernel.org> */ #include <linux/slab.h> diff --git a/mm/damon/sysfs-common.h b/mm/damon/sysfs-common.h index 3079306966a9..733764716e8d 100644 --- a/mm/damon/sysfs-common.h +++ b/mm/damon/sysfs-common.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Common Code for DAMON Sysfs Interface - * - * Author: SeongJae Park <sj@kernel.org> */ #include <linux/damon.h> diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 0134111c3c1f..3cbeccd436e4 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON sysfs Interface - * - * Copyright (c) 2022 SeongJae Park <sj@kernel.org> */ #include <linux/slab.h> diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 2e95e3bac774..a9e187158067 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON sysfs Interface - * - * Copyright (c) 2022 SeongJae Park <sj@kernel.org> */ #include <linux/pid.h> diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h index 1cfb8c176b87..fcf7c7fadb5f 100644 --- a/mm/damon/tests/core-kunit.h +++ b/mm/damon/tests/core-kunit.h @@ -1,10 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Data Access Monitor Unit Tests - * - * Copyright 2019 Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Author: SeongJae Park <sj@kernel.org> */ #ifdef CONFIG_DAMON_KUNIT_TEST diff --git a/mm/damon/tests/sysfs-kunit.h b/mm/damon/tests/sysfs-kunit.h index f9ec5e795b34..138a4b8d14e7 100644 --- a/mm/damon/tests/sysfs-kunit.h +++ b/mm/damon/tests/sysfs-kunit.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Data Access Monitor Unit Tests - * - * Author: SeongJae Park <sj@kernel.org> */ #ifdef CONFIG_DAMON_SYSFS_KUNIT_TEST diff --git a/mm/damon/tests/vaddr-kunit.h b/mm/damon/tests/vaddr-kunit.h index 563fbc7e3f44..61f844336ffb 100644 --- a/mm/damon/tests/vaddr-kunit.h +++ b/mm/damon/tests/vaddr-kunit.h @@ -1,10 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Data Access Monitor Unit Tests - * - * Copyright 2019 Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Author: SeongJae Park <sj@kernel.org> */ #ifdef CONFIG_DAMON_VADDR_KUNIT_TEST diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index d27147603564..e73ec1ce016e 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON Code for Virtual Address Spaces - * - * Author: SeongJae Park <sj@kernel.org> */ #define pr_fmt(fmt) "damon-va: " fmt diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 2bccb0a53a0a..b5d1e9d4463d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3587,10 +3587,6 @@ static void __split_folio_to_order(struct folio *folio, int old_order, (1L << PG_dropbehind) | LRU_GEN_MASK | LRU_REFS_MASK)); - if (handle_hwpoison && - page_range_has_hwpoisoned(new_head, new_nr_pages)) - folio_set_has_hwpoisoned(new_folio); - new_folio->mapping = folio->mapping; new_folio->index = folio->index + i; @@ -3612,6 +3608,14 @@ static void __split_folio_to_order(struct folio *folio, int old_order, folio_set_large_rmappable(new_folio); } + /* + * PG_has_hwpoisoned is on the 2nd page, so set it after + * the compound head is prepped. + */ + if (handle_hwpoison && + page_range_has_hwpoisoned(new_head, new_nr_pages)) + folio_set_has_hwpoisoned(new_folio); + if (folio_test_young(folio)) folio_set_young(new_folio); if (folio_test_idle(folio)) diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 7c7ba17ce7af..e196f53f9b46 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -687,7 +687,7 @@ static struct kmemleak_object *__alloc_object(gfp_t gfp) atomic_set(&object->use_count, 1); object->excess_ref = 0; object->count = 0; /* white color initially */ - object->checksum = 0; + object->checksum = ~0; object->del_state = 0; /* task information */ @@ -981,7 +981,7 @@ static void reset_checksum(unsigned long ptr) } raw_spin_lock_irqsave(&object->lock, flags); - object->checksum = 0; + object->checksum = ~0; raw_spin_unlock_irqrestore(&object->lock, flags); put_object(object); } @@ -1410,7 +1410,8 @@ static bool update_checksum(struct kmemleak_object *object) for_each_possible_cpu(cpu) { void *ptr = per_cpu_ptr((void __percpu *)object->pointer, cpu); - object->checksum ^= crc32(0, kasan_reset_tag((void *)ptr), object->size); + object->checksum = crc32(object->checksum, + kasan_reset_tag((void *)ptr), object->size); } } else { object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size); diff --git a/mm/page_reporting.c b/mm/page_reporting.c index 7418f2e500bb..942e84b6908a 100644 --- a/mm/page_reporting.c +++ b/mm/page_reporting.c @@ -173,11 +173,8 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone, * any pages that may have already been present from the previous * list processed. This should result in us reporting all pages on * an idle system in about 30 seconds. - * - * The division here should be cheap since PAGE_REPORTING_CAPACITY - * should always be a power of 2. */ - budget = DIV_ROUND_UP(area->nr_free, PAGE_REPORTING_CAPACITY * 16); + budget = DIV_ROUND_UP(area->nr_free, prdev->capacity * 16); /* loop through free list adding unreported pages to sg list */ list_for_each_entry_safe(page, next, list, lru) { @@ -222,10 +219,10 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone, spin_unlock_irq(&zone->lock); /* begin processing pages in local list */ - err = prdev->report(prdev, sgl, PAGE_REPORTING_CAPACITY); + err = prdev->report(prdev, sgl, prdev->capacity); /* reset offset since the full list was reported */ - *offset = PAGE_REPORTING_CAPACITY; + *offset = prdev->capacity; /* update budget to reflect call to report function */ budget--; @@ -234,7 +231,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone, spin_lock_irq(&zone->lock); /* flush reported pages from the sg list */ - page_reporting_drain(prdev, sgl, PAGE_REPORTING_CAPACITY, !err); + page_reporting_drain(prdev, sgl, prdev->capacity, !err); /* * Reset next to first entry, the old next isn't valid @@ -260,13 +257,13 @@ static int page_reporting_process_zone(struct page_reporting_dev_info *prdev, struct scatterlist *sgl, struct zone *zone) { - unsigned int order, mt, leftover, offset = PAGE_REPORTING_CAPACITY; + unsigned int order, mt, leftover, offset = prdev->capacity; unsigned long watermark; int err = 0; /* Generate minimum watermark to be able to guarantee progress */ watermark = low_wmark_pages(zone) + - (PAGE_REPORTING_CAPACITY << page_reporting_order); + (prdev->capacity << page_reporting_order); /* * Cancel request if insufficient free memory or if we failed @@ -290,7 +287,7 @@ page_reporting_process_zone(struct page_reporting_dev_info *prdev, } /* report the leftover pages before going idle */ - leftover = PAGE_REPORTING_CAPACITY - offset; + leftover = prdev->capacity - offset; if (leftover) { sgl = &sgl[offset]; err = prdev->report(prdev, sgl, leftover); @@ -322,11 +319,11 @@ static void page_reporting_process(struct work_struct *work) atomic_set(&prdev->state, state); /* allocate scatterlist to store pages being reported on */ - sgl = kmalloc_objs(*sgl, PAGE_REPORTING_CAPACITY); + sgl = kmalloc_objs(*sgl, prdev->capacity); if (!sgl) goto err_out; - sg_init_table(sgl, PAGE_REPORTING_CAPACITY); + sg_init_table(sgl, prdev->capacity); for_each_zone(zone) { err = page_reporting_process_zone(prdev, sgl, zone); @@ -377,6 +374,9 @@ int page_reporting_register(struct page_reporting_dev_info *prdev) page_reporting_order = pageblock_order; } + if (!prdev->capacity || prdev->capacity > PAGE_REPORTING_CAPACITY) + prdev->capacity = PAGE_REPORTING_CAPACITY; + /* initialize state and work structures */ atomic_set(&prdev->state, PAGE_REPORTING_IDLE); INIT_DELAYED_WORK(&prdev->work, &page_reporting_process); diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c index 2ccbabfb2cc1..bac2eb5de63d 100644 --- a/mm/page_vma_mapped.c +++ b/mm/page_vma_mapped.c @@ -243,21 +243,31 @@ restart: */ pmde = pmdp_get_lockless(pvmw->pmd); - if (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde)) { + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && + (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde) || + pmd_is_device_private_entry(pmde))) { pvmw->ptl = pmd_lock(mm, pvmw->pmd); pmde = *pvmw->pmd; - if (!pmd_present(pmde)) { + if (pmd_is_migration_entry(pmde)) { softleaf_t entry; - if (!thp_migration_supported() || - !(pvmw->flags & PVMW_MIGRATION)) + if (!(pvmw->flags & PVMW_MIGRATION)) return not_found(pvmw); entry = softleaf_from_pmd(pmde); + if (!check_pmd(softleaf_to_pfn(entry), pvmw)) + return not_found(pvmw); + return true; + } else if (pmd_is_device_private_entry(pmde)) { + softleaf_t entry; - if (!softleaf_is_migration(entry) || - !check_pmd(softleaf_to_pfn(entry), pvmw)) + if (pvmw->flags & PVMW_MIGRATION) + return not_found(pvmw); + entry = softleaf_from_pmd(pmde); + if (!check_pmd(softleaf_to_pfn(entry), pvmw)) return not_found(pvmw); return true; + } else if (!pmd_present(pmde)) { + return not_found(pvmw); } if (likely(pmd_trans_huge(pmde))) { if (pvmw->flags & PVMW_MIGRATION) @@ -266,17 +276,10 @@ restart: return not_found(pvmw); return true; } - /* THP pmd was split under us: handle on pte level */ + /* THP/device-private pmd was split under us: handle on pte level */ spin_unlock(pvmw->ptl); pvmw->ptl = NULL; } else if (!pmd_present(pmde)) { - const softleaf_t entry = softleaf_from_pmd(pmde); - - if (softleaf_is_device_private(entry)) { - pvmw->ptl = pmd_lock(mm, pvmw->pmd); - return true; - } - if ((pvmw->flags & PVMW_SYNC) && thp_vma_suitable_order(vma, pvmw->address, PMD_ORDER) && diff --git a/mm/slab.h b/mm/slab.h index 281a65233795..f5e336b6b6b0 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -22,6 +22,7 @@ #define SLAB_ALLOC_NOLOCK 0x01 /* a kmalloc_nolock() allocation */ #define SLAB_ALLOC_NEW_SLAB 0x02 /* a flag for alloc_slab_obj_exts() */ #define SLAB_ALLOC_NO_RECURSE 0x04 /* prevent kmalloc() recursion */ +#define SLAB_ALLOC_NO_OBJ_EXT 0x08 /* prevent obj_exts array allocation */ static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) { @@ -386,12 +387,17 @@ static inline unsigned int size_index_elem(unsigned int bytes) * KMALLOC_MAX_CACHE_SIZE and the caller must check that. */ static inline struct kmem_cache * -kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, kmalloc_token_t token) +kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, kmalloc_token_t token, + unsigned int alloc_flags) { unsigned int index; + enum kmalloc_cache_type type = kmalloc_type(flags, token); + + if (alloc_flags & SLAB_ALLOC_NO_OBJ_EXT) + type = KMALLOC_NO_OBJ_EXT; if (!b) - b = &kmalloc_caches[kmalloc_type(flags, token)]; + b = &kmalloc_caches[type]; if (size <= 192) index = kmalloc_size_index[size_index_elem(size)]; else @@ -426,7 +432,8 @@ static inline bool is_kmalloc_normal(struct kmem_cache *s) { if (!is_kmalloc_cache(s)) return false; - return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT)); + + return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT|SLAB_NO_OBJ_EXT)); } bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj); @@ -529,6 +536,25 @@ static inline void metadata_access_disable(void) kasan_enable_current(); } +/* + * Return true if KMALLOC_NORMAL caches may need obj_exts arrays. + * + * Memory allocation profiling requires obj_exts for all caches. + * Memcg usually doesn't need them for normal kmalloc caches, but kmalloc types + * with a priority higher than KMALLOC_CGROUP can be aliased with KMALLOC_NORMAL. + */ +static inline bool need_kmalloc_no_objext(void) +{ + if (!mem_alloc_profiling_permanently_disabled()) + return true; + + if (!mem_cgroup_kmem_disabled() && + (KMALLOC_NORMAL == KMALLOC_RECLAIM)) + return true; + + return false; +} + #ifdef CONFIG_SLAB_OBJ_EXT /* diff --git a/mm/slab_common.c b/mm/slab_common.c index b6426d7ceec9..03ecac12cd86 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -783,11 +783,15 @@ u8 kmalloc_size_index[24] __ro_after_init = { size_t kmalloc_size_roundup(size_t size) { if (size && size <= KMALLOC_MAX_CACHE_SIZE) { + struct kmem_cache *s; + /* * The flags don't matter since size_index is common to all. * Neither does the caller for just getting ->object_size. */ - return kmalloc_slab(size, NULL, GFP_KERNEL, __kmalloc_token(0))->object_size; + s = kmalloc_slab(size, NULL, GFP_KERNEL, __kmalloc_token(0), + SLAB_ALLOC_DEFAULT); + return s->object_size; } /* Above the smaller buckets, size is a multiple of page size. */ @@ -843,6 +847,12 @@ EXPORT_SYMBOL(kmalloc_size_roundup); #define KMALLOC_PARTITION_NAME(N, sz) #endif +#ifdef CONFIG_SLAB_OBJ_EXT +#define KMALLOC_NO_OBJ_EXT_NAME(sz) .name[KMALLOC_NO_OBJ_EXT] = "kmalloc-no-objext-" #sz, +#else +#define KMALLOC_NO_OBJ_EXT_NAME(sz) +#endif + #define INIT_KMALLOC_INFO(__size, __short_size) \ { \ .name[KMALLOC_NORMAL] = "kmalloc-" #__short_size, \ @@ -850,6 +860,7 @@ EXPORT_SYMBOL(kmalloc_size_roundup); KMALLOC_CGROUP_NAME(__short_size) \ KMALLOC_DMA_NAME(__short_size) \ KMALLOC_PARTITION_NAME(KMALLOC_PARTITION_CACHES_NR, __short_size) \ + KMALLOC_NO_OBJ_EXT_NAME(__short_size) \ .size = __size, \ } @@ -957,6 +968,12 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type) return; } flags |= SLAB_ACCOUNT; + } else if (IS_ENABLED(CONFIG_SLAB_OBJ_EXT) && type == KMALLOC_NO_OBJ_EXT) { + if (!need_kmalloc_no_objext()) { + kmalloc_caches[type][idx] = kmalloc_caches[KMALLOC_NORMAL][idx]; + return; + } + flags |= SLAB_NO_OBJ_EXT | SLAB_NO_MERGE; } else if (IS_ENABLED(CONFIG_ZONE_DMA) && (type == KMALLOC_DMA)) { flags |= SLAB_CACHE_DMA; } diff --git a/mm/slub.c b/mm/slub.c index 9ec774dc7009..0337e60db5ac 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2123,42 +2123,6 @@ static inline void init_slab_obj_exts(struct slab *slab) slab->obj_exts = 0; } -/* - * Calculate the allocation size for slabobj_ext array. - * - * When memory allocation profiling is enabled, the obj_exts array - * could be allocated from the same slab cache it's being allocated for. - * This would prevent the slab from ever being freed because it would - * always contain at least one allocated object (its own obj_exts array). - * - * To avoid this, increase the allocation size when we detect the array - * may come from the same cache, forcing it to use a different cache. - */ -static inline size_t obj_exts_alloc_size(struct kmem_cache *s, - struct slab *slab, gfp_t gfp) -{ - size_t sz = sizeof(struct slabobj_ext) * slab->objects; - struct kmem_cache *obj_exts_cache; - - if (sz > KMALLOC_MAX_CACHE_SIZE) - return sz; - - if (!is_kmalloc_normal(s)) - return sz; - - obj_exts_cache = kmalloc_slab(sz, NULL, gfp, __kmalloc_token(0)); - /* - * We can't simply compare s with obj_exts_cache, because partitioned kmalloc - * caches have multiple caches per size, selected by caller address or type. - * Since caller address or type may differ between kmalloc_slab() and actual - * allocation, bump size when sizes are equal. - */ - if (s->object_size == obj_exts_cache->object_size) - return obj_exts_cache->object_size + 1; - - return sz; -} - int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, gfp_t gfp, unsigned int alloc_flags) { @@ -2168,14 +2132,18 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, unsigned long new_exts; unsigned long old_exts; struct slabobj_ext *vec; - size_t sz; + size_t sz = sizeof(struct slabobj_ext) * slab->objects; gfp &= ~OBJCGS_CLEAR_MASK; - /* Prevent recursive extension vector allocation */ - alloc_flags |= SLAB_ALLOC_NO_RECURSE; - alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; + /* + * In most cases, obj_exts arrays are allocated from normal kmalloc. + * However, normal kmalloc caches must allocate them from + * KMALLOC_NO_OBJ_EXT caches to prevent recursion. + */ + if (is_kmalloc_normal(s)) + alloc_flags |= SLAB_ALLOC_NO_OBJ_EXT; - sz = obj_exts_alloc_size(s, slab, gfp); + alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; /* This will use kmalloc_nolock() if alloc_flags say so */ vec = kmalloc_flags(sz, gfp | __GFP_ZERO, alloc_flags, slab_nid(slab)); @@ -2193,8 +2161,21 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, return -ENOMEM; } - VM_WARN_ON_ONCE(virt_to_slab(vec) != NULL && - virt_to_slab(vec)->slab_cache == s); + if (IS_ENABLED(CONFIG_DEBUG_VM)) { + struct kmem_cache *exts_cache; + struct slab *exts_slab; + + exts_slab = virt_to_slab(vec); + if (exts_slab) { + /* + * The vector must be allocated from either normal or + * KMALLOC_NO_OBJ_EXT kmalloc caches to avoid cycles. + */ + exts_cache = exts_slab->slab_cache; + WARN_ON_ONCE(!is_kmalloc_normal(exts_cache) && + !(exts_cache->flags & SLAB_NO_OBJ_EXT)); + } + } new_exts = (unsigned long)vec; #ifdef CONFIG_MEMCG @@ -2217,7 +2198,6 @@ retry: * assign slabobj_exts in parallel. In this case the existing * objcg vector should be reused. */ - mark_obj_codetag_empty(vec); if (unlikely(!allow_spin)) kfree_nolock(vec); else @@ -2253,14 +2233,6 @@ static inline void free_slab_obj_exts(struct slab *slab, bool allow_spin) return; } - /* - * obj_exts was created with SLAB_ALLOC_NO_RECURSE flag, therefore its - * corresponding extension will be NULL. alloc_tag_sub() will throw a - * warning if slab has extensions but the extension of an object is - * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that - * the extension for obj_exts is expected to be NULL. - */ - mark_obj_codetag_empty(obj_exts); if (allow_spin) kfree(obj_exts); else @@ -5356,7 +5328,7 @@ void *__do_kmalloc_node(kmem_buckets *b, gfp_t flags, int node, if (unlikely(!size)) return ZERO_SIZE_PTR; - s = kmalloc_slab(size, b, flags, token); + s = kmalloc_slab(size, b, flags, token, ac->alloc_flags); ret = slab_alloc_node(s, flags, node, ac); ret = kasan_kmalloc(s, ret, size, flags); @@ -5419,7 +5391,9 @@ static void *__kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_f retry: if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return NULL; - s = kmalloc_slab(size, NULL, gfp_flags, PASS_TOKEN_PARAM(token)); + + s = kmalloc_slab(size, NULL, gfp_flags, PASS_TOKEN_PARAM(token), + ac->alloc_flags); if (!(s->flags & __CMPXCHG_DOUBLE) && !kmem_cache_debug(s)) /* @@ -6217,7 +6191,6 @@ static void free_to_pcs_bulk(struct kmem_cache *s, size_t size, void **p) void *remote_objects[PCS_BATCH_MAX]; unsigned int remote_nr = 0; -next_remote_batch: while (i < size) { struct slab *slab = virt_to_slab(p[i]); @@ -6232,8 +6205,11 @@ next_remote_batch: if (unlikely(!can_free_to_pcs(slab))) { remote_objects[remote_nr] = p[i]; p[i] = p[--size]; - if (++remote_nr >= PCS_BATCH_MAX) - goto flush_remote; + if (++remote_nr >= PCS_BATCH_MAX) { + __kmem_cache_free_bulk(s, remote_nr, &remote_objects[0]); + stat_add(s, FREE_SLOWPATH, remote_nr); + remote_nr = 0; + } continue; } @@ -6317,10 +6293,6 @@ flush_remote: if (remote_nr) { __kmem_cache_free_bulk(s, remote_nr, &remote_objects[0]); stat_add(s, FREE_SLOWPATH, remote_nr); - if (i < size) { - remote_nr = 0; - goto next_remote_batch; - } } } @@ -7801,12 +7773,12 @@ static unsigned int calculate_sheaf_capacity(struct kmem_cache *s, return 0; /* - * Bootstrap caches can't have sheaves for now (SLAB_NO_OBJ_EXT). + * Bootstrap caches can't have sheaves for now (SLAB_NO_SHEAVES). * SLAB_NOLEAKTRACE caches (e.g., kmemleak's object_cache) must not * have sheaves to avoid recursion when sheaf allocation triggers * kmemleak tracking. */ - if (s->flags & (SLAB_NO_OBJ_EXT | SLAB_NOLEAKTRACE)) + if (s->flags & (SLAB_NO_SHEAVES | SLAB_NOLEAKTRACE)) return 0; /* @@ -7981,10 +7953,10 @@ static int calculate_sizes(struct kmem_cache_args *args, struct kmem_cache *s) s->allocflags |= __GFP_RECLAIMABLE; /* - * For KMALLOC_NORMAL caches we enable sheaves later by - * bootstrap_kmalloc_sheaves() to avoid recursion + * For kmalloc caches we enable sheaves later by + * bootstrap_kmalloc_sheaves() to avoid recursion. */ - if (!is_kmalloc_normal(s)) + if (!is_kmalloc_cache(s)) s->sheaf_capacity = calculate_sheaf_capacity(s, args); /* @@ -8499,6 +8471,8 @@ static void __init bootstrap_cache_sheaves(struct kmem_cache *s) bool failed = false; int node, cpu; + VM_WARN_ON_ONCE(cache_has_sheaves(s)); + capacity = calculate_sheaf_capacity(s, &empty_args); /* capacity can be 0 due to debugging or SLUB_TINY */ @@ -8548,10 +8522,13 @@ static void __init bootstrap_kmalloc_sheaves(void) { enum kmalloc_cache_type type; - for (type = KMALLOC_NORMAL; type <= KMALLOC_PARTITION_END; type++) { + for (type = KMALLOC_NORMAL; type < NR_KMALLOC_TYPES; type++) { for (int idx = 0; idx < KMALLOC_SHIFT_HIGH + 1; idx++) { - if (kmalloc_caches[type][idx]) - bootstrap_cache_sheaves(kmalloc_caches[type][idx]); + struct kmem_cache *s = kmalloc_caches[type][idx]; + + /* Do not bootstrap twice when caches are aliased */ + if (s && !cache_has_sheaves(s)) + bootstrap_cache_sheaves(s); } } } @@ -8583,7 +8560,8 @@ void __init kmem_cache_init(void) create_boot_cache(kmem_cache_node, "kmem_cache_node", sizeof(struct kmem_cache_node), - SLAB_HWCACHE_ALIGN | SLAB_NO_OBJ_EXT, 0, 0); + SLAB_HWCACHE_ALIGN | SLAB_NO_SHEAVES | SLAB_NO_OBJ_EXT, + 0, 0); hotplug_node_notifier(slab_memory_callback, SLAB_CALLBACK_PRI); @@ -8593,7 +8571,8 @@ void __init kmem_cache_init(void) create_boot_cache(kmem_cache, "kmem_cache", offsetof(struct kmem_cache, per_node) + nr_node_ids * sizeof(struct kmem_cache_per_node_ptrs), - SLAB_HWCACHE_ALIGN | SLAB_NO_OBJ_EXT, 0, 0); + SLAB_HWCACHE_ALIGN | SLAB_NO_SHEAVES | SLAB_NO_OBJ_EXT, + 0, 0); kmem_cache = bootstrap(&boot_kmem_cache); kmem_cache_node = bootstrap(&boot_kmem_cache_node); diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 246af12bf801..c3adedaaf7d5 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2111,7 +2111,10 @@ static bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, { const struct vm_uffd_ops *ops = vma_uffd_ops(vma); - 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; vm_flags &= __VM_UFFD_FLAGS; diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 532534bc601c..c0b1fc293b49 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -929,12 +929,16 @@ int hci_update_eir_sync(struct hci_dev *hdev) memset(&cp, 0, sizeof(cp)); + hci_dev_lock(hdev); eir_create(hdev, cp.data); - if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) + if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) { + hci_dev_unlock(hdev); return 0; + } memcpy(hdev->eir, cp.data, sizeof(cp.data)); + hci_dev_unlock(hdev); return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp, HCI_CMD_TIMEOUT); @@ -966,6 +970,7 @@ int hci_update_class_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) return 0; + hci_dev_lock(hdev); cod[0] = hdev->minor_class; cod[1] = hdev->major_class; cod[2] = get_service_classes(hdev); @@ -973,8 +978,12 @@ int hci_update_class_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) cod[1] |= 0x20; - if (memcmp(cod, hdev->dev_class, 3) == 0) + if (memcmp(cod, hdev->dev_class, 3) == 0) { + hci_dev_unlock(hdev); return 0; + } + + hci_dev_unlock(hdev); return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod, HCI_CMD_TIMEOUT); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ebeae17b71d1..75f7512dec54 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1028,6 +1028,23 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, return rfcomm_send_frame(s, buf, ptr - buf); } +int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits, + u8 stop_bits, u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask) +{ + int err = -ENOTCONN; + + rfcomm_lock(); + if (d->session) + err = rfcomm_send_rpn(d->session, 1, d->dlci, bit_rate, + data_bits, stop_bits, parity, + flow_ctrl_settings, xon_char, xoff_char, + param_mask); + rfcomm_unlock(); + + return err; +} + static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) { struct rfcomm_hdr *hdr; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 4b9a699ec59b..b2c1060394e6 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -858,7 +858,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, BT_DBG("tty %p termios %p", tty, old); - if (!dev || !dev->dlc || !dev->dlc->session) + if (!dev || !dev->dlc) return; /* Handle turning off CRTSCTS */ @@ -979,9 +979,8 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, } if (changes) - rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud, - data_bits, stop_bits, parity, - RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); + rfcomm_dlc_send_rpn(dev->dlc, baud, data_bits, stop_bits, parity, + RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); } static void rfcomm_tty_throttle(struct tty_struct *tty) diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c index 71a12da30004..a713668ea34f 100644 --- a/net/bridge/br_netlink_tunnel.c +++ b/net/bridge/br_netlink_tunnel.c @@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p, if (!*v_start) goto out_init; - if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) { + if (v && curr_change && + br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) { *v_end = v; return; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index d55ea9516e3e..d3880f31edc4 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1627,7 +1627,8 @@ void br_vlan_notify(const struct net_bridge *br, u16 vid, u16 vid_range, int cmd); bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end); + const struct net_bridge_vlan *range_end, + u16 pvid); void br_vlan_fill_forward_path_pvid(struct net_bridge *br, struct net_device_path_ctx *ctx, @@ -1874,7 +1875,8 @@ static inline void br_vlan_notify(const struct net_bridge *br, } static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end) + const struct net_bridge_vlan *range_end, + u16 pvid) { return true; } diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 5560afcaaca3..31c1b2cf75d9 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -1982,9 +1982,11 @@ out_kfree: /* check if v_curr can enter a range ending in range_end */ bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end) + const struct net_bridge_vlan *range_end, + u16 pvid) { - return v_curr->vid - range_end->vid == 1 && + return v_curr->vid != pvid && range_end->vid != pvid && + v_curr->vid - range_end->vid == 1 && range_end->flags == v_curr->flags && br_vlan_opts_eq_range(v_curr, range_end); } @@ -2066,8 +2068,8 @@ static int br_vlan_dump_dev(const struct net_device *dev, idx += range_end->vid - range_start->vid + 1; range_start = v; - } else if (dump_stats || v->vid == pvid || - !br_vlan_can_enter_range(v, range_end)) { + } else if (dump_stats || + !br_vlan_can_enter_range(v, range_end, pvid)) { u16 vlan_flags = br_vlan_flags(range_start, pvid); if (!br_vlan_fill_vids(skb, range_start->vid, diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index fcc200c3e3da..cb0f556ff40d 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -350,8 +350,7 @@ int br_vlan_process_options(const struct net_bridge *br, continue; } - if (v->vid == pvid || - !br_vlan_can_enter_range(v, curr_end)) { + if (!br_vlan_can_enter_range(v, curr_end, pvid)) { br_vlan_notify(br, p, curr_start->vid, curr_end->vid, RTM_NEWVLAN); curr_start = v; diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c index 9e64e82d0b63..50a79e8aa656 100644 --- a/net/ceph/auth_x.c +++ b/net/ceph/auth_x.c @@ -849,9 +849,16 @@ static int ceph_x_update_authorizer( au = (struct ceph_x_authorizer *)auth->authorizer; if (au->secret_id < th->secret_id) { + int ret; + dout("ceph_x_update_authorizer service %u secret %llu < %llu\n", au->service, au->secret_id, th->secret_id); - return ceph_x_build_authorizer(ac, th, au); + ret = ceph_x_build_authorizer(ac, th, au); + if (ret) + return ret; + + auth->authorizer_buf = au->buf->vec.iov_base; + auth->authorizer_buf_len = au->buf->vec.iov_len; } return 0; } diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 952121849180..a797c7360e3c 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -762,13 +762,13 @@ void ceph_destroy_client(struct ceph_client *client) atomic_set(&client->msgr.stopping, 1); + ceph_debugfs_client_cleanup(client); + /* unmount */ ceph_osdc_stop(&client->osdc); ceph_monc_stop(&client->monc); ceph_messenger_fini(&client->msgr); - ceph_debugfs_client_cleanup(client); - ceph_destroy_options(client->options); kfree(client); diff --git a/net/ceph/cls_lock_client.c b/net/ceph/cls_lock_client.c index c6956f1df333..377336982f7d 100644 --- a/net/ceph/cls_lock_client.c +++ b/net/ceph/cls_lock_client.c @@ -299,7 +299,7 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, if (ret) return ret; - *num_lockers = ceph_decode_32(p); + ceph_decode_32_safe(p, end, *num_lockers, err_inval); *lockers = kzalloc_objs(**lockers, *num_lockers, GFP_NOIO); if (!*lockers) return -ENOMEM; @@ -310,7 +310,8 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, goto err_free_lockers; } - *type = ceph_decode_8(p); + ret = -EINVAL; + ceph_decode_8_safe(p, end, *type, err_free_lockers); s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO); if (IS_ERR(s)) { ret = PTR_ERR(s); @@ -320,6 +321,9 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, *tag = s; return 0; +err_inval: + return -EINVAL; + err_free_lockers: ceph_free_lockers(*lockers, *num_lockers); return ret; diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index d2cdc8ee3155..c56457378d00 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_decode(void **p, void *end, bool msgr2) dout("%s fsid %pU epoch %u num_mon %u\n", __func__, &fsid, epoch, num_mon); - if (num_mon > CEPH_MAX_MON) + if (num_mon == 0 || num_mon > CEPH_MAX_MON) goto e_inval; monmap = kmalloc_flex(*monmap, mon_inst, num_mon, GFP_NOIO); @@ -821,7 +821,7 @@ static void handle_get_version_reply(struct ceph_mon_client *monc, struct ceph_mon_generic_request *req; u64 tid = le64_to_cpu(msg->hdr.tid); void *p = msg->front.iov_base; - void *end = p + msg->front_alloc_len; + void *const end = p + msg->front.iov_len; u64 handle; dout("%s msg %p tid %llu\n", __func__, msg, tid); diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 8b5b0587a0cf..a4b0dd8672ec 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -518,6 +518,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end) ceph_decode_need(p, end, 4*sizeof(u32), bad); b->id = ceph_decode_32(p); b->type = ceph_decode_16(p); + if (b->type == 0) + goto bad; b->alg = ceph_decode_8(p); if (b->alg != alg) { b->alg = 0; @@ -1436,7 +1438,7 @@ static struct ceph_pg_mapping *__decode_pg_temp(void **p, void *end, ceph_decode_32_safe(p, end, len, e_inval); if (len == 0 && incremental) return NULL; /* new_pg_temp: [] to remove */ - if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32)) + if (len > CEPH_PG_MAX_SIZE) return ERR_PTR(-EINVAL); ceph_decode_need(p, end, len * sizeof(u32), e_inval); @@ -1617,7 +1619,7 @@ static struct ceph_pg_mapping *__decode_pg_upmap_items(void **p, void *end, u32 len, i; ceph_decode_32_safe(p, end, len, e_inval); - if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32))) + if (len > CEPH_PG_MAX_SIZE) return ERR_PTR(-EINVAL); ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval); @@ -1842,6 +1844,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, void *new_up_client; void *new_state; void *new_weight_end; + const u32 new_state_item_size = + sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8)); u32 len; int ret; int i; @@ -1862,7 +1866,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, new_state = *p; ceph_decode_32_safe(p, end, len, e_inval); - len *= sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8)); + if (check_mul_overflow(len, new_state_item_size, &len)) + goto e_inval; ceph_decode_need(p, end, len, e_inval); *p += len; @@ -3055,8 +3060,11 @@ static int get_immediate_parent(struct crush_map *c, int id, if (b->items[j] != id) continue; - *parent_type_id = b->type; type_cn = lookup_crush_name(&c->type_names, b->type); + if (WARN_ON_ONCE(!type_cn)) + continue; + + *parent_type_id = b->type; parent_loc->cl_type_name = type_cn->cn_name; parent_loc->cl_name = cn->cn_name; return b->id; diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 2bf3cab5e557..abaf108ac4db 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -530,10 +530,10 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore, return; unlock_free: - spin_unlock_irqrestore(&data->drop_queue.lock, flags); u64_stats_update_begin(&data->stats.syncp); u64_stats_inc(&data->stats.dropped); u64_stats_update_end(&data->stats.syncp); + spin_unlock_irqrestore(&data->drop_queue.lock, flags); consume_skb(nskb); } @@ -566,13 +566,13 @@ static size_t net_dm_packet_report_size(size_t payload_len) /* NET_DM_ATTR_ORIGIN */ nla_total_size(sizeof(u16)) + /* NET_DM_ATTR_PC */ - nla_total_size(sizeof(u64)) + + nla_total_size_64bit(sizeof(u64)) + /* NET_DM_ATTR_SYMBOL */ nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) + /* NET_DM_ATTR_IN_PORT */ net_dm_in_port_size() + /* NET_DM_ATTR_TIMESTAMP */ - nla_total_size(sizeof(u64)) + + nla_total_size_64bit(sizeof(u64)) + /* NET_DM_ATTR_ORIG_LEN */ nla_total_size(sizeof(u32)) + /* NET_DM_ATTR_PROTO */ @@ -671,9 +671,7 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb, if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol))) goto nla_put_failure; - attr = skb_put(msg, nla_total_size(payload_len)); - attr->nla_type = NET_DM_ATTR_PAYLOAD; - attr->nla_len = nla_attr_size(payload_len); + attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len); if (skb_copy_bits(skb, 0, nla_data(attr), payload_len)) goto nla_put_failure; @@ -768,7 +766,7 @@ net_dm_hw_packet_report_size(size_t payload_len, /* NET_DM_ATTR_FLOW_ACTION_COOKIE */ net_dm_flow_action_cookie_size(hw_metadata) + /* NET_DM_ATTR_TIMESTAMP */ - nla_total_size(sizeof(u64)) + + nla_total_size_64bit(sizeof(u64)) + /* NET_DM_ATTR_ORIG_LEN */ nla_total_size(sizeof(u32)) + /* NET_DM_ATTR_PROTO */ @@ -831,9 +829,7 @@ static int net_dm_hw_packet_report_fill(struct sk_buff *msg, if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol))) goto nla_put_failure; - attr = skb_put(msg, nla_total_size(payload_len)); - attr->nla_type = NET_DM_ATTR_PAYLOAD; - attr->nla_len = nla_attr_size(payload_len); + attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len); if (skb_copy_bits(skb, 0, nla_data(attr), payload_len)) goto nla_put_failure; @@ -1001,10 +997,10 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink, return; unlock_free: - spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags); u64_stats_update_begin(&hw_data->stats.syncp); u64_stats_inc(&hw_data->stats.dropped); u64_stats_update_end(&hw_data->stats.syncp); + spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags); net_dm_hw_metadata_free(n_hw_metadata); free: consume_skb(nskb); diff --git a/net/core/filter.c b/net/core/filter.c index b446aa8be5c3..11bb0d236822 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2552,11 +2552,13 @@ out_drop: BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags) { - struct bpf_redirect_info *ri = bpf_net_ctx_get_ri(); + struct bpf_redirect_info *ri; - if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))) + if (unlikely(!bpf_net_ctx_get() || + (flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))) return TC_ACT_SHOT; + ri = bpf_net_ctx_get_ri(); ri->flags = flags; ri->tgt_index = ifindex; @@ -2573,11 +2575,12 @@ static const struct bpf_func_proto bpf_redirect_proto = { BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags) { - struct bpf_redirect_info *ri = bpf_net_ctx_get_ri(); + struct bpf_redirect_info *ri; - if (unlikely(flags)) + if (unlikely(!bpf_net_ctx_get() || flags)) return TC_ACT_SHOT; + ri = bpf_net_ctx_get_ri(); ri->flags = BPF_F_PEER; ri->tgt_index = ifindex; @@ -2595,11 +2598,13 @@ static const struct bpf_func_proto bpf_redirect_peer_proto = { BPF_CALL_4(bpf_redirect_neigh, u32, ifindex, struct bpf_redir_neigh *, params, int, plen, u64, flags) { - struct bpf_redirect_info *ri = bpf_net_ctx_get_ri(); + struct bpf_redirect_info *ri; - if (unlikely((plen && plen < sizeof(*params)) || flags)) + if (unlikely((plen && plen < sizeof(*params)) || + !bpf_net_ctx_get() || flags)) return TC_ACT_SHOT; + ri = bpf_net_ctx_get_ri(); ri->flags = BPF_F_NEIGH | (plen ? BPF_F_NEXTHOP : 0); ri->tgt_index = ifindex; diff --git a/net/core/gro.c b/net/core/gro.c index 35f2f708f010..29b4d02bf519 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -229,7 +229,9 @@ done: int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) { - if (unlikely(p->len + skb->len >= 65536)) + /* make sure to check flush flag and to not merge */ + if (unlikely(p->len + skb->len >= 65536 || + NAPI_GRO_CB(skb)->flush)) return -E2BIG; if (!pskb_may_pull(skb, skb_gro_offset(skb))) { diff --git a/net/core/sock.c b/net/core/sock.c index 498a57f34f5b..ffa73594c13c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2547,6 +2547,11 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, cgroup_sk_clone(&newsk->sk_cgrp_data); + RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); + + if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP) + net_enable_timestamp(); + rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter != NULL) @@ -2569,8 +2574,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, goto free; } - RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); - if (bpf_sk_storage_clone(sk, newsk)) goto free; @@ -2598,9 +2601,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, if (newsk->sk_prot->sockets_allocated) sk_sockets_allocated_inc(newsk); - - if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP) - net_enable_timestamp(); out: return newsk; free: diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index d9af9e65f72f..01c73b4b50dd 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -242,6 +242,8 @@ void hsr_del_port(struct hsr_port *port) netdev_rx_handler_unregister(port->dev); if (!port->hsr->fwd_offloaded) dev_set_promiscuity(port->dev, -1); + if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) + vlan_vids_del_by_dev(port->dev, master->dev); netdev_upper_dev_unlink(port->dev, master->dev); if (hsr->prot_version == PRP_V1 && port->type == HSR_PT_SLAVE_B) { diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index 1614593b6d72..7d25519a6cdd 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -63,12 +63,12 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int } /* Update addresses before rehashing */ - inet->inet_daddr = fl4->daddr; + WRITE_ONCE(inet->inet_daddr, fl4->daddr); inet->inet_dport = usin->sin_port; if (!inet->inet_saddr) inet->inet_saddr = fl4->saddr; if (!inet->inet_rcv_saddr) { - inet->inet_rcv_saddr = fl4->saddr; + WRITE_ONCE(inet->inet_rcv_saddr, fl4->saddr); if (sk->sk_prot->rehash) sk->sk_prot->rehash(sk); } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 23e921d313b3..0caedfc7ca92 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -548,11 +548,23 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, if (IS_ERR(rt2)) err = PTR_ERR(rt2); } else { - struct flowi4 fl4_2 = {}; + struct flowi4 fl4_2 = fl4_dec; unsigned long orefdst; - fl4_2.daddr = fl4_dec.saddr; - rt2 = ip_route_output_key(net, &fl4_2); + swap(fl4_2.daddr, fl4_2.saddr); + switch (fl4_2.flowi4_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + swap(fl4_2.fl4_sport, fl4_2.fl4_dport); + break; + } + + fl4_2.flowi4_oif = l3mdev_master_ifindex(route_lookup_dev); + fl4_2.flowi4_flags |= FLOWI_FLAG_ANYSRC; + + rt2 = __ip_route_output_key(net, &fl4_2); if (IS_ERR(rt2)) { err = PTR_ERR(rt2); goto relookup_failed; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 3efdfb4ffa21..0ba1e94e9012 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1018,6 +1018,8 @@ static void __gre_tunnel_init(struct net_device *dev) dev->features |= GRE_FEATURES; dev->hw_features |= GRE_FEATURES; + dev->lltx = true; + /* TCP offload with GRE SEQ is not supported, nor can we support 2 * levels of outer headers requiring an update. */ @@ -1029,8 +1031,6 @@ static void __gre_tunnel_init(struct net_device *dev) dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - - dev->lltx = true; } static int ipgre_tunnel_init(struct net_device *dev) diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 6205bd57aa85..44fe75004cac 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -1788,8 +1788,8 @@ static bool nh_res_bucket_migrate(struct nh_res_table *res_table, bool notify_nl, bool force) { struct nh_res_bucket *bucket = &res_table->nh_buckets[bucket_index]; + struct netlink_ext_ack extack = {}; struct nh_grp_entry *new_nhge; - struct netlink_ext_ack extack; int err; new_nhge = list_first_entry_or_null(&res_table->uw_nh_entries, diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index e9fbab6ad914..7f74d8b95a37 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -118,13 +118,21 @@ bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num, __be32 raddr, __be32 laddr, int dif, int sdif) { const struct inet_sock *inet = inet_sk(sk); + __be32 daddr, rcv_saddr; - if (net_eq(sock_net(sk), net) && inet->inet_num == num && - !(inet->inet_daddr && inet->inet_daddr != raddr) && - !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) && - raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif)) - return true; - return false; + if (!net_eq(sock_net(sk), net) || inet->inet_num != num) + return false; + + daddr = READ_ONCE(inet->inet_daddr); + if (daddr && daddr != raddr) + return false; + + rcv_saddr = READ_ONCE(inet->inet_rcv_saddr); + if (rcv_saddr && rcv_saddr != laddr) + return false; + + return raw_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if), + dif, sdif); } EXPORT_SYMBOL_GPL(raw_v4_match); @@ -722,7 +730,8 @@ static int raw_bind(struct sock *sk, struct sockaddr_unsized *uaddr, chk_addr_ret)) goto out; - inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; + inet->inet_saddr = addr->sin_addr.s_addr; + WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr); if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) inet->inet_saddr = 0; /* Use device */ sk_dst_reset(sk); diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 8e905b50dead..a30475afb6f8 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -604,7 +604,7 @@ wait_for_sndbuf: wait_for_memory: err = sk_stream_wait_memory(sk, &timeo); if (err) { - if (msg_tx && msg_tx != psock->cork) + if (msg_tx == &tmp) sk_msg_free(sk, msg_tx); goto out_err; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 61045a8886e4..daff93d51342 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4038,24 +4038,17 @@ static void tcp_send_ack_reflect_ect(struct sock *sk, bool accecn_reflector) __tcp_send_ack(sk, tp->rcv_nxt, flags); } -/* RFC 5961 7 [ACK Throttling] */ -static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) +/* Consume one slot from the per-netns RFC 5961 challenge ACK quota. + * Returns true if a challenge ACK may be sent. + */ +static bool tcp_challenge_ack_allowed(struct net *net) { - struct tcp_sock *tp = tcp_sk(sk); - struct net *net = sock_net(sk); u32 count, now, ack_limit; - /* First check our per-socket dupack rate limit. */ - if (__tcp_oow_rate_limited(net, - LINUX_MIB_TCPACKSKIPPEDCHALLENGE, - &tp->last_oow_ack_time)) - return; - ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit); if (ack_limit == INT_MAX) - goto send_ack; + return true; - /* Then check host-wide RFC 5961 rate limit. */ now = jiffies / HZ; if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) { u32 half = (ack_limit + 1) >> 1; @@ -4067,12 +4060,49 @@ static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) count = READ_ONCE(net->ipv4.tcp_challenge_count); if (count > 0) { WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1); -send_ack: + return true; + } + return false; +} + +/* RFC 5961 7 [ACK Throttling] */ +static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); + + /* First check our per-socket dupack rate limit. */ + if (__tcp_oow_rate_limited(net, + LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tp->last_oow_ack_time)) + return; + + /* Then check the per-netns RFC 5961 rate limit. */ + if (tcp_challenge_ack_allowed(net)) { NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); tcp_send_ack_reflect_ect(sk, accecn_reflector); } } +/* Send a challenge ACK from a SYN-RECEIVED request socket. Uses + * __tcp_oow_rate_limited() directly so that an RST carrying payload + * cannot bypass the per-request rate limit. + */ +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +{ + struct net *net = sock_net(sk); + + if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tcp_rsk(req)->last_oow_ack_time)) + return; + + if (tcp_challenge_ack_allowed(net)) { + NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); + req->rsk_ops->send_ack(sk, skb, req); + } +} + static void tcp_store_ts_recent(struct tcp_sock *tp) { tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4a46da375043..b8887cdd66c5 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -971,6 +971,9 @@ static void tcp_v4_send_ack(const struct sock *sk, key->rcv_next); arg.iov[0].iov_len += tcp_ao_len_aligned(key->ao_key); rep.th.doff = arg.iov[0].iov_len / 4; + memset((u8 *)&rep.opt[offset] + tcp_ao_maclen(key->ao_key), + TCPOPT_NOP, tcp_ao_len_aligned(key->ao_key) - + tcp_ao_len(key->ao_key)); tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset], key->ao_key, key->traffic_key, @@ -3143,8 +3146,11 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) bpf_iter_tcp_put_batch(iter); err = bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2, GFP_USER); - if (err) + if (err) { + iter->cur_sk = 0; + iter->end_sk = 0; return ERR_PTR(err); + } sk = bpf_iter_tcp_resume(seq); if (!sk) diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index ddc4b17a826b..6ab3e3a0b431 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -833,7 +833,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * elsewhere and is checked directly against the child socket rather * than req because user data may have been sent out. */ - if ((flg & TCP_FLAG_ACK) && !fastopen && + if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen && (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1)) return sk; @@ -872,6 +872,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, flg &= ~TCP_FLAG_SYN; } + /* RFC 5961 section 3.2, as clarified by RFC 9293 section + * 3.10.7.4, requires a challenge ACK for a non-exact + * in-window RST in SYN-RECEIVED. + */ + if ((flg & TCP_FLAG_RST) && + TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) { + tcp_reqsk_send_challenge_ack(sk, skb, req); + return NULL; + } + /* RFC793: "second check the RST bit" and * "fourth, check the SYN bit" */ diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c index e71571455c8a..b78179bfc4c7 100644 --- a/net/ipv6/ila/ila_common.c +++ b/net/ipv6/ila/ila_common.c @@ -85,6 +85,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, struct tcphdr *th = (struct tcphdr *) (skb_network_header(skb) + nhoff); + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&th->check, skb, diff, true, true); @@ -96,6 +97,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, (skb_network_header(skb) + nhoff); if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&uh->check, skb, diff, true, true); @@ -110,6 +112,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, struct icmp6hdr *ih = (struct icmp6hdr *) (skb_network_header(skb) + nhoff); + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb, diff, true, true); @@ -127,6 +130,15 @@ void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, switch (p->csum_mode) { case ILA_CSUM_ADJUST_TRANSPORT: ila_csum_adjust_transport(skb, p); + /* + * ila_csum_adjust_transport() calls pskb_may_pull(), which can + * reallocate the skb head and leave ip6h (and the iaddr derived + * from it) dangling; reload both before the write below. The + * other csum modes do not pull, so their cached pointers stay + * valid. + */ + ip6h = ipv6_hdr(skb); + iaddr = ila_a2i(&ip6h->daddr); break; case ILA_CSUM_NEUTRAL_MAP: if (sir2ila) { diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 7c09a269b352..b843116e9b70 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1455,6 +1455,8 @@ static void ip6gre_tnl_init_features(struct net_device *dev) dev->features |= GRE6_FEATURES; dev->hw_features |= GRE6_FEATURES; + dev->lltx = true; + /* TCP offload with GRE SEQ is not supported, nor can we support 2 * levels of outer headers requiring an update. */ @@ -1466,8 +1468,6 @@ static void ip6gre_tnl_init_features(struct net_device *dev) dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - - dev->lltx = true; } static int ip6gre_tunnel_init_common(struct net_device *dev) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 3cc58698cbbd..b88d364e78aa 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -349,7 +349,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr, const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr, - inet6_iif(skb), inet6_iif(skb))) + inet6_iif(skb), inet6_sdif(skb))) continue; rawv6_err(sk, skb, type, code, inner_offset, info); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index a1301334da48..fc42d67e5822 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -4022,7 +4022,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) struct fib6_node *fn; /* prefer to send a single notification with all hops */ - skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); + skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC); if (skb) { u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; @@ -4078,7 +4078,7 @@ out_put: if (skb) { rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, - info->nlh, gfp_any()); + info->nlh, GFP_ATOMIC); } return err; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 522ba45ce9b7..9e9155b1b3aa 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -923,6 +923,8 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 (tcp_ao_len(key->ao_key) << 16) | (key->ao_key->sndid << 8) | (key->rcv_next)); + memset((u8 *)topt + tcp_ao_maclen(key->ao_key), TCPOPT_NOP, + tcp_ao_len_aligned(key->ao_key) - tcp_ao_len(key->ao_key)); tcp_ao_hash_hdr(AF_INET6, (char *)topt, key->ao_key, key->traffic_key, diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index b85fb9767dec..ea047bab65e7 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -337,6 +337,7 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) unsigned char user_data[16]; struct iucv_sock *iucv = iucv_sk(sk); struct iucv_path *path = iucv->path; + struct sock_msg_q *p, *n; /* Whoever resets the path pointer, must sever and free it. */ if (xchg(&iucv->path, NULL)) { @@ -348,6 +349,19 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) } else pr_iucv->path_sever(path, NULL); iucv_path_free(path); + + /* + * Message notifications queued on message_q still reference + * the now freed path; drop them, otherwise a later recvmsg() + * would pass the freed iucv_path to message_receive() via + * iucv_process_message_q(). + */ + spin_lock_bh(&iucv->message_q.lock); + list_for_each_entry_safe(p, n, &iucv->message_q.list, list) { + list_del(&p->list); + kfree(p); + } + spin_unlock_bh(&iucv->message_q.lock); } } @@ -1872,7 +1886,8 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb) afiucv_swap_src_dest(skb); trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN; err = dev_queue_xmit(skb); - iucv_sock_kill(nsk); + if (nsk) + iucv_sock_kill(nsk); bh_unlock_sock(sk); goto out; } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b00191e02a63..43f142624d33 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2611,6 +2611,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, memcpy(&sta->deflink.pub->supp_rates, &nmi_sta->deflink.pub->supp_rates, sizeof(sta->deflink.pub->supp_rates)); + + sta->deflink.pub->agg = nmi_sta->deflink.pub->agg; + __ieee80211_sta_recalc_aggregates(sta, 0); } /* set the STA state after all sta info from usermode has been set */ diff --git a/net/mac80211/link.c b/net/mac80211/link.c index d0535268962c..dc68144dc363 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -399,6 +399,10 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, memcpy(sdata->link, old_data, sizeof(old_data)); memcpy(sdata->vif.link_conf, old, sizeof(old)); ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links); + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + ieee80211_link_debugfs_remove(&links[link_id]->data); + ieee80211_link_stop(&links[link_id]->data); + } /* and free (only) the newly allocated links */ memset(to_free, 0, sizeof(links)); goto free; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index d9ea19be075d..5e26be8e27d8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1717,6 +1717,8 @@ static void sta_ps_start(struct sta_info *sta) else clear_bit(tid, &sta->txq_buffered_tids); } + + sta_info_recalc_tim(sta); } static void sta_ps_end(struct sta_info *sta) diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 5e7cc11fab3a..85452ef9a58c 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -891,6 +891,11 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, data = skb_mac_header(skb) + skb->mac_len; datalen = skb_tail_pointer(skb) - data; + if (datalen < authlen) { + kfree_sensitive(req); + return -EBADMSG; + } + sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen); if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) { diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 300d4584533e..65089826ff59 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -179,6 +179,7 @@ void mac802154_scan_worker(struct work_struct *work) enum nl802154_scan_types scan_req_type; struct ieee802154_sub_if_data *sdata; unsigned int scan_duration = 0; + netdevice_tracker dev_tracker; struct wpan_phy *wpan_phy; u8 scan_req_duration; u8 page, channel; @@ -209,6 +210,14 @@ void mac802154_scan_worker(struct work_struct *work) return; } + /* + * sdata->dev is dereferenced below after rcu_read_unlock() and outside + * the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it + * asynchronously from netdev_run_todo(). Pin it with a reference taken + * while the RCU read lock is still held, and drop it at every exit. + */ + netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC); + wpan_phy = scan_req->wpan_phy; scan_req_type = scan_req->type; scan_req_duration = scan_req->duration; @@ -262,12 +271,14 @@ void mac802154_scan_worker(struct work_struct *work) "Scan page %u channel %u for %ums\n", page, channel, jiffies_to_msecs(scan_duration)); queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration); + netdev_put(sdata->dev, &dev_tracker); return; end_scan: rtnl_lock(); mac802154_scan_cleanup_locked(local, sdata, false); rtnl_unlock(); + netdev_put(sdata->dev, &dev_tracker); } int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata, diff --git a/net/mctp/device.c b/net/mctp/device.c index 2c84df674669..822120e860c8 100644 --- a/net/mctp/device.c +++ b/net/mctp/device.c @@ -536,7 +536,9 @@ int __init mctp_device_init(void) { int err; - register_netdevice_notifier(&mctp_dev_nb); + err = register_netdevice_notifier(&mctp_dev_nb); + if (err) + return err; err = rtnl_af_register(&mctp_af_ops); if (err) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 318cb7e2ac5f..4406c304b639 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -922,8 +922,7 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, struct nlattr *nla_via, *nla_newdst; int remaining = cfg->rc_mp_len; int err = 0; - - rt->rt_nhn = 0; + u8 nhs = 0; change_nexthops(rt) { int attrlen; @@ -959,12 +958,15 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, rt->rt_nhn_alive--; rtnh = rtnh_next(rtnh, &remaining); - rt->rt_nhn++; + nhs++; } endfor_nexthops(rt); + rt->rt_nhn = nhs; + return 0; errout: + rt->rt_nhn = nhs; return err; } diff --git a/net/mptcp/options.c b/net/mptcp/options.c index dff3fd5d3b55..c664023d37ba 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -157,17 +157,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, ptr++; flags = (*ptr++) & MPTCP_DSS_FLAG_MASK; - mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0; mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0; mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0; mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0; mp_opt->use_ack = (flags & MPTCP_DSS_HAS_ACK); - pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n", - mp_opt->data_fin, mp_opt->dsn64, - mp_opt->use_map, mp_opt->ack64, - mp_opt->use_ack); - expected_opsize = TCPOLEN_MPTCP_DSS_BASE; if (mp_opt->use_ack) { @@ -178,12 +172,18 @@ static void mptcp_parse_option(const struct sk_buff *skb, } if (mp_opt->use_map) { + mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0; if (mp_opt->dsn64) expected_opsize += TCPOLEN_MPTCP_DSS_MAP64; else expected_opsize += TCPOLEN_MPTCP_DSS_MAP32; } + pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n", + mp_opt->data_fin, mp_opt->dsn64, + mp_opt->use_map, mp_opt->ack64, + mp_opt->use_ack); + /* Always parse any csum presence combination, we will enforce * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */ @@ -571,10 +571,7 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, bool ret = false; /* Zero `use_ack` and `use_map` flags with one shot. */ - BUILD_BUG_ON(sizeof_field(struct mptcp_ext, flags) != sizeof(u16)); - BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct mptcp_ext, flags), - sizeof(u16))); - *(u16 *)&opts->ext_copy.flags = 0; + memset(&opts->ext_copy.flags, 0, sizeof(opts->ext_copy.flags)); opts->csum_reqd = READ_ONCE(msk->csum_enabled); mpext = skb ? mptcp_get_ext(skb) : NULL; diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index d100867e9202..945aa5afc2dd 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -132,12 +132,15 @@ int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, __be16 msk_sport = ((struct inet_sock *) inet_sk((struct sock *)msk))->inet_sport; struct mptcp_pm_addr_entry *entry; + int id; spin_lock_bh(&msk->pm.lock); entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr); + id = entry ? entry->addr.id : -1; spin_unlock_bh(&msk->pm.lock); - if (entry) - return entry->addr.id; + + if (id != -1) + return id; if (skc->addr.port == msk_sport) skc->addr.port = 0; diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index cb9515f505aa..ca644ec53eed 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2545,6 +2545,22 @@ static void __mptcp_subflow_disconnect(struct sock *ssk, } } +static void mptcp_cleanup_ssk_backlog(struct sock *sk, struct sock *ssk) +{ + struct mptcp_sock *msk = mptcp_sk(sk); + struct sk_buff *skb; + + mptcp_data_lock(sk); + list_for_each_entry(skb, &msk->backlog_list, list) { + if (skb->sk != ssk) + continue; + + atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc); + skb->sk = NULL; + } + mptcp_data_unlock(sk); +} + /* subflow sockets can be either outgoing (connect) or incoming * (accept). * @@ -2568,6 +2584,9 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); subflow->closing = 1; + if (flags & MPTCP_CF_PUSH) + mptcp_cleanup_ssk_backlog(sk, ssk); + /* Borrow the fwd allocated page left-over; fwd memory for the subflow * could be negative at this point, but will be reach zero soon - when * the data allocated using such fragment will be freed. @@ -2659,9 +2678,6 @@ out: void mptcp_close_ssk(struct sock *sk, struct sock *ssk, struct mptcp_subflow_context *subflow) { - struct mptcp_sock *msk = mptcp_sk(sk); - struct sk_buff *skb; - /* The first subflow can already be closed or disconnected */ if (subflow->close_event_done || READ_ONCE(subflow->local_id) < 0) return; @@ -2671,17 +2687,6 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk, if (sk->sk_state == TCP_ESTABLISHED) mptcp_event(MPTCP_EVENT_SUB_CLOSED, mptcp_sk(sk), ssk, GFP_KERNEL); - /* Remove any reference from the backlog to this ssk; backlog skbs consume - * space in the msk receive queue, no need to touch sk->sk_rmem_alloc - */ - list_for_each_entry(skb, &msk->backlog_list, list) { - if (skb->sk != ssk) - continue; - - atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc); - skb->sk = NULL; - } - /* subflow aborted before reaching the fully_established status * attempt the creation of the next subflow */ @@ -3907,6 +3912,7 @@ bool mptcp_finish_join(struct sock *ssk) mptcp_data_unlock(parent); if (!ret) { + mptcp_pm_close_subflow(msk); err_prohibited: subflow->reset_reason = MPTCP_RST_EPROHIBIT; return false; diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 140388a18ae0..513fca6a8e8a 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -837,12 +837,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, u16 mru = OVS_CB(skb)->mru; u32 cutlen = OVS_CB(skb)->cutlen; - if (unlikely(cutlen > 0)) { - if (skb->len - cutlen > ovs_mac_header_len(key)) - pskb_trim(skb, skb->len - cutlen); - else - pskb_trim(skb, ovs_mac_header_len(key)); - } + if (unlikely(cutlen < skb->len)) + pskb_trim(skb, max(cutlen, ovs_mac_header_len(key))); if (likely(!mru || (skb->len <= mru + vport->dev->hard_header_len))) { @@ -1234,7 +1230,7 @@ static void execute_psample(struct datapath *dp, struct sk_buff *skb, psample_group.net = ovs_dp_get_net(dp); md.in_ifindex = OVS_CB(skb)->input_vport->dev->ifindex; - md.trunc_size = skb->len - OVS_CB(skb)->cutlen; + md.trunc_size = min(skb->len, OVS_CB(skb)->cutlen); md.rate_as_probability = 1; rate = OVS_CB(skb)->probability ? OVS_CB(skb)->probability : U32_MAX; @@ -1284,22 +1280,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, clone = skb_clone(skb, GFP_ATOMIC); if (clone) do_output(dp, clone, port, key); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; break; } case OVS_ACTION_ATTR_TRUNC: { struct ovs_action_trunc *trunc = nla_data(a); - if (skb->len > trunc->max_len) - OVS_CB(skb)->cutlen = skb->len - trunc->max_len; + OVS_CB(skb)->cutlen = trunc->max_len; break; } case OVS_ACTION_ATTR_USERSPACE: output_userspace(dp, skb, key, a, attr, len, OVS_CB(skb)->cutlen); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; if (nla_is_last(a, rem)) { consume_skb(skb); return 0; @@ -1453,7 +1448,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_PSAMPLE: execute_psample(dp, skb, a); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; if (nla_is_last(a, rem)) { consume_skb(skb); return 0; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index f0164817d9b7..eaf332b156d7 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -276,7 +276,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) upcall.portid = ovs_vport_find_upcall_portid(p, skb); upcall.mru = OVS_CB(skb)->mru; - error = ovs_dp_upcall(dp, skb, key, &upcall, 0); + error = ovs_dp_upcall(dp, skb, key, &upcall, U32_MAX); switch (error) { case 0: case -EAGAIN: @@ -457,7 +457,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, struct sk_buff *nskb = NULL; struct sk_buff *user_skb = NULL; /* to be queued to userspace */ struct nlattr *nla; - size_t len; + size_t msg_size; + size_t skb_len; unsigned int hlen; int err, dp_ifindex; u64 hash; @@ -478,7 +479,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, skb = nskb; } - if (nla_attr_size(skb->len) > USHRT_MAX) { + skb_len = min(skb->len, cutlen); + if (nla_attr_size(skb_len) > USHRT_MAX) { err = -EFBIG; goto out; } @@ -493,13 +495,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, * padding logic. Only perform zerocopy if padding is not required. */ if (dp->user_features & OVS_DP_F_UNALIGNED) - hlen = skb_zerocopy_headlen(skb); + hlen = min(skb_zerocopy_headlen(skb), cutlen); else - hlen = skb->len; + hlen = skb_len; - len = upcall_msg_size(upcall_info, hlen - cutlen, - OVS_CB(skb)->acts_origlen); - user_skb = genlmsg_new(len, GFP_ATOMIC); + msg_size = upcall_msg_size(upcall_info, hlen, + OVS_CB(skb)->acts_origlen); + user_skb = genlmsg_new(msg_size, GFP_ATOMIC); if (!user_skb) { err = -ENOMEM; goto out; @@ -560,7 +562,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, } /* Add OVS_PACKET_ATTR_LEN when packet is truncated */ - if (cutlen > 0 && + if (skb_len < skb->len && nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN, skb->len)) { err = -ENOBUFS; goto out; @@ -585,9 +587,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, err = -ENOBUFS; goto out; } - nla->nla_len = nla_attr_size(skb->len - cutlen); + nla->nla_len = nla_attr_size(skb_len); - err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen); + err = skb_zerocopy(user_skb, skb, skb_len, hlen); if (err) goto out; @@ -644,6 +646,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) packet->ignore_df = 1; } OVS_CB(packet)->mru = mru; + OVS_CB(packet)->cutlen = U32_MAX; if (a[OVS_PACKET_ATTR_HASH]) { hash = nla_get_u64(a[OVS_PACKET_ATTR_HASH]); diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index db0c3e69d66c..696640e88fa7 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -118,7 +118,7 @@ struct datapath { * @mru: The maximum received fragement size; 0 if the packet is not * fragmented. * @acts_origlen: The netlink size of the flow actions applied to this skb. - * @cutlen: The number of bytes from the packet end to be removed. + * @cutlen: The number of bytes in the packet to preserve on output. * @probability: The sampling probability that was applied to this skb; 0 means * no sampling has occurred; U32_MAX means 100% probability. * @upcall_pid: Netlink socket PID to use for sending this packet to userspace; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 56b2e2d1a749..12741485c939 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -502,7 +502,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, OVS_CB(skb)->input_vport = vport; OVS_CB(skb)->mru = 0; - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; OVS_CB(skb)->probability = 0; OVS_CB(skb)->upcall_pid = 0; if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8e6f3a734ba0..e75d2932475a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, spin_lock(&po->bind_lock); WRITE_ONCE(po->num, num); - if (was_running) + /* + * NETDEV_UNREGISTER may have invalidated the binding while bind_lock + * was dropped above. Do not re-add a fanout hook to a dead device. + */ + if (was_running && READ_ONCE(po->ifindex) != -1) register_prot_hook(sk); spin_unlock(&po->bind_lock); diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 706927139393..31b29e3ca7bc 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -55,6 +55,8 @@ static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, ph = skb_header_pointer(skb, 0, 2, &h); if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) return NULL; + /* pskb_may_pull() may have reallocated the head; refetch ph. */ + ph = skb_header_pointer(skb, 0, 2, &h); ph->sb_len -= 2; *ptype = ph->sb_type; *plen = ph->sb_len; diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index ad44831d6745..1272d49cd038 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -350,16 +350,34 @@ static struct pernet_operations phonet_net_ops = { /* Initialize Phonet devices list */ int __init phonet_device_init(void) { - int err = register_pernet_subsys(&phonet_net_ops); + int err; + + err = register_pernet_subsys(&phonet_net_ops); if (err) return err; - proc_create_net("pnresource", 0, init_net.proc_net, &pn_res_seq_ops, - sizeof(struct seq_net_private)); - register_netdevice_notifier(&phonet_device_notifier); + if (!proc_create_net("pnresource", 0, init_net.proc_net, + &pn_res_seq_ops, sizeof(struct seq_net_private))) { + err = -ENOMEM; + goto err_pernet; + } + + err = register_netdevice_notifier(&phonet_device_notifier); + if (err) + goto err_proc; + err = phonet_netlink_register(); if (err) - phonet_device_exit(); + goto err_notifier; + + return 0; + +err_notifier: + unregister_netdevice_notifier(&phonet_device_notifier); +err_proc: + remove_proc_entry("pnresource", init_net.proc_net); +err_pernet: + unregister_pernet_subsys(&phonet_net_ops); return err; } @@ -367,8 +385,8 @@ void phonet_device_exit(void) { rtnl_unregister_all(PF_PHONET); unregister_netdevice_notifier(&phonet_device_notifier); - unregister_pernet_subsys(&phonet_net_ops); remove_proc_entry("pnresource", init_net.proc_net); + unregister_pernet_subsys(&phonet_net_ops); } int phonet_route_add(struct net_device *dev, u8 daddr) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index d02ef9a74c3c..a30fa56e6aa3 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -1263,6 +1263,14 @@ static int qrtr_create(struct net *net, struct socket *sock, if (sock->type != SOCK_DGRAM) return -EPROTOTYPE; + /* QRTR keeps its port and node state in module-global variables that + * are not partitioned per network namespace, and the in-kernel name + * service only operates in init_net. Confine the family to init_net so + * a socket in another namespace cannot reach the global control plane. + */ + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern); if (!sk) return -ENOMEM; diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index b3f9bbcf9ab9..e5b2adb161d9 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -76,11 +76,11 @@ struct qrtr_node { * requirements. If the requirement changes in the future, these values can be * increased. */ -#define QRTR_NS_MAX_NODES 64 +#define QRTR_NS_MAX_NODES 512 #define QRTR_NS_MAX_SERVERS 256 #define QRTR_NS_MAX_LOOKUPS 64 -static u8 node_count; +static u16 node_count; static struct qrtr_node *node_get(unsigned int node_id) { diff --git a/net/rds/ib.c b/net/rds/ib.c index 39f87272e071..8f9cf491984f 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -429,6 +429,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, sa = (struct sockaddr *)&sin; } else { #if IS_ENABLED(CONFIG_IPV6) + if (!ipv6_mod_enabled()) { + ret = -EADDRNOTAVAIL; + goto out; + } memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr; diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 5667f0173b47..d46146887ba4 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, dp = event->param.conn.private_data; if (isv6) { #if IS_ENABLED(CONFIG_IPV6) + if (!ipv6_mod_enabled()) { + err = -EOPNOTSUPP; + goto out; + } dp_cmn = &dp->ricp_v6.dp_cmn; saddr6 = &dp->ricp_v6.dp_saddr; daddr6 = &dp->ricp_v6.dp_daddr; diff --git a/net/rds/recv.c b/net/rds/recv.c index 4b3f9e4a8bfd..cf3884d87931 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -399,6 +399,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr, goto out; } + /* + * rds_find_bound() uses a global (netns-agnostic) hash table. + * An RDS connection created in netns A can match a socket bound + * in the init netns, delivering inc cross-netns with inc->i_conn + * pointing into netns A. When cleanup_net() then frees that conn, + * any subsequent dereference of inc->i_conn is a use-after-free. + * Drop the inc if the receiving socket lives in a different netns. + */ + if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) { + rds_stats_inc(s_recv_drop_no_sock); + rds_sock_put(rs); + rs = NULL; + goto out; + } + /* Process extension headers */ rds_recv_incoming_exthdrs(inc, rs); diff --git a/net/rds/tcp.c b/net/rds/tcp.c index a1de114d5e2e..5de35d556f29 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -366,9 +366,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, rcu_read_unlock(); } #if IS_ENABLED(CONFIG_IPV6) - ret = ipv6_chk_addr(net, addr, dev, 0); - if (ret) - return 0; + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); + if (ret) + return 0; + } #endif return -EADDRNOTAVAIL; } @@ -655,13 +657,13 @@ static void __net_exit rds_tcp_exit_net(struct net *net) { struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid); - rds_tcp_kill_sock(net); - if (rtn->rds_tcp_sysctl) unregister_net_sysctl_table(rtn->rds_tcp_sysctl); if (net != &init_net) kfree(rtn->ctl_table); + + rds_tcp_kill_sock(net); } static struct pernet_operations rds_tcp_net_ops = { diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ce946b0a03e2..865f05fe37ab 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1285,9 +1285,11 @@ int rxrpc_io_thread(void *data); void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb); static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local) { - if (!local->io_thread) + struct task_struct *io_thread = READ_ONCE(local->io_thread); + + if (!io_thread) return; - wake_up_process(READ_ONCE(local->io_thread)); + wake_up_process(io_thread); } static inline bool rxrpc_protocol_error(struct sk_buff *skb, enum rxrpc_abort_reason why) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index ffeea6db8337..fee4524adc98 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -4045,7 +4045,7 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru fl = rcu_dereference_bh(qe->filter_chain); - switch (tcf_classify(skb, NULL, fl, &cl_res, false)) { + switch (tcf_classify_qdisc(skb, fl, &cl_res, false)) { case TC_ACT_SHOT: qdisc_qstats_drop(sch); __qdisc_drop(skb, to_free); @@ -4057,10 +4057,6 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru __qdisc_drop(skb, to_free); *ret = __NET_XMIT_STOLEN; return NULL; - case TC_ACT_REDIRECT: - skb_do_redirect(skb); - *ret = __NET_XMIT_STOLEN; - return NULL; case TC_ACT_CONSUMED: *ret = __NET_XMIT_STOLEN; return NULL; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 8a3236456db4..668bcd60d183 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -415,12 +415,13 @@ static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab) } static struct qdisc_rate_table *qdisc_rtab_list; +static DEFINE_SPINLOCK(qdisc_rtab_lock); struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab, struct netlink_ext_ack *extack) { - struct qdisc_rate_table *rtab; + struct qdisc_rate_table *rtab, *new_rtab; if (tab == NULL || r->rate == 0 || r->cell_log == 0 || r->cell_log >= 32 || @@ -429,15 +430,20 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, return NULL; } + new_rtab = kmalloc_obj(*new_rtab); + + spin_lock(&qdisc_rtab_lock); for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) { if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) && !memcmp(&rtab->data, nla_data(tab), TC_RTAB_SIZE)) { rtab->refcnt++; + spin_unlock(&qdisc_rtab_lock); + kfree(new_rtab); return rtab; } } - rtab = kmalloc_obj(*rtab); + rtab = new_rtab; if (rtab) { rtab->rate = *r; rtab->refcnt = 1; @@ -449,6 +455,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, } else { NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table"); } + spin_unlock(&qdisc_rtab_lock); return rtab; } EXPORT_SYMBOL(qdisc_get_rtab); @@ -457,18 +464,25 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab) { struct qdisc_rate_table *rtab, **rtabp; - if (!tab || --tab->refcnt) + if (!tab) return; + spin_lock(&qdisc_rtab_lock); + if (--tab->refcnt) { + spin_unlock(&qdisc_rtab_lock); + return; + } + for (rtabp = &qdisc_rtab_list; (rtab = *rtabp) != NULL; rtabp = &rtab->next) { if (rtab == tab) { *rtabp = rtab->next; - kfree(rtab); - return; + break; } } + spin_unlock(&qdisc_rtab_lock); + kfree(tab); } EXPORT_SYMBOL(qdisc_put_rtab); diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index f78f8e950776..505f63fecf64 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1727,7 +1727,7 @@ static u32 cake_classify(struct Qdisc *sch, struct cake_tin_data **t, goto hash; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, filter, &res, false); + result = tcf_classify_qdisc(skb, filter, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 020657f959b5..91b1ef824afa 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -312,7 +312,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; fl = rcu_dereference_bh(q->filter_list); - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 27088760eff4..4f678d4ff10e 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -364,7 +364,7 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, if (!fl) return NET_XMIT_SUCCESS; - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index cb8cf437ce87..25fcf4079fec 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -391,7 +391,7 @@ static struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch, *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; if (TC_H_MAJ(skb->priority) != sch->handle) { fl = rcu_dereference_bh(q->filter_list); - err = tcf_classify(skb, NULL, fl, &res, false); + err = tcf_classify_qdisc(skb, fl, &res, false); #ifdef CONFIG_NET_CLS_ACT switch (err) { case TC_ACT_STOLEN: diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index cafd1f943d99..6cce86ba383c 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -91,7 +91,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch, return fq_codel_hash(q, skb) + 1; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, filter, &res, false); + result = tcf_classify_qdisc(skb, filter, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 72f48fa4010b..069e1facd413 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -96,7 +96,7 @@ static unsigned int fq_pie_classify(struct sk_buff *skb, struct Qdisc *sch, return fq_pie_hash(q, skb) + 1; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, filter, &res, false); + result = tcf_classify_qdisc(skb, filter, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 7e537295b8b6..e87f5021a199 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1143,7 +1143,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; head = &q->root; tcf = rcu_dereference_bh(q->root.filter_list); - while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) { + while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { case TC_ACT_QUEUED: diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 908b9ba9ba2e..fdac0dc8f35a 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -243,7 +243,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch, } *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) { + while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { case TC_ACT_QUEUED: diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index a467dd122369..66df30939aa5 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -36,7 +36,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) int err; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - err = tcf_classify(skb, NULL, fl, &res, false); + err = tcf_classify_qdisc(skb, fl, &res, false); #ifdef CONFIG_NET_CLS_ACT switch (err) { case TC_ACT_STOLEN: diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index e4dd56a89072..79437c587e7e 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -39,7 +39,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; if (TC_H_MAJ(skb->priority) != sch->handle) { fl = rcu_dereference_bh(q->filter_list); - err = tcf_classify(skb, NULL, fl, &res, false); + err = tcf_classify_qdisc(skb, fl, &res, false); #ifdef CONFIG_NET_CLS_ACT switch (err) { case TC_ACT_STOLEN: diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index cb56787e1d25..6f3b7273cb16 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -709,7 +709,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch, *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; fl = rcu_dereference_bh(q->filter_list); - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index b1d465094276..ed39869199c0 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -260,7 +260,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl, struct tcf_result res; int result; - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 758b88f21865..77675f9a4c46 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -171,7 +171,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch, return sfq_hash(q, skb) + 1; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sctp/auth.c b/net/sctp/auth.c index be9782760f50..c901d373af80 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -672,7 +672,7 @@ int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id) /* Check if we can add this chunk to the array */ param_len = ntohs(p->param_hdr.length); nchunks = param_len - sizeof(struct sctp_paramhdr); - if (nchunks == SCTP_NUM_CHUNK_TYPES) + if (nchunks == SCTP_AUTH_MAX_CHUNKS) return -EINVAL; p->chunks[nchunks] = chunk_id; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index cf335494bffe..27c26e12f95d 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1383,10 +1383,6 @@ static int __net_init sctp_defaults_init(struct net *net) net->sctp.l3mdev_accept = 1; #endif - status = sctp_sysctl_net_register(net); - if (status) - goto err_sysctl_register; - /* Allocate and initialise sctp mibs. */ status = init_sctp_mibs(net); if (status) @@ -1420,8 +1416,6 @@ err_init_proc: cleanup_sctp_mibs(net); #endif err_init_mibs: - sctp_sysctl_net_unregister(net); -err_sysctl_register: return status; } @@ -1436,7 +1430,6 @@ static void __net_exit sctp_defaults_exit(struct net *net) net->sctp.proc_net_sctp = NULL; #endif cleanup_sctp_mibs(net); - sctp_sysctl_net_unregister(net); } static struct pernet_operations sctp_defaults_ops = { @@ -1450,16 +1443,28 @@ static int __net_init sctp_ctrlsock_init(struct net *net) /* Initialize the control inode/socket for handling OOTB packets. */ status = sctp_ctl_sock_init(net); - if (status) + if (status) { pr_err("Failed to initialize the SCTP control sock\n"); + return status; + } + + status = sctp_sysctl_net_register(net); + if (status) { + inet_ctl_sock_destroy(net->sctp.ctl_sock); + net->sctp.ctl_sock = NULL; + } return status; } static void __net_exit sctp_ctrlsock_exit(struct net *net) { + sctp_sysctl_net_unregister(net); + sctp_udp_sock_stop(net); + /* Free the control endpoint. */ inet_ctl_sock_destroy(net->sctp.ctl_sock); + net->sctp.ctl_sock = NULL; } static struct pernet_operations sctp_ctrlsock_ops = { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 8adac9e0cd66..c02809264075 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3153,6 +3153,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, if (!peer) return SCTP_ERROR_DNS_FAILED; + /* Don't free asconf->transport; a later wildcard DEL-IP + * parameter reuses it. + */ + if (peer == asconf->transport) + return SCTP_ERROR_REQ_REFUSED; + sctp_assoc_rm_peer(asoc, peer); break; case SCTP_PARAM_SET_PRIMARY: diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 3893b44448b3..708fa07d5fff 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -643,7 +643,7 @@ static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk, struct sctp_chunk auth; if (!chunk->auth_chunk) - return true; + return !sctp_auth_recv_cid(chunk->chunk_hdr->type, asoc); /* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo * is supposed to be authenticated and we have to do delayed diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 5c2fdedea088..34ffe6c945a4 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -308,7 +308,8 @@ int sctp_send_reset_streams(struct sctp_association *asoc, goto out; param_len += str_nums * sizeof(__u16) + - sizeof(struct sctp_strreset_inreq); + (out ? sizeof(struct sctp_strreset_inreq) + : sizeof(struct sctp_strreset_outreq)); } if (param_len > SCTP_MAX_CHUNK_LEN - @@ -639,6 +640,9 @@ struct sctp_chunk *sctp_process_strreset_inreq( nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); str_p = inreq->list_of_streams; + if (nums * sizeof(__u16) + sizeof(struct sctp_strreset_outreq) > + SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_reconf_chunk)) + goto out; for (i = 0; i < nums; i++) { if (ntohs(str_p[i]) >= stream->outcnt) { result = SCTP_STRRESET_ERR_WRONG_SSN; diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 15e7db9a3ab2..fca840484ebf 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -615,11 +615,16 @@ int sctp_sysctl_net_register(struct net *net) void sctp_sysctl_net_unregister(struct net *net) { + struct ctl_table_header *header = net->sctp.sysctl_header; const struct ctl_table *table; - table = net->sctp.sysctl_header->ctl_table_arg; - unregister_net_sysctl_table(net->sctp.sysctl_header); + if (!header) + return; + + table = header->ctl_table_arg; + unregister_net_sysctl_table(header); kfree(table); + net->sctp.sysctl_header = NULL; } static struct ctl_table_header *sctp_sysctl_header; diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 8336a9664703..1307dd1a9613 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { }; /* Properties valid for media, bearer and link */ +static const struct netlink_range_validation tipc_nl_mtu_range = { + .max = U16_MAX, +}; + const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC }, [TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 }, [TIPC_NLA_PROP_TOL] = { .type = NLA_U32 }, [TIPC_NLA_PROP_WIN] = { .type = NLA_U32 }, - [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 }, + [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range), [TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 }, [TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 } }; diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 2a786c56c8c5..d9a4f94ea2d4 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -221,6 +221,10 @@ static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, int rem; len = (*cmd->dumpit)(buf, &cb); + if (len < 0) { + err = len; + goto err_out; + } nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) { err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN, diff --git a/net/tipc/socket.c b/net/tipc/socket.c index e564341e0216..185c24003b82 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -502,6 +502,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, tipc_set_sk_state(sk, TIPC_OPEN); if (tipc_sk_insert(tsk)) { sk_free(sk); + sock->sk = NULL; pr_warn("Socket create failed; port number exhausted\n"); return -EINVAL; } @@ -1935,7 +1936,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, if (likely(!err)) { int offset = skb_cb->bytes_read; - copy = min_t(int, dlen - offset, buflen); + copy = min_t(size_t, dlen - offset, buflen); rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); if (unlikely(rc)) goto exit; @@ -2067,7 +2068,7 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m, /* Copy data if msg ok, otherwise return error/partial data */ if (likely(!err)) { offset = skb_cb->bytes_read; - copy = min_t(int, dlen - offset, buflen - copied); + copy = min_t(size_t, dlen - offset, buflen - copied); rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); if (unlikely(rc)) break; diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 62ae7f5b5840..230645cc01c9 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -94,6 +94,8 @@ struct udp_replicast { * @ifindex: local address scope * @work: used to schedule deferred work on a bearer * @rcast: associated udp_replicast container + * @rcast_lock: serialize updates to @rcast.list against concurrent updaters + * @disabled: bearer is being torn down; reject further @rcast.list additions */ struct udp_bearer { struct tipc_bearer __rcu *bearer; @@ -101,6 +103,8 @@ struct udp_bearer { u32 ifindex; struct work_struct work; struct udp_replicast rcast; + spinlock_t rcast_lock; + bool disabled; }; static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr) @@ -278,26 +282,6 @@ out: return err; } -static bool tipc_udp_is_known_peer(struct tipc_bearer *b, - struct udp_media_addr *addr) -{ - struct udp_replicast *rcast, *tmp; - struct udp_bearer *ub; - - ub = rcu_dereference_rtnl(b->media_ptr); - if (!ub) { - pr_err_ratelimited("UDP bearer instance not found\n"); - return false; - } - - list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { - if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr))) - return true; - } - - return false; -} - static int tipc_udp_rcast_add(struct tipc_bearer *b, struct udp_media_addr *addr) { @@ -308,16 +292,34 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b, if (!ub) return -ENODEV; + spin_lock_bh(&ub->rcast_lock); + if (ub->disabled) { + spin_unlock_bh(&ub->rcast_lock); + return 0; + } + list_for_each_entry(rcast, &ub->rcast.list, list) { + if (!memcmp(&rcast->addr, addr, sizeof(*addr))) { + spin_unlock_bh(&ub->rcast_lock); + return 0; + } + } + rcast = kmalloc_obj(*rcast, GFP_ATOMIC); - if (!rcast) + if (!rcast) { + spin_unlock_bh(&ub->rcast_lock); return -ENOMEM; + } if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) { + spin_unlock_bh(&ub->rcast_lock); kfree(rcast); return -ENOMEM; } memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr)); + list_add_rcu(&rcast->list, &ub->rcast.list); + b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT; + spin_unlock_bh(&ub->rcast_lock); if (ntohs(addr->proto) == ETH_P_IP) pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4); @@ -325,8 +327,6 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b, else if (ntohs(addr->proto) == ETH_P_IPV6) pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6); #endif - b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT; - list_add_rcu(&rcast->list, &ub->rcast.list); return 0; } @@ -361,9 +361,6 @@ static int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb) return 0; } - if (likely(tipc_udp_is_known_peer(b, &src))) - return 0; - return tipc_udp_rcast_add(b, &src); } @@ -644,9 +641,6 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr) return -EINVAL; } - if (tipc_udp_is_known_peer(b, &addr)) - return 0; - return tipc_udp_rcast_add(b, &addr); } @@ -679,6 +673,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, return -ENOMEM; INIT_LIST_HEAD(&ub->rcast.list); + spin_lock_init(&ub->rcast_lock); if (!attrs[TIPC_NLA_BEARER_UDP_OPTS]) goto err; @@ -819,10 +814,13 @@ static void cleanup_bearer(struct work_struct *work) struct udp_replicast *rcast, *tmp; struct tipc_net *tn; + spin_lock_bh(&ub->rcast_lock); list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { list_del_rcu(&rcast->list); call_rcu_hurry(&rcast->rcu, rcast_free_rcu); } + ub->disabled = true; + spin_unlock_bh(&ub->rcast_lock); tn = tipc_net(sock_net(ub->sk)); diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 741aef09bfd3..37bb06a8e8f5 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -595,13 +595,15 @@ void tls_device_splice_eof(struct socket *sock) struct tls_context *tls_ctx = tls_get_ctx(sk); struct iov_iter iter = {}; - if (!tls_is_partially_sent_record(tls_ctx)) + if (!tls_is_partially_sent_record(tls_ctx) && + !tls_is_pending_open_record(tls_ctx)) return; mutex_lock(&tls_ctx->tx_lock); lock_sock(sk); - if (tls_is_partially_sent_record(tls_ctx)) { + if (tls_is_partially_sent_record(tls_ctx) || + tls_is_pending_open_record(tls_ctx)) { iov_iter_bvec(&iter, ITER_SOURCE, NULL, 0, 0); tls_push_data(sk, &iter, 0, 0, TLS_RECORD_TYPE_DATA); } diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 09475007165b..8becad81279c 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -26,6 +26,13 @@ /* Threshold for detecting small packets to copy */ #define GOOD_COPY_LEN 128 +/* Max payload that can be collapsed into a single linear skb, using the same + * allocation threshold as virtio_vsock_alloc_skb() to avoid adding pressure + * on the page allocator. + */ +#define MAX_COLLAPSE_LEN \ + SKB_MAX_ORDER(VIRTIO_VSOCK_SKB_HEADROOM, PAGE_ALLOC_COSTLY_ORDER) + static void virtio_transport_cancel_close_work(struct vsock_sock *vsk, bool cancel_timeout); static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs); @@ -420,6 +427,145 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, return ret; } +static bool virtio_transport_can_collapse(struct sk_buff *skb) +{ + /* skbs that are partially consumed, mark a SEQPACKET message boundary, + * or are already large enough should not be collapsed: they either + * need special accounting, carry protocol state, or already have a + * good data-to-overhead ratio. + */ + if (VIRTIO_VSOCK_SKB_CB(skb)->offset) + return false; + if (le32_to_cpu(virtio_vsock_hdr(skb)->flags) & VIRTIO_VSOCK_SEQ_EOM) + return false; + if (skb->len >= MAX_COLLAPSE_LEN) + return false; + return true; +} + +/* Iterate through the packets in the queue starting from the current skb to + * count the number of bytes we can collapse. + */ +static unsigned int +virtio_transport_collapse_size(struct sk_buff *skb, struct sk_buff_head *queue) +{ + unsigned int target = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset; + + while ((skb = skb_peek_next(skb, queue)) && + virtio_transport_can_collapse(skb)) { + unsigned int len = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset; + + if (len > MAX_COLLAPSE_LEN - target) + return target; + + target += len; + } + + return target; +} + +/* Called under lock_sock to compact the receive queue by merging small skbs. + * @min_to_free: minimum number of skbs to eliminate from the queue. May free + * more to fill each collapsed skb to capacity. + */ +static void +virtio_transport_collapse_rx_queue(struct virtio_vsock_sock *vvs, + u32 min_to_free) +{ + struct sk_buff *skb, *next_skb, *new_skb = NULL; + struct sk_buff_head new_queue; + u32 saved = 0; + + __skb_queue_head_init(&new_queue); + + skb_queue_walk_safe(&vvs->rx_queue, skb, next_skb) { + struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb); + u32 src_off = VIRTIO_VSOCK_SKB_CB(skb)->offset; + u32 src_len = skb->len - src_off; + bool keep; + + keep = !virtio_transport_can_collapse(skb); + if (keep) { + /* Finalize pending collapsed skb to preserve packet + * ordering. + */ + if (new_skb) { + __skb_queue_tail(&new_queue, new_skb); + new_skb = NULL; + saved--; + } + goto next; + } + + /* Finalize if this packet won't fit in the remaining tailroom, + * so we can allocate a right-sized new_skb. + */ + if (new_skb && src_len > skb_tailroom(new_skb)) { + __skb_queue_tail(&new_queue, new_skb); + new_skb = NULL; + saved--; + } + + if (!new_skb) { + unsigned int alloc_size; + + /* Check after finalizing to opportunistically fill + * each collapsed skb to capacity, merging more skbs + * than strictly required. + */ + if (saved >= min_to_free) + break; + + alloc_size = virtio_transport_collapse_size(skb, &vvs->rx_queue); + + /* Only this skb's data is eligible, nothing to merge + * with. Keep as-is. + */ + if (alloc_size <= src_len) { + keep = true; + goto next; + } + + new_skb = virtio_vsock_alloc_linear_skb(alloc_size + + VIRTIO_VSOCK_SKB_HEADROOM, GFP_KERNEL); + if (!new_skb) + break; + + memcpy(virtio_vsock_hdr(new_skb), hdr, + sizeof(struct virtio_vsock_hdr)); + virtio_vsock_hdr(new_skb)->len = 0; + } + + /* Cannot fail since src_off/src_len are within bounds, but if + * it does, discard new_skb to avoid queuing corrupted data. + */ + if (WARN_ON_ONCE(skb_copy_bits(skb, src_off, + skb_put(new_skb, src_len), + src_len))) { + kfree_skb(new_skb); + new_skb = NULL; + break; + } + + le32_add_cpu(&virtio_vsock_hdr(new_skb)->len, src_len); + virtio_vsock_hdr(new_skb)->flags |= hdr->flags; + +next: + __skb_unlink(skb, &vvs->rx_queue); + if (keep) { + __skb_queue_tail(&new_queue, skb); + } else { + consume_skb(skb); + saved++; + } + } + + if (new_skb) + __skb_queue_tail(&new_queue, new_skb); + + skb_queue_splice(&new_queue, &vvs->rx_queue); +} + static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs, u32 len) { @@ -1354,12 +1500,29 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk, { struct virtio_vsock_sock *vvs = vsk->trans; bool can_enqueue, free_pkt = false; + u32 len, queue_max, queue_len; struct virtio_vsock_hdr *hdr; - u32 len; hdr = virtio_vsock_hdr(skb); len = le32_to_cpu(hdr->len); + /* virtio_transport_inc_rx_pkt() rejects packets when the per-skb + * overhead (skb_queue_len * SKB_TRUESIZE(0)) exceeds buf_alloc. + * Proactively collapse the queue before that happens. + * No rx_lock needed: lock_sock is held by caller, preventing + * concurrent enqueue or dequeue. + */ + queue_max = vvs->buf_alloc / SKB_TRUESIZE(0); + queue_len = skb_queue_len(&vvs->rx_queue); + if (queue_len >= queue_max) { + /* Walking a large queue may take a significant amount of time + * and cache misses, causing traffic burstiness. Limit the + * collapse to freeing room for this packet and the next one. + * It may free more to fill each collapsed skb to capacity. + */ + virtio_transport_collapse_rx_queue(vvs, queue_len + 2 - queue_max); + } + spin_lock_bh(&vvs->rx_lock); can_enqueue = virtio_transport_inc_rx_pkt(vvs, len); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 34c3625f7fd5..d1e2fae5bc0e 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -263,8 +263,9 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, "FTM: nominal time is required for PD NTB ranging"); return -EINVAL; } - out->ftm.nominal_time = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]); + if (tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]) + out->ftm.nominal_time = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]); if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]) out->ftm.min_time_between_measurements = diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index c31d2af5dd22..8aae9273b7c1 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1768,15 +1768,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb) { struct sock *s; +again: write_lock_bh(&x25_list_lock); sk_for_each(s, &x25_list) { if (x25_sk(s)->neighbour == nb) { + sock_hold(s); write_unlock_bh(&x25_list_lock); lock_sock(s); - x25_disconnect(s, ENETUNREACH, 0, 0); + if (x25_sk(s)->neighbour == nb) + x25_disconnect(s, ENETUNREACH, 0, 0); release_sock(s); - write_lock_bh(&x25_list_lock); + sock_put(s); + goto again; } } write_unlock_bh(&x25_list_lock); diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 4cd7211ccf48..419288eec4bb 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -61,12 +61,23 @@ struct snd_seq_timer *snd_seq_timer_new(void) void snd_seq_timer_delete(struct snd_seq_timer **tmr) { struct snd_seq_timer *t = *tmr; - *tmr = NULL; + struct snd_timer_instance *ti; if (t == NULL) { pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n"); return; } + + scoped_guard(spinlock_irq, &t->lock) { + ti = t->timeri; + t->timeri = NULL; + } + if (ti) { + snd_timer_close(ti); + snd_timer_instance_free(ti); + } + + *tmr = NULL; t->running = 0; /* reset time */ diff --git a/sound/core/timer.c b/sound/core/timer.c index 51c6ac4df9f4..07ef127b29e1 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -489,10 +489,20 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri, snd_timer_stop(timeri); if (timer) { + struct snd_timer_instance *slave; + bool busy; + timer->num_instances--; - /* wait, until the active callback is finished */ + /* unqueue then drain the slaves' callbacks before remove_slave_links() severs them */ spin_lock_irq(&timer->lock); - while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { + list_for_each_entry(slave, &timeri->slave_list_head, open_list) + list_del_init(&slave->ack_list); + for (;;) { + busy = timeri->flags & SNDRV_TIMER_IFLG_CALLBACK; + list_for_each_entry(slave, &timeri->slave_list_head, open_list) + busy |= slave->flags & SNDRV_TIMER_IFLG_CALLBACK; + if (!busy) + break; spin_unlock_irq(&timer->lock); udelay(10); spin_lock_irq(&timer->lock); @@ -927,12 +937,15 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ack_list_head = &timer->ack_list_head; else ack_list_head = &timer->sack_list_head; - if (list_empty(&ti->ack_list)) + /* don't requeue an instance whose callback is still running */ + if (list_empty(&ti->ack_list) && + !(ti->flags & SNDRV_TIMER_IFLG_CALLBACK)) list_add_tail(&ti->ack_list, ack_list_head); list_for_each_entry(ts, &ti->slave_active_head, active_list) { ts->pticks = ti->pticks; ts->resolution = resolution; - if (list_empty(&ts->ack_list)) + if (list_empty(&ts->ack_list) && + !(ts->flags & SNDRV_TIMER_IFLG_CALLBACK)) list_add_tail(&ts->ack_list, ack_list_head); } } diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 1f4d646724ed..a4a2f2f0540c 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -1688,6 +1688,9 @@ int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, per_pin->channels = substream->runtime->channels; per_pin->setup = true; + if (spec->ops.prepare) + spec->ops.prepare(codec, per_pin); + if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, substream); diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h index 548241ad3fa9..dce9a335cff2 100644 --- a/sound/hda/codecs/hdmi/hdmi_local.h +++ b/sound/hda/codecs/hdmi/hdmi_local.h @@ -74,6 +74,15 @@ struct hdmi_ops { hda_nid_t pin_nid, int dev_id, u32 stream_tag, int format); + /* + * Optional hook invoked at the beginning of the PCM prepare + * sequence, before the audio infoframe and stream format are + * (re)programmed. Used to disable keep-alive / silent stream so + * that the format change is not done while keep-alive is active. + */ + void (*prepare)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin); + void (*pin_cvt_fixup)(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid); diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index 6a7882544ab7..73141046d502 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -418,6 +418,28 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); } +/* + * prepare ops override for HSW+ + * + * Disable keep-alive before the converter format and audio infoframe are + * reprogrammed by the PCM prepare sequence. Changing the audio format (e.g. + * the channel count when switching to multichannel PCM) while a keep-alive + * stream is active is not safe, so release keep-alive here, early in the + * sequence. It is re-enabled once the new stream has been set up, in + * i915_hsw_setup_stream(). + */ +static void i915_hsw_prepare(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin->silent_stream) { + silent_stream_set_kae(codec, per_pin, false); + /* wait for pending transfers in codec to clear */ + usleep_range(100, 200); + } +} + /* setup_stream ops override for HSW+ */ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, int dev_id, u32 stream_tag, @@ -435,15 +457,16 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, haswell_verify_D0(codec, cvt_nid, pin_nid); - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - silent_stream_set_kae(codec, per_pin, false); - /* wait for pending transfers in codec to clear */ - usleep_range(100, 200); - } - res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, stream_tag, format); + /* + * Keep-alive was disabled in i915_hsw_prepare(), re-enable it now. + * The pin lookup above resolves to the same per_pin that prepare + * used (pin_nid comes from that per_pin), so this stays balanced; a + * NULL per_pin only occurs on a lookup failure that also implies no + * active keep-alive stream to restore. + */ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { usleep_range(100, 200); silent_stream_set_kae(codec, per_pin, true); @@ -607,6 +630,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, codec->depop_delay = 0; codec->auto_runtime_pm = 1; + spec->ops.prepare = i915_hsw_prepare; spec->ops.setup_stream = i915_hsw_setup_stream; spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; spec->ops.silent_stream = i915_set_silent_stream; diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 443bc92c5e4b..379e1458f4ac 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3773,6 +3773,15 @@ static void alc245_hp_spk_mute_led_update(void *private_data, int enabled) alc_update_coef_idx(codec, 0x0b, 0x0c, val); } +static void alc245_hp_spk_mute_led_update_inverted(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + unsigned int val; + + val = enabled ? 0x04 : 0x08; /* inverted: 0x04 led off, 0x08 led on */ + alc_update_coef_idx(codec, 0x0b, 0x0c, val); +} + /* JD2: mute led GPIO3: micmute led */ static void alc245_tas2781_i2c_hp_fixup_muteled(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -3801,6 +3810,33 @@ static void alc245_tas2781_i2c_hp_fixup_muteled(struct hda_codec *codec, alc245_fixup_hp_mute_led_coefbit(codec, fix, action); alc285_fixup_hp_coef_micmute_led(codec, fix, action); } + +/* Same as alc245_tas2781_i2c_hp_fixup_muteled(), but with inverted speaker + * mute LED polarity. The HP ZBook 8 G2a 14/16 (0x103c:0x8f94, 0x103c:0x8f95) + * are speaker-only models without an HP pin, so the LED is driven directly + * through the vmaster_mute hook. + */ +static void alc245_tas2781_i2c_hp_fixup_muteled_inverted(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const hda_nid_t conn[] = { 0x02 }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.vmaster_mute.hook = alc245_hp_spk_mute_led_update_inverted; + spec->gen.vmaster_mute_led = 1; + spec->gen.auto_mute_via_amp = 1; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + case HDA_FIXUP_ACT_INIT: + alc245_hp_spk_mute_led_update_inverted(codec, !spec->gen.master_mute); + break; + } + + tas2781_fixup_txnw_i2c(codec, fix, action); + alc285_fixup_hp_coef_micmute_led(codec, fix, action); +} /* * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly * at PM resume @@ -3903,6 +3939,7 @@ enum { ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, ALC269VC_FIXUP_INFINIX_Y4_MAX, + ALC269VC_FIXUP_LUNNEN_GROUND_14, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -3983,6 +4020,7 @@ enum { ALC256_FIXUP_HUAWEI_MACH_WX9_PINS, ALC298_FIXUP_HUAWEI_MBX_STEREO, ALC295_FIXUP_HP_X360, + ALC295_FIXUP_HP_PAVILION_X360, ALC221_FIXUP_HP_HEADSET_MIC, ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, ALC295_FIXUP_HP_AUTO_MUTE, @@ -4163,6 +4201,7 @@ enum { ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE, ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED, ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED, + ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED, ALC288_FIXUP_SURFACE_SWAP_DACS, ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO, ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY, @@ -4370,6 +4409,13 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC269VC_FIXUP_LUNNEN_GROUND_14] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* internal speaker */ + { } + }, + }, [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -5306,6 +5352,19 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3 }, + [ALC295_FIXUP_HP_PAVILION_X360] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* force amp gain and processing state */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7770 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0d }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3000 }, + {} + }, + .chained = true, + .chain_id = ALC295_FIXUP_HP_X360 + }, [ALC221_FIXUP_HP_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -6734,6 +6793,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc245_tas2781_i2c_hp_fixup_muteled, }, + [ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_tas2781_i2c_hp_fixup_muteled_inverted, + }, [ALC288_FIXUP_SURFACE_SWAP_DACS] = { .type = HDA_FIXUP_FUNC, .v.func = alc288_fixup_surface_swap_dacs, @@ -6932,6 +6995,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), SND_PCI_QUIRK(0x1028, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x1028, 0x0c90, "Alienware x16 R2", ALC289_FIXUP_DUAL_SPK), SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC295_FIXUP_DELL_TAS2781_I2C), SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC295_FIXUP_DELL_TAS2781_I2C), SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), @@ -6943,7 +7007,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0e6b, "Dell Pro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0d56, "Dell Pro Tower QCT1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0d58, "Dell Pro Micro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0d5a, "Dell Pro Slim QCS1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0e6b, "Dell Pro Tower QCT1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0e6c, "Dell Pro Slim QCS1255", ALC236_FIXUP_DELL_HP_POP_NOISE), SND_PCI_QUIRK(0x1028, 0x0e6d, "Dell Pro Micro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), @@ -7017,6 +7085,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3), + HDA_CODEC_QUIRK(0x103c, 0x8486, "HP Pavilion x360", ALC295_FIXUP_HP_PAVILION_X360), SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), @@ -7172,6 +7241,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ab8, "HP EliteBook 830 G8 Notebook PC (MB 8AB8)", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -7382,6 +7452,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f57, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f94, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), + SND_PCI_QUIRK(0x103c, 0x8f95, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x1043, 0x1024, "ASUS Zephyrus G14 2025", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), @@ -7588,6 +7660,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP), + HDA_CODEC_QUIRK(0x144d, 0xc824, "Samsung ELECTRONICS (750XBE/730XBE)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc876, "Samsung 730QED (NP730QED-KA2US)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), @@ -7953,6 +8026,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), + SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 64a5bd895fd1..237059ef22f5 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1434,10 +1434,19 @@ static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle guid_parse(CS35L41_UUID, &guid); if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) { - ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL); + ret = acpi_evaluate_dsm_typed(handle, &guid, 0, + CS35L41_DSM_GET_MUTE, NULL, + ACPI_TYPE_BUFFER); + if (!ret) return -EINVAL; + if (!ret->buffer.length || !ret->buffer.pointer) { + ACPI_FREE(ret); + return -EINVAL; + } + mute = *ret->buffer.pointer; + ACPI_FREE(ret); dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute); } diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 69a22fdfeedb..624db967f17b 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -354,6 +354,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h) if (ret < 0 || ret >= sizeof(var8) - 1) { dev_err(p->dev, "%s: Read %s failed\n", __func__, var8); + cd->total_sz = 0; return -EINVAL; } /* @@ -371,6 +372,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h) dev_warn(p->dev, "Dev %d: Caldat[%d] read failed %ld\n", i, j, status); + cd->total_sz = 0; return -EINVAL; } bedata = cpu_to_be32(*(uint32_t *)&data[offset]); @@ -382,6 +384,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h) if (cd->total_sz != offset) { dev_err(p->dev, "%s: tot_size(%lu) and offset(%u) mismatch\n", __func__, cd->total_sz, offset); + cd->total_sz = 0; return -EINVAL; } diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 0d977f4f758d..051946562725 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -44,6 +44,18 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "83W5"), }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), + DMI_MATCH(DMI_PRODUCT_NAME, "TUF Gaming A14 FA401EA"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), + }, + }, {} }; diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index d6df7de70b27..af1dce9c9b8a 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -525,6 +525,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WIG"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), DMI_MATCH(DMI_PRODUCT_NAME, "Raider A18 HX A9WJG"), } }, diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 3afcef2dfa35..c0bf45b76cb8 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -17,11 +17,17 @@ static const struct snd_soc_dapm_widget bt_sco_widgets[] = { SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("BT_SCO_RX_WB", "WB Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("BT_SCO_TX_WB", "WB Capture", 0, + SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_dapm_route bt_sco_routes[] = { { "BT_SCO_TX", NULL, "RX" }, { "TX", NULL, "BT_SCO_RX" }, + { "BT_SCO_TX_WB", NULL, "RX" }, + { "TX", NULL, "BT_SCO_RX_WB" }, }; static struct snd_soc_dai_driver bt_sco_dai[] = { @@ -45,14 +51,14 @@ static struct snd_soc_dai_driver bt_sco_dai[] = { { .name = "bt-sco-pcm-wb", .playback = { - .stream_name = "Playback", + .stream_name = "WB Playback", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { - .stream_name = "Capture", + .stream_name = "WB Capture", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 0f64ab628b03..4f6ddf1c5a3f 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -55,9 +55,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) if (ret != 0) return ret; - ret = cs35l56_init(cs35l56); - if (ret == 0) - ret = cs35l56_irq_request(&cs35l56->base, client->irq); + ret = cs35l56_irq_request(&cs35l56->base, client->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index 9bc9b7c98390..b1eb924a5b6c 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -44,9 +44,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) if (ret != 0) return ret; - ret = cs35l56_init(cs35l56); - if (ret == 0) - ret = cs35l56_irq_request(&cs35l56->base, spi->irq); + ret = cs35l56_irq_request(&cs35l56->base, spi->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 570a68829ccd..063ef7a70de0 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -2008,6 +2008,16 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err; } + /* + * On SoundWire the cs35l56_init() cannot be run until after the + * device has been enumerated by the SoundWire core. + */ + if (!cs35l56->sdw_peripheral) { + ret = cs35l56_init(cs35l56); + if (ret) + goto err_remove_wm_adsp; + } + ret = snd_soc_register_component(cs35l56->base.dev, &soc_component_dev_cs35l56, cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); @@ -2022,6 +2032,11 @@ err_remove_wm_adsp: wm_adsp2_remove(&cs35l56->dsp); err: + if (pm_runtime_enabled(cs35l56->base.dev)) { + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); + pm_runtime_disable(cs35l56->base.dev); + } + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); @@ -2108,7 +2123,7 @@ post_soft_reset: return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n"); cs35l56->base.init_done = true; - complete(&cs35l56->init_completion); + complete_all(&cs35l56->init_completion); return 0; } diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c index 5f381fe063e8..d794ff918b6f 100644 --- a/sound/soc/codecs/fs210x.c +++ b/sound/soc/codecs/fs210x.c @@ -607,6 +607,7 @@ tag_power_down: regcache_cache_bypass(fs210x->regmap, false); if (!ret) { + regcache_cache_only(fs210x->regmap, false); regcache_mark_dirty(fs210x->regmap); regcache_sync(fs210x->regmap); fs210x->is_inited = true; diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index bd16d5326a23..dcbeb9618195 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -12,6 +12,7 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/limits.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> @@ -1100,13 +1101,42 @@ static int tasdevice_load_block_kernel( return 0; } +static int tasdevice_fw_strnlen(const struct firmware *fmw, int offset) +{ + const u8 *start; + const u8 *nul; + size_t remaining; + size_t len; + + if (offset < 0 || offset >= fmw->size) + return -EINVAL; + + start = fmw->data + offset; + remaining = fmw->size - offset; + nul = memchr(start, '\0', remaining); + if (!nul) + return -EINVAL; + + len = nul - start; + if (len > INT_MAX) + return -EOVERFLOW; + + return len; +} + static int fw_parse_variable_hdr(struct tasdevice_priv *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, const struct firmware *fmw, int offset) { const unsigned char *buf = fmw->data; - int len = strlen((char *)&buf[offset]); + int len; + len = tasdevice_fw_strnlen(fmw, offset); + if (len < 0) { + dev_err(tas_priv->dev, "%s: Description error\n", __func__); + offset = len; + goto out; + } len++; if (offset + len + 8 > fmw->size) { @@ -1238,7 +1268,12 @@ static int fw_parse_data(struct tasdevice_fw *tas_fmw, memcpy(img_data->name, &data[offset], 64); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + offset = n; + goto out; + } n++; if (offset + n + 2 > fmw->size) { dev_err(tas_fmw->dev, "%s: Description error\n", __func__); @@ -1309,7 +1344,12 @@ static int fw_parse_program_data(struct tasdevice_priv *tas_priv, } offset += 64; - n = strlen((char *)&buf[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } /* skip '\0' and 5 unused bytes */ n += 6; if (offset + n > fmw->size) { @@ -1372,7 +1412,12 @@ static int fw_parse_configuration_data( memcpy(config->name, &data[offset], 64); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } n += 15; if (offset + n > fmw->size) { dev_err(tas_priv->dev, "Description err\n"); @@ -2134,7 +2179,8 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, { struct tasdevice_calibration *calibration; unsigned char *data = (unsigned char *)fmw->data; - unsigned int i, n; + unsigned int i; + int n; if (offset + 2 > fmw->size) { dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__); @@ -2166,7 +2212,12 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, calibration = &(tas_fmw->calibrations[i]); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } /* skip '\0' and 2 unused bytes */ n += 3; if (offset + n > fmw->size) { diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 9661602b53c5..d232c8f53061 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -808,6 +808,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; unsigned int ofs = sai->soc_data->reg_offset; + int adir = tx ? RX : TX; + int dir = tx ? TX : RX; /* Clear xMR to avoid channel swap with mclk_with_tere enabled case */ regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0); @@ -815,10 +817,29 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), FSL_SAI_CR3_TRCE_MASK, 0); - if (!sai->is_consumer_mode[tx] && - sai->mclk_streams & BIT(substream->stream)) { - clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); - sai->mclk_streams &= ~BIT(substream->stream); + if (!sai->is_consumer_mode[tx]) { + bool adir_active = !!(sai->mclk_streams & BIT(!substream->stream)); + /* + * If opposite stream provides clocks for synchronous mode and + * it is inactive, Clear BYP and BCI + */ + if (fsl_sai_dir_is_synced(sai, adir) && !adir_active) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), + FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0); + /* + * Clear BYP and BCI of current stream if either of: + * 1. current stream doesn't provide clocks for synchronous mode + * 2. current stream provides clocks for synchronous mode but no + * more stream is active. + */ + if (!fsl_sai_dir_is_synced(sai, dir) || !adir_active) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), + FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0); + + if (sai->mclk_streams & BIT(substream->stream)) { + clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); + sai->mclk_streams &= ~BIT(substream->stream); + } } return 0; diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index a4518fefad69..43438af1e1c6 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -496,11 +496,15 @@ static void imx_aif_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai; int i; - for_each_rtd_cpu_dais(rtd, i, cpu_dai) - snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + if (!snd_soc_dai_active(cpu_dai)) + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + } - for_each_rtd_codec_dais(rtd, i, codec_dai) - snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (!snd_soc_dai_active(codec_dai)) + snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); + } } static const struct snd_soc_ops imx_aif_ops = { diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 59eb0328bbb5..c527d575d1ed 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -849,6 +849,8 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }; static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = { + SND_PCI_QUIRK(0x1028, 0x0e53, "Dell XPS WCL", SOC_SDW_SIDECAR_AMPS), + SND_PCI_QUIRK(0x1028, 0x0e54, "Dell XPS PTL", SOC_SDW_SIDECAR_AMPS), SND_PCI_QUIRK(0x1043, 0x1e13, "ASUS Zenbook S14", SOC_SDW_CODEC_MIC), SND_PCI_QUIRK(0x1043, 0x1f43, "ASUS Zenbook S16", SOC_SDW_CODEC_MIC), SND_PCI_QUIRK(0x17aa, 0x2347, "Lenovo P16", SOC_SDW_CODEC_MIC), diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index f4e22af594fa..93f511fb6190 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -2237,6 +2237,7 @@ static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = { .reg_dac_fifoc = REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31), .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, .has_reset = true, + .playback_only = true, .dma_max_burst = SUN4I_DMA_MAX_BURST, }; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 41149561aa06..0de7c9d94259 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2346,6 +2346,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ QUIRK_FLAG_FIXED_RATE), + DEVICE_FLG(0x0ecb, 0x2125, /* JBL Quantum650 Wireless */ + QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0fd9, 0x0008, /* Hauppauge HVR-950Q */ diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index adb25146e88c..ea7044f30adc 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -82,6 +82,7 @@ CONFIG_NET_SCH_BPF=y CONFIG_NET_SCH_FQ=y CONFIG_NET_SCH_INGRESS=y CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_RED=y CONFIG_NET_SCHED=y CONFIG_NETDEVSIM=y CONFIG_NETFILTER=y diff --git a/tools/testing/selftests/bpf/prog_tests/tc_qevent.c b/tools/testing/selftests/bpf/prog_tests/tc_qevent.c new file mode 100644 index 000000000000..67e1d17567ab --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/tc_qevent.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <test_progs.h> +#include <network_helpers.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <string.h> + +#include "test_tc_qevent.skel.h" + +#define NS_TX "tc_qevent_tx" +#define NS_RX "tc_qevent_rx" +#define IP_TX "10.255.0.1" +#define IP_RX "10.255.0.2" +#define PIN_PATH "/sys/fs/bpf/tc_qevent_redirect" + +static void blast_udp(void) +{ + struct sockaddr_in dst = {}; + char buf[1400] = {}; + int fd, i; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (!ASSERT_GE(fd, 0, "udp socket")) + return; + + dst.sin_family = AF_INET; + dst.sin_port = htons(12345); + inet_pton(AF_INET, IP_RX, &dst.sin_addr); + + /* + * Push far more than the RED queue can hold. Once qavg crosses qth_min + * every further packet hits the congestion_drop / early_drop qevent. + */ + for (i = 0; i < 50000; i++) + sendto(fd, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *)&dst, sizeof(dst)); + + close(fd); +} + +static void run_qevent_redirect(struct bpf_program *prog, __u64 *counter) +{ + struct nstoken *tok = NULL; + int err; + + SYS_NOFAIL("ip netns del %s", NS_TX); + SYS_NOFAIL("ip netns del %s", NS_RX); + unlink(PIN_PATH); + + err = bpf_program__pin(prog, PIN_PATH); + if (!ASSERT_OK(err, "pin prog")) + return; + + SYS(unpin, "ip netns add %s", NS_TX); + SYS(del_tx, "ip netns add %s", NS_RX); + SYS(del_rx, "ip -n %s link add veth0 type veth peer name veth1 netns %s", NS_TX, NS_RX); + SYS(del_rx, "ip -n %s addr add %s/24 dev veth0", NS_TX, IP_TX); + SYS(del_rx, "ip -n %s link set veth0 up", NS_TX); + SYS(del_rx, "ip -n %s addr add %s/24 dev veth1", NS_RX, IP_RX); + SYS(del_rx, "ip -n %s link set veth1 up", NS_RX); + + tok = open_netns(NS_TX); + if (!ASSERT_OK_PTR(tok, "open_netns")) + goto del_rx; + + SYS(close_ns, "tc qdisc add dev veth0 root handle 1: htb default 1"); + SYS(close_ns, "tc class add dev veth0 parent 1: classid 1:1 htb rate 1mbit ceil 1mbit"); + + if (system("tc qdisc add dev veth0 parent 1:1 handle 11: red " + "limit 500000 avpkt 1000 probability 1 min 5000 max 6000 " + "burst 6 qevent early_drop block 10 2>/dev/null")) { + test__skip(); + goto close_ns; + } + + if (system("tc filter add block 10 bpf da object-pinned " + PIN_PATH " 2>/dev/null")) { + test__skip(); + goto close_ns; + } + + blast_udp(); + ASSERT_GT(*counter, 0, "qevent classifier ran"); +close_ns: + close_netns(tok); +del_rx: + SYS_NOFAIL("ip netns del %s", NS_RX); +del_tx: + SYS_NOFAIL("ip netns del %s", NS_TX); +unpin: + bpf_program__unpin(prog, PIN_PATH); +} + +void test_tc_qevent(void) +{ + struct test_tc_qevent *skel; + + skel = test_tc_qevent__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + if (test__start_subtest("redirect_verdict")) + run_qevent_redirect(skel->progs.qevent_redirect_verdict, + &skel->bss->verdict_calls); + if (test__start_subtest("redirect_helper")) + run_qevent_redirect(skel->progs.qevent_redirect_helper, + &skel->bss->helper_calls); + + test_tc_qevent__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/test_tc_qevent.c b/tools/testing/selftests/bpf/progs/test_tc_qevent.c new file mode 100644 index 000000000000..1529c111f4aa --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_tc_qevent.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> + +int redirect_ifindex = 1; +__u64 verdict_calls = 0; +__u64 helper_calls = 0; + +SEC("tc") +int qevent_redirect_verdict(struct __sk_buff *skb) +{ + __sync_fetch_and_add(&verdict_calls, 1); + return TCX_REDIRECT; +} + +SEC("tc") +int qevent_redirect_helper(struct __sk_buff *skb) +{ + __sync_fetch_and_add(&helper_calls, 1); + return bpf_redirect(redirect_ifindex, 0); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c index 75a2e3f48d0f..67dc352addfd 100644 --- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c +++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c @@ -185,6 +185,16 @@ int arg_tag_nonnull_ptr_good(void *ctx) return subprog_nonnull_ptr_good(&x, &y); } +SEC("?raw_tp") +__failure __log_level(2) +__msg("R1 is expected to be non-NULL") +int arg_tag_nonnull_ptr_null_bad(void *ctx) +{ + int y = 74; + + return subprog_nonnull_ptr_good(NULL, &y); +} + /* this global subprog can be now called from many types of entry progs, each * with different context type */ diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config index 91d4fd410914..2070e890e064 100644 --- a/tools/testing/selftests/drivers/net/config +++ b/tools/testing/selftests/drivers/net/config @@ -4,6 +4,8 @@ CONFIG_DEBUG_INFO_BTF_MODULES=n CONFIG_INET_PSP=y CONFIG_IPV6=y CONFIG_MACSEC=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_CLS_BPF=y CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y CONFIG_NETCONSOLE_EXTENDED_LOG=y @@ -11,6 +13,7 @@ CONFIG_NETDEVSIM=m CONFIG_NETKIT=y CONFIG_NET_SCH_ETF=m CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_INGRESS=y CONFIG_PPP=y CONFIG_PPPOE=y CONFIG_VLAN_8021Q=m diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh index 96d704b8d9d9..4436567abc94 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh @@ -50,7 +50,7 @@ do # Send the message echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk - busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true # Make sure the message was received in the dst part # and exit validate_msg "${OUTPUT_FILE}" diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh index 0dc7280c3080..fc3db40c1df5 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh @@ -104,7 +104,7 @@ wait_local_port_listen "${NAMESPACE}" "${PORT}" udp # Send the message echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk -busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" +busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true # Check if the message was not corrupted validate_fragmented_result "${OUTPUT_FILE}" @@ -117,6 +117,6 @@ disable_release_append listen_port_and_save_to "${OUTPUT_FILE}" & wait_local_port_listen "${NAMESPACE}" "${PORT}" udp echo "${MSG}: ${TARGET}" > /dev/kmsg -busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" +busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true validate_fragmented_result "${OUTPUT_FILE}" exit "${ksft_pass}" diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh index d9111f2102bc..b379dff9087e 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh @@ -108,7 +108,7 @@ do # Send the message echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk - busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true # Make sure the message was received in the dst part # and exit validate_msg "${OUTPUT_FILE}" diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh index 3fb8c4afe3d2..7089f7bd1e34 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh @@ -197,7 +197,7 @@ function runtest { # Send the message taskset -c "${CPU}" echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk - busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true } # ========== # diff --git a/tools/testing/selftests/drivers/net/settings b/tools/testing/selftests/drivers/net/settings new file mode 100644 index 000000000000..eef533824a3c --- /dev/null +++ b/tools/testing/selftests/drivers/net/settings @@ -0,0 +1 @@ +timeout=360 diff --git a/tools/testing/selftests/kvm/x86/sev_init2_tests.c b/tools/testing/selftests/kvm/x86/sev_init2_tests.c index 8db88c355f16..689390c10f7c 100644 --- a/tools/testing/selftests/kvm/x86/sev_init2_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_init2_tests.c @@ -130,12 +130,18 @@ int main(int argc, char *argv[]) KVM_X86_SEV_VMSA_FEATURES, &supported_vmsa_features); - have_sev = kvm_cpu_has(X86_FEATURE_SEV); - TEST_ASSERT(have_sev == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)), - "sev: KVM_CAP_VM_TYPES (%x) does not match cpuid (checking %x)", - kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_VM); + /* + * Whether a VM type is available depends on KVM, not just CPUID: e.g. + * when all SEV ASIDs are assigned to SEV-SNP, KVM does not offer the + * SEV VM type even though X86_FEATURE_SEV is set. Derive availability + * from KVM_CAP_VM_TYPES and only assert the one-way implication that a + * type offered by KVM must also be reported in CPUID. + */ + have_sev = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM); + TEST_ASSERT(!have_sev || kvm_cpu_has(X86_FEATURE_SEV), + "sev: SEV_VM supported without SEV in CPUID"); - TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)); + TEST_REQUIRE(have_sev); have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM); TEST_ASSERT(!have_sev_es || kvm_cpu_has(X86_FEATURE_SEV_ES), diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c index 6b2cbe2a90b7..bf27b6187afa 100644 --- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c +++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c @@ -247,7 +247,14 @@ int main(int argc, char *argv[]) { TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); - test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0); + /* + * Only exercise VM types the host actually offers. CPUID reporting + * SEV does not guarantee KVM offers the SEV VM type: when all SEV + * ASIDs are assigned to SEV-SNP, KVM_X86_SEV_VM is unavailable even + * though X86_FEATURE_SEV is set. Gate every type on KVM_CAP_VM_TYPES. + */ + if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)) + test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0); if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM)) test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES); diff --git a/tools/testing/selftests/net/af_unix/config b/tools/testing/selftests/net/af_unix/config index b5429c15a53c..41dbb03c747e 100644 --- a/tools/testing/selftests/net/af_unix/config +++ b/tools/testing/selftests/net/af_unix/config @@ -1,3 +1,4 @@ CONFIG_AF_UNIX_OOB=y CONFIG_UNIX=y CONFIG_UNIX_DIAG=m +CONFIG_USER_NS=y diff --git a/tools/testing/selftests/net/bridge_vlan_dump.sh b/tools/testing/selftests/net/bridge_vlan_dump.sh index ad66731d2a6f..90e18e2104e3 100755 --- a/tools/testing/selftests/net/bridge_vlan_dump.sh +++ b/tools/testing/selftests/net/bridge_vlan_dump.sh @@ -13,6 +13,7 @@ ALL_TESTS=" vlan_range_mcast_max_groups vlan_range_mcast_n_groups vlan_range_mcast_enabled + vlan_range_pvid " setup_prepare() @@ -191,6 +192,28 @@ vlan_range_mcast_enabled() log_test "VLAN range grouping with mcast_enabled" } +vlan_range_pvid() +{ + RET=0 + + ip -n "$NS" link set dev br0 type bridge vlan_default_pvid 1 + check_err $? "Failed to configure default PVID" + defer ip -n "$NS" link set dev br0 type bridge vlan_default_pvid 0 + + bridge -n "$NS" vlan add vid 2 dev dummy0 untagged + check_err $? "Failed to add VLAN 2" + defer bridge -n "$NS" vlan del vid 2 dev dummy0 + + bridge -n "$NS" -d vlan show dev dummy0 | + grep -Eq '(^|[[:space:]])2([[:space:]]|$)' + check_err $? "VLAN following PVID is missing from detailed dump" + + bridge -n "$NS" -d vlan show dev dummy0 | grep -q "1-2" + check_fail $? "PVID was incorrectly included in a VLAN range" + + log_test "PVID is isolated from VLAN dump ranges" +} + # Verify the newest tested option is supported if ! bridge vlan help 2>&1 | grep -q "neigh_suppress"; then echo "SKIP: iproute2 too old, missing per-VLAN neighbor suppression support" diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index e9ae1806ab07..30a809752d1b 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -212,7 +212,7 @@ make_connection() ./mptcp_connect -s MPTCP -w 300 -p $app_port -l $listen_addr > /dev/null 2>&1 & local server_pid=$! - mptcp_lib_wait_local_port_listen "${ns1}" "${port}" + mptcp_lib_wait_local_port_listen "${ns1}" "${app_port}" # Run the client, transfer $file and stay connected to the server # to conduct tests diff --git a/tools/testing/selftests/net/openvswitch/config b/tools/testing/selftests/net/openvswitch/config new file mode 100644 index 000000000000..c659749cd086 --- /dev/null +++ b/tools/testing/selftests/net/openvswitch/config @@ -0,0 +1,16 @@ +CONFIG_GENEVE=m +CONFIG_INET_DIAG=y +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_OVS=y +CONFIG_OPENVSWITCH=m +CONFIG_OPENVSWITCH_GENEVE=m +CONFIG_OPENVSWITCH_GRE=m +CONFIG_OPENVSWITCH_VXLAN=m +CONFIG_PSAMPLE=m +CONFIG_VETH=y +CONFIG_VLAN_8021Q=y +CONFIG_VXLAN=m diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config index d6cf033d555e..6b424762e46e 100644 --- a/tools/testing/selftests/net/ovpn/config +++ b/tools/testing/selftests/net/ovpn/config @@ -4,6 +4,7 @@ CONFIG_CRYPTO_CHACHA20POLY1305=y CONFIG_CRYPTO_GCM=y CONFIG_DST_CACHE=y CONFIG_INET=y +CONFIG_IPV6=y CONFIG_NET=y CONFIG_NETFILTER=y CONFIG_NET_UDP_TUNNEL=y @@ -11,3 +12,4 @@ CONFIG_NF_TABLES=m CONFIG_NF_TABLES_INET=y CONFIG_OVPN=m CONFIG_STREAM_PARSER=y +CONFIG_VETH=y diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c index d40953375c86..f4effa7580c0 100644 --- a/tools/testing/selftests/net/ovpn/ovpn-cli.c +++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c @@ -1785,7 +1785,7 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host, const char *service, const char *vpnip) { int ret; - struct addrinfo *result; + struct addrinfo *result = NULL; struct addrinfo hints = { .ai_family = ovpn->sa_family, .ai_socktype = SOCK_DGRAM, @@ -1809,6 +1809,8 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host, } memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen); + freeaddrinfo(result); + result = NULL; } if (vpnip) { diff --git a/tools/testing/selftests/net/ovpn/settings b/tools/testing/selftests/net/ovpn/settings new file mode 100644 index 000000000000..ba4d85f74cd6 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/settings @@ -0,0 +1 @@ +timeout=90 diff --git a/tools/testing/selftests/net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt new file mode 100644 index 000000000000..3fc2de03658a --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// RFC 9293 Section 3.10.7.4: in SYN-RECEIVED, an exact RST resets +// the connection. A non-exact in-window RST elicits a challenge ACK, +// while an out-of-window RST is silently discarded. + +`./defaults.sh` + +// An exact RST removes the request socket. + 0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 <mss 1000,sackOK,nop,nop,nop,wscale 0> + +0 > S. 0:0(0) ack 1 <...> + +0 < R 1:1(0) win 1000 + +.1 < . 1:1(0) ack 1 win 1000 + +0 > R 1:1(0) + +0 close(3) = 0 + +// A non-exact in-window RST gets a challenge ACK and the request survives. + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 <mss 1000,sackOK,nop,nop,nop,wscale 0> + +0 > S. 0:0(0) ack 1 <...> + +0 < R 2:2(0) win 1000 + +0 > . 1:1(0) ack 1 + +0 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +0 close(4) = 0 + +0 close(3) = 0 + +// RST sequence validation precedes ACK validation. Even an RST|ACK +// with an unacceptable ACK value gets a challenge ACK. + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 <mss 1000,sackOK,nop,nop,nop,wscale 0> + +0 > S. 0:0(0) ack 1 <...> + +0 < R. 2:2(0) ack 100 win 1000 + +0 > . 1:1(0) ack 1 + +0 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +0 close(4) = 0 + +0 close(3) = 0 + +// An out-of-window RST is silent and does not remove the request. + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 <mss 1000,sackOK,nop,nop,nop,wscale 0> + +0 > S. 0:0(0) ack 1 <...> + +0 < R 100001:100001(0) win 1000 + +.1 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +0 close(4) = 0 + +0 close(3) = 0 diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c index cf106a49b55e..abe488bac50b 100644 --- a/tools/testing/selftests/net/tun.c +++ b/tools/testing/selftests/net/tun.c @@ -42,19 +42,19 @@ static struct in_addr param_ipaddr4_inner_src = { }; static struct in6_addr param_ipaddr6_outer_dst = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, }; static struct in6_addr param_ipaddr6_outer_src = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, }; static struct in6_addr param_ipaddr6_inner_dst = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, }; static struct in6_addr param_ipaddr6_inner_src = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, }; #ifndef BIT diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 76be0e4a7f0e..b4ff9f946565 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -2347,6 +2347,88 @@ static void test_stream_tx_credit_bounds_server(const struct test_opts *opts) close(fd); } +/* Test that many small packets don't cause a connection reset under pressure + * and that data integrity is preserved. Packet sizes vary randomly between + * 129 and 512 bytes, above GOOD_COPY_LEN (128) to bypass in-place coalescing + * in recv_enqueue, forcing each one into its own skb. Without receive queue + * collapsing, the per-skb overhead eventually exceeds buf_alloc and the + * connection is reset. + */ +#define COLLAPSE_PKT_MIN 129 +#define COLLAPSE_PKT_MAX 512 +#define COLLAPSE_TOTAL (2 * 1024 * 1024) + +static void test_stream_collapse_client(const struct test_opts *opts) +{ + unsigned char *data; + unsigned long hash; + size_t offset = 0; + int i, fd; + + data = malloc(COLLAPSE_TOTAL); + if (!data) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < COLLAPSE_TOTAL; i++) + data[i] = rand() & 0xff; + + fd = vsock_stream_connect(opts->peer_cid, opts->peer_port); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + while (offset < COLLAPSE_TOTAL) { + size_t pkt_size = COLLAPSE_PKT_MIN + + rand() % (COLLAPSE_PKT_MAX - COLLAPSE_PKT_MIN + 1); + + pkt_size = min(pkt_size, COLLAPSE_TOTAL - offset); + + send_buf(fd, data + offset, pkt_size, 0, pkt_size); + offset += pkt_size; + } + + hash = hash_djb2(data, COLLAPSE_TOTAL); + control_writeulong(hash); + + free(data); + close(fd); +} + +static void test_stream_collapse_server(const struct test_opts *opts) +{ + unsigned long hash, remote_hash; + unsigned char *data; + int fd; + + data = malloc(COLLAPSE_TOTAL); + if (!data) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + recv_buf(fd, data, COLLAPSE_TOTAL, 0, COLLAPSE_TOTAL); + + hash = hash_djb2(data, COLLAPSE_TOTAL); + remote_hash = control_readulong(); + if (hash != remote_hash) { + fprintf(stderr, "hash mismatch: local %lu remote %lu\n", + hash, remote_hash); + exit(EXIT_FAILURE); + } + + free(data); + close(fd); +} + static struct test_case test_cases[] = { { .name = "SOCK_STREAM connection reset", @@ -2546,6 +2628,11 @@ static struct test_case test_cases[] = { .run_client = test_stream_msg_peek_client, .run_server = test_stream_peek_after_recv_server, }, + { + .name = "SOCK_STREAM small packets backpressure", + .run_client = test_stream_collapse_client, + .run_server = test_stream_collapse_server, + }, {}, }; |