From d32bf877c0c3ebc345b444cbe009b3f44f9f8073 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Wed, 27 May 2026 15:55:06 -0700 Subject: perf/core: out-of-line and export perf_allow_cpu/tracepoint() These helpers are static inline in and reach into sysctl_perf_event_paranoid and security_perf_event_open(), neither of which is itself exported. The perf_allow_* trio is therefore asymmetric: built-in callers can use any of the three, but modular code can only call perf_allow_kernel(). Move both bodies into kernel/events/core.c next to perf_allow_kernel() and export them with EXPORT_SYMBOL_GPL, following the shape of commit 5e9629d0ae97 ("drivers/perf: arm_spe: Use perf_allow_kernel() for permissions"). Existing in-tree callers live in built-in arch and tracing code, so the change is invisible to them. Provide !CONFIG_PERF_EVENTS stubs that fall back to perfmon_capable(), so the helpers stay callable when perf is compiled out. Signed-off-by: John Hubbard Reviewed-by: Ashutosh Dixit Link: https://patch.msgid.link/20260527225507.2044027-2-ashutosh.dixit@intel.com Signed-off-by: Ashutosh Dixit --- include/linux/perf_event.h | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 48d851fbd8ea..5842552294c1 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1791,22 +1791,8 @@ static inline int perf_is_paranoid(void) } extern int perf_allow_kernel(void); - -static inline int perf_allow_cpu(void) -{ - if (sysctl_perf_event_paranoid > 0 && !perfmon_capable()) - return -EACCES; - - return security_perf_event_open(PERF_SECURITY_CPU); -} - -static inline int perf_allow_tracepoint(void) -{ - if (sysctl_perf_event_paranoid > -1 && !perfmon_capable()) - return -EPERM; - - return security_perf_event_open(PERF_SECURITY_TRACEPOINT); -} +extern int perf_allow_cpu(void); +extern int perf_allow_tracepoint(void); extern int perf_exclude_event(struct perf_event *event, struct pt_regs *regs); @@ -2023,6 +2009,19 @@ perf_event_pause(struct perf_event *event, bool reset) { return 0; } static inline int perf_exclude_event(struct perf_event *event, struct pt_regs *regs) { return 0; } +static inline int perf_allow_kernel(void) +{ + return perfmon_capable() ? 0 : -EACCES; +} +static inline int perf_allow_cpu(void) +{ + return perfmon_capable() ? 0 : -EACCES; +} +static inline int perf_allow_tracepoint(void) +{ + return perfmon_capable() ? 0 : -EPERM; +} + #endif /* !CONFIG_PERF_EVENTS */ #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_INTEL) -- cgit From bde05d3527eadaf3164bac6a04a938acfde14d0e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:16 +0200 Subject: drm/atomic: Expand atomic_create_state expectations for drm_private_obj The atomic_create_state callback documentation for planes, CRTCs, and connectors explicitly states the expected behaviour: the returned state must not be assigned to the object's state pointer, and hardware must not be touched. The drm_private_state_funcs.atomic_create_state documentation is missing this clarification. Add it for consistency. Reviewed-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-4-852346394200@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 1a80a8cdf269..88087910ab1a 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -265,7 +265,10 @@ struct drm_private_state_funcs { * @atomic_create_state: * * Allocates a pristine, initialized, state for the private - * object and returns it. + * object and returns it. This callback must have no side + * effects: in particular, the returned state must not be + * assigned to the object's state pointer and it must not affect + * the hardware state. * * RETURNS: * -- cgit From 8497ae67d3788fab2fdde38c8e51591f706debfd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:19 +0200 Subject: drm/colorop: Create drm_atomic_helper_colorop_create_state() Commit 47b5ac7daa46 ("drm/atomic: Add new atomic_create_state callback to drm_private_obj") introduced a new pattern for allocating drm object states. Instead of relying on the reset() callback, it created a new atomic_create_state hook. This is helpful because reset is a bit overloaded: it's used to create the initial software state, reset it, but also reset the hardware. It can also be used either at probe time, to create the initial state and possibly reset the hardware to an expected default, but also during suspend/resume. Both these cases come with different expectations too: during the initialization, we want to initialize all states, but during suspend/resume, drm_private_states for example are expected to be kept around. reset() also isn't fallible, which makes it harder to handle initialization errors properly. This is only really relevant for some drivers though, since all the helpers for reset only create a new state, and don't touch the hardware at all. It was thus decided to create a new hook that would allocate and initialize a pristine state without any side effect: atomic_create_state to untangle a bit some of it, and to separate the initialization with the actual reset one might need during a suspend/resume. Continue the transition to the new pattern with drm_colorop. Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-7-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_colorop.c | 23 +++++++++++++++++++++++ include/drm/drm_colorop.h | 2 ++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 4c4d0a953e35..c0eecde8c176 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -523,6 +523,29 @@ static void __drm_colorop_state_init(struct drm_colorop_state *colorop_state, } } +/** + * drm_atomic_helper_colorop_create_state - Allocates and initializes colorop atomic state + * @colorop: drm colorop + * + * Initializes a pristine @drm_colorop_state. + * + * RETURNS: + * Pointer to new colorop state, or ERR_PTR on failure. + */ +struct drm_colorop_state * +drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop) +{ + struct drm_colorop_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_colorop_state_init(state, colorop); + + return state; +} + /** * __drm_colorop_reset - reset state on colorop * @colorop: drm colorop diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index c873199c60da..b4b9e4f558ab 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -425,6 +425,8 @@ int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *col enum drm_colorop_lut3d_interpolation_type interpolation, uint32_t flags); +struct drm_colorop_state * +drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop); struct drm_colorop_state * drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop); -- cgit From 3e5656ea5e7360dc98ec38ecfa9b971ace390781 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:21 +0200 Subject: drm/atomic-state-helper: Rename __drm_atomic_helper_plane_state_reset() __drm_atomic_helper_plane_state_reset() is used to initialize a newly allocated drm_plane_state, and is being typically called by the drm_plane_funcs.reset implementation. Since we want to consolidate DRM objects state allocation around the atomic_create_state callback that will only allocate and initialize a new drm_plane_state instance, we will need to call __drm_atomic_helper_plane_state_reset() from both the reset and atomic_create hooks. To avoid any confusion, we can thus rename __drm_atomic_helper_plane_state_reset() to __drm_atomic_helper_plane_state_init(). Suggested-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-9-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 12 ++++++------ drivers/gpu/drm/i915/display/intel_plane.c | 2 +- include/drm/drm_atomic_state_helper.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index c8ccf8be5074..ee01700d4ca7 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -237,15 +237,15 @@ void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); /** - * __drm_atomic_helper_plane_state_reset - resets plane state to default values + * __drm_atomic_helper_plane_state_init - Initialize the plane state * @plane_state: atomic plane state, must not be NULL * @plane: plane object, must not be NULL * * Initializes the newly allocated @plane_state with default - * values. This is useful for drivers that subclass the CRTC state. + * values. This is useful for drivers that subclass the plane state. */ -void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, - struct drm_plane *plane) +void __drm_atomic_helper_plane_state_init(struct drm_plane_state *plane_state, + struct drm_plane *plane) { u64 val; @@ -297,7 +297,7 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, plane_state->hotspot_y = val; } } -EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_plane_state_init); /** * __drm_atomic_helper_plane_reset - reset state on plane @@ -315,7 +315,7 @@ void __drm_atomic_helper_plane_reset(struct drm_plane *plane, struct drm_plane_state *plane_state) { if (plane_state) - __drm_atomic_helper_plane_state_reset(plane_state, plane); + __drm_atomic_helper_plane_state_init(plane_state, plane); plane->state = plane_state; } diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index a1f9558d53af..43b3547096ae 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -67,7 +67,7 @@ static void intel_plane_state_reset(struct intel_plane_state *plane_state, { memset(plane_state, 0, sizeof(*plane_state)); - __drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base); + __drm_atomic_helper_plane_state_init(&plane_state->uapi, &plane->base); plane_state->scaler_id = -1; plane_state->fence_id = -1; diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 61a3b38ad49f..691c1ccfa4e0 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -53,7 +53,7 @@ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state); void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); -void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *state, +void __drm_atomic_helper_plane_state_init(struct drm_plane_state *state, struct drm_plane *plane); void __drm_atomic_helper_plane_reset(struct drm_plane *plane, struct drm_plane_state *state); -- cgit From 1a185ddecaf9609e76ed289037afd6cb289e7bee Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:22 +0200 Subject: drm/plane: Add new atomic_create_state callback Commit 47b5ac7daa46 ("drm/atomic: Add new atomic_create_state callback to drm_private_obj") introduced a new pattern for allocating drm object states. Instead of relying on the reset() callback, it created a new atomic_create_state hook. This is helpful because reset is a bit overloaded: it's used to create the initial software state, reset it, but also reset the hardware. It can also be used either at probe time, to create the initial state and possibly reset the hardware to an expected default, but also during suspend/resume. Both these cases come with different expectations too: during the initialization, we want to initialize all states, but during suspend/resume, drm_private_states for example are expected to be kept around. reset() also isn't fallible, which makes it harder to handle initialization errors properly. This is only really relevant for some drivers though, since all the helpers for reset only create a new state, and don't touch the hardware at all. It was thus decided to create a new hook that would allocate and initialize a pristine state without any side effect: atomic_create_state to untangle a bit some of it, and to separate the initialization with the actual reset one might need during a suspend/resume. Continue the transition to the new pattern with planes. Reviewed-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-10-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 25 +++++++++++++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 31 ++++++++++++++++++++++++++++++- include/drm/drm_atomic_state_helper.h | 2 ++ include/drm/drm_plane.h | 16 ++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index ee01700d4ca7..ab171bfe6e86 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -340,6 +340,31 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); +/** + * drm_atomic_helper_plane_create_state - default &drm_plane_funcs.atomic_create_state hook for planes + * @plane: plane object + * + * Allocates and initializes pristine @drm_plane_state. + * + * This is useful for drivers that don't subclass @drm_plane_state. + * + * RETURNS: + * Pointer to new plane state, or ERR_PTR on failure. + */ +struct drm_plane_state *drm_atomic_helper_plane_create_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_plane_state_init(state, plane); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_plane_create_state); + /** * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state * @plane: plane object diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index c33382a38191..fa609357858f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -182,6 +182,32 @@ int drm_mode_getresources(struct drm_device *dev, void *data, return ret; } +static int drm_mode_config_plane_create_state(struct drm_plane *plane) +{ + struct drm_plane_state *plane_state; + + if (!plane->funcs->atomic_create_state) + return 0; + + plane_state = plane->funcs->atomic_create_state(plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + plane->state = plane_state; + + return 0; +} + +static int drm_mode_config_plane_reset_with_create_state(struct drm_plane *plane) +{ + if (plane->state) { + plane->funcs->atomic_destroy_state(plane, plane->state); + plane->state = NULL; + } + + return drm_mode_config_plane_create_state(plane); +} + /** * drm_mode_config_reset - call ->reset callbacks * @dev: drm device @@ -206,9 +232,12 @@ void drm_mode_config_reset(struct drm_device *dev) drm_for_each_colorop(colorop, dev) drm_colorop_reset(colorop); - drm_for_each_plane(plane, dev) + drm_for_each_plane(plane, dev) { if (plane->funcs->reset) plane->funcs->reset(plane); + else if (plane->funcs->atomic_create_state) + drm_mode_config_plane_reset_with_create_state(plane); + } drm_for_each_crtc(crtc, dev) if (crtc->funcs->reset) diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 691c1ccfa4e0..8d1ef268fdef 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -55,6 +55,8 @@ void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, void __drm_atomic_helper_plane_state_init(struct drm_plane_state *state, struct drm_plane *plane); +struct drm_plane_state * +drm_atomic_helper_plane_create_state(struct drm_plane *plane); void __drm_atomic_helper_plane_reset(struct drm_plane *plane, struct drm_plane_state *state); void drm_atomic_helper_plane_reset(struct drm_plane *plane); diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 419c88c873a6..2c5a5a70a71b 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -388,6 +388,22 @@ struct drm_plane_funcs { int (*set_property)(struct drm_plane *plane, struct drm_property *property, uint64_t val); + /** + * @atomic_create_state: + * + * Allocate a pristine, initialized, state for the plane object + * and return it. This callback must have no side effects: in + * particular, the returned state must not be assigned to the + * object's state pointer and it must not affect the hardware + * state. + * + * RETURNS: + * + * A new, pristine, plane state instance or an error pointer + * on failure. + */ + struct drm_plane_state *(*atomic_create_state)(struct drm_plane *plane); + /** * @atomic_duplicate_state: * -- cgit From 915fdd2c7e87c5af4fe2d1a0ef4fac515da6ef9b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:23 +0200 Subject: drm/atomic-state-helper: Rename __drm_atomic_helper_crtc_state_reset() __drm_atomic_helper_crtc_state_reset() is used to initialize a newly allocated drm_crtc_state, and is being typically called by the drm_crtc_funcs.reset implementation. Since we want to consolidate DRM objects state allocation around the atomic_create_state callback that will only allocate and initialize a new drm_crtc_state instance, we will need to call __drm_atomic_helper_crtc_state_reset() from both the reset and atomic_create hooks. To avoid any confusion, we can thus rename __drm_atomic_helper_crtc_state_reset() to __drm_atomic_helper_crtc_state_init(). Suggested-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-11-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 10 +++++----- drivers/gpu/drm/i915/display/intel_crtc.c | 2 +- include/drm/drm_atomic_state_helper.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index ab171bfe6e86..b277f92f4532 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -63,7 +63,7 @@ */ /** - * __drm_atomic_helper_crtc_state_reset - reset the CRTC state + * __drm_atomic_helper_crtc_state_init - Initialize the CRTC state * @crtc_state: atomic CRTC state, must not be NULL * @crtc: CRTC object, must not be NULL * @@ -71,13 +71,13 @@ * values. This is useful for drivers that subclass the CRTC state. */ void -__drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *crtc_state, - struct drm_crtc *crtc) +__drm_atomic_helper_crtc_state_init(struct drm_crtc_state *crtc_state, + struct drm_crtc *crtc) { crtc_state->crtc = crtc; crtc_state->background_color = DRM_ARGB64_PREP(0xffff, 0, 0, 0); } -EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_init); /** * __drm_atomic_helper_crtc_reset - reset state on CRTC @@ -96,7 +96,7 @@ __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { if (crtc_state) - __drm_atomic_helper_crtc_state_reset(crtc_state, crtc); + __drm_atomic_helper_crtc_state_init(crtc_state, crtc); if (drm_dev_has_vblank(crtc->dev)) drm_crtc_vblank_reset(crtc); diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index 03de219f7a64..7486f2dc60ef 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -181,7 +181,7 @@ void intel_crtc_state_reset(struct intel_crtc_state *crtc_state, { memset(crtc_state, 0, sizeof(*crtc_state)); - __drm_atomic_helper_crtc_state_reset(&crtc_state->uapi, &crtc->base); + __drm_atomic_helper_crtc_state_init(&crtc_state->uapi, &crtc->base); crtc_state->cpu_transcoder = INVALID_TRANSCODER; crtc_state->master_transcoder = INVALID_TRANSCODER; diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 8d1ef268fdef..0bb72453464a 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -40,7 +40,7 @@ struct drm_private_state; struct drm_modeset_acquire_ctx; struct drm_device; -void __drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *state, +void __drm_atomic_helper_crtc_state_init(struct drm_crtc_state *state, struct drm_crtc *crtc); void __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, struct drm_crtc_state *state); -- cgit From 58d426b5ed0d3ec6d7f3df32e32aab2c5b1daab5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:24 +0200 Subject: drm/crtc: Add new atomic_create_state callback Commit 47b5ac7daa46 ("drm/atomic: Add new atomic_create_state callback to drm_private_obj") introduced a new pattern for allocating drm object states. Instead of relying on the reset() callback, it created a new atomic_create_state hook. This is helpful because reset is a bit overloaded: it's used to create the initial software state, reset it, but also reset the hardware. It can also be used either at probe time, to create the initial state and possibly reset the hardware to an expected default, but also during suspend/resume. Both these cases come with different expectations too: during the initialization, we want to initialize all states, but during suspend/resume, drm_private_states for example are expected to be kept around. reset() also isn't fallible, which makes it harder to handle initialization errors properly. This is only really relevant for some drivers though, since all the helpers for reset only create a new state, and don't touch the hardware at all. It was thus decided to create a new hook that would allocate and initialize a pristine state without any side effect: atomic_create_state to untangle a bit some of it, and to separate the initialization with the actual reset one might need during a suspend/resume. Continue the transition to the new pattern with CRTCs. Reviewed-by: Dmitry Baryshkov Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-12-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 25 +++++++++++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 34 ++++++++++++++++++++++++++++++- include/drm/drm_atomic_state_helper.h | 2 ++ include/drm/drm_crtc.h | 16 +++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index b277f92f4532..8762171c9432 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -124,6 +124,31 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); +/** + * drm_atomic_helper_crtc_create_state - default &drm_crtc_funcs.atomic_create_state hook for crtcs + * @crtc: crtc object + * + * Allocates and initializes pristine @drm_crtc_state. + * + * This is useful for drivers that don't subclass @drm_crtc_state. + * + * RETURNS: + * Pointer to new crtc state, or ERR_PTR on failure. + */ +struct drm_crtc_state *drm_atomic_helper_crtc_create_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(state, crtc); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_create_state); + /** * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state * @crtc: CRTC object diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index fa609357858f..2e2cd18a14b4 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -208,6 +208,35 @@ static int drm_mode_config_plane_reset_with_create_state(struct drm_plane *plane return drm_mode_config_plane_create_state(plane); } +static int drm_mode_config_crtc_create_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *crtc_state; + + if (!crtc->funcs->atomic_create_state) + return 0; + + crtc_state = crtc->funcs->atomic_create_state(crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (drm_dev_has_vblank(crtc->dev)) + drm_crtc_vblank_reset(crtc); + + crtc->state = crtc_state; + + return 0; +} + +static int drm_mode_config_crtc_reset_with_create_state(struct drm_crtc *crtc) +{ + if (crtc->state) { + crtc->funcs->atomic_destroy_state(crtc, crtc->state); + crtc->state = NULL; + } + + return drm_mode_config_crtc_create_state(crtc); +} + /** * drm_mode_config_reset - call ->reset callbacks * @dev: drm device @@ -239,9 +268,12 @@ void drm_mode_config_reset(struct drm_device *dev) drm_mode_config_plane_reset_with_create_state(plane); } - drm_for_each_crtc(crtc, dev) + drm_for_each_crtc(crtc, dev) { if (crtc->funcs->reset) crtc->funcs->reset(crtc); + else if (crtc->funcs->atomic_create_state) + drm_mode_config_crtc_reset_with_create_state(crtc); + } drm_for_each_encoder(encoder, dev) if (encoder->funcs && encoder->funcs->reset) diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 0bb72453464a..213f7e298008 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -45,6 +45,8 @@ void __drm_atomic_helper_crtc_state_init(struct drm_crtc_state *state, void __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, struct drm_crtc_state *state); void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state * +drm_atomic_helper_crtc_create_state(struct drm_crtc *crtc); void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, struct drm_crtc_state *state); struct drm_crtc_state * diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c6dbe8b7db9e..152349f973e3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -638,6 +638,22 @@ struct drm_crtc_funcs { int (*set_property)(struct drm_crtc *crtc, struct drm_property *property, uint64_t val); + /** + * @atomic_create_state: + * + * Allocate a pristine, initialized, state for the CRTC object + * and return it. This callback must have no side effects: in + * particular, the returned state must not be assigned to the + * object's state pointer and it must not affect the hardware + * state. + * + * RETURNS: + * + * A new, pristine, CRTC state instance or an error pointer + * on failure. + */ + struct drm_crtc_state *(*atomic_create_state)(struct drm_crtc *crtc); + /** * @atomic_duplicate_state: * -- cgit From b74940afe5bc06dff15dcbfcb01500177574c778 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:25 +0200 Subject: drm/atomic-state-helper: Rename __drm_atomic_helper_connector_state_reset() __drm_atomic_helper_connector_state_reset() is used to initialize a newly allocated drm_connector_state, and is being typically called by the drm_connector_funcs.reset implementation. Since we want to consolidate DRM objects state allocation around the atomic_create_state callback that will only allocate and initialize a new drm_connector_state instance, we will need to call __drm_atomic_helper_connector_state_reset() from both the reset and atomic_create hooks. To avoid any confusion, we can thus rename __drm_atomic_helper_connector_state_reset() to __drm_atomic_helper_connector_state_init(). Suggested-by: Laurent Pinchart Reviewed-by: Dmitry Baryshkov Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-13-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 12 ++++++------ include/drm/drm_atomic_state_helper.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 8762171c9432..e2e5a1b8a820 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -476,20 +476,20 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); /** - * __drm_atomic_helper_connector_state_reset - reset the connector state + * __drm_atomic_helper_connector_state_init - Initialize the connector state * @conn_state: atomic connector state, must not be NULL - * @connector: connectotr object, must not be NULL + * @connector: connector object, must not be NULL * * Initializes the newly allocated @conn_state with default * values. This is useful for drivers that subclass the connector state. */ void -__drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, - struct drm_connector *connector) +__drm_atomic_helper_connector_state_init(struct drm_connector_state *conn_state, + struct drm_connector *connector) { conn_state->connector = connector; } -EXPORT_SYMBOL(__drm_atomic_helper_connector_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_connector_state_init); /** * __drm_atomic_helper_connector_reset - reset state on connector @@ -508,7 +508,7 @@ __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state) { if (conn_state) - __drm_atomic_helper_connector_state_reset(conn_state, connector); + __drm_atomic_helper_connector_state_init(conn_state, connector); connector->state = conn_state; } diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 213f7e298008..9634a70e0401 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -70,7 +70,7 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state); void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); -void __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, +void __drm_atomic_helper_connector_state_init(struct drm_connector_state *conn_state, struct drm_connector *connector); void __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state); -- cgit From f029d933561e6b79b86c726db93893a54001b461 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:26 +0200 Subject: drm/hdmi: Rename __drm_atomic_helper_connector_hdmi_reset() __drm_atomic_helper_connector_hdmi_reset() is typically used to initialize a newly allocated drm_connector_state when the connector is using the HDMI helpers, and is being called by the drm_connector_funcs.reset implementation. Since we want to consolidate DRM objects state allocation around the atomic_create_state callback that will only allocate and initialize a new drm_connector_state instance, we will need to call __drm_atomic_helper_connector_hdmi_reset() from both the reset and atomic_create hooks. To avoid any confusion, we can thus rename __drm_atomic_helper_connector_hdmi_reset() to __drm_atomic_helper_connector_hdmi_state_init(). Suggested-by: Laurent Pinchart Reviewed-by: Dmitry Baryshkov Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-14-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/display/drm_bridge_connector.c | 4 ++-- drivers/gpu/drm/display/drm_hdmi_state_helper.c | 15 ++++++++------- drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 2 +- drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 2 +- drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- include/drm/display/drm_hdmi_state_helper.h | 4 ++-- 6 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 649969fca141..50408af746d8 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -272,8 +272,8 @@ static void drm_bridge_connector_reset(struct drm_connector *connector) drm_atomic_helper_connector_reset(connector); if (bridge_connector->bridge_hdmi) - __drm_atomic_helper_connector_hdmi_reset(connector, - connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, + connector->state); } static const struct drm_connector_funcs drm_bridge_connector_funcs = { diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index 4867edbf2622..a331ebdd65af 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -306,17 +306,18 @@ */ /** - * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources + * __drm_atomic_helper_connector_hdmi_state_init() - Initialize all HDMI @drm_connector_state resources * @connector: DRM connector - * @new_conn_state: connector state to reset + * @new_conn_state: connector state to initialize * * Initializes all HDMI resources from a @drm_connector_state without * actually allocating it. This is useful for HDMI drivers, in - * combination with __drm_atomic_helper_connector_reset() or - * drm_atomic_helper_connector_reset(). + * combination with __drm_atomic_helper_connector_state_init(), + * drm_atomic_helper_connector_reset(), or + * drm_atomic_helper_connector_create_state(). */ -void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, - struct drm_connector_state *new_conn_state) +void __drm_atomic_helper_connector_hdmi_state_init(struct drm_connector *connector, + struct drm_connector_state *new_conn_state) { unsigned int max_bpc = connector->max_bpc; @@ -324,7 +325,7 @@ void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, new_conn_state->max_requested_bpc = max_bpc; new_conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO; } -EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset); +EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_state_init); static enum hdmi_colorspace output_color_format_to_hdmi_colorspace(const struct drm_connector *connector, diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index 74c7c3720ba8..8f64464621c9 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -285,7 +285,7 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force) static void sun4i_hdmi_connector_reset(struct drm_connector *connector) { drm_atomic_helper_connector_reset(connector); - __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state); } static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = { diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index c9819c3fc635..e89e1af7a811 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -168,7 +168,7 @@ static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = { static void dummy_hdmi_connector_reset(struct drm_connector *connector) { drm_atomic_helper_connector_reset(connector); - __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state); } static const struct drm_connector_funcs dummy_connector_funcs = { diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index a161d3b00a25..74dce4be0c00 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -508,7 +508,7 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, static void vc4_hdmi_connector_reset(struct drm_connector *connector) { drm_atomic_helper_connector_reset(connector); - __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state); drm_atomic_helper_connector_tv_margins_reset(connector); } diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h index 0adc30c55ec9..13375bd0f4ae 100644 --- a/include/drm/display/drm_hdmi_state_helper.h +++ b/include/drm/display/drm_hdmi_state_helper.h @@ -11,8 +11,8 @@ struct hdmi_audio_infoframe; enum drm_connector_status; -void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, - struct drm_connector_state *new_conn_state); +void __drm_atomic_helper_connector_hdmi_state_init(struct drm_connector *connector, + struct drm_connector_state *new_conn_state); int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, struct drm_atomic_commit *state); -- cgit From 6db0e11f480632986a16a61833e2904e809c4692 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:27 +0200 Subject: drm/connector: Add new atomic_create_state callback Commit 47b5ac7daa46 ("drm/atomic: Add new atomic_create_state callback to drm_private_obj") introduced a new pattern for allocating drm object states. Instead of relying on the reset() callback, it created a new atomic_create_state hook. This is helpful because reset is a bit overloaded: it's used to create the initial software state, reset it, but also reset the hardware. It can also be used either at probe time, to create the initial state and possibly reset the hardware to an expected default, but also during suspend/resume. Both these cases come with different expectations too: during the initialization, we want to initialize all states, but during suspend/resume, drm_private_states for example are expected to be kept around. reset() also isn't fallible, which makes it harder to handle initialization errors properly. This is only really relevant for some drivers though, since all the helpers for reset only create a new state, and don't touch the hardware at all. It was thus decided to create a new hook that would allocate and initialize a pristine state without any side effect: atomic_create_state to untangle a bit some of it, and to separate the initialization with the actual reset one might need during a suspend/resume. Continue the transition to the new pattern with connectors. Reviewed-by: Dmitry Baryshkov Reviewed-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-15-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 26 ++++++++++++++++++++++++++ drivers/gpu/drm/drm_connector.c | 11 ++++++++++- drivers/gpu/drm/drm_mode_config.c | 31 ++++++++++++++++++++++++++++++- include/drm/drm_atomic_state_helper.h | 2 ++ include/drm/drm_connector.h | 16 ++++++++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index e2e5a1b8a820..07686e94aae0 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -534,6 +534,32 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); +/** + * drm_atomic_helper_connector_create_state - default &drm_connector_funcs.atomic_create_state hook for connectors + * @connector: connector object + * + * Allocates and initializes pristine @drm_connector_state. + * + * This is useful for drivers that don't subclass @drm_connector_state. + * + * RETURNS: + * Pointer to new connector state, or ERR_PTR on failure. + */ +struct drm_connector_state * +drm_atomic_helper_connector_create_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_connector_state_init(state, connector); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_create_state); + /** * drm_atomic_helper_connector_tv_margins_reset - Resets TV connector properties * @connector: DRM connector diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3fa4d2082cd7..a5d13b92b665 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -618,8 +618,17 @@ int drmm_connector_hdmi_init(struct drm_device *dev, * drm_connector_attach_max_bpc_property() requires the * connector to have a state. */ - if (connector->funcs->reset) + if (connector->funcs->atomic_create_state) { + struct drm_connector_state *state; + + state = connector->funcs->atomic_create_state(connector); + if (IS_ERR(state)) + return PTR_ERR(state); + + connector->state = state; + } else if (connector->funcs->reset) { connector->funcs->reset(connector); + } drm_connector_attach_max_bpc_property(connector, 8, max_bpc); connector->max_bpc = max_bpc; diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 2e2cd18a14b4..9d240817f8b6 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -237,6 +237,32 @@ static int drm_mode_config_crtc_reset_with_create_state(struct drm_crtc *crtc) return drm_mode_config_crtc_create_state(crtc); } +static int drm_mode_config_connector_create_state(struct drm_connector *connector) +{ + struct drm_connector_state *conn_state; + + if (!connector->funcs->atomic_create_state) + return 0; + + conn_state = connector->funcs->atomic_create_state(connector); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + + connector->state = conn_state; + + return 0; +} + +static int drm_mode_config_connector_reset_with_create_state(struct drm_connector *connector) +{ + if (connector->state) { + connector->funcs->atomic_destroy_state(connector, connector->state); + connector->state = NULL; + } + + return drm_mode_config_connector_create_state(connector); +} + /** * drm_mode_config_reset - call ->reset callbacks * @dev: drm device @@ -280,9 +306,12 @@ void drm_mode_config_reset(struct drm_device *dev) encoder->funcs->reset(encoder); drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->funcs->reset) connector->funcs->reset(connector); + else if (connector->funcs->atomic_create_state) + drm_mode_config_connector_reset_with_create_state(connector); + } drm_connector_list_iter_end(&conn_iter); } EXPORT_SYMBOL(drm_mode_config_reset); diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 9634a70e0401..f4b6d8833bc2 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -75,6 +75,8 @@ void __drm_atomic_helper_connector_state_init(struct drm_connector_state *conn_s void __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +struct drm_connector_state * +drm_atomic_helper_connector_create_state(struct drm_connector *connector); void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector); int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, struct drm_atomic_commit *state); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 5ad62c207d00..529755c2e862 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1571,6 +1571,22 @@ struct drm_connector_funcs { */ void (*destroy)(struct drm_connector *connector); + /** + * @atomic_create_state: + * + * Allocate a pristine, initialized, state for the connector + * object and return it. This callback must have no side + * effects: in particular, the returned state must not be + * assigned to the object's state pointer and it must not affect + * the hardware state. + * + * RETURNS: + * + * A new, pristine, connector state instance or an error pointer + * on failure. + */ + struct drm_connector_state *(*atomic_create_state)(struct drm_connector *connector); + /** * @atomic_duplicate_state: * -- cgit From c9497dda313c8d830357669400393d7eacc9ea13 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 26 May 2026 18:46:28 +0200 Subject: drm/mode-config: Create drm_mode_config_create_initial_state() drm_mode_config_reset() can be used to create the initial state, but also to return to the initial state, when doing a suspend/resume cycle for example. It also affects both the software and the hardware, and drivers can choose to reset the hardware as well. Most will just create an empty state and the synchronisation between hardware and software states will effectively be done when the first commit is done. That dual role can be harmful, since some objects do need to be initialized but also need to be preserved across a suspend/resume cycle. drm_private_obj are such objects for example. Thus, create another helper for drivers to call to initialize their state when the driver is loaded, so we can make drm_mode_config_reset() only about handling suspend/resume and similar. Reviewed-by: Dmitry Baryshkov Reviewed-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260526-drm-mode-config-init-v6-16-852346394200@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 12 +++++- drivers/gpu/drm/drm_mode_config.c | 89 +++++++++++++++++++++++++++++++++++++++ include/drm/drm_mode_config.h | 1 + 3 files changed, 100 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index a1bcaaf71eea..3af1b9cc9a06 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -60,8 +60,16 @@ * * Their respective lifetimes are: * - * - at reset time, the object reset implementation allocates a new - * default state and stores it in the object state pointer. + * - at driver initialization time, the driver calls + * drm_mode_config_create_initial_state() to allocate an initial, + * pristine, state for each object and stores it in the objects state + * pointer. Historically, this was one of drm_mode_config_reset() job, + * so one might still encounter it in a driver. + * + * - When resuming from suspend, drm_mode_config_reset() resets the + * software and hardware state to a known default and stores it in the + * object's state pointer. Not all objects are affected by + * drm_mode_config_reset() though. * * - whenever a new update is needed: * diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 9d240817f8b6..f432f485a914 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -316,6 +317,94 @@ void drm_mode_config_reset(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_config_reset); +/** + * drm_mode_config_create_initial_state - Allocates the initial state + * @dev: drm device + * + * This functions creates the initial state for all the objects. Drivers + * can use this in e.g. probe to initialize their software state. + * + * It has two main differences with drm_mode_config_reset(): the reset() + * hooks aren't called and thus the hardware will be left untouched, but + * also the &drm_private_obj structures will be initialized as opposed + * to drm_mode_config_reset() that skips them. + * + * Returns: 0 on success, negative error value on failure. + */ +int drm_mode_config_create_initial_state(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_colorop *colorop; + struct drm_plane *plane; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + struct drm_private_obj *privobj; + int ret; + + drm_for_each_privobj(privobj, dev) { + struct drm_private_state *privobj_state; + + if (privobj->state) + continue; + + if (!privobj->funcs->atomic_create_state) + continue; + + privobj_state = privobj->funcs->atomic_create_state(privobj); + if (IS_ERR(privobj_state)) + return PTR_ERR(privobj_state); + + privobj->state = privobj_state; + } + + drm_for_each_colorop(colorop, dev) { + struct drm_colorop_state *colorop_state; + + if (colorop->state) + continue; + + colorop_state = drm_atomic_helper_colorop_create_state(colorop); + if (IS_ERR(colorop_state)) + return PTR_ERR(colorop_state); + + colorop->state = colorop_state; + } + + drm_for_each_plane(plane, dev) { + if (plane->state) + continue; + + ret = drm_mode_config_plane_create_state(plane); + if (ret) + return ret; + } + + drm_for_each_crtc(crtc, dev) { + if (crtc->state) + continue; + + ret = drm_mode_config_crtc_create_state(crtc); + if (ret) + return ret; + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->state) + continue; + + ret = drm_mode_config_connector_create_state(connector); + if (ret) { + drm_connector_list_iter_end(&conn_iter); + return ret; + } + } + drm_connector_list_iter_end(&conn_iter); + + return 0; +} +EXPORT_SYMBOL(drm_mode_config_create_initial_state); + /* * Global properties */ diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index e584652ddf67..d8f5b7e9673e 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -1007,6 +1007,7 @@ static inline int drm_mode_config_init(struct drm_device *dev) return drmm_mode_config_init(dev); } +int drm_mode_config_create_initial_state(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev); -- cgit From a03721ee484c6a5cbc58ece2cf6feaa2159761e7 Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Fri, 22 May 2026 11:25:31 +0200 Subject: gpu/buddy: Track per-order free blocks with a scoreboard Reporting per-order free block counts in drm_buddy_print() currently requires walking all rbtrees, which is O(n) over the total number of free blocks and holds the allocator lock for the duration. This becomes expensive on large VRAM heaps with many small free fragments. Maintain a free_scoreboard[] array indexed by order instead, so that the count for any order is always available in O(1). The scoreboard is kept accurate by hooking into the four places where a block's free state changes: mark_free(), mark_allocated(), mark_split(), and the sites in __gpu_buddy_free(), __force_merge(), and the four err_undo paths that call rbtree_remove() directly on free blocks without going through mark_*(). The print functions are simplified as a result: the rbtree traversal is replaced by a direct array lookup. v3: Update after introducing __gpu_buddy_undo_splits() helper v2: Update after fix for use-after-free in split_block() call sites Assisted-by: GitHub Copilot:claude-sonnet-4.6 Reviewed-by: Matthew Auld Link: https://lore.kernel.org/r/20260522092600.32818-5-francois.dugast@intel.com Signed-off-by: Francois Dugast --- drivers/gpu/buddy.c | 36 +++++++++++++++++++++--------------- drivers/gpu/drm/drm_buddy.c | 16 ++-------------- include/linux/gpu_buddy.h | 7 +++++++ 3 files changed, 30 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index 8654604b87a4..de18b63fef0a 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -193,6 +193,8 @@ static void mark_allocated(struct gpu_buddy *mm, block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_ALLOCATED; + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + rbtree_remove(mm, block); } @@ -204,6 +206,8 @@ static void mark_free(struct gpu_buddy *mm, block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_FREE; + mm->free_scoreboard[gpu_buddy_block_order(block)]++; + tree = get_block_tree(block); rbtree_insert(mm, block, tree); } @@ -214,6 +218,8 @@ static void mark_split(struct gpu_buddy *mm, block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_SPLIT; + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + rbtree_remove(mm, block); } @@ -271,6 +277,7 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm, } rbtree_remove(mm, buddy); + mm->free_scoreboard[gpu_buddy_block_order(buddy)]--; if (force_merge && gpu_buddy_block_is_clear(buddy)) mm->clear_avail -= gpu_buddy_block_size(mm, buddy); @@ -335,6 +342,7 @@ static int __force_merge(struct gpu_buddy *mm, iter = rb_prev(iter); rbtree_remove(mm, block); + mm->free_scoreboard[gpu_buddy_block_order(block)]--; if (gpu_buddy_block_is_clear(block)) mm->clear_avail -= gpu_buddy_block_size(mm, block); @@ -384,11 +392,17 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER); + mm->free_scoreboard = kcalloc(mm->max_order + 1, + sizeof(*mm->free_scoreboard), + GFP_KERNEL); + if (!mm->free_scoreboard) + return -ENOMEM; + mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES, sizeof(*mm->free_trees), GFP_KERNEL); if (!mm->free_trees) - return -ENOMEM; + goto out_free_scoreboard; for_each_free_tree(i) { mm->free_trees[i] = kmalloc_array(mm->max_order + 1, @@ -450,6 +464,8 @@ out_free_tree: while (i--) kfree(mm->free_trees[i]); kfree(mm->free_trees); +out_free_scoreboard: + kfree(mm->free_scoreboard); return -ENOMEM; } EXPORT_SYMBOL(gpu_buddy_init); @@ -488,6 +504,7 @@ void gpu_buddy_fini(struct gpu_buddy *mm) kfree(mm->free_trees[i]); kfree(mm->free_trees); kfree(mm->roots); + kfree(mm->free_scoreboard); } EXPORT_SYMBOL(gpu_buddy_fini); @@ -659,6 +676,7 @@ static void __gpu_buddy_undo_splits(struct gpu_buddy *mm, (gpu_buddy_block_is_free(block) && gpu_buddy_block_is_free(buddy))) { rbtree_remove(mm, block); + mm->free_scoreboard[gpu_buddy_block_order(block)]--; __gpu_buddy_free(mm, block, false); } } @@ -1487,21 +1505,9 @@ void gpu_buddy_print(struct gpu_buddy *mm) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct gpu_buddy_block *block, *tmp; - struct rb_root *root; - u64 count = 0, free; - unsigned int tree; - - for_each_free_tree(tree) { - root = &mm->free_trees[tree][order]; - - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - BUG_ON(!gpu_buddy_block_is_free(block)); - count++; - } - } + u64 count = mm->free_scoreboard[order]; + u64 free = count * (mm->chunk_size << order); - free = count * (mm->chunk_size << order); if (free < SZ_1M) pr_info("order-%2d free: %8llu KiB, blocks: %llu\n", order, free >> 10, count); diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index faa025498de4..eef995e08a37 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -47,23 +47,11 @@ void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct gpu_buddy_block *block, *tmp; - struct rb_root *root; - u64 count = 0, free; - unsigned int tree; - - for_each_free_tree(tree) { - root = &mm->free_trees[tree][order]; - - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - BUG_ON(!gpu_buddy_block_is_free(block)); - count++; - } - } + u64 count = mm->free_scoreboard[order]; + u64 free = count * (mm->chunk_size << order); drm_printf(p, "order-%2d ", order); - free = count * (mm->chunk_size << order); if (free < SZ_1M) drm_printf(p, "free: %8llu KiB", free >> 10); else diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h index 71941a039648..a28f7d7637ca 100644 --- a/include/linux/gpu_buddy.h +++ b/include/linux/gpu_buddy.h @@ -173,6 +173,13 @@ struct gpu_buddy { * that fits in the remaining space. */ struct gpu_buddy_block **roots; + /* + * Per-order free block scoreboard: free_scoreboard[order] holds the + * number of blocks of that order currently in the free state. + * Incremented in mark_free(), decremented wherever rbtree_remove() is + * called on a free block. + */ + u64 *free_scoreboard; /* public: */ unsigned int n_roots; unsigned int max_order; -- cgit From 25d912475e8b734ac7bf1880920b6b42514e9472 Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Fri, 22 May 2026 11:25:32 +0200 Subject: gpu/buddy: Track per-order used blocks with a scoreboard Extend the scoreboard approach from the previous commit to used blocks, so drm_buddy_print() can report per-order allocation pressure in O(1). Unlike free blocks, an allocated block can leave the allocated state through mark_free() (normal free and gpu_buddy_block_trim()) or be consumed directly by gpu_block_free() during coalescing. Both sites are guarded by gpu_buddy_block_is_allocated() and paired with the increment in mark_allocated(). v3: - Assert scoreboard is empty at fini(), as sanity check (Matthew Auld) v2: - Update after fix for use-after-free in split_block() call sites - Change goto label to out_free_used_scoreboard for clarity - Make drm_buddy_print() and gpu_buddy_print() symmetric for used and free Assisted-by: GitHub Copilot:claude-sonnet-4.6 Reviewed-by: Matthew Auld Link: https://lore.kernel.org/r/20260522092600.32818-6-francois.dugast@intel.com Signed-off-by: Francois Dugast --- drivers/gpu/buddy.c | 42 ++++++++++++++++++++++++++++++++---------- drivers/gpu/drm/drm_buddy.c | 18 ++++++++++++------ include/linux/gpu_buddy.h | 8 ++++++++ 3 files changed, 52 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index de18b63fef0a..dc81fe0301ce 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -194,6 +194,7 @@ static void mark_allocated(struct gpu_buddy *mm, block->header |= GPU_BUDDY_ALLOCATED; mm->free_scoreboard[gpu_buddy_block_order(block)]--; + mm->used_scoreboard[gpu_buddy_block_order(block)]++; rbtree_remove(mm, block); } @@ -203,6 +204,9 @@ static void mark_free(struct gpu_buddy *mm, { enum gpu_buddy_free_tree tree; + if (gpu_buddy_block_is_allocated(block)) + mm->used_scoreboard[gpu_buddy_block_order(block)]--; + block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_FREE; @@ -281,6 +285,9 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm, if (force_merge && gpu_buddy_block_is_clear(buddy)) mm->clear_avail -= gpu_buddy_block_size(mm, buddy); + if (gpu_buddy_block_is_allocated(block)) + mm->used_scoreboard[gpu_buddy_block_order(block)]--; + gpu_block_free(mm, block); gpu_block_free(mm, buddy); @@ -398,11 +405,17 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) if (!mm->free_scoreboard) return -ENOMEM; + mm->used_scoreboard = kcalloc(mm->max_order + 1, + sizeof(*mm->used_scoreboard), + GFP_KERNEL); + if (!mm->used_scoreboard) + goto out_free_free_scoreboard; + mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES, sizeof(*mm->free_trees), GFP_KERNEL); if (!mm->free_trees) - goto out_free_scoreboard; + goto out_free_used_scoreboard; for_each_free_tree(i) { mm->free_trees[i] = kmalloc_array(mm->max_order + 1, @@ -464,7 +477,9 @@ out_free_tree: while (i--) kfree(mm->free_trees[i]); kfree(mm->free_trees); -out_free_scoreboard: +out_free_used_scoreboard: + kfree(mm->used_scoreboard); +out_free_free_scoreboard: kfree(mm->free_scoreboard); return -ENOMEM; } @@ -500,11 +515,15 @@ void gpu_buddy_fini(struct gpu_buddy *mm) gpu_buddy_assert(mm->avail == mm->size); + for (i = 0; i <= mm->max_order; ++i) + gpu_buddy_assert(!mm->used_scoreboard[i]); + for_each_free_tree(i) kfree(mm->free_trees[i]); kfree(mm->free_trees); kfree(mm->roots); kfree(mm->free_scoreboard); + kfree(mm->used_scoreboard); } EXPORT_SYMBOL(gpu_buddy_fini); @@ -1505,15 +1524,18 @@ void gpu_buddy_print(struct gpu_buddy *mm) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - u64 count = mm->free_scoreboard[order]; - u64 free = count * (mm->chunk_size << order); - - if (free < SZ_1M) - pr_info("order-%2d free: %8llu KiB, blocks: %llu\n", - order, free >> 10, count); + u64 free_count = mm->free_scoreboard[order]; + u64 used_count = mm->used_scoreboard[order]; + u64 block_size = mm->chunk_size << order; + u64 free = free_count * block_size; + u64 used = used_count * block_size; + + if (block_size < SZ_1M) + pr_info("order-%2d free: %8llu KiB, used: %8llu KiB, free_blocks: %llu, used_blocks: %llu\n", + order, free >> 10, used >> 10, free_count, used_count); else - pr_info("order-%2d free: %8llu MiB, blocks: %llu\n", - order, free >> 20, count); + pr_info("order-%2d free: %8llu MiB, used: %8llu MiB, free_blocks: %llu, used_blocks: %llu\n", + order, free >> 20, used >> 20, free_count, used_count); } } EXPORT_SYMBOL(gpu_buddy_print); diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index eef995e08a37..1536e59c6fe7 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -47,17 +47,23 @@ void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - u64 count = mm->free_scoreboard[order]; - u64 free = count * (mm->chunk_size << order); + u64 free_count = mm->free_scoreboard[order]; + u64 used_count = mm->used_scoreboard[order]; + u64 block_size = mm->chunk_size << order; + u64 free = free_count * block_size; + u64 used = used_count * block_size; drm_printf(p, "order-%2d ", order); - if (free < SZ_1M) - drm_printf(p, "free: %8llu KiB", free >> 10); + if (block_size < SZ_1M) + drm_printf(p, "free: %8llu KiB, used: %8llu KiB", + free >> 10, used >> 10); else - drm_printf(p, "free: %8llu MiB", free >> 20); + drm_printf(p, "free: %8llu MiB, used: %8llu MiB", + free >> 20, used >> 20); - drm_printf(p, ", blocks: %llu\n", count); + drm_printf(p, ", free_blocks: %llu, used_blocks: %llu\n", + free_count, used_count); } } EXPORT_SYMBOL(drm_buddy_print); diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h index a28f7d7637ca..e037714563d8 100644 --- a/include/linux/gpu_buddy.h +++ b/include/linux/gpu_buddy.h @@ -180,6 +180,14 @@ struct gpu_buddy { * called on a free block. */ u64 *free_scoreboard; + /* + * Per-order used block scoreboard: used_scoreboard[order] holds the + * number of blocks of that order currently in the allocated state. + * Incremented in mark_allocated(), decremented in mark_free() (guarded + * by gpu_buddy_block_is_allocated()) and in __gpu_buddy_free() when an + * allocated block is consumed directly during buddy coalescing. + */ + u64 *used_scoreboard; /* public: */ unsigned int n_roots; unsigned int max_order; -- cgit From 21fcb222f0d1e1c9f5b04c09e9fb3408e13a0264 Mon Sep 17 00:00:00 2001 From: Laura Nao Date: Tue, 21 Apr 2026 10:47:01 +0200 Subject: drm: Remove DRIVER_GEM_GPUVA feature flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DRIVER_GEM_GPUVA feature flag is currently only used to control two behaviors within the DRM core: - calling drm_gem_gpuva_init() during GEM object initialization - creating the "gpuvas" debugfs entry drm_gem_gpuva_init() is a plain INIT_LIST_HEAD() and therefore is cheap to run for every GEM object. The DRM_DEBUGFS_GPUVA_INFO macro is only referenced by GPU-VA capable drivers, so clearing the feature bit does not cause any unrelated drivers to get the "gpuvas" debugfs node. The flag doesn't have any relevant purpose (e.g. gating ioctl handlers or MM logic) and doesn't provide any practical benefit. Remove the flag definition and drop it from all drivers that use it, call drm_gem_gpuva_init() unconditionally and clear the driver features bit in DRM_DEBUGFS_GPUVA_INFO. Signed-off-by: Laura Nao Acked-by: Rob Clark Acked-by: Liviu Dudau Acked-by: Thomas Hellström Link: https://patch.msgid.link/20260421084701.24227-1-laura.nao@collabora.com Signed-off-by: Boris Brezillon --- drivers/gpu/drm/drm_gem.c | 3 +-- drivers/gpu/drm/imagination/pvr_drv.c | 2 +- drivers/gpu/drm/msm/msm_drv.c | 2 -- drivers/gpu/drm/nouveau/nouveau_drm.c | 1 - drivers/gpu/drm/panthor/panthor_drv.c | 2 +- drivers/gpu/drm/xe/xe_device.c | 4 ++-- include/drm/drm_debugfs.h | 2 +- include/drm/drm_drv.h | 6 ------ include/drm/drm_gem.h | 3 --- 9 files changed, 6 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 4c781c431642..4d75ce66bd4d 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -232,8 +232,7 @@ void drm_gem_private_object_init(struct drm_device *dev, if (!obj->resv) obj->resv = &obj->_resv; - if (drm_core_check_feature(dev, DRIVER_GEM_GPUVA)) - drm_gem_gpuva_init(obj); + drm_gem_gpuva_init(obj); drm_vma_node_reset(&obj->vma_node); INIT_LIST_HEAD(&obj->lru_node); diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index b20c462bcba0..ca3042d14253 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -1378,7 +1378,7 @@ pvr_drm_driver_postclose(__always_unused struct drm_device *drm_dev, DEFINE_DRM_GEM_FOPS(pvr_drm_driver_fops); static struct drm_driver pvr_drm_driver = { - .driver_features = DRIVER_GEM | DRIVER_GEM_GPUVA | DRIVER_RENDER | + .driver_features = DRIVER_GEM | DRIVER_RENDER | DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE, .open = pvr_drm_driver_open, .postclose = pvr_drm_driver_postclose, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index cc2bcd14b1c2..ebd0f659e910 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -824,7 +824,6 @@ static const struct file_operations fops = { #define DRIVER_FEATURES_GPU ( \ DRIVER_GEM | \ - DRIVER_GEM_GPUVA | \ DRIVER_RENDER | \ DRIVER_SYNCOBJ | \ DRIVER_SYNCOBJ_TIMELINE | \ @@ -832,7 +831,6 @@ static const struct file_operations fops = { #define DRIVER_FEATURES_KMS ( \ DRIVER_GEM | \ - DRIVER_GEM_GPUVA | \ DRIVER_ATOMIC | \ DRIVER_MODESET | \ 0 ) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index e16f59b00f6f..42a81166f3a9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1364,7 +1364,6 @@ static struct drm_driver driver_stub = { .driver_features = DRIVER_GEM | DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE | - DRIVER_GEM_GPUVA | DRIVER_MODESET | DRIVER_RENDER, .open = nouveau_drm_open, diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index e8dc4096c1d2..1b8f5d5c2ee9 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -1782,7 +1782,7 @@ static void panthor_debugfs_init(struct drm_minor *minor) */ static const struct drm_driver panthor_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ | - DRIVER_SYNCOBJ_TIMELINE | DRIVER_GEM_GPUVA, + DRIVER_SYNCOBJ_TIMELINE, .open = panthor_open, .postclose = panthor_postclose, .show_fdinfo = panthor_show_fdinfo, diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 576095cf0952..d51573cf7f2f 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -396,7 +396,7 @@ static const struct drm_driver regular_driver = { XE_DISPLAY_DRIVER_FEATURES | DRIVER_GEM | DRIVER_RENDER | DRIVER_SYNCOBJ | - DRIVER_SYNCOBJ_TIMELINE | DRIVER_GEM_GPUVA, + DRIVER_SYNCOBJ_TIMELINE, .open = xe_file_open, .postclose = xe_file_close, @@ -427,7 +427,7 @@ static const struct drm_ioctl_desc xe_ioctls_admin_only[] = { static const struct drm_driver admin_only_driver = { .driver_features = XE_DISPLAY_DRIVER_FEATURES | - DRIVER_GEM | DRIVER_RENDER | DRIVER_GEM_GPUVA, + DRIVER_GEM | DRIVER_RENDER, .open = xe_file_open, .postclose = xe_file_close, .ioctls = xe_ioctls_admin_only, diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index ea8cba94208a..eb93512b0f23 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -48,7 +48,7 @@ * For each DRM GPU VA space drivers should call drm_debugfs_gpuva_info() from * their @show callback. */ -#define DRM_DEBUGFS_GPUVA_INFO(show, data) {"gpuvas", show, DRIVER_GEM_GPUVA, data} +#define DRM_DEBUGFS_GPUVA_INFO(show, data) {"gpuvas", show, 0, data} /** * struct drm_info_list - debugfs info list entry diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 42fc085f986d..e09559495c5b 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -107,12 +107,6 @@ enum drm_driver_feature { * acceleration should be handled by two drivers that are connected using auxiliary bus. */ DRIVER_COMPUTE_ACCEL = BIT(7), - /** - * @DRIVER_GEM_GPUVA: - * - * Driver supports user defined GPU VA bindings for GEM objects. - */ - DRIVER_GEM_GPUVA = BIT(8), /** * @DRIVER_CURSOR_HOTSPOT: * diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 8a704f6a65c1..885244e375d3 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -661,9 +661,6 @@ static inline bool drm_gem_is_imported(const struct drm_gem_object *obj) * * This initializes the &drm_gem_object's &drm_gpuvm_bo list. * - * Calling this function is only necessary for drivers intending to support the - * &drm_driver_feature DRIVER_GEM_GPUVA. - * * See also drm_gem_gpuva_set_lock(). */ static inline void drm_gem_gpuva_init(struct drm_gem_object *obj) -- cgit From 17cdb54644e7d92b62cff1c4d1bd3d1486515f68 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Sat, 23 May 2026 10:37:22 +0200 Subject: accel: ethosu: Add performance counter support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Arm Ethos-U NPUs have a PMU with performance counters. The PMU h/w supports up to 4 (U65) or 8 (U85) counters which can be programmed for different events. There is also a dedicated cycle counter. The ABI and implementation are copied from the V3D driver. The main difference in the ABI is there is no query API for the event list. The events differ between the U65 and U85, so the events lists are maintained in userspace along with other differences between the U65 and U85. The cycle counter is always enabled when the PMU is enabled. When the user requests N events, reading the counters will return the N events plus the cycle counter. Signed-off-by: Rob Herring (Arm) Signed-off-by: Tomeu Vizoso Reviewed-by: Maíra Canal Link: https://patch.msgid.link/20260601173814.250071-1-tomeu@tomeuvizoso.net --- v2: - Use XArray instead of idr - Rework locking to use per device spinlock to protect modifying active perfmon. Based on pending V3D changes: https://lore.kernel.org/all/20260508-v3d-perfmon-lifetime-v1-1-f5b5642c085f@igalia.com/ - Add missing perfmon puts in ethosu_ioctl_perfmon_set_global() and ethosu_ioctl_perfmon_get_values() error paths. - Fix reading number of counters on U85. - Add defines NPU_REG_PMCCNTR_CFG v3: - Add explicit padding to drm_ethosu_perfmon_destroy - Fix SPDX license expression - Fix comment typos - Convert perfmon lock from spinlock to mutex - Simplify switch_perfmon condition check - Remove unused ethosu_perfmon_init - Add lockdep_assert_held to ethosu_perfmon_stop_locked v4: - Use drmm_mutex_init() for perfmon lock - Add lockdep_assert_held() to ethosu_perfmon_start() - Fix a few style issues reported by Maíra --- drivers/accel/ethosu/Makefile | 2 +- drivers/accel/ethosu/ethosu_device.h | 33 ++++ drivers/accel/ethosu/ethosu_drv.c | 23 ++- drivers/accel/ethosu/ethosu_drv.h | 61 ++++++- drivers/accel/ethosu/ethosu_job.c | 39 ++++- drivers/accel/ethosu/ethosu_job.h | 2 + drivers/accel/ethosu/ethosu_perfmon.c | 301 ++++++++++++++++++++++++++++++++++ include/uapi/drm/ethosu_accel.h | 60 ++++++- 8 files changed, 508 insertions(+), 13 deletions(-) create mode 100644 drivers/accel/ethosu/ethosu_perfmon.c (limited to 'include') diff --git a/drivers/accel/ethosu/Makefile b/drivers/accel/ethosu/Makefile index 17db5a600416..598a388b7179 100644 --- a/drivers/accel/ethosu/Makefile +++ b/drivers/accel/ethosu/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o -ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o +ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o ethosu_perfmon.o diff --git a/drivers/accel/ethosu/ethosu_device.h b/drivers/accel/ethosu/ethosu_device.h index b189fa783d6a..3a1d07d94785 100644 --- a/drivers/accel/ethosu/ethosu_device.h +++ b/drivers/accel/ethosu/ethosu_device.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -43,6 +44,15 @@ struct gen_pool; #define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8) #define NPU_BASEP_REGION_MAX 8 +#define NPU_REG_PMCR 0x0180 +#define NPU_REG_PMCNTENSET 0x0184 +#define NPU_REG_PMCNTENCLR 0x0188 +#define NPU_REG_PMCCNTR_LO 0x01A0 +#define NPU_REG_PMCCNTR_HI 0x01A4 +#define NPU_REG_PMCCNTR_CFG 0x01A8 +#define NPU_REG_PMU_EVCNTR(x) (0x0300 + (x) * 4) +#define NPU_REG_PMU_EVTYPER(x) (0x0380 + (x) * 4) + #define ID_ARCH_MAJOR_MASK GENMASK(31, 28) #define ID_ARCH_MINOR_MASK GENMASK(27, 20) #define ID_ARCH_PATCH_MASK GENMASK(19, 16) @@ -67,6 +77,15 @@ struct gen_pool; #define PROT_ACTIVE_CSL BIT(1) +#define PMCR_NUM_EVENT_CNT_MASK GENMASK(15, 11) +#define PMCR_CYCLE_CNT_RST BIT(2) +#define PMCR_EVENT_CNT_RST BIT(1) +#define PMCR_CNT_EN BIT(0) + +#define PMU_EV_TYPE_NONE 0 +#define PMU_EV_TYPE_CYCLES 0x11 +#define PMU_EV_TYPE_IDLE 0x20 + enum ethosu_cmds { NPU_OP_CONV = 0x2, NPU_OP_DEPTHWISE = 0x3, @@ -152,6 +171,8 @@ enum ethosu_cmds { #define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */ +struct ethosu_perfmon; + /** * struct ethosu_device - Ethosu device */ @@ -161,6 +182,7 @@ struct ethosu_device { /** @iomem: CPU mapping of the registers. */ void __iomem *regs; + void __iomem *pmu_regs; void __iomem *sram; struct gen_pool *srampool; @@ -184,6 +206,17 @@ struct ethosu_device { struct mutex sched_lock; u64 fence_context; u64 emit_seqno; + + /* Tracks the performance monitor state. */ + struct { + /* Protects @active. */ + struct mutex lock; + + /* Perfmon currently programmed in HW (or NULL if none). */ + struct ethosu_perfmon *active; + } perfmon_state; + + struct ethosu_perfmon *global_perfmon; }; #define to_ethosu_device(drm_dev) \ diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c index 9992193d7338..fef4713e8061 100644 --- a/drivers/accel/ethosu/ethosu_drv.c +++ b/drivers/accel/ethosu/ethosu_drv.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "ethosu_drv.h" @@ -155,6 +156,7 @@ static int ethosu_open(struct drm_device *ddev, struct drm_file *file) if (ret) goto err_put_mod; + ethosu_perfmon_open_file(priv); file->driver_priv = no_free_ptr(priv); return 0; @@ -166,6 +168,7 @@ err_put_mod: static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file) { ethosu_job_close(file->driver_priv); + ethosu_perfmon_close_file(file->driver_priv); kfree(file->driver_priv); module_put(THIS_MODULE); } @@ -180,6 +183,10 @@ static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = { ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0), ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0), ETHOSU_IOCTL(SUBMIT, submit, 0), + ETHOSU_IOCTL(PERFMON_CREATE, perfmon_create, 0), + ETHOSU_IOCTL(PERFMON_DESTROY, perfmon_destroy, 0), + ETHOSU_IOCTL(PERFMON_GET_VALUES, perfmon_get_values, 0), + ETHOSU_IOCTL(PERFMON_SET_GLOBAL, perfmon_set_global, 0), }; DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops); @@ -315,8 +322,14 @@ static int ethosu_init(struct ethosu_device *ethosudev) ethosu_sram_init(ethosudev); + if (!ethosu_is_u65(ethosudev)) + ethosudev->pmu_regs += 0x1000; + + ethosudev->npu_info.pmu_counters = FIELD_GET(PMCR_NUM_EVENT_CNT_MASK, + readl_relaxed(ethosudev->pmu_regs + NPU_REG_PMCR)); + dev_info(ethosudev->base.dev, - "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n", + "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM, %d PMU cntrs\n", FIELD_GET(ID_ARCH_MAJOR_MASK, id), FIELD_GET(ID_ARCH_MINOR_MASK, id), FIELD_GET(ID_ARCH_PATCH_MASK, id), @@ -324,7 +337,8 @@ static int ethosu_init(struct ethosu_device *ethosudev) FIELD_GET(ID_VER_MINOR_MASK, id), FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config), 1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config), - ethosudev->npu_info.sram_size / 1024); + ethosudev->npu_info.sram_size / 1024, + ethosudev->npu_info.pmu_counters); return 0; } @@ -343,11 +357,16 @@ static int ethosu_probe(struct platform_device *pdev) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); ethosudev->regs = devm_platform_ioremap_resource(pdev, 0); + ethosudev->pmu_regs = ethosudev->regs; ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, ðosudev->clks); if (ethosudev->num_clks < 0) return ethosudev->num_clks; + ret = drmm_mutex_init(ðosudev->base, ðosudev->perfmon_state.lock); + if (ret) + return ret; + ret = ethosu_job_init(ethosudev); if (ret) return ret; diff --git a/drivers/accel/ethosu/ethosu_drv.h b/drivers/accel/ethosu/ethosu_drv.h index 9e21dfe94184..2193bc51d425 100644 --- a/drivers/accel/ethosu/ethosu_drv.h +++ b/drivers/accel/ethosu/ethosu_drv.h @@ -1,15 +1,74 @@ /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ -/* Copyright 2025 Arm, Ltd. */ +/* Copyright 2025-2026 Arm, Ltd. */ #ifndef __ETHOSU_DRV_H__ #define __ETHOSU_DRV_H__ +#include +#include #include struct ethosu_device; +struct drm_device; +struct drm_file; struct ethosu_file_priv { struct ethosu_device *edev; struct drm_sched_entity sched_entity; + struct xarray perfmons; }; +/* Performance monitor object. The perfmon lifetime is controlled by userspace + * using perfmon related ioctls. A perfmon can be attached to a DRM_ETHOSU_SUBMIT + * request, and when this is the case, HW perf counters will be activated just + * before the job is submitted to the NPU and disabled when the job is + * done. This way, only events related to a specific job will be counted. + */ +struct ethosu_perfmon { + /* Tracks the number of users of the perfmon, when this counter reaches + * zero the perfmon is destroyed. + */ + refcount_t refcnt; + + /* Number of counters activated in this perfmon instance + * (should be less than or equal to DRM_ETHOSU_MAX_PERF_COUNTERS). + */ + u8 ncounters; + + /* Events counted by the HW perf counters. */ + u16 counters[DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS]; + + /* + * Storage for counter values. Counters are incremented by the HW + * perf counter values every time the perfmon is attached to an + * NPU job. This way, perfmon users don't have to retrieve the + * results after each job if they want to track events covering + * several submissions. Note that counter values can't be reset, + * but you can fake a reset by destroying the perfmon and + * creating a new one. + */ + u64 values[] __counted_by(ncounters); +}; + +/* ethosu_perfmon.c */ +void ethosu_perfmon_get(struct ethosu_perfmon *perfmon); +void ethosu_perfmon_put(struct ethosu_perfmon *perfmon); +void ethosu_perfmon_start(struct ethosu_device *ethosu, + struct ethosu_perfmon *perfmon); +void ethosu_perfmon_stop(struct ethosu_device *ethosu, + struct ethosu_perfmon *perfmon, bool capture); +void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture); +struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, + int id); +void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv); +void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv); +int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data, + struct drm_file *file_priv); + #endif diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c index b76924645aaa..99dec33f526b 100644 --- a/drivers/accel/ethosu/ethosu_job.c +++ b/drivers/accel/ethosu/ethosu_job.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2024-2025 Tomeu Vizoso */ -/* Copyright 2025 Arm, Ltd. */ +/* Copyright 2025-2026 Arm, Ltd. */ #include #include @@ -147,6 +147,8 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job) { unsigned int i; + ethosu_perfmon_put(job->perfmon); + for (i = 0; i < job->region_cnt; i++) drm_gem_object_put(job->region_bo[i]); @@ -181,6 +183,26 @@ static void ethosu_job_free(struct drm_sched_job *sched_job) ethosu_job_put(job); } +static void +ethosu_switch_perfmon(struct ethosu_device *ethosu, struct ethosu_job *job) +{ + struct ethosu_perfmon *perfmon; + + guard(mutex)(ðosu->perfmon_state.lock); + + perfmon = ethosu->global_perfmon; + if (!perfmon) + perfmon = job->perfmon; + + if (perfmon == ethosu->perfmon_state.active) + return; + + ethosu_perfmon_stop_locked(ethosu, ethosu->perfmon_state.active, true); + + if (perfmon) + ethosu_perfmon_start(ethosu, perfmon); +} + static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) { struct ethosu_job *job = to_ethosu_job(sched_job); @@ -194,6 +216,8 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) dev->fence_context, ++dev->emit_seqno); dma_fence_get(fence); + ethosu_switch_perfmon(dev, job); + scoped_guard(mutex, &dev->job_lock) { dev->in_flight_job = job; ethosu_job_hw_submit(dev, job); @@ -365,7 +389,8 @@ void ethosu_job_close(struct ethosu_file_priv *ethosu_priv) } static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file, - struct drm_ethosu_job *job) + struct drm_ethosu_job *job, + int perfmon_id) { struct ethosu_device *edev = to_ethosu_device(dev); struct ethosu_file_priv *file_priv = file->driver_priv; @@ -389,6 +414,9 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file ejob->dev = edev; ejob->sram_size = job->sram_size; + if (perfmon_id) + ejob->perfmon = ethosu_perfmon_find(file_priv, perfmon_id); + ejob->done_fence = kzalloc_obj(*ejob->done_fence); if (!ejob->done_fence) { ret = -ENOMEM; @@ -491,11 +519,6 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil int ret = 0; unsigned int i = 0; - if (args->pad) { - drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n"); - return -EINVAL; - } - struct drm_ethosu_job __free(kvfree) *jobs = kvmalloc_objs(*jobs, args->job_count); if (!jobs) @@ -509,7 +532,7 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil } for (i = 0; i < args->job_count; i++) { - ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]); + ret = ethosu_ioctl_submit_job(dev, file, &jobs[i], args->perfmon_id); if (ret) return ret; } diff --git a/drivers/accel/ethosu/ethosu_job.h b/drivers/accel/ethosu/ethosu_job.h index ff1cf448d094..8988edd00eed 100644 --- a/drivers/accel/ethosu/ethosu_job.h +++ b/drivers/accel/ethosu/ethosu_job.h @@ -21,6 +21,8 @@ struct ethosu_job { u8 region_cnt; u32 sram_size; + struct ethosu_perfmon *perfmon; + /* Fence to be signaled by drm-sched once its done with the job */ struct dma_fence *inference_done_fence; diff --git a/drivers/accel/ethosu/ethosu_perfmon.c b/drivers/accel/ethosu/ethosu_perfmon.c new file mode 100644 index 000000000000..26f625374f9d --- /dev/null +++ b/drivers/accel/ethosu/ethosu_perfmon.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2026 Arm, Ltd. */ +/* Based on v3d_perfmon.c, Copyright (C) 2021 Raspberry Pi */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "ethosu_drv.h" +#include "ethosu_device.h" + +void ethosu_perfmon_get(struct ethosu_perfmon *perfmon) +{ + if (perfmon) + refcount_inc(&perfmon->refcnt); +} + +void ethosu_perfmon_put(struct ethosu_perfmon *perfmon) +{ + if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) + kfree(perfmon); +} + +void ethosu_perfmon_start(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon) +{ + unsigned int i; + u8 ncounters; + u32 mask; + + lockdep_assert_held(ðosu->perfmon_state.lock); + + if (WARN_ON_ONCE(!perfmon || ethosu->perfmon_state.active)) + return; + + writel_relaxed(PMCR_CNT_EN, ethosu->pmu_regs + NPU_REG_PMCR); + writel_relaxed(PMU_EV_TYPE_CYCLES, ethosu->pmu_regs + NPU_REG_PMCCNTR_CFG); + + mask = 0x80000000; + ncounters = perfmon->ncounters - 1; + if (ncounters) + mask |= GENMASK(ncounters - 1, 0); + + for (i = 0; i < ncounters; i++) + writel_relaxed(perfmon->counters[i], ethosu->pmu_regs + NPU_REG_PMU_EVTYPER(i)); + + writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENSET); + writel_relaxed(PMCR_CNT_EN | PMCR_EVENT_CNT_RST | PMCR_CYCLE_CNT_RST, + ethosu->pmu_regs + NPU_REG_PMCR); + ethosu->perfmon_state.active = perfmon; +} + +void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture) +{ + unsigned int i; + u8 ncounters; + u32 mask; + + lockdep_assert_held(ðosu->perfmon_state.lock); + + if (!perfmon || perfmon != ethosu->perfmon_state.active) + return; + + ncounters = perfmon->ncounters - 1; + + if (!pm_runtime_get_if_active(ethosu->base.dev)) { + ethosu->perfmon_state.active = NULL; + return; + } + + if (capture) { + for (i = 0; i < ncounters; i++) + perfmon->values[i] += readl_relaxed(ethosu->pmu_regs + NPU_REG_PMU_EVCNTR(i)); + + perfmon->values[ncounters] += + readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_LO) | + (u64)readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_HI) << 32; + } + + mask = 0x80000000; + if (ncounters) + mask |= GENMASK(ncounters - 1, 0); + writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENCLR); + + writel_relaxed(0, ethosu->pmu_regs + NPU_REG_PMCR); + ethosu->perfmon_state.active = NULL; + + pm_runtime_put(ethosu->base.dev); +} + +void ethosu_perfmon_stop(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture) +{ + if (!perfmon) + return; + + guard(mutex)(ðosu->perfmon_state.lock); + ethosu_perfmon_stop_locked(ethosu, perfmon, capture); +} + +struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, int id) +{ + struct ethosu_perfmon *perfmon; + + xa_lock(ðosu_priv->perfmons); + perfmon = xa_load(ðosu_priv->perfmons, id); + ethosu_perfmon_get(perfmon); + xa_unlock(ðosu_priv->perfmons); + + return perfmon; +} + +void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv) +{ + xa_init_flags(ðosu_priv->perfmons, XA_FLAGS_ALLOC1); +} + +static void ethosu_perfmon_delete(struct ethosu_file_priv *ethosu_priv, + struct ethosu_perfmon *perfmon) +{ + struct ethosu_device *ethosu = ethosu_priv->edev; + + /* If the active perfmon is being destroyed, stop it first */ + scoped_guard(mutex, ðosu->perfmon_state.lock) { + /* If the global perfmon is being destroyed, set it to NULL */ + if (ethosu->global_perfmon == perfmon) { + ethosu->global_perfmon = NULL; + ethosu_perfmon_put(perfmon); + } + + ethosu_perfmon_stop_locked(ethosu, perfmon, false); + } + + ethosu_perfmon_put(perfmon); +} + +void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv) +{ + struct ethosu_perfmon *perfmon; + unsigned long id; + + xa_for_each(ðosu_priv->perfmons, id, perfmon) + ethosu_perfmon_delete(ethosu_priv, perfmon); + + xa_destroy(ðosu_priv->perfmons); +} + +int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_create *req = data; + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_perfmon *perfmon; + unsigned int i, event_max; + int ret; + u32 id; + + /* Number of monitored counters cannot exceed HW limits. */ + if (req->ncounters > ethosu->npu_info.pmu_counters) + return -EINVAL; + + /* Make sure all counters are valid. */ + event_max = ethosu_is_u65(ethosu) ? 433 : 671; + for (i = 0; i < req->ncounters; i++) { + if (req->counters[i] > event_max) + return -EINVAL; + } + + /* Add 1 more counter for cycle counter */ + req->ncounters++; + + perfmon = kzalloc_flex(*perfmon, values, req->ncounters); + if (!perfmon) + return -ENOMEM; + + for (i = 0; i < req->ncounters - 1; i++) + perfmon->counters[i] = req->counters[i]; + + perfmon->ncounters = req->ncounters; + + refcount_set(&perfmon->refcnt, 1); + + ret = xa_alloc(ðosu_priv->perfmons, &id, perfmon, xa_limit_32b, + GFP_KERNEL); + + if (ret < 0) { + kfree(perfmon); + return ret; + } + + req->id = id; + + return 0; +} + +int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_destroy *req = data; + struct ethosu_perfmon *perfmon; + + perfmon = xa_erase(ðosu_priv->perfmons, req->id); + if (!perfmon) + return -EINVAL; + + ethosu_perfmon_delete(ethosu_priv, perfmon); + + return 0; +} + +int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_get_values *req = data; + struct ethosu_perfmon *perfmon; + int ret = 0; + + if (req->pad != 0) + return -EINVAL; + + perfmon = ethosu_perfmon_find(ethosu_priv, req->id); + if (!perfmon) + return -EINVAL; + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret) { + ethosu_perfmon_put(perfmon); + return ret; + } + ethosu_perfmon_stop(ethosu, perfmon, true); + + pm_runtime_put_autosuspend(dev->dev); + + if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values, + perfmon->ncounters * sizeof(u64))) + ret = -EFAULT; + + ethosu_perfmon_put(perfmon); + + return ret; +} + +int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_set_global *req = data; + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_perfmon *perfmon; + + if (req->flags & ~DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) + return -EINVAL; + + perfmon = ethosu_perfmon_find(ethosu_priv, req->id); + if (!perfmon) + return -EINVAL; + + /* If the request is to clear the global performance monitor */ + if (req->flags & DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) { + struct ethosu_perfmon *old; + + scoped_guard(mutex, ðosu->perfmon_state.lock) { + old = ethosu->global_perfmon; + if (!old) { + ethosu_perfmon_put(perfmon); + return -EINVAL; + } + + ethosu->global_perfmon = NULL; + ethosu_perfmon_stop_locked(ethosu, old, true); + } + + ethosu_perfmon_put(old); + ethosu_perfmon_put(perfmon); + + return 0; + } + + scoped_guard(mutex, ðosu->perfmon_state.lock) { + if (ethosu->perfmon_state.active || ethosu->global_perfmon) { + ethosu_perfmon_put(perfmon); + return -EBUSY; + } + + ethosu->global_perfmon = perfmon; + } + + return 0; +} diff --git a/include/uapi/drm/ethosu_accel.h b/include/uapi/drm/ethosu_accel.h index af78bb4686d7..5b97d59a7806 100644 --- a/include/uapi/drm/ethosu_accel.h +++ b/include/uapi/drm/ethosu_accel.h @@ -43,6 +43,11 @@ enum drm_ethosu_ioctl_id { /** @DRM_ETHOSU_SUBMIT: Submit a job and BOs to run. */ DRM_ETHOSU_SUBMIT, + + DRM_ETHOSU_PERFMON_CREATE, + DRM_ETHOSU_PERFMON_DESTROY, + DRM_ETHOSU_PERFMON_GET_VALUES, + DRM_ETHOSU_PERFMON_SET_GLOBAL, }; /** @@ -79,6 +84,7 @@ struct drm_ethosu_npu_info { __u32 config; __u32 sram_size; + __u32 pmu_counters; }; /** @@ -220,10 +226,54 @@ struct drm_ethosu_submit { /** Input: Number of jobs passed in. */ __u32 job_count; - /** Reserved, must be zero. */ + /** Input: Id returned by DRM_ETHOSU_PERFMON_CREATE */ + __u32 perfmon_id; +}; + +#define DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS 8 +#define DRM_ETHOSU_MAX_PERF_COUNTERS \ + (DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS + 1) + +struct drm_ethosu_perfmon_create { + __u32 id; + __u32 ncounters; + __u16 counters[DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS]; +}; + +struct drm_ethosu_perfmon_destroy { + __u32 id; __u32 pad; }; +/* + * Returns the values of the performance counters tracked by this + * perfmon (as an array of (ncounters + 1) u64 values). + * + * No implicit synchronization is performed, so the user has to + * guarantee that any jobs using this perfmon have already been + * completed. + */ +struct drm_ethosu_perfmon_get_values { + __u32 id; + __u32 pad; + __u64 values_ptr; +}; + +#define DRM_ETHOSU_PERFMON_CLEAR_GLOBAL 0x0001 + +/** + * struct drm_ethosu_perfmon_set_global - ioctl to define a global performance + * monitor + * + * The global performance monitor will be used for all jobs. If a global + * performance monitor is defined, jobs with a self-defined performance + * monitor won't be allowed. + */ +struct drm_ethosu_perfmon_set_global { + __u32 flags; + __u32 id; +}; + /** * DRM_IOCTL_ETHOSU() - Build a ethosu IOCTL number * @__access: Access type. Must be R, W or RW. @@ -252,6 +302,14 @@ enum { DRM_IOCTL_ETHOSU(WR, CMDSTREAM_BO_CREATE, cmdstream_bo_create), DRM_IOCTL_ETHOSU_SUBMIT = DRM_IOCTL_ETHOSU(WR, SUBMIT, submit), + DRM_IOCTL_ETHOSU_PERFMON_CREATE = + DRM_IOCTL_ETHOSU(WR, PERFMON_CREATE, perfmon_create), + DRM_IOCTL_ETHOSU_PERFMON_DESTROY = + DRM_IOCTL_ETHOSU(WR, PERFMON_DESTROY, perfmon_destroy), + DRM_IOCTL_ETHOSU_PERFMON_GET_VALUES = + DRM_IOCTL_ETHOSU(WR, PERFMON_GET_VALUES, perfmon_get_values), + DRM_IOCTL_ETHOSU_PERFMON_SET_GLOBAL = + DRM_IOCTL_ETHOSU(WR, PERFMON_SET_GLOBAL, perfmon_set_global), }; #if defined(__cplusplus) -- cgit From ed04e8e2307f35b3d8d49a554faf5e72d3d224e6 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 1 Jun 2026 19:13:44 +0300 Subject: drm/bridge: synopsys: dw-dp: Support unregistering the AUX channel The DisplayPort AUX channel gets initialized and registered during dw_dp_bind(), but it is never unregistered, which may lead to resource leaks and/or use-after-free. Add the missing dw_dp_unbind() function to allow the users of the library to handle the required cleanup, i.e. unregister the AUX adapter. Fixes: 86eecc3a9c2e ("drm/bridge: synopsys: Add DW DPTX Controller support library") Reviewed-by: Andy Yan Signed-off-by: Cristian Ciocaltea Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260601-drm-rk-fixes-v4-1-c3f3f123e1da@collabora.com --- drivers/gpu/drm/bridge/synopsys/dw-dp.c | 6 ++++++ include/drm/bridge/dw_dp.h | 1 + 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c index 21541be094c4..36ee6e027af5 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c @@ -2093,6 +2093,12 @@ unregister_aux: } EXPORT_SYMBOL_GPL(dw_dp_bind); +void dw_dp_unbind(struct dw_dp *dp) +{ + drm_dp_aux_unregister(&dp->aux); +} +EXPORT_SYMBOL_GPL(dw_dp_unbind); + MODULE_AUTHOR("Andy Yan "); MODULE_DESCRIPTION("DW DP Core Library"); MODULE_LICENSE("GPL"); diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h index 25363541e69d..22105c3e8e4d 100644 --- a/include/drm/bridge/dw_dp.h +++ b/include/drm/bridge/dw_dp.h @@ -24,4 +24,5 @@ struct dw_dp_plat_data { struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder, const struct dw_dp_plat_data *plat_data); +void dw_dp_unbind(struct dw_dp *dp); #endif /* __DW_DP__ */ -- cgit From 709445fb6fc57aa96d48c8492e84ed4b2a9a4b5e Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Mon, 1 Jun 2026 14:50:58 +0800 Subject: drm/bridge: analogix_dp: Rename and simplify is_rockchip() Rename inline helper is_rockchip() to analogix_dp_is_rockchip() to follow driver namespace convention consistently across code. Replace chained equality comparisons with switch-case layout to improve readability and simplify adding new SoC entries later. Signed-off-by: Damon Ding Suggested-by: Nicolas Frattaroli Reviewed-by: Luca Ceresoli Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260601065100.1103873-10-damon.ding@rock-chips.com --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 2 +- drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 18 +++++++++--------- include/drm/bridge/analogix_dp.h | 11 +++++++++-- 3 files changed, 19 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 8cf6b73bceac..116de3bd83a3 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -870,7 +870,7 @@ static int analogix_dp_bridge_atomic_check(struct drm_bridge *bridge, struct drm_display_info *di = &conn_state->connector->display_info; u32 mask = BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422); - if (is_rockchip(dp->plat_data->dev_type)) { + if (analogix_dp_is_rockchip(dp->plat_data->dev_type)) { if ((di->color_formats & mask)) { DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n"); di->color_formats &= ~mask; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 38fd8d5014d2..6207ded7ffd5 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -72,7 +72,7 @@ void analogix_dp_init_analog_param(struct analogix_dp_device *dp) reg = SEL_24M | TX_DVDD_BIT_1_0625V; writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { reg = REF_CLK_24M; if (dp->plat_data->dev_type == RK3288_DP) reg ^= REF_CLK_MASK; @@ -123,7 +123,7 @@ void analogix_dp_reset(struct analogix_dp_device *dp) analogix_dp_stop_video(dp); analogix_dp_enable_video_mute(dp, 0); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) reg = RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N | SW_FUNC_EN_N; else @@ -233,7 +233,7 @@ void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable) u32 mask = DP_PLL_PD; u32 pd_addr = ANALOGIX_DP_PLL_CTL; - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { pd_addr = ANALOGIX_DP_PD; mask = RK_PLL_PD; } @@ -254,12 +254,12 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, u32 phy_pd_addr = ANALOGIX_DP_PHY_PD; u32 mask; - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) phy_pd_addr = ANALOGIX_DP_PD; switch (block) { case AUX_BLOCK: - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) mask = RK_AUX_PD; else mask = AUX_PD; @@ -317,7 +317,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, * to power off everything instead of DP_PHY_PD in * Rockchip */ - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) mask = DP_INC_BG; else mask = DP_PHY_PD; @@ -329,7 +329,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, reg &= ~mask; writel(reg, dp->reg_base + phy_pd_addr); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) usleep_range(10, 15); break; case POWER_ALL: @@ -465,7 +465,7 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp) analogix_dp_reset_aux(dp); /* AUX_BIT_PERIOD_EXPECTED_DELAY doesn't apply to Rockchip IP */ - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) reg = 0; else reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3); @@ -837,7 +837,7 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp) u32 reg; reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { reg &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N); } else { reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N); diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 854af692229b..7b670dd769e9 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -19,9 +19,16 @@ enum analogix_dp_devtype { RK3588_EDP, }; -static inline bool is_rockchip(enum analogix_dp_devtype type) +static inline bool analogix_dp_is_rockchip(enum analogix_dp_devtype type) { - return type == RK3288_DP || type == RK3399_EDP || type == RK3588_EDP; + switch (type) { + case RK3288_DP: + case RK3399_EDP: + case RK3588_EDP: + return true; + default: + return false; + } } struct analogix_dp_plat_data { -- cgit From 5442cdc38470ecd6b51562d378b6ed852a4d9d45 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Mon, 1 Jun 2026 14:50:59 +0800 Subject: drm/bridge: analogix_dp: Add support for RK3576 Add RK3576_EDP device type entry and extend Rockchip check to match existing hardware capabilities shared with RK3588. Set identical maximum link rate and lane count parameters for RK3576 eDP controller to reuse existing RK3588 config. Reviewed-by: Luca Ceresoli Signed-off-by: Damon Ding Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260601065100.1103873-11-damon.ding@rock-chips.com --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 1 + include/drm/bridge/analogix_dp.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 116de3bd83a3..c8eb3511f92a 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1249,6 +1249,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp) video_info->max_link_rate = 0x0A; video_info->max_lane_count = 0x04; break; + case RK3576_EDP: case RK3588_EDP: video_info->max_link_rate = 0x14; video_info->max_lane_count = 0x04; diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 7b670dd769e9..0e0b87abee59 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -16,6 +16,7 @@ enum analogix_dp_devtype { EXYNOS_DP, RK3288_DP, RK3399_EDP, + RK3576_EDP, RK3588_EDP, }; @@ -24,6 +25,7 @@ static inline bool analogix_dp_is_rockchip(enum analogix_dp_devtype type) switch (type) { case RK3288_DP: case RK3399_EDP: + case RK3576_EDP: case RK3588_EDP: return true; default: -- cgit From 96889ef7dcf70bd7f8d78a6e6f255d9436d59993 Mon Sep 17 00:00:00 2001 From: Michał Grzelak Date: Fri, 22 May 2026 15:55:19 +0200 Subject: drm/print: describe 6th & 9th bit of drm.debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting 6th or 9th bit of drm.debug change debug logging. Meanwhile `modinfo drm` does not inform about it at all. Add info to MODULE_PARAM_DESC(debug, ...) about setting 6th and 9th bit basing on DECLARE_DYNDBG_CLASSMAP(drm_debug_classes, ...). Match description of corresponding bits with enum drm_debug_category. Include 9th bit in the example with enabling all possible logging provided at comment at include/drm/drm_print.h. Signed-off-by: Michał Grzelak Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260522135520.1862848-2-michal.grzelak@intel.com --- drivers/gpu/drm/drm_print.c | 4 +++- include/drm/drm_print.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index ded9461df5f2..86cef1a37678 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -50,8 +50,10 @@ MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug cat "\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n" "\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n" "\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n" +"\t\tBit 6 (0x40) will enable STATE messages (atomic state code)\n" "\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n" -"\t\tBit 8 (0x100) will enable DP messages (displayport code)"); +"\t\tBit 8 (0x100) will enable DP messages (displayport code)\n" +"\t\tBit 9 (0x200) will enable DRMRES messages (managed resources code)"); #if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) module_param_named(debug, __drm_debug, ulong, 0600); diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index ab017b05e175..2adc5ac688e1 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -87,7 +87,7 @@ extern unsigned long __drm_debug; * - drm.debug=0x2 will enable DRIVER messages * - drm.debug=0x3 will enable CORE and DRIVER messages * - ... - * - drm.debug=0x1ff will enable all messages + * - drm.debug=0x3ff will enable all messages * * An interesting feature is that it's possible to enable verbose logging at * run-time by echoing the debug value in its sysfs node:: -- cgit From b0d4740dae77c9e7b97344d6d81c8727a6ea8795 Mon Sep 17 00:00:00 2001 From: Michał Grzelak Date: Fri, 22 May 2026 15:55:20 +0200 Subject: drm/managed: fix drmm_add_action() kernel-doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kernel-doc of drmm_add_action() references @releases which is not on argument list. Swap '@' between 'releases' and 'action' words to fix the documentation. Signed-off-by: Michał Grzelak Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260522135520.1862848-3-michal.grzelak@intel.com --- include/drm/drm_managed.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 72bfac002c06..72d0d68be226 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,7 +18,7 @@ typedef void (*drmres_release_t)(struct drm_device *dev, void *res); * @action: function which should be called when @dev is released * @data: opaque pointer, passed to @action * - * This function adds the @release action with optional parameter @data to the + * This function adds the release @action with optional parameter @data to the * list of cleanup actions for @dev. The cleanup actions will be run in reverse * order in the final drm_dev_put() call for @dev. */ -- cgit From 98a6cabfd805d55a1b2c70b01923ec9b9bbc706a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 29 May 2026 16:01:23 +0200 Subject: lib/fonts: Look up glyph data with font_data_glyph_buf() Add font_data_glyph_buf() to retrieve a character's glyph data or NULL otherwise. Console fonts can currently contain 256 or 512 glyphs. The kernel-internal characters are of type char, unsigned short or unsigned int. Catch all of them by accepting unsigned int. Callers possibly have to cast from signed to unsigned types to reach all glyphs in a font. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/20260529140759.529929-2-tzimmermann@suse.de --- include/linux/font.h | 3 +++ lib/fonts/fonts.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/linux/font.h b/include/linux/font.h index 6845f02d739a..ea23b727388b 100644 --- a/include/linux/font.h +++ b/include/linux/font.h @@ -101,6 +101,9 @@ font_data_t *font_data_import(const struct console_font *font, unsigned int vpit void font_data_get(font_data_t *fd); bool font_data_put(font_data_t *fd); unsigned int font_data_size(font_data_t *fd); +const unsigned char *font_data_glyph_buf(font_data_t *fd, + unsigned int width, unsigned int vpitch, + unsigned int c); bool font_data_is_equal(font_data_t *lhs, font_data_t *rhs); int font_data_export(font_data_t *fd, struct console_font *font, unsigned int vpitch); diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c index f5d5333450a0..4fc66722d00d 100644 --- a/lib/fonts/fonts.c +++ b/lib/fonts/fonts.c @@ -178,6 +178,37 @@ unsigned int font_data_size(font_data_t *fd) } EXPORT_SYMBOL_GPL(font_data_size); +static unsigned int font_data_num_glyphs(font_data_t *fd, unsigned int width, unsigned int height) +{ + return font_data_size(fd) / font_glyph_size(width, height); +} + +/** + * font_data_glyph_buf() - Returns the glyph for a specific character as raw bytes + * @fd: The font data + * @width: The glyph width in bits per scanline + * @vpitch: The number of scanlines per glyph + * @c: The character + * + * Glyphs start at fixed intervals within the font data. font_data_glyph_buf() + * returns the glyph shape of the specified character. If no such glyph + * exists in the font, it returns NULL. + * + * Returns: + * The character's raw glyph shape, or NULL if no glyph exists for the character. The + * provided buffer is read-only. + */ +const unsigned char *font_data_glyph_buf(font_data_t *fd, + unsigned int width, unsigned int vpitch, + unsigned int c) +{ + if (c >= font_data_num_glyphs(fd, width, vpitch)) + return NULL; + + return font_data_buf(fd) + font_glyph_size(width, vpitch) * c; +} +EXPORT_SYMBOL_GPL(font_data_glyph_buf); + /** * font_data_is_equal - Compares font data for equality * @lhs: Left-hand side font data -- cgit From 6c1427fd03d8c30be810592523c5ba8fe864c2b0 Mon Sep 17 00:00:00 2001 From: Adrián Larumbe Date: Fri, 22 May 2026 19:51:55 +0100 Subject: drm/panthor: Expose GPU page sizes to UM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In future commits that will implement repeated mappings, only repeat values multiple of GPU page sizes will be tolerated. That means these values must be made known to UM. Do it through a queriable GPU info value. Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Signed-off-by: Adrián Larumbe Link: https://patch.msgid.link/20260522185206.2798288-2-adrian.larumbe@collabora.com Signed-off-by: Steven Price --- drivers/gpu/drm/panthor/panthor_device.h | 3 +++ drivers/gpu/drm/panthor/panthor_drv.c | 8 ++++++++ drivers/gpu/drm/panthor/panthor_mmu.c | 9 ++++++++- include/uapi/drm/panthor_drm.h | 13 +++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index a412a50eec76..35679bfa1f3a 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -161,6 +161,9 @@ struct panthor_device { /** @csif_info: Command stream interface information. */ struct drm_panthor_csif_info csif_info; + /** @mmu_info: MMU info */ + struct drm_panthor_mmu_info mmu_info; + /** @hw: GPU-specific data. */ struct panthor_hw *hw; diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 1b8f5d5c2ee9..557e1bfddfd7 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -175,6 +175,7 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride, _Generic(_obj_name, \ PANTHOR_UOBJ_DECL(struct drm_panthor_gpu_info, tiler_present), \ PANTHOR_UOBJ_DECL(struct drm_panthor_csif_info, pad), \ + PANTHOR_UOBJ_DECL(struct drm_panthor_mmu_info, page_size_bitmap), \ PANTHOR_UOBJ_DECL(struct drm_panthor_timestamp_info, current_timestamp), \ PANTHOR_UOBJ_DECL(struct drm_panthor_group_priorities_info, pad), \ PANTHOR_UOBJ_DECL(struct drm_panthor_sync_op, timeline_value), \ @@ -954,6 +955,10 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d args->size = sizeof(priorities_info); return 0; + case DRM_PANTHOR_DEV_QUERY_MMU_INFO: + args->size = sizeof(ptdev->mmu_info); + return 0; + default: return -EINVAL; } @@ -984,6 +989,9 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d panthor_query_group_priorities_info(file, &priorities_info); return PANTHOR_UOBJ_SET(args->pointer, args->size, priorities_info); + case DRM_PANTHOR_DEV_QUERY_MMU_INFO: + return PANTHOR_UOBJ_SET(args->pointer, args->size, ptdev->mmu_info); + default: return -EINVAL; } diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 9d4500850561..271a3ea55b61 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -2782,7 +2782,7 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu, refcount_set(&vm->as.active_cnt, 0); pgtbl_cfg = (struct io_pgtable_cfg) { - .pgsize_bitmap = SZ_4K | SZ_2M, + .pgsize_bitmap = ptdev->mmu_info.page_size_bitmap, .ias = va_bits, .oas = pa_bits, .coherent_walk = ptdev->coherent, @@ -3228,6 +3228,11 @@ static void panthor_mmu_release_wq(struct drm_device *ddev, void *res) destroy_workqueue(res); } +static void panthor_mmu_info_init(struct panthor_device *ptdev) +{ + ptdev->mmu_info.page_size_bitmap = SZ_4K | SZ_2M; +} + /** * panthor_mmu_init() - Initialize the MMU logic. * @ptdev: Device. @@ -3240,6 +3245,8 @@ int panthor_mmu_init(struct panthor_device *ptdev) struct panthor_mmu *mmu; int ret, irq; + panthor_mmu_info_init(ptdev); + mmu = drmm_kzalloc(&ptdev->base, sizeof(*mmu), GFP_KERNEL); if (!mmu) return -ENOMEM; diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h index 0e455d91e77d..b462752c793d 100644 --- a/include/uapi/drm/panthor_drm.h +++ b/include/uapi/drm/panthor_drm.h @@ -253,6 +253,9 @@ enum drm_panthor_dev_query_type { * @DRM_PANTHOR_DEV_QUERY_GROUP_PRIORITIES_INFO: Query allowed group priorities information. */ DRM_PANTHOR_DEV_QUERY_GROUP_PRIORITIES_INFO, + + /** @DRM_PANTHOR_DEV_QUERY_MMU_INFO: Query MMU information. */ + DRM_PANTHOR_DEV_QUERY_MMU_INFO, }; /** @@ -487,6 +490,16 @@ struct drm_panthor_timestamp_info { __u64 cpu_timestamp_nsec; }; +/** + * struct drm_panthor_mmu_info - MMU information + * + * Structure grouping all queryable information relating to the MMU. + */ +struct drm_panthor_mmu_info { + /** @page_size_bitmap: Allowed page sizes */ + __u64 page_size_bitmap; +}; + /** * struct drm_panthor_group_priorities_info - Group priorities information * -- cgit From 8e54aac5af6a3712a72678f7585eb605e40b89e8 Mon Sep 17 00:00:00 2001 From: Adrián Larumbe Date: Fri, 22 May 2026 19:51:57 +0100 Subject: drm/panthor: Delete spurious whitespace from uAPI header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no extra blank line after the last member of any other uAPI structures, so delete it. Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Signed-off-by: Adrián Larumbe Link: https://patch.msgid.link/20260522185206.2798288-4-adrian.larumbe@collabora.com Signed-off-by: Steven Price --- include/uapi/drm/panthor_drm.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h index b462752c793d..14a93a4ef6ff 100644 --- a/include/uapi/drm/panthor_drm.h +++ b/include/uapi/drm/panthor_drm.h @@ -677,7 +677,6 @@ struct drm_panthor_vm_bind_op { * This array shall not be empty for sync-only operations. */ struct drm_panthor_obj_array syncs; - }; /** -- cgit From 12cf826bf1dd9275773cbef02c81ec1c67def7c3 Mon Sep 17 00:00:00 2001 From: Adrián Larumbe Date: Fri, 22 May 2026 19:51:59 +0100 Subject: drm/panthor: Support sparse mappings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow UM to bind sparsely populated memory regions by cyclically mapping virtual ranges over a kernel-allocated dummy BO. This alternative is preferable to the old method of handling sparseness in the UMD, because it relied on the creation of a buffer object to the same end, despite the fact Vulkan sparse resources don't need to be backed by a driver BO. The choice of backing sparsely-bound regions with a Panthor BO was made so as to profit from the existing shrinker reclaim code. That way no special treatment must be given to the dummy sparse BOs when reclaiming memory, as would be the case if we had chosen a raw kernel page implementation. A new dummy BO is allocated per open file context, because even though the Vulkan spec mandates that writes into sparsely bound regions must be discarded, our implementation is still a workaround over the fact Mali CSF GPUs cannot support this behaviour on the hardware level, so writes still make it into the backing BO. If we had a global one, then it could be a venue for information leaks between file contexts, which should never happen in DRM. As a side note, care was put to adjust dummy BO offsets for sparse mappings so that all addresses in the new VA are mapped aligned against it. Signed-off-by: Adrián Larumbe Reviewed-by: Boris Brezillon Reviewed-by: Steven Price Link: https://patch.msgid.link/20260522185206.2798288-6-adrian.larumbe@collabora.com Signed-off-by: Steven Price --- drivers/gpu/drm/panthor/panthor_gem.c | 18 ++++ drivers/gpu/drm/panthor/panthor_gem.h | 2 + drivers/gpu/drm/panthor/panthor_mmu.c | 195 +++++++++++++++++++++++++++++----- include/uapi/drm/panthor_drm.h | 12 +++ 4 files changed, 201 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index a1e2eb1ca7bb..9855df738194 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -1347,6 +1347,24 @@ err_free_kbo: return ERR_PTR(ret); } +/** + * panthor_dummy_bo_create() - Create a Panthor BO meant to back sparse bindings. + * @ptdev: Device. + * + * Return: A valid pointer in case of success, an ERR_PTR() otherwise. + */ +struct panthor_gem_object * +panthor_dummy_bo_create(struct panthor_device *ptdev) +{ + /* Since even when the DRM device's mount point has enabled THP we have no guarantee + * that drm_gem_get_pages() will return a single 2MiB PMD, and also we cannot be sure + * that the 2MiB won't be reclaimed and re-allocated later on as 4KiB chunks, it doesn't + * make sense to pre-populate this object's page array, nor to fall back on a BO size + * of 4KiB. Sticking to a dummy object size of 2MiB lets us keep things simple for now. + */ + return panthor_gem_create(&ptdev->base, SZ_2M, DRM_PANTHOR_BO_NO_MMAP, NULL, 0); +} + static bool can_swap(void) { return get_nr_swap_pages() > 0; diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h index 56d63137b4eb..5ae37d0d3646 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.h +++ b/drivers/gpu/drm/panthor/panthor_gem.h @@ -325,6 +325,8 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm, void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo); +struct panthor_gem_object *panthor_dummy_bo_create(struct panthor_device *ptdev); + #ifdef CONFIG_DEBUG_FS void panthor_gem_debugfs_init(struct drm_minor *minor); #endif diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index f3cb94611e9c..31cc57029c12 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -116,6 +116,17 @@ struct panthor_mmu { struct panthor_vm_pool { /** @xa: Array used for VM handle tracking. */ struct xarray xa; + + /** + * @dummy: Dummy object used for sparse mappings + * + * Sparse bindings map virtual address ranges onto a dummy + * BO in a modulo fashion. Even though sparse writes are meant + * to be discarded and reads undefined, writes are still reflected + * in the dummy buffer. That means we must keep a dummy object per + * file context, to avoid data leaks between them. + */ + struct panthor_gem_object *dummy; }; /** @@ -395,6 +406,15 @@ struct panthor_vm { */ struct list_head lru_node; } reclaim; + + /** + * @dummy: Dummy object used for sparse mappings. + * + * VM's must keep a reference to the file context-wide dummy BO because + * they can outlive the file context, which includes the VM pool holding + * the original dummy BO reference. + */ + struct panthor_gem_object *dummy; }; /** @@ -1027,6 +1047,30 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot, return 0; } +static int +panthor_vm_map_sparse(struct panthor_vm *vm, u64 iova, int prot, + struct sg_table *sgt, u64 size) +{ + u64 mapped = 0; + int ret; + + while (mapped < size) { + u64 addr = iova + mapped; + u32 chunk_size = min(size - mapped, SZ_2M - (addr & (SZ_2M - 1))); + + ret = panthor_vm_map_pages(vm, addr, prot, sgt, + addr % SZ_2M, chunk_size); + if (ret) { + panthor_vm_unmap_pages(vm, iova, mapped); + return ret; + } + + mapped += chunk_size; + } + + return 0; +} + static int flags_to_prot(u32 flags) { int prot = 0; @@ -1269,6 +1313,7 @@ static int panthor_vm_op_ctx_prealloc_pts(struct panthor_vm_op_ctx *op_ctx) (DRM_PANTHOR_VM_BIND_OP_MAP_READONLY | \ DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC | \ DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED | \ + DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE | \ DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, @@ -1276,6 +1321,7 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, struct panthor_gem_object *bo, const struct drm_panthor_vm_bind_op *op) { + bool is_sparse = op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE; struct drm_gpuvm_bo *preallocated_vm_bo; struct sg_table *sgt = NULL; int ret; @@ -1287,8 +1333,21 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) != DRM_PANTHOR_VM_BIND_OP_TYPE_MAP) return -EINVAL; - /* Make sure the VA and size are in-bounds. */ - if (op->size > bo->base.size || op->bo_offset > bo->base.size - op->size) + /* uAPI mandates sparsely bound regions must not be executable. */ + if (is_sparse && !(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC)) + return -EINVAL; + + /* For non-sparse, make sure the VA and size are in-bounds. + * For sparse, this is not applicable, because the dummy BO is + * repeatedly mapped over a potentially wider VA range. + */ + if (!is_sparse && (op->size > bo->base.size || op->bo_offset > bo->base.size - op->size)) + return -EINVAL; + + /* For sparse, we don't expect any user BO, the BO we get passed + * is the dummy BO attached to the VM pool. + */ + if (is_sparse && (op->bo_handle || op->bo_offset)) return -EINVAL; /* If the BO has an exclusive VM attached, it can't be mapped to other VMs. */ @@ -1437,7 +1496,9 @@ panthor_vm_get_bo_for_va(struct panthor_vm *vm, u64 va, u64 *bo_offset) if (vma && vma->base.gem.obj) { drm_gem_object_get(vma->base.gem.obj); bo = to_panthor_bo(vma->base.gem.obj); - *bo_offset = vma->base.gem.offset + (va - vma->base.va.addr); + *bo_offset = !(vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) ? + vma->base.gem.offset + (va - vma->base.va.addr) : + va & (SZ_2M - 1); } mutex_unlock(&vm->op_lock); @@ -1542,6 +1603,9 @@ int panthor_vm_pool_create_vm(struct panthor_device *ptdev, if (IS_ERR(vm)) return PTR_ERR(vm); + drm_gem_object_get(&pool->dummy->base); + vm->dummy = pool->dummy; + ret = xa_alloc(&pool->xa, &id, vm, XA_LIMIT(1, PANTHOR_MAX_VMS_PER_FILE), GFP_KERNEL); @@ -1641,6 +1705,8 @@ void panthor_vm_pool_destroy(struct panthor_file *pfile) xa_for_each(&pfile->vms->xa, i, vm) panthor_vm_destroy(vm); + if (pfile->vms->dummy) + drm_gem_object_put(&pfile->vms->dummy->base); xa_destroy(&pfile->vms->xa); kfree(pfile->vms); } @@ -1653,12 +1719,28 @@ void panthor_vm_pool_destroy(struct panthor_file *pfile) */ int panthor_vm_pool_create(struct panthor_file *pfile) { + struct panthor_gem_object *dummy; + int ret; + pfile->vms = kzalloc_obj(*pfile->vms); if (!pfile->vms) return -ENOMEM; xa_init_flags(&pfile->vms->xa, XA_FLAGS_ALLOC1); + + dummy = panthor_dummy_bo_create(pfile->ptdev); + if (IS_ERR(dummy)) { + ret = PTR_ERR(dummy); + goto err_destroy_vm_pool; + } + + pfile->vms->dummy = dummy; + return 0; + +err_destroy_vm_pool: + panthor_vm_pool_destroy(pfile); + return ret; } /* dummy TLB ops, the real TLB flush happens in panthor_vm_flush_range() */ @@ -1995,6 +2077,9 @@ static void panthor_vm_free(struct drm_gpuvm *gpuvm) free_io_pgtable_ops(vm->pgtbl_ops); + if (vm->dummy) + drm_gem_object_put(&vm->dummy->base); + drm_mm_takedown(&vm->mm); kfree(vm); } @@ -2154,7 +2239,30 @@ static void panthor_vma_init(struct panthor_vma *vma, u32 flags) #define PANTHOR_VM_MAP_FLAGS \ (DRM_PANTHOR_VM_BIND_OP_MAP_READONLY | \ DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC | \ - DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED) + DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED | \ + DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) + +static void +panthor_fix_sparse_map_offset(struct drm_gpuva_op_map *op, u32 flags) +{ + if (op && (flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) + op->gem.offset = op->va.addr & (SZ_2M - 1); +} + +static int +panthor_vm_exec_map_op(struct panthor_vm *vm, u32 flags, + const struct drm_gpuva_op_map *op) +{ + struct panthor_gem_object *bo = to_panthor_bo(op->gem.obj); + int prot = flags_to_prot(flags); + + if (flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) + return panthor_vm_map_sparse(vm, op->va.addr, prot, + bo->dmap.sgt, op->va.range); + + return panthor_vm_map_pages(vm, op->va.addr, prot, bo->dmap.sgt, + op->gem.offset, op->va.range); +} static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv) { @@ -2167,10 +2275,9 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv) return -EINVAL; panthor_vma_init(vma, op_ctx->flags & PANTHOR_VM_MAP_FLAGS); + panthor_fix_sparse_map_offset(&op->map, vma->flags); - ret = panthor_vm_map_pages(vm, op->map.va.addr, flags_to_prot(vma->flags), - op_ctx->map.bo->dmap.sgt, op->map.gem.offset, - op->map.va.range); + ret = panthor_vm_exec_map_op(vm, vma->flags, &op->map); if (ret) { panthor_vm_op_ctx_return_vma(op_ctx, vma); return ret; @@ -2202,6 +2309,8 @@ static void unmap_hugepage_align(const struct drm_gpuva_op_remap *op, u64 *unmap_start, u64 *unmap_range) { + struct panthor_vma *unmap_vma = container_of(op->unmap->va, struct panthor_vma, base); + bool is_sparse = unmap_vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE; u64 aligned_unmap_start, aligned_unmap_end, unmap_end; unmap_end = *unmap_start + *unmap_range; @@ -2209,11 +2318,15 @@ unmap_hugepage_align(const struct drm_gpuva_op_remap *op, aligned_unmap_end = ALIGN(unmap_end, SZ_2M); /* If we're dealing with a huge page, make sure the unmap region is - * aligned on the start of the page. + * aligned on the start of the page. If the unmapped VMA stands for + * a sparse mapping, always assume the backing storage is a THP, since + * the overhead of unmapping 2MiB worth of 4KiB pages and remapping + * some of them is offset by the logic of working out whether it's + * the opposite case right below. This also holds true for op->next. */ if (op->prev && aligned_unmap_start < *unmap_start && op->prev->va.addr <= aligned_unmap_start && - iova_mapped_as_huge_page(op->prev, *unmap_start)) { + (is_sparse || iova_mapped_as_huge_page(op->prev, *unmap_start))) { *unmap_range += *unmap_start - aligned_unmap_start; *unmap_start = aligned_unmap_start; } @@ -2223,7 +2336,7 @@ unmap_hugepage_align(const struct drm_gpuva_op_remap *op, */ if (op->next && aligned_unmap_end > unmap_end && op->next->va.addr + op->next->va.range >= aligned_unmap_end && - iova_mapped_as_huge_page(op->next, unmap_end - 1)) { + (is_sparse || iova_mapped_as_huge_page(op->next, unmap_end - 1))) { *unmap_range += aligned_unmap_end - unmap_end; } } @@ -2240,6 +2353,11 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op, drm_gpuva_op_remap_to_unmap_range(&op->remap, &unmap_start, &unmap_range); + /* op->remap.prev's BO offset is always the same as the unmap va's, but + * that of op->remap.next must be adjusted so as to remain < SZ_2M + */ + panthor_fix_sparse_map_offset(op->remap.next, unmap_vma->flags); + /* * ARM IOMMU page table management code disallows partial unmaps of huge pages, * so when a partial unmap is requested, we must first unmap the entire huge @@ -2259,14 +2377,19 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op, } if (op->remap.prev) { - struct panthor_gem_object *bo = to_panthor_bo(op->remap.prev->gem.obj); u64 offset = op->remap.prev->gem.offset + unmap_start - op->remap.prev->va.addr; u64 size = op->remap.prev->va.addr + op->remap.prev->va.range - unmap_start; - if (!unmap_vma->evicted) { - ret = panthor_vm_map_pages(vm, unmap_start, - flags_to_prot(unmap_vma->flags), - bo->dmap.sgt, offset, size); + if (!unmap_vma->evicted && size > 0) { + struct drm_gpuva_op_map map_op = { + .va.addr = unmap_start, + .va.range = size, + .gem.obj = op->remap.prev->gem.obj, + .gem.offset = offset, + }; + panthor_fix_sparse_map_offset(&map_op, unmap_vma->flags); + + ret = panthor_vm_exec_map_op(vm, unmap_vma->flags, &map_op); if (ret) return ret; } @@ -2277,14 +2400,19 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op, } if (op->remap.next) { - struct panthor_gem_object *bo = to_panthor_bo(op->remap.next->gem.obj); u64 addr = op->remap.next->va.addr; u64 size = unmap_start + unmap_range - op->remap.next->va.addr; - if (!unmap_vma->evicted) { - ret = panthor_vm_map_pages(vm, addr, flags_to_prot(unmap_vma->flags), - bo->dmap.sgt, op->remap.next->gem.offset, - size); + if (!unmap_vma->evicted && size > 0) { + struct drm_gpuva_op_map map_op = { + .va.addr = addr, + .va.range = size, + .gem.obj = op->remap.next->gem.obj, + .gem.offset = op->remap.next->gem.offset, + }; + panthor_fix_sparse_map_offset(&map_op, unmap_vma->flags); + + ret = panthor_vm_exec_map_op(vm, unmap_vma->flags, &map_op); if (ret) return ret; } @@ -2481,11 +2609,17 @@ static int remap_evicted_vma(struct drm_gpuvm_bo *vm_bo, ret = panthor_vm_lock_region(vm, evicted_vma->base.va.addr, evicted_vma->base.va.range); if (!ret) { - ret = panthor_vm_map_pages(vm, evicted_vma->base.va.addr, - flags_to_prot(evicted_vma->flags), - bo->dmap.sgt, - evicted_vma->base.gem.offset, - evicted_vma->base.va.range); + struct drm_gpuva_op_map map_op = { + .va.addr = evicted_vma->base.va.addr, + .va.range = evicted_vma->base.va.range, + .gem.obj = &bo->base, + .gem.offset = evicted_vma->base.gem.offset, + }; + if (evicted_vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) + drm_WARN_ON_ONCE(&vm->ptdev->base, map_op.gem.offset != + (map_op.va.addr & (SZ_2M - 1))); + + ret = panthor_vm_exec_map_op(vm, evicted_vma->flags, &map_op); if (!ret) evicted_vma->evicted = false; @@ -2849,7 +2983,13 @@ panthor_vm_bind_prepare_op_ctx(struct drm_file *file, switch (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) { case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP: - gem = drm_gem_object_lookup(file, op->bo_handle); + if (!(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) { + gem = drm_gem_object_lookup(file, op->bo_handle); + } else { + gem = &vm->dummy->base; + drm_gem_object_get(&vm->dummy->base); + } + ret = panthor_vm_prepare_map_op_ctx(op_ctx, vm, gem ? to_panthor_bo(gem) : NULL, op); @@ -3057,6 +3197,9 @@ int panthor_vm_map_bo_range(struct panthor_vm *vm, struct panthor_gem_object *bo struct panthor_vm_op_ctx op_ctx; int ret; + if (drm_WARN_ON(&vm->ptdev->base, flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) + return -EINVAL; + ret = panthor_vm_prepare_map_op_ctx(&op_ctx, vm, bo, &op); if (ret) return ret; diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h index 14a93a4ef6ff..a2ff0f4ec691 100644 --- a/include/uapi/drm/panthor_drm.h +++ b/include/uapi/drm/panthor_drm.h @@ -614,6 +614,18 @@ enum drm_panthor_vm_bind_op_flags { */ DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED = 1 << 2, + /** + * @DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE: Sparsely map a virtual memory range + * + * Only valid with DRM_PANTHOR_VM_BIND_OP_TYPE_MAP. + * + * When this flag is set, the whole vm_bind range is mapped over a dummy object in a cyclic + * fashion, and all GPU reads from addresses in the range return undefined values. This flag + * being set means drm_panthor_vm_bind_op::bo_offset and drm_panthor_vm_bind_op::bo_handle + * must both be set to 0. DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC must also be set. + */ + DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE = 1 << 3, + /** * @DRM_PANTHOR_VM_BIND_OP_TYPE_MASK: Mask used to determine the type of operation. */ -- cgit From ffa88de8ddb64067df49e4d9f253d09a9c247059 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Wed, 18 Mar 2026 09:54:04 +0000 Subject: drm/mipi-dsi: add flag for sending all DSC slices in one packet The MIPI DSI v1.3 spec defines two modes for transporting compressed pixel data: one slice per packet or multiple slice widths in a single packet (Section 8.8.24 Figure 40). Add a MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT flag that panel drivers can set to indicate that all DSC slices for a line should be packed into a single packet. When unset should default to 1 slice per packet. Reviewed-by: Dmitry Baryshkov Signed-off-by: Alexander Koskovich Tested-by: Junjie Cao Acked-by: Maxime Ripard # from v1 Link: https://patch.msgid.link/20260318-dsi-dsc-slice-per-pkt-v2-1-0a1b316f8250@pm.me Signed-off-by: Dmitry Baryshkov --- include/drm/drm_mipi_dsi.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index b429acde4f71..7ff43967251e 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -138,6 +138,8 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node); #define MIPI_DSI_MODE_LPM BIT(11) /* transmit data ending at the same time for all lanes within one hsync */ #define MIPI_DSI_HS_PKT_END_ALIGNED BIT(12) +/* pack all DSC slices for a line into a single packet */ +#define MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT BIT(13) enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB888, -- cgit From cd0d23939b8775482c30ca7ab207f185aecd6555 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 28 May 2026 10:10:48 +0300 Subject: drm/bridge: split hpd_mutex into two mutexes Currently almost all bridge drivers which implement hpd_enable / hpd_disable callbacks simply toggle the hardware registers generating the interrupt. However, as pointed out by Jonas Karlman and Sashiko bot, using those callbacks for enable_irq() / disable_irq() calls or scheduling and cancelling the work can cause a AB-BA deadlock (between hpd_mutex lock and the corresponding lock). Split the hpd_mutex into two locks: one simply making sure that hpd_cb / hpd_data are consistent and another one, hpd_state_mutex, making sure that concurrent drm_bridge_hpd_enable() / drm_bridge_hpd_disable() calls can't end up with inconsistency between hpd_cb/_data and bridge's internal state. Link: https://lore.kernel.org/dri-devel/9aa4bd35-bff6-4009-a959-ce31010c7b35@kwiboo.se Link: https://sashiko.dev/#/patchset/20260513-dp-connector-hpd-v2-0-42f757bfcbf9%40oss.qualcomm.com Reviewed-by: Sebastian Reichel Link: https://patch.msgid.link/20260528-dp-connector-hpd-v3-1-d656eb1079b7@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/drm_bridge.c | 16 +++++++++++++--- include/drm/drm_bridge.h | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 687b36eea0c7..9a185032a3bd 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -417,6 +417,7 @@ void drm_bridge_add(struct drm_bridge *bridge) if (!list_empty(&bridge->list)) list_del_init(&bridge->list); + mutex_init(&bridge->hpd_state_mutex); mutex_init(&bridge->hpd_mutex); if (bridge->ops & DRM_BRIDGE_OP_HDMI) @@ -469,6 +470,7 @@ void drm_bridge_remove(struct drm_bridge *bridge) mutex_unlock(&bridge_lock); mutex_destroy(&bridge->hpd_mutex); + mutex_destroy(&bridge->hpd_state_mutex); drm_bridge_put(bridge); } @@ -1451,19 +1453,25 @@ void drm_bridge_hpd_enable(struct drm_bridge *bridge, if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) return; + mutex_lock(&bridge->hpd_state_mutex); + mutex_lock(&bridge->hpd_mutex); - if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) + if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) { + mutex_unlock(&bridge->hpd_mutex); goto unlock; + } bridge->hpd_cb = cb; bridge->hpd_data = data; + mutex_unlock(&bridge->hpd_mutex); + if (bridge->funcs->hpd_enable) bridge->funcs->hpd_enable(bridge); unlock: - mutex_unlock(&bridge->hpd_mutex); + mutex_unlock(&bridge->hpd_state_mutex); } EXPORT_SYMBOL_GPL(drm_bridge_hpd_enable); @@ -1484,13 +1492,15 @@ void drm_bridge_hpd_disable(struct drm_bridge *bridge) if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) return; - mutex_lock(&bridge->hpd_mutex); + mutex_lock(&bridge->hpd_state_mutex); if (bridge->funcs->hpd_disable) bridge->funcs->hpd_disable(bridge); + mutex_lock(&bridge->hpd_mutex); bridge->hpd_cb = NULL; bridge->hpd_data = NULL; mutex_unlock(&bridge->hpd_mutex); + mutex_unlock(&bridge->hpd_state_mutex); } EXPORT_SYMBOL_GPL(drm_bridge_hpd_disable); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 4ba3a5deef9a..00a95f927e34 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1256,6 +1256,10 @@ struct drm_bridge { * @hpd_mutex: Protects the @hpd_cb and @hpd_data fields. */ struct mutex hpd_mutex; + /** + * @hpd_state_mutex: Protects the HPD en/disablement state for the bridge. + */ + struct mutex hpd_state_mutex; /** * @hpd_cb: Hot plug detection callback, registered with * drm_bridge_hpd_enable(). -- cgit From ca24e8d9fa48c7c121614c1a80971aecda640674 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Thu, 21 May 2026 15:03:59 -0300 Subject: drm/xe/nvls: Update PCI IDs Bspec has been updated with respect to NVL-S PCI IDs. Update INTEL_NVLS_IDS() accordingly. Bspec: 74201 Reviewed-by: Dnyaneshwar Bhadane Link: https://patch.msgid.link/20260521-nvl-s-update-pci-ids-v1-1-ec59e5d6bf12@intel.com Signed-off-by: Gustavo Sousa --- include/drm/intel/pciids.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/intel/pciids.h b/include/drm/intel/pciids.h index e32ef763427c..dff389b56eb3 100644 --- a/include/drm/intel/pciids.h +++ b/include/drm/intel/pciids.h @@ -893,8 +893,9 @@ MACRO__(0xD741, ## __VA_ARGS__), \ MACRO__(0xD742, ## __VA_ARGS__), \ MACRO__(0xD743, ## __VA_ARGS__), \ - MACRO__(0xD744, ## __VA_ARGS__), \ - MACRO__(0xD745, ## __VA_ARGS__) + MACRO__(0xD745, ## __VA_ARGS__), \ + MACRO__(0xD74A, ## __VA_ARGS__), \ + MACRO__(0xD74B, ## __VA_ARGS__) /* CRI */ #define INTEL_CRI_IDS(MACRO__, ...) \ -- cgit From 1b61bea2373d157a8b9db0e7304ec72fd80f0759 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 9 Jun 2026 14:43:50 +0200 Subject: drm: Add new general DRM property "color format" Add a new general DRM property named "color format" which can be used by userspace to request the display driver to output a particular color format. Possible string values for the new enum property are: - "AUTO" (setup by default, driver internally picks the color format) - "RGB" - "YUV 4:4:4" - "YUV 4:2:2" - "YUV 4:2:0" Drivers should advertise from this list the formats they support in an optimistic best-case scenario. EDID data from the sink can then be used in the kernel's atomic check phase to restrict this set of formats, as well as by userspace to make a correct choice in the first place. Co-developed-by: Werner Sembach Signed-off-by: Werner Sembach Co-developed-by: Andri Yngvason Signed-off-by: Andri Yngvason Signed-off-by: Marius Vlad Reviewed-by: Maxime Ripard Reviewed-by: Daniel Stone Signed-off-by: Nicolas Frattaroli Link: https://patch.msgid.link/20260609-color-format-v17-3-35739b5782cc@collabora.com Signed-off-by: Daniel Stone --- Documentation/gpu/drm-kms.rst | 6 ++ drivers/gpu/drm/drm_atomic_helper.c | 5 ++ drivers/gpu/drm/drm_atomic_uapi.c | 4 + drivers/gpu/drm/drm_connector.c | 155 ++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 84 +++++++++++++++++++ 5 files changed, 254 insertions(+) (limited to 'include') diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index e4f7153543b3..0dd440a14946 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -610,6 +610,12 @@ Color Management Properties .. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c :doc: overview +Color Format Property +--------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_connector.c + :doc: Color format + Tile Group Property ------------------- diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 51f39edc31ed..3547f797a850 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -737,6 +737,11 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, if (old_connector_state->max_requested_bpc != new_connector_state->max_requested_bpc) new_crtc_state->connectors_changed = true; + + if (old_connector_state->color_format != + new_connector_state->color_format) + new_crtc_state->connectors_changed = true; + } if (funcs->atomic_check) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 6441b55cc274..c7f80d90794c 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -935,6 +935,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, state->privacy_screen_sw_state = val; } else if (property == connector->broadcast_rgb_property) { state->hdmi.broadcast_rgb = val; + } else if (property == connector->color_format_property) { + state->color_format = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -1020,6 +1022,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->privacy_screen_sw_state; } else if (property == connector->broadcast_rgb_property) { *val = state->hdmi.broadcast_rgb; + } else if (property == connector->color_format_property) { + *val = state->color_format; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index a5d13b92b665..296195087a4c 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1397,6 +1397,18 @@ static const u32 hdmi_colorspaces = BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65) | BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER); +static const u32 hdmi_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + +static const u32 dp_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + /* * As per DP 1.4a spec, 2.2.5.7.5 VSC SDP Payload for Pixel Encoding/Colorimetry * Format Table 2-120 @@ -2944,6 +2956,149 @@ int drm_connector_attach_colorspace_property(struct drm_connector *connector) } EXPORT_SYMBOL(drm_connector_attach_colorspace_property); +/** + * DOC: Color format + * + * The connector "color format" property allows userspace to request a specific + * color model on the output of the connector. Not all values listed by the + * property are guaranteed to work for every sink; rather, it is an optimistic + * listing of color formats that the source could output depending on + * circumstances. + * + * Whether it actually can output a certain color format is determined during + * the atomic check phase. Consequently, a userspace application that sets the + * color format to a value other than "AUTO" should check whether its atomic + * commit succeeded. + * + * Possible values for "color format": + * + * "AUTO": + * The driver or display protocol helpers should pick a suitable color + * format. All implementations of a specific display protocol will behave + * the same way with "AUTO", but different display protocols do not + * necessarily have the same "AUTO" semantics. + * + * For HDMI connectors, "AUTO" picks RGB, but falls back to YUV 4:2:0 if + * the bandwidth required for full-scale RGB is not available, or the mode + * is YUV 4:2:0-only, as long as the mode, source, and sink all support + * YUV 4:2:0. + * "RGB": + * RGB output format. The quantization range (limited/full) depends on the + * value of the "Broadcast RGB" property if it is present on the connector. + * "YUV 4:4:4": + * YUV 4:4:4 (a.k.a. YCbCr 4:4:4) output format. Chroma is not subsampled. + * The quantization range defaults to limited. + * "YUV 4:2:2": + * YUV 4:2:2 (a.k.a. YCbCr 4:2:2) output format. Chroma has half the + * horizontal resolution of Luma. The quantization range defaults to + * limited. + * "YUV 4:2:0": + * YUV 4:2:0 (a.k.a. YCbCr 4:2:0) output format. Chroma has half the + * horizontal and vertical resolution of Luma. The quantization range + * defaults to limited. + * + * A sink may only support some color formats in specific modes and at specific + * bit depths. The atomic modesetting API should be used to set a working + * configuration in one go, as an unsupported combination of parameters is + * rejected. + */ + +/** + * drm_connector_attach_color_format_property - create and attach color format property + * @connector: connector to create the color format property on + * @supported_color_formats: bitmask of bit-shifted &enum drm_output_color_format + * values the connector supports + * + * Called by a driver to create a color format property. The property is + * attached to the connector automatically on success. + * + * @supported_color_formats should only include color formats the connector + * type can actually support. + * + * Returns: + * 0 on success, negative errno on error + */ +int drm_connector_attach_color_format_property(struct drm_connector *connector, + unsigned long supported_color_formats) +{ + struct drm_device *dev = connector->dev; + struct drm_prop_enum_list enum_list[DRM_CONNECTOR_COLOR_FORMAT_COUNT]; + unsigned int i = 0; + unsigned long fmt; + + if (connector->color_format_property) + return 0; + + if (!supported_color_formats) { + drm_err(dev, "No supported color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + if (supported_color_formats & ~GENMASK(DRM_OUTPUT_COLOR_FORMAT_COUNT - 1, 0)) { + drm_err(dev, "Unknown color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_HDMIB: + if (supported_color_formats & ~hdmi_colorformats) { + drm_err(dev, "Color formats not allowed for HDMI on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + if (supported_color_formats & ~dp_colorformats) { + drm_err(dev, "Color formats not allowed for DP on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + } + + enum_list[0].name = "AUTO"; + enum_list[0].type = DRM_CONNECTOR_COLOR_FORMAT_AUTO; + + for_each_set_bit(fmt, &supported_color_formats, DRM_OUTPUT_COLOR_FORMAT_COUNT) { + switch (fmt) { + case DRM_OUTPUT_COLOR_FORMAT_RGB444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_RGB444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR422: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR420: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + break; + default: + drm_warn(dev, "Unknown supported format %ld on [CONNECTOR:%d:%s]\n", + fmt, connector->base.id, connector->name); + continue; + } + enum_list[i].name = drm_hdmi_connector_get_output_format_name(fmt); + } + + connector->color_format_property = + drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "color format", + enum_list, i + 1); + + if (!connector->color_format_property) + return -ENOMEM; + + drm_object_attach_property(&connector->base, connector->color_format_property, + DRM_CONNECTOR_COLOR_FORMAT_AUTO); + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_color_format_property); + /** * drm_connector_atomic_hdr_metadata_equal - checks if the hdr metadata changed * @old_state: old connector state to compare diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 529755c2e862..e1f6072ce7c9 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -571,12 +571,80 @@ enum drm_colorspace { * YCbCr 4:2:2 output format (ie. with horizontal subsampling) * @DRM_OUTPUT_COLOR_FORMAT_YCBCR420: * YCbCr 4:2:0 output format (ie. with horizontal and vertical subsampling) + * @DRM_OUTPUT_COLOR_FORMAT_COUNT: + * Number of valid output color format values in this enum */ enum drm_output_color_format { DRM_OUTPUT_COLOR_FORMAT_RGB444 = 0, DRM_OUTPUT_COLOR_FORMAT_YCBCR444, DRM_OUTPUT_COLOR_FORMAT_YCBCR422, DRM_OUTPUT_COLOR_FORMAT_YCBCR420, + DRM_OUTPUT_COLOR_FORMAT_COUNT, +}; + +/** + * enum drm_connector_color_format - Connector Color Format Request + * + * This enum, unlike &enum drm_output_color_format, is used to specify requests + * for a specific color format on a connector through the DRM "color format" + * property. The difference is that it has an "AUTO" value to specify that + * no specific choice has been made. + */ +enum drm_connector_color_format { + /** + * @DRM_CONNECTOR_COLOR_FORMAT_AUTO: The driver or display protocol + * helpers should pick a suitable color format. All implementations of a + * specific display protocol must behave the same way with "AUTO", but + * different display protocols do not necessarily have the same "AUTO" + * semantics. + * + * For HDMI, "AUTO" picks RGB, but falls back to YCbCr 4:2:0 if the + * bandwidth required for full-scale RGB is not available, or the mode + * is YCbCr 4:2:0-only, as long as the mode and output both support + * YCbCr 4:2:0. + * + * For display protocols other than HDMI, the recursive bridge chain + * format selection picks the first chain of bridge formats that works, + * as has already been the case before the introduction of the "color + * format" property. Non-HDMI bridges should therefore either sort their + * bus output formats by preference, or agree on a unified auto format + * selection logic that's implemented in a common state helper (like + * how HDMI does it). + */ + DRM_CONNECTOR_COLOR_FORMAT_AUTO = 0, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_RGB444: RGB output format. The + * quantization range depends on the value of the "Broadcast RGB" + * property if it is present on the connector. + */ + DRM_CONNECTOR_COLOR_FORMAT_RGB444, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: YCbCr 4:4:4 output format (ie. + * not subsampled). Quantization range is "Limited" by default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR444, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: YCbCr 4:2:2 output format (ie. + * with horizontal subsampling). Quantization range is "Limited" by + * default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR422, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: YCbCr 4:2:0 output format (ie. + * with horizontal and vertical subsampling). Quantization range is + * "Limited" by default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR420, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_COUNT: Number of valid connector color + * format values in this enum + */ + DRM_CONNECTOR_COLOR_FORMAT_COUNT, }; const char * @@ -1167,6 +1235,13 @@ struct drm_connector_state { */ enum drm_colorspace colorspace; + /** + * @color_format: State variable for Connector property to request + * color format change on Sink. This is most commonly used to switch + * between RGB to YUV and vice-versa. + */ + enum drm_connector_color_format color_format; + /** * @writeback_job: Writeback job for writeback connectors * @@ -2181,6 +2256,12 @@ struct drm_connector { */ struct drm_property *colorspace_property; + /** + * @color_format_property: Connector property to set the suitable + * color format supported by the sink. + */ + struct drm_property *color_format_property; + /** * @path_blob_ptr: * @@ -2664,6 +2745,9 @@ bool drm_connector_has_possible_encoder(struct drm_connector *connector, struct drm_encoder *encoder); const char *drm_get_colorspace_name(enum drm_colorspace colorspace); +int drm_connector_attach_color_format_property(struct drm_connector *connector, + unsigned long supported_color_formats); + /** * drm_for_each_connector_iter - connector_list iterator macro * @connector: &struct drm_connector pointer used as cursor -- cgit From c761e890657c6c257df0dc81e1f9da5e45e3837e Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 9 Jun 2026 14:43:51 +0200 Subject: drm/connector: Let connectors have a say in their color format Add a function to get the connector color format from a connector state, and a new function pointer in drm_connector_funcs to allow connectors to override what connector color format it returns. This is useful for the bridge chain recursive bus format selection code, which does not wish to implement connector implementation specific checks like whether it involves HDMI. Reviewed-by: Dmitry Baryshkov Reviewed-by: Daniel Stone Signed-off-by: Nicolas Frattaroli Link: https://patch.msgid.link/20260609-color-format-v17-4-35739b5782cc@collabora.com Signed-off-by: Daniel Stone --- drivers/gpu/drm/drm_connector.c | 16 ++++++++++++++++ include/drm/drm_connector.h | 12 ++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 296195087a4c..95c65ed18709 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -3694,6 +3694,22 @@ void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode, } EXPORT_SYMBOL(drm_connector_oob_hotplug_event); +/** + * drm_connector_get_color_format - Return connector color format of @conn_state + * @conn_state: pointer to the &struct drm_connector_state to go check + * + */ +enum drm_connector_color_format +drm_connector_get_color_format(const struct drm_connector_state *conn_state) +{ + struct drm_connector *connector = conn_state->connector; + + if (connector->funcs->color_format) + return connector->funcs->color_format(conn_state); + + return conn_state->color_format; +} +EXPORT_SYMBOL(drm_connector_get_color_format); /** * DOC: Tile group diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index e1f6072ce7c9..4317166562cf 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1803,6 +1803,16 @@ struct drm_connector_funcs { * Allows connectors to create connector-specific debugfs files. */ void (*debugfs_init)(struct drm_connector *connector, struct dentry *root); + + /** + * @color_format: + * + * Allows connectors to return a connector color format other than + * @conn_state.color_format for purposes of e.g. display protocol + * specific helper logic having already mapped it to an output format. + */ + enum drm_connector_color_format (*color_format)( + const struct drm_connector_state *conn_state); }; /** @@ -2619,6 +2629,8 @@ drm_connector_is_unregistered(struct drm_connector *connector) void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode, enum drm_connector_status status); +enum drm_connector_color_format +drm_connector_get_color_format(const struct drm_connector_state *conn_state); const char *drm_get_connector_type_name(unsigned int connector_type); const char *drm_get_connector_status_name(enum drm_connector_status status); const char *drm_get_subpixel_order_name(enum subpixel_order order); -- cgit From 3f0f770ccc8d724fa3b1f5b4bde5addfc4b843b8 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 9 Jun 2026 14:43:54 +0200 Subject: drm/atomic-helper: Add HDMI bridge output bus formats helper The drm_bridge_funcs atomic_get_output_bus_fmts operation should be the same for likely every HDMI connector bridge, unless such an HDMI connector bridge has some special hardware restrictions that I cannot envision yet. To avoid code duplication and standardize on a set of media bus formats that the HDMI output color formats translate to, add a common helper function that implements this operation to the drm bridge helpers. The function returns a list of output bus formats based on the HDMI bridge's current output bits-per-component, and its bitmask of supported color formats. To guard against future expansion of DRM_OUTPUT_COLOR_FORMAT outgrowing the hweight8 call, add a BUILD_BUG_ON statement where it's used that checks for DRM_OUTPUT_COLOR_FORMAT_COUNT. The justification for not using hweight32 in all cases is that not all ISAs have a popcount instruction, and will benefit from a smaller/faster software implementation that doesn't have to operate across all bits. The justification for not defining an hweight_color depending on the value of DRM_OUTPUT_COLOR_FORMAT_COUNT is that this count enum value is only known at compile time, not at preprocessor time. Reviewed-by: Dmitry Baryshkov Reviewed-by: Daniel Stone Signed-off-by: Nicolas Frattaroli Link: https://patch.msgid.link/20260609-color-format-v17-7-35739b5782cc@collabora.com Signed-off-by: Daniel Stone --- drivers/gpu/drm/drm_atomic_helper.c | 81 +++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic_helper.h | 7 ++++ 2 files changed, 88 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 3547f797a850..285aac3554df 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -4107,3 +4108,83 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, return input_fmts; } EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt); + +/** + * drm_atomic_helper_bridge_get_hdmi_output_bus_fmts - helper implementing + * atomic_get_output_bus_fmts for HDMI + * @bridge: pointer to &struct drm_bridge + * @bridge_state: pointer to the current bridge state + * @crtc_state: pointer to the current CRTC state + * @conn_state: pointer to the current connector state + * @num_output_fmts: pointer to where the number of entries in the returned array + * will be stored. Set to 0 if unsuccessful. + * + * Common implementation for the &drm_bridge_funcs.atomic_get_output_bus_fmts + * operation that's applicable to HDMI connectors. + * + * Returns: a newly allocated array of u32 values of length \*@num_output_fmts, + * representing all the MEDIA_BUS_FMTS\_ for the current connector state's + * chosen HDMI output bits per compoennt, or %NULL if it fails to allocate one. + */ +u32 * +drm_atomic_helper_bridge_get_hdmi_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + unsigned int num_fmts = 0; + u32 *out_fmts; + + /* + * bridge->supported_formats is a bit field of BIT(enum drm_output_color_format) + * values. The smallest hweight that is smaller than or equal to + * %DRM_OUTPUT_COLOR_FORMAT_COUNT will do for counting set bits here. + */ + BUILD_BUG_ON(const_true(DRM_OUTPUT_COLOR_FORMAT_COUNT > 8)); + out_fmts = kmalloc_array(hweight8(bridge->supported_formats), + sizeof(u32), GFP_KERNEL); + if (!out_fmts) { + *num_output_fmts = 0; + return NULL; + } + + switch (conn_state->hdmi.output_bpc) { + case 12: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB121212_1X36; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV12_1X36; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY12_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36; + break; + case 10: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB101010_1X30; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV10_1X30; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY10_1X20; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30; + break; + default: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB888_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV8_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY8_1X16; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; + break; + } + + *num_output_fmts = num_fmts; + + return out_fmts; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_get_hdmi_output_bus_fmts); + diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index b84152810abb..4cfeec70d648 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -295,4 +295,11 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, u32 output_fmt, unsigned int *num_input_fmts); +u32 * +drm_atomic_helper_bridge_get_hdmi_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts); + #endif /* DRM_ATOMIC_HELPER_H_ */ -- cgit From 2afdfc658f7a7e9ee2a67ec6663922da9c799c53 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Wed, 10 Jun 2026 12:52:48 -0700 Subject: sysfb: correct CONFIG_SYSFB_SIMPLEFB macro name in #endif comment A comment in incorrectly refers to CONFIG_SYSFB_SIMPLE instead of CONFIG_SYSFB_SIMPLEFB. Correct it. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Signed-off-by: Thomas Zimmermann Reviewed-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patch.msgid.link/20260610195248.19442-1-enelsonmoore@gmail.com --- include/linux/sysfb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h index 5226efde9ad4..ed23d6516223 100644 --- a/include/linux/sysfb.h +++ b/include/linux/sysfb.h @@ -118,7 +118,7 @@ struct platform_device *sysfb_create_simplefb(const struct screen_info *si, const struct simplefb_platform_data *mode, struct device *parent); -#else /* CONFIG_SYSFB_SIMPLE */ +#else /* CONFIG_SYSFB_SIMPLEFB */ static inline bool sysfb_parse_mode(const struct screen_info *si, struct simplefb_platform_data *mode) @@ -133,6 +133,6 @@ static inline struct platform_device *sysfb_create_simplefb(const struct screen_ return ERR_PTR(-EINVAL); } -#endif /* CONFIG_SYSFB_SIMPLE */ +#endif /* CONFIG_SYSFB_SIMPLEFB */ #endif /* _LINUX_SYSFB_H */ -- cgit From 5c1e93131268353ba02c41518386f942aee5e6f9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 18:28:37 +0300 Subject: drm/intel: drop driver include from mchbar_regs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headers under include/ aren't supposed to try to include headers from driver directories, such as i915_reg_defs.h. Remove it. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260615152837.1898991-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- include/drm/intel/mchbar_regs.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/drm/intel/mchbar_regs.h b/include/drm/intel/mchbar_regs.h index ca0d421be16c..66498ca5e40b 100644 --- a/include/drm/intel/mchbar_regs.h +++ b/include/drm/intel/mchbar_regs.h @@ -6,8 +6,6 @@ #ifndef __INTEL_MCHBAR_REGS__ #define __INTEL_MCHBAR_REGS__ -#include "i915_reg_defs.h" - /* * MCHBAR mirror. * -- cgit From 445075e199526096bc6f47dace4391efec88cf7e Mon Sep 17 00:00:00 2001 From: Yifan Zhang Date: Wed, 6 May 2026 21:45:05 +0800 Subject: drm/amdgpu: add ioctl to handle RAS poison error Add a new DRM_IOCTL_AMDGPU_PROC_OPTIONS ioctl with the AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY option, allowing userspace (ROCr) to control per-process SIGBUS delivery. Userspace for this can be found at: https://github.com/ROCm/rocm-systems/pull/6190 Reviewed-by: Lijo Lazar Reviewed-by: Alex Deucher Signed-off-by: Yifan Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h | 6 +++ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 27 ++++++++++++ drivers/gpu/drm/amd/amdkfd/kfd_events.c | 69 +++++++++++++++++++++++++++++- drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 15 +++++++ drivers/gpu/drm/amd/amdkfd/kfd_process.c | 33 ++++++++++++++ include/uapi/drm/amdgpu_drm.h | 21 +++++++++ 8 files changed, 173 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 7b09410d6d8f..5f775c6e9240 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1468,6 +1468,8 @@ int amdgpu_enable_vblank_kms(struct drm_crtc *crtc); void amdgpu_disable_vblank_kms(struct drm_crtc *crtc); int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); /* * functions used by amdgpu_encoder.c diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index d403af5fb552..32132be6e683 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -210,6 +210,7 @@ int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni, int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo, uint32_t domain, struct dma_fence *fence); +int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms); #else static inline bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm) @@ -241,6 +242,11 @@ int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo, { return 0; } +static inline +int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms) +{ + return -EOPNOTSUPP; +} #endif /* Shared API */ int amdgpu_amdkfd_alloc_kernel_mem(struct amdgpu_device *adev, size_t size, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index bf4260269681..503bb64c1e55 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -3076,6 +3076,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_SIGNAL, amdgpu_userq_signal_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES, amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_PROC_OPTIONS, amdgpu_proc_options_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct drm_driver amdgpu_kms_driver = { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 71272f40feef..72b6f55699a4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1423,6 +1423,33 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return 0; } +/** + * amdgpu_proc_options_ioctl - set per-fd user options + * + * @dev: drm dev pointer + * @data: pointer to struct drm_amdgpu_proc_options + * @filp: drm file + * + * Sets options stored on the per-file amdgpu_fpriv. Currently the only + * supported option is %AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY which + * controls how KFD delivers SIGBUS for poison/RAS events to the calling + * process (immediate, suppressed, or delayed by N milliseconds). + */ +int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_amdgpu_proc_options *args = data; + + switch (args->op) { + case AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY: + return amdgpu_amdkfd_set_sigbus_delay(current, + args->kfd_sigbus_delay.value); + default: + DRM_DEBUG_KMS("Invalid user option op %u\n", args->op); + return -EINVAL; + } +} + /** * amdgpu_driver_open_kms - drm callback for open * diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 81900b49d9d5..71e8f9a23215 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -29,10 +29,12 @@ #include #include #include +#include #include "kfd_priv.h" #include "kfd_events.h" #include "kfd_device_queue_manager.h" #include +#include /* * Wrapper around wait_queue_entry_t @@ -1338,6 +1340,71 @@ void kfd_signal_reset_event(struct kfd_node *dev) srcu_read_unlock(&kfd_processes_srcu, idx); } +/* + * Per-process opt-in for poison-consumption SIGBUS handling. + * + * Default: kernel sends SIGBUS to the process immediately when poison is + * consumed, in addition to delivering the KFD HW/MEMORY exception events. + * + * Userspace (ROCr) can opt-in per-process via the + * DRM_IOCTL_AMDGPU_PROC_OPTIONS / AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY + * option. This lets the app's registered system-event callback handle the + * RAS error first, instead of being killed by SIGBUS. + * + * Encoded value (stored on the kfd_process): + * 0 - default: SIGBUS immediately (no opt-in) + * 0xFFFFFFFF - opt-in, never escalate to SIGBUS + * N (other) - opt-in, escalate to SIGBUS after N ms if app does not + * handle the error in time (safety timeout) + */ + +void kfd_signal_sigbus_delayed_fn(struct work_struct *work) +{ + struct kfd_process *p = container_of(to_delayed_work(work), + struct kfd_process, signal_work); + + if (p->lead_thread) + send_sig(SIGBUS, p->lead_thread, 0); + + kfd_unref_process(p); +} + +static void kfd_signal_sigbus_with_delay(struct kfd_node *dev, + struct kfd_process *p) +{ + u32 delay_ms = atomic_read(&p->kfd_sigbus_delay_ms); + + if (delay_ms == AMDGPU_PROC_OPTIONS_KFD_SIGBUS_DELAY_DISABLED) { + dev_info(dev->adev->dev, + "SIGBUS suppressed for process %s(pid:%d): app opted in to handle RAS error\n", + p->lead_thread->comm, p->lead_thread->pid); + return; + } + + if (delay_ms == 0) + goto send_now; + + /* + * Take an extra reference for the delayed worker. If the work is + * already pending (e.g. another device of this process consumed poison + * just before), drop the reference and skip rescheduling - the process + * only needs to be notified once. + */ + kref_get(&p->ref); + if (!schedule_delayed_work(&p->signal_work, msecs_to_jiffies(delay_ms))) { + kfd_unref_process(p); + return; + } + + dev_info(dev->adev->dev, + "Deferring SIGBUS to process %s(pid:%d) by %u ms (RAS error opt-in safety timeout)\n", + p->lead_thread->comm, p->lead_thread->pid, delay_ms); + return; + +send_now: + send_sig(SIGBUS, p->lead_thread, 0); +} + void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid) { struct kfd_process *p = kfd_lookup_process_by_pasid(pasid, NULL); @@ -1392,7 +1459,7 @@ void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid) rcu_read_unlock(); /* user application will handle SIGBUS signal */ - send_sig(SIGBUS, p->lead_thread, 0); + kfd_signal_sigbus_with_delay(dev, p); kfd_unref_process(p); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index acd0e41e744c..591f41eadae2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -957,6 +957,20 @@ struct kfd_process { size_t signal_event_count; bool signal_event_limit_reached; + /** + * @kfd_sigbus_delay_ms: Per-process KFD SIGBUS delivery option for + * poison/RAS events (set via DRM_IOCTL_AMDGPU_PROC_OPTIONS / + * AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY). + * + * 0 - send SIGBUS immediately (default) + * 0xFFFFFFFF - suppress SIGBUS delivery + * other - delay SIGBUS delivery by this many milliseconds + */ + atomic_t kfd_sigbus_delay_ms; + + /* Delayed signal delivery to user */ + struct delayed_work signal_work; + /* Information used for memory eviction */ void *kgd_process_info; /* Eviction fence that is attached to all the BOs of this process. The @@ -1554,6 +1568,7 @@ void kfd_signal_vm_fault_event(struct kfd_process_device *pdd, void kfd_signal_reset_event(struct kfd_node *dev); void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid); +void kfd_signal_sigbus_delayed_fn(struct work_struct *work); void kfd_signal_process_terminate_event(struct kfd_process *p); static inline void kfd_flush_tlb(struct kfd_process_device *pdd) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 368283d53077..9838954d77da 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -986,6 +986,33 @@ out: return process; } +/** + * amdgpu_amdkfd_set_sigbus_delay - Set per-process KFD SIGBUS delay + * @task: task in the target process + * @ms: encoded delay value (0 = immediate, 0xFFFFFFFF = suppress, + * otherwise delay in milliseconds) + * + * Stores the SIGBUS delivery option on the kfd_process associated with + * @task. If the calling process has not opened /dev/kfd yet (no + * kfd_process exists), this is a no-op - the option only applies to + * processes that actually use KFD. + */ +int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms) +{ + struct kfd_process *p; + + if (!task->mm) + return -EINVAL; + + p = kfd_lookup_process_by_mm(task->mm); + if (!p) + return 0; + + atomic_set(&p->kfd_sigbus_delay_ms, ms); + kfd_unref_process(p); + return 0; +} + static struct kfd_process *find_process_by_mm(const struct mm_struct *mm) { struct kfd_process *process; @@ -1322,6 +1349,11 @@ void kfd_process_notifier_release_internal(struct kfd_process *p) kfd_process_table_remove(p); cancel_delayed_work_sync(&p->eviction_work); cancel_delayed_work_sync(&p->restore_work); + /* + * If work pending, cancel it and drop the extra ref + */ + if (cancel_delayed_work_sync(&p->signal_work)) + kfd_unref_process(p); /* * Dequeue and destroy user queues, it is not safe for GPU to access @@ -1578,6 +1610,7 @@ struct kfd_process *create_process(const struct task_struct *thread, bool primar INIT_DELAYED_WORK(&process->eviction_work, evict_process_worker); INIT_DELAYED_WORK(&process->restore_work, restore_process_worker); + INIT_DELAYED_WORK(&process->signal_work, kfd_signal_sigbus_delayed_fn); process->last_restore_timestamp = get_jiffies_64(); err = kfd_event_init_process(process); if (err) diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 9f3090db2f16..b32c72a662b6 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -58,6 +58,7 @@ extern "C" { #define DRM_AMDGPU_USERQ_SIGNAL 0x17 #define DRM_AMDGPU_USERQ_WAIT 0x18 #define DRM_AMDGPU_GEM_LIST_HANDLES 0x19 +#define DRM_AMDGPU_PROC_OPTIONS 0x1A #define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) @@ -79,6 +80,7 @@ extern "C" { #define DRM_IOCTL_AMDGPU_USERQ_SIGNAL DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_USERQ_SIGNAL, struct drm_amdgpu_userq_signal) #define DRM_IOCTL_AMDGPU_USERQ_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_USERQ_WAIT, struct drm_amdgpu_userq_wait) #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) +#define DRM_IOCTL_AMDGPU_PROC_OPTIONS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) /** * DOC: memory domains @@ -1673,6 +1675,25 @@ struct drm_amdgpu_info_uq_metadata { #define AMDGPU_FAMILY_GC_11_5_4 154 /* GC 11.5.4 */ #define AMDGPU_FAMILY_GC_12_0_0 152 /* GC 12.0.0 */ +/* + * Definition of user options + * + * option: AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY + * 0: Disable sigbus delay - SIGBUS will be raised immediately + * 0xFFFFFFFF: SIGBUS will not be raised + * other: Set the sigbus delay in milliseconds + */ +#define AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY 0 + +#define AMDGPU_PROC_OPTIONS_KFD_SIGBUS_DELAY_DISABLED 0xFFFFFFFFu + +struct drm_amdgpu_proc_options { + __u32 op; + struct { + __u32 value; + } kfd_sigbus_delay; +}; + #if defined(__cplusplus) } #endif -- cgit From e239d3e3cbb7b27522727371fa66523fc769f454 Mon Sep 17 00:00:00 2001 From: Chenyu Chen Date: Tue, 26 May 2026 09:52:28 +0800 Subject: drm/edid: parse panel type from DisplayID 2.x Display Parameters Parse the Display Parameters Data Block (tag 0x21) defined in DisplayID v2.1a Section 4.2.6. Extract the Display Device Technology field from the color depth and device technology byte, which indicates whether the panel uses LCD or OLED technology. Add a panel_type field to struct drm_display_info and populate it during DisplayID iteration so downstream drivers can use it for panel-type-dependent behavior. Add DRM_MODE_PANEL_TYPE_LCD to the UAPI panel type property alongside the existing OLED value. Assisted-by: Copilot:Claude-Opus-4.6 Signed-off-by: Chenyu Chen Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_connector.c | 3 ++- drivers/gpu/drm/drm_displayid_internal.h | 24 +++++++++++++++++ drivers/gpu/drm/drm_edid.c | 45 ++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 6 +++++ include/uapi/drm/drm_mode.h | 1 + 5 files changed, 78 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3fa4d2082cd7..9d820a2a87ce 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1176,6 +1176,7 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = { static const struct drm_prop_enum_list drm_panel_type_enum_list[] = { { DRM_MODE_PANEL_TYPE_UNKNOWN, "unknown" }, { DRM_MODE_PANEL_TYPE_OLED, "OLED" }, + { DRM_MODE_PANEL_TYPE_LCD, "LCD" }, }; /** @@ -1508,7 +1509,7 @@ EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name); * never read back the value of "DPMS" because it can be incorrect. * panel_type: * Immutable enum property to indicate the type of connected panel. - * Possible values are "unknown" (default) and "OLED". + * Possible values are "unknown" (default), "OLED", and "LCD". * PATH: * Connector path property to identify how this sink is physically * connected. Used by DP MST. This should be set by calling diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h index 5b1b32f73516..6f431aafafcf 100644 --- a/drivers/gpu/drm/drm_displayid_internal.h +++ b/drivers/gpu/drm/drm_displayid_internal.h @@ -142,6 +142,30 @@ struct displayid_formula_timing_block { struct displayid_formula_timings_9 timings[]; } __packed; +#define DISPLAYID_DEVICE_TECH_UNSPECIFIED 0 +#define DISPLAYID_DEVICE_TECH_LCD 1 +#define DISPLAYID_DEVICE_TECH_OLED 2 + +#define DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH GENMASK(6, 4) + +struct displayid_display_params_block { + struct displayid_block base; + __le16 horiz_image_size; + __le16 vert_image_size; + __le16 horiz_pixel_count; + __le16 vert_pixel_count; + u8 features; + u8 primary_color1[3]; + u8 primary_color2[3]; + u8 primary_color3[3]; + u8 white_point[3]; + __le16 max_luminance_full; + __le16 max_luminance_10; + __le16 min_luminance; + u8 color_depth_and_tech; /* [2:0] depth, [6:4] device tech, [7] theme */ + u8 gamma_eotf; +} __packed; + #define DISPLAYID_VESA_MSO_OVERLAP GENMASK(3, 0) #define DISPLAYID_VESA_MSO_MODE GENMASK(6, 5) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index aebbff8ac992..ae26618a9a57 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6713,6 +6713,8 @@ static void drm_reset_display_info(struct drm_connector *connector) info->source_physical_address = CEC_PHYS_ADDR_INVALID; memset(&info->amd_vsdb, 0, sizeof(info->amd_vsdb)); + + info->panel_type = DRM_MODE_PANEL_TYPE_UNKNOWN; } static void drm_displayid_process_base_section_header(struct drm_connector *connector, @@ -6731,6 +6733,45 @@ static void drm_displayid_process_base_section_header(struct drm_connector *conn info->non_desktop = true; } +static void +drm_displayid_parse_display_params(struct drm_connector *connector, + const struct displayid_block *block) +{ + struct drm_display_info *info = &connector->display_info; + const struct displayid_display_params_block *params = + (const struct displayid_display_params_block *)block; + u8 tech; + + if (block->num_bytes < sizeof(*params) - sizeof(params->base)) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID Display Parameters block too short (%u < %zu)\n", + connector->base.id, connector->name, + block->num_bytes, + sizeof(*params) - sizeof(params->base)); + return; + } + + tech = FIELD_GET(DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH, + params->color_depth_and_tech); + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID Display Parameters: device technology %s\n", + connector->base.id, connector->name, + tech == DISPLAYID_DEVICE_TECH_LCD ? "LCD" : + tech == DISPLAYID_DEVICE_TECH_OLED ? "OLED" : "unspecified"); + + switch (tech) { + case DISPLAYID_DEVICE_TECH_LCD: + info->panel_type = DRM_MODE_PANEL_TYPE_LCD; + break; + case DISPLAYID_DEVICE_TECH_OLED: + info->panel_type = DRM_MODE_PANEL_TYPE_OLED; + break; + default: + break; + } +} + static void update_displayid_info(struct drm_connector *connector, const struct drm_edid *drm_edid) { @@ -6744,6 +6785,10 @@ static void update_displayid_info(struct drm_connector *connector, drm_displayid_process_base_section_header(connector, &iter); base_section_header_processed = true; } + + if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 && + block->tag == DATA_BLOCK_2_DISPLAY_PARAMETERS) + drm_displayid_parse_display_params(connector, block); } displayid_iter_end(&iter); } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 5ad62c207d00..cd06a3b914a0 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -921,6 +921,12 @@ struct drm_display_info { * @amd_vsdb: AMD-specific VSDB information. */ struct drm_amd_vsdb_info amd_vsdb; + + /** + * @panel_type: Panel type from DisplayID Display Parameters + * Data Block (tag 0x21). Uses DRM_MODE_PANEL_TYPE_* constants. + */ + u8 panel_type; }; int drm_display_info_set_bus_formats(struct drm_display_info *info, diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 381a3e857d4e..bd435effdcee 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -155,6 +155,7 @@ extern "C" { /* Panel type property */ #define DRM_MODE_PANEL_TYPE_UNKNOWN 0 #define DRM_MODE_PANEL_TYPE_OLED 1 +#define DRM_MODE_PANEL_TYPE_LCD 2 /* * DRM_MODE_ROTATE_ -- cgit From cdeb5e248de11537cf23cd5174f6c55bab2e850b Mon Sep 17 00:00:00 2001 From: Riana Tauro Date: Thu, 18 Jun 2026 11:36:35 +0530 Subject: drm/xe/uapi: Add additional error components to xe drm_ras Add additional Error components supported by XE drm_ras (Reliability, Availability and Serviceability). Reviewed-by: Aravind Iddamsetty Reviewed-by: Mallesh Koujalagi Acked-by: Rodrigo Vivi Link: https://patch.msgid.link/20260618060633.2790109-9-riana.tauro@intel.com Signed-off-by: Riana Tauro --- include/uapi/drm/xe_drm.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 48e9f1fdb78d..50c80af4ad4e 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -2589,6 +2589,12 @@ enum drm_xe_ras_error_component { DRM_XE_RAS_ERR_COMP_CORE_COMPUTE = 1, /** @DRM_XE_RAS_ERR_COMP_SOC_INTERNAL: SoC Internal Error */ DRM_XE_RAS_ERR_COMP_SOC_INTERNAL, + /** @DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY: Device Memory Error */ + DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY, + /** @DRM_XE_RAS_ERR_COMP_PCIE: PCIe Subsystem Error */ + DRM_XE_RAS_ERR_COMP_PCIE, + /** @DRM_XE_RAS_ERR_COMP_FABRIC: Fabric Subsystem Error */ + DRM_XE_RAS_ERR_COMP_FABRIC, /** @DRM_XE_RAS_ERR_COMP_MAX: Max Error */ DRM_XE_RAS_ERR_COMP_MAX /* non-ABI */ }; @@ -2606,7 +2612,10 @@ enum drm_xe_ras_error_component { */ #define DRM_XE_RAS_ERROR_COMPONENT_NAMES { \ [DRM_XE_RAS_ERR_COMP_CORE_COMPUTE] = "core-compute", \ - [DRM_XE_RAS_ERR_COMP_SOC_INTERNAL] = "soc-internal" \ + [DRM_XE_RAS_ERR_COMP_SOC_INTERNAL] = "soc-internal", \ + [DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY] = "device-memory", \ + [DRM_XE_RAS_ERR_COMP_PCIE] = "pcie", \ + [DRM_XE_RAS_ERR_COMP_FABRIC] = "fabric", \ } #if defined(__cplusplus) -- cgit From c4a16f90797e2d8bebf875fd02547fef76ae3b76 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 15 Jun 2026 11:48:43 +0100 Subject: dt-bindings: clock: renesas,r9a09g077/87: Add LCDC_CLKD clock ID Add the LCDC clockd (LCDC_CLKD) definition for the Renesas RZ/T2H (R9A09G077) and RZ/N2H (R9A09G087) SoCs. LCDC_CLKD is used as the operating clock for LCDC. Signed-off-by: Lad Prabhakar Acked-by: Conor Dooley Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260615104845.4122868-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h | 1 + include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h b/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h index c4863e444458..f6cb8d649a46 100644 --- a/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h +++ b/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h @@ -34,5 +34,6 @@ #define R9A09G077_XSPI_CLK0 22 #define R9A09G077_XSPI_CLK1 23 #define R9A09G077_PCLKCAN 24 +#define R9A09G077_LCDC_CLKD 25 #endif /* __DT_BINDINGS_CLOCK_RENESAS_R9A09G077_CPG_H__ */ diff --git a/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h b/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h index 0d53f1e65077..312e563b322e 100644 --- a/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h +++ b/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h @@ -34,5 +34,6 @@ #define R9A09G087_XSPI_CLK0 22 #define R9A09G087_XSPI_CLK1 23 #define R9A09G087_PCLKCAN 24 +#define R9A09G087_LCDC_CLKD 25 #endif /* __DT_BINDINGS_CLOCK_RENESAS_R9A09G087_CPG_H__ */ -- cgit From 2b005b458f6eeffdbe7705e6667437013b54e209 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 15 Jun 2026 15:39:42 +0100 Subject: dt-bindings: clock: renesas,r9a09g077/87: Add PCLKRTC clock ID Add the peripheral clock ID definition for the Real-Time Clock (PCLKRTC) on the Renesas RZ/T2H (R9A09G077) and RZ/N2H (R9A09G087) SoCs. Note that the PCLKRTC clock is utilized as the operating clock source for the RTC IP. Signed-off-by: Lad Prabhakar Acked-by: Conor Dooley Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260615143943.1610095-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h | 1 + include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h b/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h index f6cb8d649a46..aa47685f329a 100644 --- a/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h +++ b/include/dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h @@ -35,5 +35,6 @@ #define R9A09G077_XSPI_CLK1 23 #define R9A09G077_PCLKCAN 24 #define R9A09G077_LCDC_CLKD 25 +#define R9A09G077_PCLKRTC 26 #endif /* __DT_BINDINGS_CLOCK_RENESAS_R9A09G077_CPG_H__ */ diff --git a/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h b/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h index 312e563b322e..1c73d0dcef18 100644 --- a/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h +++ b/include/dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h @@ -35,5 +35,6 @@ #define R9A09G087_XSPI_CLK1 23 #define R9A09G087_PCLKCAN 24 #define R9A09G087_LCDC_CLKD 25 +#define R9A09G087_PCLKRTC 26 #endif /* __DT_BINDINGS_CLOCK_RENESAS_R9A09G087_CPG_H__ */ -- cgit From e723909e9d6426a7877cd0ea4cbfaeb408cab3e8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 19 Jun 2026 14:24:08 +0200 Subject: drm/atomic-state-helper: Rename __drm_atomic_helper_bridge_reset() __drm_atomic_helper_bridge_reset() is used to initialize a newly allocated drm_bridge_state, and is being typically called by the drm_bridge_funcs.atomic_reset implementation. Since we want to consolidate DRM objects state allocation around the atomic_create_state callback that will only allocate and initialize a new drm_bridge_state instance, we will need to call __drm_atomic_helper_bridge_reset() from both the atomic_reset and atomic_create_state hooks. To avoid any confusion, we can thus rename __drm_atomic_helper_bridge_reset() to __drm_atomic_helper_bridge_state_init(). Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli # imx8mp + sn65dsi84 + bridge hotplug Link: https://patch.msgid.link/20260619-drm-no-more-bridge-reset-v3-3-ff399263111b@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c | 2 +- drivers/gpu/drm/drm_atomic_state_helper.c | 8 ++++---- include/drm/drm_atomic_state_helper.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 46779b49545b..2e74dc33e085 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -1929,7 +1929,7 @@ cdns_mhdp_bridge_atomic_reset(struct drm_bridge *bridge) if (!cdns_mhdp_state) return ERR_PTR(-ENOMEM); - __drm_atomic_helper_bridge_reset(bridge, &cdns_mhdp_state->base); + __drm_atomic_helper_bridge_state_init(bridge, &cdns_mhdp_state->base); return &cdns_mhdp_state->base; } diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 07686e94aae0..73e76426da1f 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -886,7 +886,7 @@ void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); /** - * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its + * __drm_atomic_helper_bridge_state_init() - Initialize a bridge state to its * default * @bridge: the bridge this state refers to * @state: bridge state to initialize @@ -895,14 +895,14 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); * by the bridge &drm_bridge_funcs.atomic_reset hook for bridges that subclass * the bridge state. */ -void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, +void __drm_atomic_helper_bridge_state_init(struct drm_bridge *bridge, struct drm_bridge_state *state) { memset(state, 0, sizeof(*state)); __drm_atomic_helper_private_obj_create_state(&bridge->base, &state->base); state->bridge = bridge; } -EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset); +EXPORT_SYMBOL(__drm_atomic_helper_bridge_state_init); /** * drm_atomic_helper_bridge_reset() - Allocate and initialize a bridge state @@ -922,7 +922,7 @@ drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) if (!bridge_state) return ERR_PTR(-ENOMEM); - __drm_atomic_helper_bridge_reset(bridge, bridge_state); + __drm_atomic_helper_bridge_state_init(bridge, bridge_state); return bridge_state; } EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index f4b6d8833bc2..6a715d8e1f4a 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -102,7 +102,7 @@ struct drm_bridge_state * drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge); void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, struct drm_bridge_state *state); -void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, +void __drm_atomic_helper_bridge_state_init(struct drm_bridge *bridge, struct drm_bridge_state *state); struct drm_bridge_state * drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); -- cgit From c8a25a2d855a62c37880d6cd02ce799d889ed973 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 19 Jun 2026 14:24:09 +0200 Subject: drm/atomic-state-helper: Reorder __drm_atomic_helper_bridge_state_init() arguments The convention for state init helpers is to pass the state pointer as the first argument and the object pointer second. __drm_atomic_helper_bridge_state_init() has them in the opposite order. Swap the arguments to follow the convention, and update the cdns-mhdp8546 caller. Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli # imx8mp + sn65dsi84 + bridge hotplug Link: https://patch.msgid.link/20260619-drm-no-more-bridge-reset-v3-4-ff399263111b@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c | 2 +- drivers/gpu/drm/drm_atomic_state_helper.c | 8 ++++---- include/drm/drm_atomic_state_helper.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 2e74dc33e085..b9574289c247 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -1929,7 +1929,7 @@ cdns_mhdp_bridge_atomic_reset(struct drm_bridge *bridge) if (!cdns_mhdp_state) return ERR_PTR(-ENOMEM); - __drm_atomic_helper_bridge_state_init(bridge, &cdns_mhdp_state->base); + __drm_atomic_helper_bridge_state_init(&cdns_mhdp_state->base, bridge); return &cdns_mhdp_state->base; } diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 73e76426da1f..8f04eae7a754 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -888,15 +888,15 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); /** * __drm_atomic_helper_bridge_state_init() - Initialize a bridge state to its * default - * @bridge: the bridge this state refers to * @state: bridge state to initialize + * @bridge: the bridge this state refers to * * Initializes the bridge state to default values. This is meant to be called * by the bridge &drm_bridge_funcs.atomic_reset hook for bridges that subclass * the bridge state. */ -void __drm_atomic_helper_bridge_state_init(struct drm_bridge *bridge, - struct drm_bridge_state *state) +void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, + struct drm_bridge *bridge) { memset(state, 0, sizeof(*state)); __drm_atomic_helper_private_obj_create_state(&bridge->base, &state->base); @@ -922,7 +922,7 @@ drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) if (!bridge_state) return ERR_PTR(-ENOMEM); - __drm_atomic_helper_bridge_state_init(bridge, bridge_state); + __drm_atomic_helper_bridge_state_init(bridge_state, bridge); return bridge_state; } EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 6a715d8e1f4a..cbc760598b9e 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -102,7 +102,7 @@ struct drm_bridge_state * drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge); void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, struct drm_bridge_state *state); -void __drm_atomic_helper_bridge_state_init(struct drm_bridge *bridge, - struct drm_bridge_state *state); +void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, + struct drm_bridge *bridge); struct drm_bridge_state * drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); -- cgit From 376542696ca1169dbcae2049d3149caefbe6253c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 19 Jun 2026 14:24:11 +0200 Subject: drm/bridge: Add new atomic_create_state callback Commit 47b5ac7daa46 ("drm/atomic: Add new atomic_create_state callback to drm_private_obj") introduced a new pattern for allocating drm object states: atomic_create_state, a dedicated hook that allocates and initializes a pristine state without any side effect. The bridge atomic_reset callback is already fallible and in practice only allocates and initializes state without touching hardware. However, the reset name does not make this contract clear: callers and implementers cannot tell from the name alone whether the hardware will be affected or when the hook is safe to call. Add an atomic_create_state callback to drm_bridge_funcs to make the contract explicit: allocate a pristine state, initialize it, no side effects. The core calls it when available, falling back to atomic_reset otherwise. Reviewed-by: Thomas Zimmermann Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli # imx8mp + sn65dsi84 + bridge hotplug Link: https://patch.msgid.link/20260619-drm-no-more-bridge-reset-v3-6-ff399263111b@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 5 +++-- drivers/gpu/drm/drm_bridge.c | 8 ++++++-- include/drm/drm_bridge.h | 19 ++++++++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index f0688f2d83fe..9dfb9b6ba392 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -894,8 +894,9 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); * @state is assumed to be zeroed. * * Initializes the bridge state to default values. This is meant to be called - * by the bridge &drm_bridge_funcs.atomic_reset hook for bridges that subclass - * the bridge state. + * by the bridge &drm_bridge_funcs.atomic_create_state or + * &drm_bridge_funcs.atomic_reset hook for bridges that subclass the bridge + * state. */ void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, struct drm_bridge *bridge) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 1daac8a7f4c9..776856e23592 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -542,7 +542,10 @@ drm_bridge_atomic_create_priv_state(struct drm_private_obj *obj) struct drm_bridge *bridge = drm_priv_to_bridge(obj); struct drm_bridge_state *state; - state = bridge->funcs->atomic_reset(bridge); + if (bridge->funcs->atomic_create_state) + state = bridge->funcs->atomic_create_state(bridge); + else + state = bridge->funcs->atomic_reset(bridge); if (IS_ERR(state)) return ERR_CAST(state); @@ -557,7 +560,8 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { static bool drm_bridge_is_atomic(struct drm_bridge *bridge) { - return bridge->funcs->atomic_reset != NULL; + return (bridge->funcs->atomic_create_state || + bridge->funcs->atomic_reset); } /** diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 00a95f927e34..70e574fbf034 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -530,6 +530,22 @@ struct drm_bridge_funcs { */ struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); + /** + * @atomic_create_state: + * + * Allocate a pristine, initialized, state for the bridge + * object and return it. This callback must have no side + * effects: in particular, the returned state must not be + * assigned to the object's state pointer and it must not affect + * the hardware state. + * + * RETURNS: + * + * A new, pristine, bridge state instance or an error pointer + * on failure. + */ + struct drm_bridge_state *(*atomic_create_state)(struct drm_bridge *bridge); + /** * @detect: * @@ -1375,7 +1391,8 @@ drm_bridge_get_current_state(struct drm_bridge *bridge) * drm_atomic_private_obj_init(), so we need to make sure we're * working with one before we try to use the lock. */ - if (!bridge->funcs || !bridge->funcs->atomic_reset) + if (!bridge->funcs || + !(bridge->funcs->atomic_reset || bridge->funcs->atomic_create_state)) return NULL; drm_modeset_lock_assert_held(&bridge->base.lock); -- cgit From 20df722d6be6a631e92f04877fde6ec31abab4b3 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 19 Jun 2026 14:24:12 +0200 Subject: drm/atomic-state-helper: Add drm_atomic_helper_bridge_create_state() The drm_atomic_helper_bridge_reset() helper is deprecated in favour of the new atomic_create_state callback. Add drm_atomic_helper_bridge_create_state() as the counterpart helper for this new callback, and make drm_atomic_helper_bridge_reset() call this new helper. Reviewed-by: Thomas Zimmermann Reviewed-by: Laurent Pinchart Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli # imx8mp + sn65dsi84 + bridge hotplug Link: https://patch.msgid.link/20260619-drm-no-more-bridge-reset-v3-7-ff399263111b@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 22 +++++++++++++++++++++- include/drm/drm_atomic_state_helper.h | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 9dfb9b6ba392..cfb54fc853ef 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -873,6 +873,7 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state); * @state: bridge state to destroy * * Destroys a bridge state previously created by + * &drm_atomic_helper_bridge_create_state(), * &drm_atomic_helper_bridge_reset() or * &drm_atomic_helper_bridge_duplicate_state(). This helper is meant to be * used as a bridge &drm_bridge_funcs.atomic_destroy_state hook for bridges @@ -917,6 +918,25 @@ EXPORT_SYMBOL(__drm_atomic_helper_bridge_state_init); */ struct drm_bridge_state * drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) +{ + return drm_atomic_helper_bridge_create_state(bridge); +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); + +/** + * drm_atomic_helper_bridge_create_state - default + * &drm_bridge_funcs.atomic_create_state hook for bridges + * @bridge: bridge object + * + * Allocates and initializes pristine @drm_bridge_state. + * + * This is useful for drivers that don't subclass @drm_bridge_state. + * + * RETURNS: + * Pointer to new bridge state, or ERR_PTR on failure. + */ +struct drm_bridge_state * +drm_atomic_helper_bridge_create_state(struct drm_bridge *bridge) { struct drm_bridge_state *bridge_state; @@ -927,4 +947,4 @@ drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) __drm_atomic_helper_bridge_state_init(bridge_state, bridge); return bridge_state; } -EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); +EXPORT_SYMBOL(drm_atomic_helper_bridge_create_state); diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index cbc760598b9e..68c685ad330f 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -105,4 +105,6 @@ void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, struct drm_bridge *bridge); struct drm_bridge_state * +drm_atomic_helper_bridge_create_state(struct drm_bridge *bridge); +struct drm_bridge_state * drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); -- cgit From 57acfbe5bbc1662439c7acf52a727e947d71622e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 19 Jun 2026 14:25:19 +0200 Subject: drm/atomic-state-helper: Remove drm_atomic_helper_bridge_reset() All drivers have been converted to the atomic_create_state callback and its drm_atomic_helper_bridge_create_state() helper. Remove the deprecated drm_atomic_helper_bridge_reset(). Reviewed-by: Thomas Zimmermann Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli # imx8mp + sn65dsi84 + bridge hotplug Link: https://patch.msgid.link/20260619-drm-no-more-bridge-reset-v3-74-ff399263111b@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 19 +------------------ include/drm/drm_atomic_state_helper.h | 2 -- 2 files changed, 1 insertion(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index cfb54fc853ef..fbdc3d893a0c 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -873,8 +873,7 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state); * @state: bridge state to destroy * * Destroys a bridge state previously created by - * &drm_atomic_helper_bridge_create_state(), - * &drm_atomic_helper_bridge_reset() or + * &drm_atomic_helper_bridge_create_state() or * &drm_atomic_helper_bridge_duplicate_state(). This helper is meant to be * used as a bridge &drm_bridge_funcs.atomic_destroy_state hook for bridges * that don't subclass the bridge state. @@ -907,22 +906,6 @@ void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, } EXPORT_SYMBOL(__drm_atomic_helper_bridge_state_init); -/** - * drm_atomic_helper_bridge_reset() - Allocate and initialize a bridge state - * to its default - * @bridge: the bridge this state refers to - * - * Allocates the bridge state and initializes it to default values. This helper - * is meant to be used as a bridge &drm_bridge_funcs.atomic_reset hook for - * bridges that don't subclass the bridge state. - */ -struct drm_bridge_state * -drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) -{ - return drm_atomic_helper_bridge_create_state(bridge); -} -EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); - /** * drm_atomic_helper_bridge_create_state - default * &drm_bridge_funcs.atomic_create_state hook for bridges diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 68c685ad330f..34a599c3d86d 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -106,5 +106,3 @@ void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, struct drm_bridge *bridge); struct drm_bridge_state * drm_atomic_helper_bridge_create_state(struct drm_bridge *bridge); -struct drm_bridge_state * -drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); -- cgit From f126289a0e98b384060c0f332a5a7cf92f4acc2e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 19 Jun 2026 14:25:23 +0200 Subject: drm/bridge: Remove atomic_reset support There are no remaining users of the atomic_reset hook. Remove it from the core. Reviewed-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli # imx8mp + sn65dsi84 + bridge hotplug Link: https://patch.msgid.link/20260619-drm-no-more-bridge-reset-v3-78-ff399263111b@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 5 ++--- drivers/gpu/drm/drm_bridge.c | 8 ++------ include/drm/drm_bridge.h | 30 +----------------------------- 3 files changed, 5 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index fbdc3d893a0c..d90d1d7c9cf9 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -894,9 +894,8 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); * @state is assumed to be zeroed. * * Initializes the bridge state to default values. This is meant to be called - * by the bridge &drm_bridge_funcs.atomic_create_state or - * &drm_bridge_funcs.atomic_reset hook for bridges that subclass the bridge - * state. + * by the bridge &drm_bridge_funcs.atomic_create_state hook for bridges that + * subclass the bridge state. */ void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, struct drm_bridge *bridge) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 776856e23592..6ead9b75ae3d 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -542,10 +542,7 @@ drm_bridge_atomic_create_priv_state(struct drm_private_obj *obj) struct drm_bridge *bridge = drm_priv_to_bridge(obj); struct drm_bridge_state *state; - if (bridge->funcs->atomic_create_state) - state = bridge->funcs->atomic_create_state(bridge); - else - state = bridge->funcs->atomic_reset(bridge); + state = bridge->funcs->atomic_create_state(bridge); if (IS_ERR(state)) return ERR_CAST(state); @@ -560,8 +557,7 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { static bool drm_bridge_is_atomic(struct drm_bridge *bridge) { - return (bridge->funcs->atomic_create_state || - bridge->funcs->atomic_reset); + return bridge->funcs->atomic_create_state != NULL; } /** diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 70e574fbf034..18f3db367dc1 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -503,33 +503,6 @@ struct drm_bridge_funcs { struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state); - /** - * @atomic_reset: - * - * Reset the bridge to a predefined state (or retrieve its current - * state) and return a &drm_bridge_state object matching this state. - * This function is called at attach time. - * - * The atomic_reset hook is mandatory if the bridge implements any of - * the atomic hooks, and should be left unassigned otherwise. For - * bridges that don't subclass &drm_bridge_state, the - * drm_atomic_helper_bridge_reset() helper function shall be used to - * implement this hook. - * - * Note that the atomic_reset() semantics is not exactly matching the - * reset() semantics found on other components (connector, plane, ...). - * - * 1. The reset operation happens when the bridge is attached, not when - * drm_mode_config_reset() is called - * 2. It's meant to be used exclusively on bridges that have been - * converted to the ATOMIC API - * - * RETURNS: - * A valid drm_bridge_state object in case of success, an ERR_PTR() - * giving the reason of the failure otherwise. - */ - struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); - /** * @atomic_create_state: * @@ -1391,8 +1364,7 @@ drm_bridge_get_current_state(struct drm_bridge *bridge) * drm_atomic_private_obj_init(), so we need to make sure we're * working with one before we try to use the lock. */ - if (!bridge->funcs || - !(bridge->funcs->atomic_reset || bridge->funcs->atomic_create_state)) + if (!bridge->funcs || !bridge->funcs->atomic_create_state) return NULL; drm_modeset_lock_assert_held(&bridge->base.lock); -- cgit From c55b693678c2c1c57ffd952d3e17562443a8d5d3 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:07 +0300 Subject: drm/{i915,xe}/panic: pass obj to panic setup Start reducing i915 and xe core dependency on struct intel_framebuffer by passing the fb obj from display. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/840c4ccaced5f1c82277285938287776c8cdf513.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_parent.c | 5 +++-- drivers/gpu/drm/i915/display/intel_parent.h | 3 ++- drivers/gpu/drm/i915/display/intel_plane.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_panic.c | 5 ++--- drivers/gpu/drm/xe/display/xe_panic.c | 6 +++--- include/drm/intel/display_parent_interface.h | 3 ++- 6 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index a5816561be40..0b2bc2d38442 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -251,9 +251,10 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display) return display->parent->panic->alloc(); } -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb) +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj) { - return display->parent->panic->setup(panic, sb); + return display->parent->panic->setup(panic, sb, obj); } void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic) diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 27e35f891a6b..4197d1b1af61 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -105,7 +105,8 @@ void intel_parent_overlay_cleanup(struct intel_display *display); /* panic */ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display); -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb); +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj); void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic); /* pc8 */ diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index acfe974cdc92..0fc7325fa96b 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -1627,7 +1627,7 @@ static int intel_get_scanout_buffer(struct drm_plane *plane, return -EOPNOTSUPP; } sb->private = fb; - ret = intel_parent_panic_setup(display, fb->panic, sb); + ret = intel_parent_panic_setup(display, fb->panic, sb, obj); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c index bb26a0ece176..001ccfbf7ab7 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_panic.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -5,7 +5,6 @@ #include #include -#include "display/intel_fb.h" #include "display/intel_display_types.h" #include "i915_gem_object.h" #include "i915_gem_panic.h" @@ -98,10 +97,10 @@ static struct intel_panic *i915_gem_object_alloc_panic(void) * Use current vaddr if it exists, or setup a list of pages. * pfn is not supported yet. */ -static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *_obj) { struct intel_framebuffer *fb = sb->private; - struct drm_gem_object *_obj = intel_fb_bo(&fb->base); bool panic_tiling = fb->panic_tiling; enum i915_map_type has_type; struct drm_i915_gem_object *obj = to_intel_bo(_obj); diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index bebb21d617f0..d7f456eec597 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -84,10 +84,10 @@ static struct intel_panic *xe_panic_alloc(void) return panic; } -static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *obj) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); + struct xe_bo *bo = gem_to_xe_bo(obj); if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo)) return -ENODEV; diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index 39991afeb173..b0362e231d84 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -167,7 +167,8 @@ struct intel_display_overlay_interface { struct intel_display_panic_interface { struct intel_panic *(*alloc)(void); - int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb); + int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *obj); void (*finish)(struct intel_panic *panic); }; -- cgit From 53f12a266c3244b4895f7a3619ca1dd2ad54cffc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:09 +0300 Subject: drm/{i915, xe}/panic: drop dependency on struct intel_framebuffer Store tiling function pointer in struct intel_panic instead of struct intel_framebuffer, and store struct intel_panic pointer instead of struct intel_framebuffer pointer in struct drm_scanout_buffer private member. To make this happen, pass the tiling function pointer to panic setup hook, and initialize sb->private in the hook for clarity. This allows us to drop the dependency on struct intel_framebuffer from i915 and xe panic code. Note: It would be less verbose to have a typedef for the tiling function pointer. However, there isn't a nice location for it that wouldn't also increase header interdependencies. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/d97abae79db3437c617cd4cb6193ba017b3a8d78.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/intel_parent.c | 5 +++-- drivers/gpu/drm/i915/display/intel_parent.h | 3 ++- drivers/gpu/drm/i915/display/intel_plane.c | 8 +++---- drivers/gpu/drm/i915/gem/i915_gem_panic.c | 26 +++++++++++++--------- drivers/gpu/drm/xe/display/xe_panic.c | 15 ++++++++----- include/drm/intel/display_parent_interface.h | 3 ++- 7 files changed, 35 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index ebd00922bf3c..b0ce1b71ca27 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -151,7 +151,6 @@ struct intel_framebuffer { unsigned int min_alignment; unsigned int vtd_guard; - unsigned int (*panic_tiling)(unsigned int x, unsigned int y, unsigned int width); struct intel_panic *panic; }; diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index 0b2bc2d38442..a5e41ea66921 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -252,9 +252,10 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display) } int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, - struct drm_scanout_buffer *sb, struct drm_gem_object *obj) + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - return display->parent->panic->setup(panic, sb, obj); + return display->parent->panic->setup(panic, sb, obj, tiling); } void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic) diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 4197d1b1af61..595d4148b8eb 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -106,7 +106,8 @@ void intel_parent_overlay_cleanup(struct intel_display *display); /* panic */ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display); int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, - struct drm_scanout_buffer *sb, struct drm_gem_object *obj); + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)); void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic); /* pc8 */ diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 0fc7325fa96b..667343bed5eb 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -1617,17 +1617,17 @@ static int intel_get_scanout_buffer(struct drm_plane *plane, if (fb == intel_fbdev_framebuffer(display->fbdev.fbdev)) { intel_fbdev_get_map(display, &sb->map[0]); } else { + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width) = NULL; int ret; /* Can't disable tiling if DPT is in use */ if (intel_fb_uses_dpt(&fb->base)) { if (fb->base.format->cpp[0] != 4) return -EOPNOTSUPP; - fb->panic_tiling = intel_get_tiling_func(fb->base.modifier); - if (!fb->panic_tiling) + tiling = intel_get_tiling_func(fb->base.modifier); + if (!tiling) return -EOPNOTSUPP; } - sb->private = fb; - ret = intel_parent_panic_setup(display, fb->panic, sb, obj); + ret = intel_parent_panic_setup(display, fb->panic, sb, obj, tiling); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c index 001ccfbf7ab7..91389d36f101 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_panic.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -5,7 +5,6 @@ #include #include -#include "display/intel_display_types.h" #include "i915_gem_object.h" #include "i915_gem_panic.h" @@ -13,6 +12,8 @@ struct intel_panic { struct page **pages; int page; void *vaddr; + + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); }; static void i915_panic_kunmap(struct intel_panic *panic) @@ -45,8 +46,8 @@ static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - unsigned int offset = fb->panic_tiling(sb->width, x, y); + struct intel_panic *panic = sb->private; + unsigned int offset = panic->tiling(sb->width, x, y); iosys_map_wr(&sb->map[0], offset, u32, color); } @@ -59,13 +60,12 @@ static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, u static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { + struct intel_panic *panic = sb->private; unsigned int new_page; unsigned int offset; - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); else offset = y * sb->pitch[0] + x * sb->format->cpp[0]; @@ -98,14 +98,15 @@ static struct intel_panic *i915_gem_object_alloc_panic(void) * pfn is not supported yet. */ static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj) + struct drm_gem_object *_obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - struct intel_framebuffer *fb = sb->private; - bool panic_tiling = fb->panic_tiling; enum i915_map_type has_type; struct drm_i915_gem_object *obj = to_intel_bo(_obj); void *ptr; + sb->private = panic; + ptr = page_unpack_bits(obj->mm.mapping, &has_type); if (ptr) { if (i915_gem_object_has_iomem(obj)) @@ -113,8 +114,10 @@ static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_sca else iosys_map_set_vaddr(&sb->map[0], ptr); - if (panic_tiling) + if (tiling) { + panic->tiling = tiling; sb->set_pixel = i915_gem_object_panic_map_set_pixel; + } return 0; } if (i915_gem_object_has_struct_page(obj)) { @@ -122,6 +125,7 @@ static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_sca if (!panic->pages) return -ENOMEM; panic->page = -1; + panic->tiling = tiling; sb->set_pixel = i915_gem_object_panic_page_set_pixel; return 0; } diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index 4b86760ec00a..12c6fb99015d 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -5,7 +5,6 @@ #include #include -#include "intel_display_types.h" #include "xe_bo.h" #include "xe_panic.h" #include "xe_res_cursor.h" @@ -17,6 +16,7 @@ struct intel_panic { int page; struct xe_bo *bo; + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); }; static void xe_panic_kunmap(struct intel_panic *panic) @@ -37,14 +37,13 @@ static void xe_panic_kunmap(struct intel_panic *panic) static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; + struct intel_panic *panic = sb->private; struct xe_bo *bo = panic->bo; unsigned int new_page; unsigned int offset; - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); else offset = y * sb->pitch[0] + x * sb->format->cpp[0]; @@ -86,7 +85,8 @@ static struct intel_panic *xe_panic_alloc(void) } static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *obj) + struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { struct xe_bo *bo = gem_to_xe_bo(obj); @@ -95,8 +95,11 @@ static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer * panic->page = -1; panic->bo = bo; + panic->tiling = tiling; + sb->private = panic; sb->set_pixel = xe_panic_page_set_pixel; + return 0; } diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index b0362e231d84..de395df9ca30 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -168,7 +168,8 @@ struct intel_display_overlay_interface { struct intel_display_panic_interface { struct intel_panic *(*alloc)(void); int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *obj); + struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)); void (*finish)(struct intel_panic *panic); }; -- cgit From 23102b004acb6f6cffe300ac66cc39f394042111 Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 23 Jun 2026 15:12:33 +0530 Subject: drm/dp: Add DP_DSC_MAX_BPP_DELTA register The dsc max bpp delta masks were incorrectly placed under the DP_DSC_BITS_PER_PIXEL_INC(0x06F) register. Move these under correct DP_DSC_MAX_BPP_DELTA(0x06E) register. v2: Separate patch for correcting register. [Ankit] Signed-off-by: Nemesa Garg Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260623094236.1586318-2-nemesa.garg@intel.com --- include/drm/display/drm_dp.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 829e4d98d61c..f34161078622 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -354,9 +354,11 @@ # define DP_DSC_20_PER_DP_DSC_SINK (1 << 1) # define DP_DSC_24_PER_DP_DSC_SINK (1 << 2) -#define DP_DSC_BITS_PER_PIXEL_INC 0x06F +#define DP_DSC_MAX_BPP_DELTA_VERSION_1 0x06E # define DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK 0x1f # define DP_DSC_RGB_YCbCr420_MAX_BPP_DELTA_MASK 0xe0 + +#define DP_DSC_BITS_PER_PIXEL_INC 0x06F # define DP_DSC_BITS_PER_PIXEL_1_16 0x0 # define DP_DSC_BITS_PER_PIXEL_1_8 0x1 # define DP_DSC_BITS_PER_PIXEL_1_4 0x2 -- cgit From ce7999a2722a8c85234fd778735f73dd1a5aa337 Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 23 Jun 2026 15:12:34 +0530 Subject: drm/dp: Rename YCbCr420 bpp delta mask to native Rename DP_DSC_RGB_YCbCr420_MAX_BPP_DELTA_MASK to DP_DSC_NATIVE_YCbCr420_MAX_BPP_DELTA_MASK to align with the DP DSC specification, where the field represents the native YCbCr 4:2:0 format. Signed-off-by: Nemesa Garg Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260623094236.1586318-3-nemesa.garg@intel.com --- include/drm/display/drm_dp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index f34161078622..57e8143a1374 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -356,7 +356,7 @@ #define DP_DSC_MAX_BPP_DELTA_VERSION_1 0x06E # define DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK 0x1f -# define DP_DSC_RGB_YCbCr420_MAX_BPP_DELTA_MASK 0xe0 +# define DP_DSC_NATIVE_YCbCr420_MAX_BPP_DELTA_MASK 0xe0 #define DP_DSC_BITS_PER_PIXEL_INC 0x06F # define DP_DSC_BITS_PER_PIXEL_1_16 0x0 -- cgit From 16bc193d78b00a1ec4eee3aad5afce5724769972 Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 23 Jun 2026 15:12:35 +0530 Subject: drm/dp: Add max bpp delta computation constants Define macros used for decoding DSC max bpp delta values from the sink DPCD. This includes per-format masks for RGB/YCbCr444 and YCbCr420, as well as definitions for delta scaling and the YCbCr420 bit shift. Also add version_1 as suffix to MAX_DELTA_BPP. v2: Move constants under 0x6E register. [Ankit] Add mask for Native 422 also. [Ankit] v3: Rename _DSC_NATIVE4222 to _DSC_NATIVE_YCbCr422. [Ankit] v4: Move Version_1 edit ti patch_1. [Ankit] Add shift mask for native also. [sashiko] Signed-off-by: Nemesa Garg Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260623094236.1586318-4-nemesa.garg@intel.com --- include/drm/display/drm_dp.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 57e8143a1374..7154216e0aff 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -358,6 +358,10 @@ # define DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK 0x1f # define DP_DSC_NATIVE_YCbCr420_MAX_BPP_DELTA_MASK 0xe0 +# define DP_DSC_BPP_DELTA_444 16 +# define DP_DSC_BPP_DELTA_420 12 +# define DP_DSC_BPP_DELTA_SHIFT_420 5 + #define DP_DSC_BITS_PER_PIXEL_INC 0x06F # define DP_DSC_BITS_PER_PIXEL_1_16 0x0 # define DP_DSC_BITS_PER_PIXEL_1_8 0x1 @@ -365,6 +369,9 @@ # define DP_DSC_BITS_PER_PIXEL_1_2 0x3 # define DP_DSC_BITS_PER_PIXEL_1_1 0x4 # define DP_DSC_BITS_PER_PIXEL_MASK 0x7 +# define DP_DSC_NATIVE_YCbCr422_MAX_BPP_DELTA_MASK 0x78 +# define DP_DSC_BPP_DELTA_NATIVE_SHIFT_422 3 +# define DP_DSC_BPP_DELTA_NATIVE_422 16 #define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ # define DP_PSR_IS_SUPPORTED 1 -- cgit From 60b5fa6edfef867322fce7c8306e5c4b46211be7 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 14:28:44 +0200 Subject: drm/hibmc: Use gem-shmem with shadow-plane helpers for memory management Replace the gem-vram memory manager with gem-shmem. Makes the driver more robust and enables dma-buf sharing with other hardware. Gem-vram was created from various drivers that used TTM for their memory management. All these drivers have meanwhile been converted to gem-shmem. Using gem-vram is deprecated because it has several problems. * TTM requires significant overcommitment of video memory for reliable page flips. There needs to be 3 times the size of the largest possible framebuffer available or page flips can fail. This leaves the display dark without further warning. Hibmc hardware with 32 MiB and a maximum framebuffer size of 1920x2000 is at the limit. * No dma-buf sharing without GTT support. Neither gem-vram nor hibmc hardware support a GTT address space. This is required to share buffers with other devices via dma-buf interfaces. * TTM requires hardware-accelerated rendering into video memory for optimal results. As hibmc hardware cannot do this, hibmc renders in system memory and copies the result to video memory. This can be more effectively implemented with gem-shmem and DRM's shadow-plane helpers. Converting hibmc to gem-shmem and shadow-plane helpers. * Replace gem-vram entry points in struct drm_driver with gem-shmem equivalents. This makes the driver allocate struct drm_gem_shmem_object for its buffers. * Use DRM_GEM_SHADOW_*_PLANE for its plane funcs and plane-helper funcs. The shadow-plane helpers map a plane's gem buffer objects into kernel address space during a page flip, so that atomic_update can copy them to video memory. * Handle framebuffer damage in hibmc_plane_atomic_update(). This updates video memory from the plane's framebuffer. It automatically synchronizes shared buffers with other devices. Create the framebuffer with drm_gem_fb_create_with_dirty() to trigger the update on each page flip. * Initialize the plane with drm_plane_enable_fb_damage_clips() to limit the damage updates to the framebuffer areas that changed. We don't want to do a full-buffer memcpy if only a small area has changed. * Test display modes against the available video memory in hibmc_mode_config_mode_valid(). We only want to announce display modes that fit into display memory. * Map the display memory itself into kernel address space. * Do not set drm_mode_config.prefer_shadow. This would advise user space to install a shadow buffer. But with gem-shmem, the gem buffer object already acts as a shadow buffer for video memory. We use these patterns in many other drivers with similar limitation as hibmc and its hardware. With these changes in place, hibmc is more robust and better integrated into the overall DRM framework. v3: - fix coding style v2: - do not select TTM symbols Signed-off-by: Thomas Zimmermann Reviewed-by: Yongbang Shi Link: https://patch.msgid.link/20260618123142.92298-7-tzimmermann@suse.de --- drivers/gpu/drm/drm_gem_shmem_helper.c | 22 +++++++-- drivers/gpu/drm/hisilicon/hibmc/Kconfig | 4 +- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 38 ++++++++++----- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 62 ++++++++++++++++++------- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 5 ++ include/drm/drm_gem_shmem_helper.h | 4 ++ 6 files changed, 100 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 545933c7f712..d4ace2a1c47d 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -453,10 +453,23 @@ void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem, } EXPORT_SYMBOL_GPL(drm_gem_shmem_vunmap_locked); -static int -drm_gem_shmem_create_with_handle(struct drm_file *file_priv, - struct drm_device *dev, size_t size, - uint32_t *handle) +/** + * drm_gem_shmem_create_with_handle - Allocate an object with the given size and + * returns a GEM handle + * @file_priv: DRM file structure to create the dumb buffer for + * @dev: DRM device + * @size: Size of the object to allocate + * @handle: Returns the GEM handle on success + * + * Allocates an shmem GEM buffer using drm_gem_shmem_create() and returns + * a GEM handle to it. + * + * Returns: + * Zero on success, or an error code otherwise. + */ +int drm_gem_shmem_create_with_handle(struct drm_file *file_priv, + struct drm_device *dev, size_t size, + uint32_t *handle) { struct drm_gem_shmem_object *shmem; int ret; @@ -475,6 +488,7 @@ drm_gem_shmem_create_with_handle(struct drm_file *file_priv, return ret; } +EXPORT_SYMBOL_GPL(drm_gem_shmem_create_with_handle); /* Update madvise status, returns true if not purged, else * false or -errno. diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig index d1f3f5793f34..adf4516bf8f6 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig +++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig @@ -5,10 +5,8 @@ config DRM_HISI_HIBMC select DRM_CLIENT_SELECTION select DRM_DISPLAY_HELPER select DRM_DISPLAY_DP_HELPER + select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER - select DRM_VRAM_HELPER - select DRM_TTM - select DRM_TTM_HELPER select I2C select I2C_ALGOBIT help diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index fe73b365e547..b4ab53db1c08 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -15,8 +15,10 @@ #include #include +#include #include -#include +#include +#include #include #include "hibmc_drm_drv.h" @@ -100,22 +102,35 @@ static int hibmc_plane_atomic_check(struct drm_plane *plane, static void hibmc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state) { + struct hibmc_drm_private *priv = to_hibmc_drm_private(plane->dev); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state); struct drm_framebuffer *fb = new_state->fb; + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); + u32 gpu_addr = 0; u32 reg; - s64 gpu_addr = 0; u32 line_l; - struct hibmc_drm_private *priv = to_hibmc_drm_private(plane->dev); - struct drm_gem_vram_object *gbo; if (!fb) return; - gbo = drm_gem_vram_of_gem(fb->obj[0]); + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) { + struct drm_rect damage; + struct drm_atomic_helper_damage_iter iter; + + drm_atomic_helper_damage_iter_init(&iter, old_state, new_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + struct iosys_map dst[DRM_FORMAT_MAX_PLANES] = { + IOSYS_MAP_INIT_VADDR_IOMEM(priv->vram + gpu_addr), + }; - gpu_addr = drm_gem_vram_offset(gbo); - if (WARN_ON_ONCE(gpu_addr < 0)) - return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */ + iosys_map_incr(&dst[0], + drm_fb_clip_offset(fb->pitches[0], fb->format, &damage)); + drm_fb_memcpy(dst, fb->pitches, shadow_plane_state->data, fb, &damage); + } + + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + } writel(gpu_addr, priv->mmio + HIBMC_CRT_FB_ADDRESS); @@ -149,13 +164,11 @@ static const struct drm_plane_funcs hibmc_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + DRM_GEM_SHADOW_PLANE_FUNCS, }; static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = { - DRM_GEM_VRAM_PLANE_HELPER_FUNCS, + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, .atomic_check = hibmc_plane_atomic_check, .atomic_update = hibmc_plane_atomic_update, }; @@ -515,6 +528,7 @@ int hibmc_de_init(struct hibmc_drm_private *priv) } drm_plane_helper_add(plane, &hibmc_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(plane); ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &hibmc_crtc_funcs, NULL); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 55d175028c46..4d85c89f3f88 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -19,9 +19,10 @@ #include #include #include -#include +#include +#include #include -#include +#include #include #include #include @@ -72,7 +73,13 @@ static irqreturn_t hibmc_dp_interrupt(int irq, void *arg) static int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - return drm_gem_vram_fill_create_dumb(file, dev, 0, 128, args); + int ret; + + ret = drm_mode_size_dumb(dev, args, SZ_128, 0); + if (ret) + return ret; + + return drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle); } static const struct drm_driver hibmc_driver = { @@ -82,10 +89,9 @@ static const struct drm_driver hibmc_driver = { .desc = "hibmc drm driver", .major = 1, .minor = 0, - .debugfs_init = drm_vram_mm_debugfs_init, - .dumb_create = hibmc_dumb_create, - .dumb_map_offset = drm_gem_ttm_dumb_map_offset, - DRM_FBDEV_TTM_DRIVER_OPS, + .gem_prime_import = drm_gem_shmem_prime_import_no_map, + .dumb_create = hibmc_dumb_create, + DRM_FBDEV_SHMEM_DRIVER_OPS, }; static int __maybe_unused hibmc_pm_suspend(struct device *dev) @@ -107,6 +113,27 @@ static const struct dev_pm_ops hibmc_pm_ops = { hibmc_pm_resume) }; +static enum drm_mode_status hibmc_mode_config_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode) +{ + const struct drm_format_info *info = + drm_get_format_info(dev, DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR); + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + unsigned long max_fb_size = priv->vram_size; + u64 pitch; + + if (drm_WARN_ON_ONCE(dev, !info)) + return MODE_ERROR; /* driver bug */ + + pitch = drm_format_info_min_pitch(info, 0, mode->hdisplay); + if (!pitch) + return MODE_BAD_WIDTH; + else if (pitch > max_fb_size / mode->vdisplay) + return MODE_MEM; + + return MODE_OK; +} + static struct drm_framebuffer *hibmc_mode_config_fb_create(struct drm_device *dev, struct drm_file *file_priv, const struct drm_format_info *info, @@ -119,11 +146,11 @@ static struct drm_framebuffer *hibmc_mode_config_fb_create(struct drm_device *de return ERR_PTR(-EINVAL); } - return drm_gem_fb_create(dev, file_priv, info, mode_cmd); + return drm_gem_fb_create_with_dirty(dev, file_priv, info, mode_cmd); } static const struct drm_mode_config_funcs hibmc_mode_funcs = { - .mode_valid = drm_vram_helper_mode_valid, + .mode_valid = hibmc_mode_config_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, .fb_create = hibmc_mode_config_fb_create, @@ -146,7 +173,6 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) dev->mode_config.max_height = 1200; dev->mode_config.preferred_depth = 24; - dev->mode_config.prefer_shadow = 1; dev->mode_config.funcs = (void *)&hibmc_mode_funcs; @@ -351,18 +377,22 @@ static int hibmc_load(struct drm_device *dev) { struct pci_dev *pdev = to_pci_dev(dev->dev); struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + resource_size_t vram_base, vram_size; int ret; ret = hibmc_hw_init(priv); if (ret) return ret; - ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (ret) { - drm_err(dev, "Error initializing VRAM MM; %d\n", ret); - return ret; - } + vram_base = pci_resource_start(pdev, 0); + vram_size = pci_resource_len(pdev, 0); + + priv->vram = devm_ioremap_wc(dev->dev, vram_base, vram_size); + if (!priv->vram) + return -ENOMEM; + + priv->vram_base = vram_base; + priv->vram_size = vram_size; ret = hibmc_kms_init(priv); if (ret) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index cd3a3fca1fe6..dce8572bf63e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -38,6 +38,11 @@ struct hibmc_drm_private { /* hw */ void __iomem *mmio; + /* vram */ + void __iomem *vram; + resource_size_t vram_base; + resource_size_t vram_size; + /* drm */ struct drm_device dev; struct drm_plane primary_plane; diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index 5ccdae21b94a..86b967174b60 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -141,6 +141,10 @@ struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem) void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem, struct drm_printer *p, unsigned int indent); +int drm_gem_shmem_create_with_handle(struct drm_file *file_priv, + struct drm_device *dev, size_t size, + uint32_t *handle); + extern const struct vm_operations_struct drm_gem_shmem_vm_ops; /* -- cgit From 230149760cdbc89d450fc7c3aa270811a60ea6d2 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 23 Jun 2026 00:35:59 +0300 Subject: drm/modes: Add DRM_MODE_MATCH_TIMINGS_VRR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new mode matching flag DRM_MODE_MATCH_TIMINGS_VRR. This is identical to DRM_MODE_MATCH_TIMINGS, except it requires the vsync pulse to remain anchored to the end of vtotal, as opposed to the start of the frame. VRR capable hardware can therefore treat matching modes as just variants of the same mode with a different vblank lengths. Reviewed-by: Suraj Kandpal Acked-by: Maarten Lankhorst Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260622213602.7244-3-ville.syrjala@linux.intel.com Tested-by: Vidya Srinivas Acked-by: Jani Nikula --- drivers/gpu/drm/drm_modes.c | 23 +++++++++++++++++++++++ include/drm/drm_modes.h | 1 + 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 3f8e025fd6d9..e1eed13a8e94 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1469,6 +1469,25 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_duplicate); +static bool drm_mode_match_timings_vrr(const struct drm_display_mode *mode1, + const struct drm_display_mode *mode2) +{ + int mode1_vsync_start_offset = mode1->vtotal - mode1->vsync_start; + int mode1_vsync_end_offset = mode1->vtotal - mode1->vsync_end; + int mode2_vsync_start_offset = mode2->vtotal - mode2->vsync_start; + int mode2_vsync_end_offset = mode2->vtotal - mode2->vsync_end; + + return mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1_vsync_start_offset == mode2_vsync_start_offset && + mode1_vsync_end_offset == mode2_vsync_end_offset && + mode1->vscan == mode2->vscan; +} + static bool drm_mode_match_timings(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) { @@ -1538,6 +1557,10 @@ bool drm_mode_match(const struct drm_display_mode *mode1, if (!mode1 || !mode2) return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS_VRR && + !drm_mode_match_timings_vrr(mode1, mode2)) + return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS && !drm_mode_match_timings(mode1, mode2)) return false; diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index b9bb92e4b029..6e3eccc3c349 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -193,6 +193,7 @@ enum drm_mode_status { #define DRM_MODE_MATCH_FLAGS (1 << 2) #define DRM_MODE_MATCH_3D_FLAGS (1 << 3) #define DRM_MODE_MATCH_ASPECT_RATIO (1 << 4) +#define DRM_MODE_MATCH_TIMINGS_VRR (1 << 5) /** * struct drm_display_mode - DRM kernel-internal display mode structure -- cgit From 8a564dfdfd88f1c5262ad1a4957310fe907650fc Mon Sep 17 00:00:00 2001 From: "Zenghui Yu (Huawei)" Date: Mon, 22 Jun 2026 19:07:08 +0800 Subject: cgroup: Fix a typo of the function name in comment ... which was wrongly written as cgroup_threadcgroup_change_begin(). Signed-off-by: Zenghui Yu (Huawei) Signed-off-by: Tejun Heo --- include/linux/cgroup-defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index de2cd6238c2a..7a631a257613 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -896,7 +896,7 @@ static inline void cgroup_threadgroup_change_begin(struct task_struct *tsk) * cgroup_threadgroup_change_end - threadgroup exclusion for cgroups * @tsk: target task * - * Counterpart of cgroup_threadcgroup_change_begin(). + * Counterpart of cgroup_threadgroup_change_begin(). */ static inline void cgroup_threadgroup_change_end(struct task_struct *tsk) { -- cgit From 252891d03dfb239fb76a78ab06b0e5a8719e0f86 Mon Sep 17 00:00:00 2001 From: Cheng-Yang Chou Date: Wed, 10 Jun 2026 23:26:56 +0800 Subject: sched_ext, rcu: Upgrade RCU stall paths to report cpumask of stalled CPUs scx_rcu_cpu_stall() previously recorded the detector CPU rather than the stalled one, and the expedited grace period path had no stalled CPU to report at all. Thread a cpumask through panic_on_rcu_stall() and scx_rcu_cpu_stall() to capture all stalled CPUs. Report cpumask_first() as exit_cpu and the full CPU list in the exit message. Task-only stalls yield exit_cpu = -1. Store the stall mask in scx_sched rather than scx_exit_info, keeping the BPF-visible struct unchanged. scx_dump_state() reads sch->stall_cpus directly and dumps all stalled CPUs first to avoid losing them to truncation. Signed-off-by: Cheng-Yang Chou Reviewed-by: Paul E. McKenney Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- include/linux/sched/ext.h | 4 +-- kernel/rcu/tree.c | 3 ++ kernel/rcu/tree_exp.h | 5 +++- kernel/rcu/tree_stall.h | 13 +++++--- kernel/sched/ext/ext.c | 73 +++++++++++++++++++++++++++++++++++++++------ kernel/sched/ext/internal.h | 1 + 6 files changed, 83 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h index 20b2343aa344..75cb8b119fb7 100644 --- a/include/linux/sched/ext.h +++ b/include/linux/sched/ext.h @@ -263,7 +263,7 @@ void sched_ext_dead(struct task_struct *p); void print_scx_info(const char *log_lvl, struct task_struct *p); void scx_softlockup(u32 dur_s); bool scx_hardlockup(int cpu); -bool scx_rcu_cpu_stall(void); +bool scx_rcu_cpu_stall(const struct cpumask *stalled_mask); #else /* !CONFIG_SCHED_CLASS_EXT */ @@ -271,7 +271,7 @@ static inline void sched_ext_dead(struct task_struct *p) {} static inline void print_scx_info(const char *log_lvl, struct task_struct *p) {} static inline void scx_softlockup(u32 dur_s) {} static inline bool scx_hardlockup(int cpu) { return false; } -static inline bool scx_rcu_cpu_stall(void) { return false; } +static inline bool scx_rcu_cpu_stall(const struct cpumask *stalled_mask) { return false; } #endif /* CONFIG_SCHED_CLASS_EXT */ diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 03a43d3d2616..415583c35f8c 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4900,6 +4900,9 @@ static void __init rcu_dump_rcu_node_tree(void) struct workqueue_struct *rcu_gp_wq; +static struct cpumask rcu_stall_cpumask; +static struct cpumask rcu_exp_stall_cpumask; + void __init rcu_init(void) { int cpu = smp_processor_id(); diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index 82cada459e5d..46b6907f1b09 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -578,6 +578,7 @@ static void synchronize_rcu_expedited_stall(unsigned long jiffies_start, unsigne if (!(READ_ONCE(rnp->expmask) & mask)) continue; ndetected++; + cpumask_set_cpu(cpu, &rcu_exp_stall_cpumask); rdp = per_cpu_ptr(&rcu_data, cpu); pr_cont(" %d-%c%c%c%c", cpu, "O."[!!cpu_online(cpu)], @@ -665,6 +666,8 @@ static void synchronize_rcu_expedited_wait(void) if (rcu_stall_is_suppressed()) continue; + cpumask_clear(&rcu_exp_stall_cpumask); + nbcon_cpu_emergency_enter(); j = jiffies; @@ -675,7 +678,7 @@ static void synchronize_rcu_expedited_wait(void) nbcon_cpu_emergency_exit(); - panic_on_rcu_stall(); + panic_on_rcu_stall(&rcu_exp_stall_cpumask); } } diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index cf7ae51cba40..ebf381936eb1 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -159,7 +159,7 @@ static int __init check_cpu_stall_init(void) early_initcall(check_cpu_stall_init); /* If so specified via sysctl, panic, yielding cleaner stall-warning output. */ -static void panic_on_rcu_stall(void) +static void panic_on_rcu_stall(const struct cpumask *stalled_mask) { static int cpu_stall; @@ -167,7 +167,7 @@ static void panic_on_rcu_stall(void) * Attempt to kick out the BPF scheduler if it's installed and defer * the panic to give the system a chance to recover. */ - if (scx_rcu_cpu_stall()) + if (scx_rcu_cpu_stall(stalled_mask)) return; if (++cpu_stall < sysctl_max_rcu_stall_to_panic) @@ -644,6 +644,8 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps) if (rcu_stall_is_suppressed()) return; + cpumask_clear(&rcu_stall_cpumask); + nbcon_cpu_emergency_enter(); /* @@ -659,6 +661,7 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps) for_each_leaf_node_possible_cpu(rnp, cpu) if (rnp->qsmask & leaf_node_cpu_bit(rnp, cpu)) { print_cpu_stall_info(cpu); + cpumask_set_cpu(cpu, &rcu_stall_cpumask); ndetected++; } } @@ -700,7 +703,7 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps) nbcon_cpu_emergency_exit(); - panic_on_rcu_stall(); + panic_on_rcu_stall(&rcu_stall_cpumask); rcu_force_quiescent_state(); /* Kick them all. */ } @@ -753,7 +756,9 @@ static void print_cpu_stall(unsigned long gp_seq, unsigned long gps) nbcon_cpu_emergency_exit(); - panic_on_rcu_stall(); + cpumask_clear(&rcu_stall_cpumask); + cpumask_set_cpu(smp_processor_id(), &rcu_stall_cpumask); + panic_on_rcu_stall(&rcu_stall_cpumask); /* * Attempt to revive the RCU machinery by forcing a context switch. diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 7044a591e4c5..9c9cb9d08bca 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -4853,6 +4853,8 @@ static const struct attribute_group scx_global_attr_group = { static void free_pnode(struct scx_sched_pnode *pnode); static void free_exit_info(struct scx_exit_info *ei); +static const char *scx_exit_reason(enum scx_exit_kind kind); +static bool scx_claim_exit(struct scx_sched *sch, enum scx_exit_kind kind); static s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch) { @@ -4909,6 +4911,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work) timer_shutdown_sync(&sch->bypass_lb_timer); free_cpumask_var(sch->bypass_lb_donee_cpumask); free_cpumask_var(sch->bypass_lb_resched_cpumask); + free_cpumask_var(sch->stall_cpus); #ifdef CONFIG_EXT_SUB_SCHED kfree(sch->cgrp_path); @@ -5138,9 +5141,46 @@ static __printf(2, 3) bool handle_lockup(int exit_cpu, const char *fmt, ...) * resolve the reported RCU stall. %false if sched_ext is not enabled or someone * else already initiated abort. */ -bool scx_rcu_cpu_stall(void) +bool scx_rcu_cpu_stall(const struct cpumask *stalled_mask) { - return handle_lockup(-1, "RCU CPU stall detected!"); + struct scx_sched *sch; + struct scx_exit_info *ei; + int exit_cpu; + + guard(rcu)(); + + sch = rcu_dereference(scx_root); + if (unlikely(!sch)) + return false; + + switch (scx_enable_state()) { + case SCX_ENABLING: + case SCX_ENABLED: + break; + default: + return false; + } + + exit_cpu = cpumask_empty(stalled_mask) ? -1 : (int)cpumask_first(stalled_mask); + ei = sch->exit_info; + + guard(preempt)(); + + if (!scx_claim_exit(sch, SCX_EXIT_ERROR)) + return false; + +#ifdef CONFIG_STACKTRACE + ei->bt_len = stack_trace_save(ei->bt, SCX_EXIT_BT_LEN, 1); +#endif + scnprintf(ei->msg, SCX_EXIT_MSG_LEN, "RCU CPU stall on CPUs (%*pbl)", + cpumask_pr_args(stalled_mask)); + ei->kind = SCX_EXIT_ERROR; + ei->reason = scx_exit_reason(SCX_EXIT_ERROR); + ei->exit_cpu = exit_cpu; + cpumask_copy(sch->stall_cpus, stalled_mask); + + irq_work_queue(&sch->disable_irq_work); + return true; } /** @@ -6587,14 +6627,23 @@ static void scx_dump_state(struct scx_sched *sch, struct scx_exit_info *ei, dump_line(&s, "----------"); /* - * Dump the exit CPU first so it isn't lost to dump truncation, then - * walk the rest in order, skipping the one already dumped. + * Dump stalled CPUs first so they aren't lost to dump truncation, then + * walk the rest in order. Fall back to exit_cpu if no stall mask set. */ - if (ei->exit_cpu >= 0) - scx_dump_cpu(sch, &s, &dctx, ei->exit_cpu, dump_all_tasks); - for_each_possible_cpu(cpu) { - if (cpu != ei->exit_cpu) + if (!cpumask_empty(sch->stall_cpus)) { + for_each_cpu(cpu, sch->stall_cpus) scx_dump_cpu(sch, &s, &dctx, cpu, dump_all_tasks); + for_each_possible_cpu(cpu) { + if (!cpumask_test_cpu(cpu, sch->stall_cpus)) + scx_dump_cpu(sch, &s, &dctx, cpu, dump_all_tasks); + } + } else { + if (ei->exit_cpu >= 0) + scx_dump_cpu(sch, &s, &dctx, ei->exit_cpu, dump_all_tasks); + for_each_possible_cpu(cpu) { + if (cpu != ei->exit_cpu) + scx_dump_cpu(sch, &s, &dctx, cpu, dump_all_tasks); + } } dump_newline(&s); @@ -6831,6 +6880,10 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, ret = -ENOMEM; goto err_free_lb_cpumask; } + if (!zalloc_cpumask_var(&sch->stall_cpus, GFP_KERNEL)) { + ret = -ENOMEM; + goto err_free_lb_resched_cpumask; + } /* * Copy ops through the right union view. For cid-form the source is * struct sched_ext_ops_cid which lacks the trailing cpu_acquire/ @@ -6914,8 +6967,10 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd, #ifdef CONFIG_EXT_SUB_SCHED err_free_lb_resched: RCU_INIT_POINTER(ops->priv, NULL); - free_cpumask_var(sch->bypass_lb_resched_cpumask); + free_cpumask_var(sch->stall_cpus); #endif +err_free_lb_resched_cpumask: + free_cpumask_var(sch->bypass_lb_resched_cpumask); err_free_lb_cpumask: free_cpumask_var(sch->bypass_lb_donee_cpumask); err_stop_helper: diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h index 65eceefcf5e2..75522a5f28f4 100644 --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -1205,6 +1205,7 @@ struct scx_sched { struct timer_list bypass_lb_timer; cpumask_var_t bypass_lb_donee_cpumask; cpumask_var_t bypass_lb_resched_cpumask; + cpumask_var_t stall_cpus; struct rcu_work rcu_work; /* all ancestors including self */ -- cgit From da43ea213936494732e52212c59f027967b97173 Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Thu, 25 Jun 2026 09:39:44 +0800 Subject: cgroup: Use data_race() for task->flags in task_css_set_check() task_css_set_check() uses rcu_dereference_check() to verify that task->cgroups can be dereferenced. One accepted condition is that the task is already exiting, tested by checking PF_EXITING in task->flags. This check is only part of the CONFIG_PROVE_RCU lockdep predicate. This was found by KCSAN during fuzz testing. KCSAN can report a data race when another task flag bit is updated concurrently. One report shows pids_release() reading task->flags through task_css_set_check() while do_task_dead() sets PF_NOFREEZE: KCSAN: data-race in task_css() [inline] KCSAN: data-race in pids_release() task_css() pids_release() cgroup_release() release_task() wait_task_zombie() value changed: 0x0040004c -> 0x0040804c The changed bit is PF_NOFREEZE, not PF_EXITING. PF_EXITING remains set before and after the update, so the task_css_set_check() condition does not change. This is not a race on task->cgroups and does not indicate incorrect pids charging or uncharging. tools/memory-model/Documentation/access-marking.txt recommends data_race() for data-racy loads used only for diagnostic purposes. Use data_race() here to mark the intended diagnostic-only access. No functional change intended. Suggested-by: Tejun Heo Signed-off-by: Guopeng Zhang Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f2aa46a4f871..b905208942bf 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -480,7 +480,7 @@ static inline void cgroup_unlock(void) rcu_read_lock_sched_held() || \ lockdep_is_held(&cgroup_mutex) || \ lockdep_is_held(&css_set_lock) || \ - ((task)->flags & PF_EXITING) || (__c)) + (data_race((task)->flags) & PF_EXITING) || (__c)) #else #define task_css_set_check(task, __c) \ rcu_dereference((task)->cgroups) -- cgit From 509ca545d425512f83ca70093f6d836ec8ab5bd1 Mon Sep 17 00:00:00 2001 From: Jordan Rife Date: Thu, 18 Jun 2026 11:20:32 -0700 Subject: bpf: Support BPF_F_EGRESS with bpf_redirect_peer We have several use cases where a pod injects traffic into the datapath of another so that the traffic appears to have originated from that pod. One such use case is a synthetic flow generator which injects synthetic traffic into a pod's datapath to enable dynamic probing and debugging. Another is a transparent proxy where connections originating from one pod are redirected towards another which proxies that connection. The new connection is bound to the IP of the original pod using IP_TRANSPARENT and its traffic is injected into that pod's datapath and handled as if it had originated there. This can be used for mTLS, etc. We use bpf_redirect(BPF_F_INGRESS) to direct traffic leaving the proxy, flow generator, etc. towards the target pod, ensuring that eBPF programs that are meant to intercept traffic leaving that pod are executed. However, this doesn't work with netkit. With netkit, an ingress redirection from proxy to workload skips eBPF programs that are meant to intercept traffic leaving the pod, since they reside on the netkit peer device. One workaround is to attach the same program to both the netkit peer device and the TCX ingress hook for the netkit pair's primary interface, but a) This seems hacky and we need to be careful not to run the same program twice for the same skb in cases where we want to pass that traffic to the host stack. b) We're trying to keep the proxy redirection / traffic injection systems as modular and separated from Cilium as possible, the system that manages netkit setup and core eBPF programming. It would be handy if instead we could redirect traffic directly from one netkit peer device to another. This patch proposes an extension to bpf_redirect_peer to allow us to do just that. With this patch, the BPF_F_EGRESS flag tells bpf_redirect_peer to emit the skb in the egress direction of the target interface's peer device While the main use case is netkit, I suppose you could also use this mode with veth as well if, e.g., there were some eBPF programs attached to that side of the veth pair that needed to intercept traffic. +---------------------------------------------------------------------+ | +-------------------------+ 6. bpf_redirect_neigh(eth0) | | | pod (10.244.0.10) | ------------------------ | | | | | | | | | +--------+ | | +---------+ | | | | 1. packet -->| | | | | | | | | | leaves ^ | netkit |<===========|======| netkit | | | | | | | peer |=======(eBPF)=====>| primary | | | | | | | | | | | | | | | | | +--------+ | | +---------+ | | | | | | | 2. bpf_redirect v | | +-----------|-------------+ |___________________ +-------| | | | | eth0 | | | 5. bpf_redirect_peer(BPF_F_EGRESS) | +-------| | |________________________ | | | +-------------------------+ | | | | | proxy (10.244.0.11) | | | | | | IP_TRANSPARENT | | | | | | +--------+ | | +---------+ | | | | 3. packet <--| | | | | |<-- | | | enters | netkit |<===========|======| netkit | | | | [proxy] | peer |=======(eBPF)=====>| primary | | | | 4. packet -->| | | | | | | | leaves +--------+ | +---------+ | | | sip=10.244.0.10 | | | +-------------------------+ | +---------------------------------------------------------------------+ Using the proxy use case as an example, in step 5 we would redirect traffic leaving the proxy towards the pod's peer device using bpf_redirect_peer(BPF_F_EGRESS). As a bonus, since the skb doesn't have to go through the backlog queue it can take full advantage of netkit's performance benefits. I set up a test where outgoing iperf3 traffic is injected into the datapath of another pod using either bpf_redirect_peer(BPF_F_EGRESS) or bpf_redirect(BPF_F_INGRESS). I used Cilium's eBPF host routing mode which skips the host stack and uses BPF redirect helpers to do all the routing. (net.ipv4.tcp_congestion_control=cubic,mtu=1500,100GiB link,Cilium eBPF host routing mode) BASELINE [bpf_redirect(BPF_F_INGRESS)] 1. [iperf pod] ==bpf_redirect([pod b], BPF_F_INGRESS)==> [pod b] 2. [pod b] ==bpf_redirect_neigh([eth0])==> eth0 3. eth0 ==over network==> [host b] [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-60.00 sec 231 GBytes 33.0 Gbits/sec 12060 sender [ 5] 0.00-60.00 sec 230 GBytes 33.0 Gbits/sec receiver TEST [bpf_redirect_peer(BPF_F_EGRESS)] 1. [iperf pod] ==bpf_redirect_peer([pod b], BPF_F_EGRESS)==> [pod b] 2. [pod b] ==bpf_redirect_neigh([eth0])==> eth0 3. eth0 ==over network==> [host b] [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-60.00 sec 272 GBytes 38.9 Gbits/sec 0 sender [ 5] 0.00-60.00 sec 272 GBytes 38.9 Gbits/sec receiver In this test, using bpf_redirect_peer(BPF_F_EGRESS) for the hop from [iperf pod] to [pod b] led to ~18% more throughput compared to bpf_redirect(BPF_F_INGRESS). Signed-off-by: Jordan Rife Acked-by: Daniel Borkmann Acked-by: Paul Chaignon Reviewed-by: Jiayuan Chen Link: https://lore.kernel.org/r/20260618182035.43811-2-jordan@jrife.io Signed-off-by: Alexei Starovoitov --- include/uapi/linux/bpf.h | 19 +++++++++++-------- net/core/filter.c | 12 +++++++----- tools/include/uapi/linux/bpf.h | 19 +++++++++++-------- 3 files changed, 29 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 89b36de5fdbb..c91b5a4bda03 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5079,17 +5079,19 @@ union bpf_attr { * Description * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_redirect**\ (), except - * that the redirection happens to the *ifindex*' peer device and - * the netns switch takes place from ingress to ingress without - * going through the CPU's backlog queue. + * that the redirection happens to the *ifindex*' peer device. If + * *flags* is 0, the netns switch takes place from ingress to + * ingress without going through the CPU's backlog queue. If the + * **BPF_F_EGRESS** flag is provided then redirection happens in + * the egress direction of the peer device. * * *skb*\ **->mark** and *skb*\ **->tstamp** are not cleared during * the netns switch. * - * The *flags* argument is reserved and must be 0. The helper is - * currently only supported for tc BPF program types at the - * ingress hook and for veth and netkit target device types. The - * peer device must reside in a different network namespace. + * If the *flags* argument is 0, the helper is currently only + * supported for tc BPF program types at the ingress hook and for + * veth and netkit target device types. The peer device must reside + * in a different network namespace. * Return * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. @@ -6336,9 +6338,10 @@ enum { /* Flags for bpf_redirect and bpf_redirect_map helpers */ enum { BPF_F_INGRESS = (1ULL << 0), /* used for skb path */ + BPF_F_EGRESS = (1ULL << 1), /* used for skb path */ BPF_F_BROADCAST = (1ULL << 3), /* used for XDP path */ BPF_F_EXCLUDE_INGRESS = (1ULL << 4), /* used for XDP path */ -#define BPF_F_REDIRECT_FLAGS (BPF_F_INGRESS | BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) +#define BPF_F_REDIRECT_FLAGS (BPF_F_INGRESS | BPF_F_EGRESS | BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) }; #define __bpf_md_ptr(type, name) \ diff --git a/net/core/filter.c b/net/core/filter.c index b446aa8be5c3..4f5cbcac3e78 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2529,16 +2529,18 @@ int skb_do_redirect(struct sk_buff *skb) if (unlikely(!dev)) goto out_drop; if (flags & BPF_F_PEER) { - if (unlikely(!skb_at_tc_ingress(skb))) - goto out_drop; dev = skb_get_peer_dev(dev); if (unlikely(!dev || !(dev->flags & IFF_UP) || net_eq(net, dev_net(dev)))) goto out_drop; + skb_scrub_packet(skb, false); + if (flags & BPF_F_EGRESS) + return __bpf_redirect(skb, dev, 0); + if (unlikely(!skb_at_tc_ingress(skb))) + goto out_drop; skb->dev = dev; dev_sw_netstats_rx_add(dev, skb->len); - skb_scrub_packet(skb, false); return -EAGAIN; } return flags & BPF_F_NEIGH ? @@ -2575,10 +2577,10 @@ BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags) { struct bpf_redirect_info *ri = bpf_net_ctx_get_ri(); - if (unlikely(flags)) + if (unlikely(flags & ~BPF_F_EGRESS)) return TC_ACT_SHOT; - ri->flags = BPF_F_PEER; + ri->flags = BPF_F_PEER | flags; ri->tgt_index = ifindex; return TC_ACT_REDIRECT; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 89b36de5fdbb..c91b5a4bda03 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5079,17 +5079,19 @@ union bpf_attr { * Description * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_redirect**\ (), except - * that the redirection happens to the *ifindex*' peer device and - * the netns switch takes place from ingress to ingress without - * going through the CPU's backlog queue. + * that the redirection happens to the *ifindex*' peer device. If + * *flags* is 0, the netns switch takes place from ingress to + * ingress without going through the CPU's backlog queue. If the + * **BPF_F_EGRESS** flag is provided then redirection happens in + * the egress direction of the peer device. * * *skb*\ **->mark** and *skb*\ **->tstamp** are not cleared during * the netns switch. * - * The *flags* argument is reserved and must be 0. The helper is - * currently only supported for tc BPF program types at the - * ingress hook and for veth and netkit target device types. The - * peer device must reside in a different network namespace. + * If the *flags* argument is 0, the helper is currently only + * supported for tc BPF program types at the ingress hook and for + * veth and netkit target device types. The peer device must reside + * in a different network namespace. * Return * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. @@ -6336,9 +6338,10 @@ enum { /* Flags for bpf_redirect and bpf_redirect_map helpers */ enum { BPF_F_INGRESS = (1ULL << 0), /* used for skb path */ + BPF_F_EGRESS = (1ULL << 1), /* used for skb path */ BPF_F_BROADCAST = (1ULL << 3), /* used for XDP path */ BPF_F_EXCLUDE_INGRESS = (1ULL << 4), /* used for XDP path */ -#define BPF_F_REDIRECT_FLAGS (BPF_F_INGRESS | BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) +#define BPF_F_REDIRECT_FLAGS (BPF_F_INGRESS | BPF_F_EGRESS | BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) }; #define __bpf_md_ptr(type, name) \ -- cgit From 69fdbe63e16919a885a8f9441e248ce0ddf15b25 Mon Sep 17 00:00:00 2001 From: Woojin Ji Date: Thu, 25 Jun 2026 19:25:37 +0900 Subject: bpf: Preserve scalar zero spills for stack reads Stack reads can read back bytes that belong to a previously spilled scalar constant zero. Today mark_reg_stack_read() only treats STACK_ZERO bytes as known zero bytes, so the destination register can become unknown even though every byte in the read range is known to be zero. This can lead to rejecting otherwise valid programs once the loaded byte is used as a pointer offset. The original reproducer uses a variable-offset stack byte read emitted by clang 22.1.6 at -O2/-O3 from a small helper-based BPF C program. Fixed offset reads have a related mixed case as well: pure scalar-zero spill reads are already handled, but a fixed read spanning both STACK_ZERO and scalar const-zero STACK_SPILL bytes still falls back to unknown. Teach mark_reg_stack_read() to also consider STACK_SPILL bytes backed by a spilled scalar constant zero as zero bytes, and use that path for both variable-offset stack reads and fixed-offset mixed reads. Keep the existing pure register-fill behavior unchanged. When a zero result depends on such a spill, mark the contributing stack slots precise before accepting the const-zero result so pruning cannot reuse a zero-spill state for a later non-zero spill state. No deployed-program regression is currently known, so target bpf-next. Assisted-by: opencode:gpt-5.5 Signed-off-by: Woojin Ji Acked-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260625-bpf-stack-var-off-zero-v1-v3-1-a068210a761b@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 5 +++++ kernel/bpf/verifier.c | 53 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 39a851e690ec..76b8b7627a10 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -1243,6 +1243,11 @@ static inline void bpf_bt_set_frame_slot(struct backtrack_state *bt, u32 frame, bt->stack_masks[frame] |= 1ull << slot; } +static inline void bpf_bt_set_frame_slot_mask(struct backtrack_state *bt, u32 frame, u64 mask) +{ + bt->stack_masks[frame] |= mask; +} + static inline void bt_set_frame_stack_arg_slot(struct backtrack_state *bt, u32 frame, u32 slot) { bt->stack_arg_masks[frame] |= 1 << slot; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 21a365d436a5..25aea4271cd0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3702,14 +3702,21 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env, * SCALAR. This function does not deal with register filling; the caller must * ensure that all spilled registers in the stack range have been marked as * read. + * + * STACK_SPILL bytes backed by spilled scalar const zeroes are also considered + * zero bytes. In that case, mark the contributing stack slots precise so + * pruning cannot reuse a zero-spill state for a later non-zero spill state. + * + * Returns an error if precision backtracking fails. */ -static void mark_reg_stack_read(struct bpf_verifier_env *env, - /* func where src register points to */ - struct bpf_func_state *ptr_state, - int min_off, int max_off, int dst_regno) +static int mark_reg_stack_read(struct bpf_verifier_env *env, + /* func where src register points to */ + struct bpf_func_state *ptr_state, + int min_off, int max_off, int dst_regno) { struct bpf_verifier_state *vstate = env->cur_state; struct bpf_func_state *state = vstate->frame[vstate->curframe]; + u64 zero_spill_mask = 0; int i, slot, spi; u8 *stype; int zeros = 0; @@ -3719,19 +3726,33 @@ static void mark_reg_stack_read(struct bpf_verifier_env *env, spi = slot / BPF_REG_SIZE; mark_stack_slot_scratched(env, spi); stype = ptr_state->stack[spi].slot_type; - if (stype[slot % BPF_REG_SIZE] != STACK_ZERO) - break; - zeros++; + if (stype[slot % BPF_REG_SIZE] == STACK_ZERO) { + zeros++; + continue; + } + if (stype[slot % BPF_REG_SIZE] == STACK_SPILL && + bpf_register_is_null(&ptr_state->stack[spi].spilled_ptr)) { + zero_spill_mask |= 1ull << spi; + zeros++; + continue; + } + break; } if (zeros == max_off - min_off) { /* Any access_size read into register is zero extended, * so the whole register == const_zero. */ __mark_reg_const_zero(env, &state->regs[dst_regno]); + if (zero_spill_mask) { + bpf_bt_set_frame_slot_mask(&env->bt, ptr_state->frameno, zero_spill_mask); + return mark_chain_precision_batch(env, env->cur_state); + } } else { /* have read misc data from the stack */ mark_reg_unknown(env, state->regs, dst_regno); } + + return 0; } /* Read the stack at 'off' and put the results into the register indicated by @@ -3753,6 +3774,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, int i, slot = -off - 1, spi = slot / BPF_REG_SIZE; struct bpf_reg_state *reg; u8 *stype, type; + int err; int insn_flags = INSN_F_STACK_ACCESS; int hist_spi = spi, hist_frame = reg_state->frameno; @@ -3835,7 +3857,10 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, __mark_reg_const_zero(env, &state->regs[dst_regno]); insn_flags = 0; /* not restoring original register state */ } else { - mark_reg_unknown(env, state->regs, dst_regno); + err = mark_reg_stack_read(env, reg_state, off, off + size, + dst_regno); + if (err) + return err; insn_flags = 0; /* not restoring original register state */ } } @@ -3880,8 +3905,11 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, } return -EACCES; } - if (dst_regno >= 0) - mark_reg_stack_read(env, reg_state, off, off + size, dst_regno); + if (dst_regno >= 0) { + err = mark_reg_stack_read(env, reg_state, off, off + size, dst_regno); + if (err) + return err; + } insn_flags = 0; /* we are not restoring spilled register */ } if (insn_flags) @@ -3935,7 +3963,10 @@ static int check_stack_read_var_off(struct bpf_verifier_env *env, struct bpf_reg min_off = reg_smin(reg) + off; max_off = reg_smax(reg) + off; - mark_reg_stack_read(env, ptr_state, min_off, max_off + size, dst_regno); + err = mark_reg_stack_read(env, ptr_state, min_off, max_off + size, + dst_regno); + if (err) + return err; check_fastcall_stack_contract(env, ptr_state, env->insn_idx, min_off); return 0; } -- cgit From e9c44d4eccc735b9b3a2c7e6324207d5ccb2d821 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 10 Jun 2026 16:20:08 +0000 Subject: media: mc-entity: Add missing kerneldoc The argument args is not documented, and the latest kernel version complains about that. This fixes the following warning: Warning: include/media/media-entity.h:1394 function parameter 'args' not described in 'media_entity_call' Fixes: 48a7c4bac94d ("[media] docs-rst: improve the kAPI documentation for the mediactl") Signed-off-by: Ricardo Ribalda Reviewed-by: Daniel Scally Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/media-entity.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/media/media-entity.h b/include/media/media-entity.h index d9b72cd87d52..fa393e840669 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -1387,6 +1387,7 @@ void media_remove_intf_links(struct media_interface *intf); * @entity: entity where the @operation will be called * @operation: type of the operation. Should be the name of a member of * struct &media_entity_operations. + * @args: arguments for the operation. * * This helper function will check if @operation is not %NULL. On such case, * it will issue a call to @operation\(@entity, @args\). -- cgit From 969076f31bf63100c8b773cfb85fd5771f5926da Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 18 Jun 2026 22:18:19 +0100 Subject: efs: Remove EFS The kernel EFS code has been unmaintained for over twenty years. It was superseded on IRIX around thirty years ago. I haven't seen an EFS filesystem in the wild since 1999. Userspace tools to read EFS filesystems exist, such as https://github.com/jkbenaim/efsextract There's no benefit to keeping this filesystem in the kernel, and it only increases the maintenance burden for tree-wide changes. Signed-off-by: Matthew Wilcox (Oracle) Link: https://patch.msgid.link/20260618211822.3599089-1-willy@infradead.org Acked-by: Jori Koolstra Signed-off-by: Christian Brauner (Amutable) --- MAINTAINERS | 5 - fs/Kconfig | 1 - fs/Makefile | 1 - fs/efs/Kconfig | 16 -- fs/efs/Makefile | 8 - fs/efs/dir.c | 105 ------------ fs/efs/efs.h | 144 ---------------- fs/efs/file.c | 42 ----- fs/efs/inode.c | 315 ----------------------------------- fs/efs/namei.c | 120 -------------- fs/efs/super.c | 368 ----------------------------------------- fs/efs/symlink.c | 50 ------ include/linux/efs_vh.h | 54 ------ include/uapi/linux/efs_fs_sb.h | 63 ------- 14 files changed, 1292 deletions(-) delete mode 100644 fs/efs/Kconfig delete mode 100644 fs/efs/Makefile delete mode 100644 fs/efs/dir.c delete mode 100644 fs/efs/efs.h delete mode 100644 fs/efs/file.c delete mode 100644 fs/efs/inode.c delete mode 100644 fs/efs/namei.c delete mode 100644 fs/efs/super.c delete mode 100644 fs/efs/symlink.c delete mode 100644 include/linux/efs_vh.h delete mode 100644 include/uapi/linux/efs_fs_sb.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..e2a0d74db4e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9493,11 +9493,6 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/fbdev/efifb.c -EFS FILESYSTEM -S: Orphan -W: http://aeschi.ch.eu.org/efs/ -F: fs/efs/ - EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER L: netdev@vger.kernel.org S: Orphan diff --git a/fs/Kconfig b/fs/Kconfig index cf6ae64776e6..64ff193fb42c 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -315,7 +315,6 @@ source "fs/hfs/Kconfig" source "fs/hfsplus/Kconfig" source "fs/befs/Kconfig" source "fs/bfs/Kconfig" -source "fs/efs/Kconfig" source "fs/jffs2/Kconfig" # UBIFS File system configuration source "fs/ubifs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 89a8a9d207d1..aa847be93bc6 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -92,7 +92,6 @@ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_NTFS3_FS) += ntfs3/ obj-$(CONFIG_UFS_FS) += ufs/ -obj-$(CONFIG_EFS_FS) += efs/ obj-$(CONFIG_JFFS2_FS) += jffs2/ obj-$(CONFIG_UBIFS_FS) += ubifs/ obj-$(CONFIG_AFFS_FS) += affs/ diff --git a/fs/efs/Kconfig b/fs/efs/Kconfig deleted file mode 100644 index 0833e533df9d..000000000000 --- a/fs/efs/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config EFS_FS - tristate "EFS file system support (read only)" - depends on BLOCK - select BUFFER_HEAD - help - EFS is an older file system used for non-ISO9660 CD-ROMs and hard - disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer - uses the XFS file system for hard disk partitions however). - - This implementation only offers read-only access. If you don't know - what all this is about, it's safe to say N. For more information - about EFS see its home page at . - - To compile the EFS file system support as a module, choose M here: the - module will be called efs. diff --git a/fs/efs/Makefile b/fs/efs/Makefile deleted file mode 100644 index 85e5b88f9471..000000000000 --- a/fs/efs/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the linux efs-filesystem routines. -# - -obj-$(CONFIG_EFS_FS) += efs.o - -efs-objs := super.o inode.o namei.o dir.o file.o symlink.o diff --git a/fs/efs/dir.c b/fs/efs/dir.c deleted file mode 100644 index 35ad0092c115..000000000000 --- a/fs/efs/dir.c +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dir.c - * - * Copyright (c) 1999 Al Smith - */ - -#include -#include -#include "efs.h" - -static int efs_readdir(struct file *, struct dir_context *); - -const struct file_operations efs_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .iterate_shared = efs_readdir, - .setlease = generic_setlease, -}; - -const struct inode_operations efs_dir_inode_operations = { - .lookup = efs_lookup, -}; - -static int efs_readdir(struct file *file, struct dir_context *ctx) -{ - struct inode *inode = file_inode(file); - efs_block_t block; - int slot; - - if (inode->i_size & (EFS_DIRBSIZE-1)) - pr_warn("%s(): directory size not a multiple of EFS_DIRBSIZE\n", - __func__); - - /* work out where this entry can be found */ - block = ctx->pos >> EFS_DIRBSIZE_BITS; - - /* each block contains at most 256 slots */ - slot = ctx->pos & 0xff; - - /* look at all blocks */ - while (block < inode->i_blocks) { - struct efs_dir *dirblock; - struct buffer_head *bh; - - /* read the dir block */ - bh = sb_bread(inode->i_sb, efs_bmap(inode, block)); - - if (!bh) { - pr_err("%s(): failed to read dir block %d\n", - __func__, block); - break; - } - - dirblock = (struct efs_dir *) bh->b_data; - - if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) { - pr_err("%s(): invalid directory block\n", __func__); - brelse(bh); - break; - } - - for (; slot < dirblock->slots; slot++) { - struct efs_dentry *dirslot; - efs_ino_t inodenum; - const char *nameptr; - int namelen; - - if (dirblock->space[slot] == 0) - continue; - - dirslot = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot)); - - inodenum = be32_to_cpu(dirslot->inode); - namelen = dirslot->namelen; - nameptr = dirslot->name; - pr_debug("%s(): block %d slot %d/%d: inode %u, name \"%s\", namelen %u\n", - __func__, block, slot, dirblock->slots-1, - inodenum, nameptr, namelen); - if (!namelen) - continue; - /* found the next entry */ - ctx->pos = (block << EFS_DIRBSIZE_BITS) | slot; - - /* sanity check */ - if (nameptr - (char *) dirblock + namelen > EFS_DIRBSIZE) { - pr_warn("directory entry %d exceeds directory block\n", - slot); - continue; - } - - /* copy filename and data in dirslot */ - if (!dir_emit(ctx, nameptr, namelen, inodenum, DT_UNKNOWN)) { - brelse(bh); - return 0; - } - } - brelse(bh); - - slot = 0; - block++; - } - ctx->pos = (block << EFS_DIRBSIZE_BITS) | slot; - return 0; -} diff --git a/fs/efs/efs.h b/fs/efs/efs.h deleted file mode 100644 index 918d2b9abb76..000000000000 --- a/fs/efs/efs.h +++ /dev/null @@ -1,144 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 1999 Al Smith, - * - * Portions derived from work (c) 1995,1996 Christian Vogelgsang. - * Portions derived from IRIX header files (c) 1988 Silicon Graphics - */ -#ifndef _EFS_EFS_H_ -#define _EFS_EFS_H_ - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include - -#define EFS_VERSION "1.0a" - -/* 1 block is 512 bytes */ -#define EFS_BLOCKSIZE_BITS 9 -#define EFS_BLOCKSIZE (1 << EFS_BLOCKSIZE_BITS) - -typedef int32_t efs_block_t; -typedef uint32_t efs_ino_t; - -#define EFS_DIRECTEXTENTS 12 - -/* - * layout of an extent, in memory and on disk. 8 bytes exactly. - */ -typedef union extent_u { - unsigned char raw[8]; - struct extent_s { - unsigned int ex_magic:8; /* magic # (zero) */ - unsigned int ex_bn:24; /* basic block */ - unsigned int ex_length:8; /* numblocks in this extent */ - unsigned int ex_offset:24; /* logical offset into file */ - } cooked; -} efs_extent; - -typedef struct edevs { - __be16 odev; - __be32 ndev; -} efs_devs; - -/* - * extent based filesystem inode as it appears on disk. The efs inode - * is exactly 128 bytes long. - */ -struct efs_dinode { - __be16 di_mode; /* mode and type of file */ - __be16 di_nlink; /* number of links to file */ - __be16 di_uid; /* owner's user id */ - __be16 di_gid; /* owner's group id */ - __be32 di_size; /* number of bytes in file */ - __be32 di_atime; /* time last accessed */ - __be32 di_mtime; /* time last modified */ - __be32 di_ctime; /* time created */ - __be32 di_gen; /* generation number */ - __be16 di_numextents; /* # of extents */ - u_char di_version; /* version of inode */ - u_char di_spare; /* spare - used by AFS */ - union di_addr { - efs_extent di_extents[EFS_DIRECTEXTENTS]; - efs_devs di_dev; /* device for IFCHR/IFBLK */ - } di_u; -}; - -/* efs inode storage in memory */ -struct efs_inode_info { - int numextents; - int lastextent; - - efs_extent extents[EFS_DIRECTEXTENTS]; - struct inode vfs_inode; -}; - -#include - -#define EFS_DIRBSIZE_BITS EFS_BLOCKSIZE_BITS -#define EFS_DIRBSIZE (1 << EFS_DIRBSIZE_BITS) - -struct efs_dentry { - __be32 inode; - unsigned char namelen; - char name[3]; -}; - -#define EFS_DENTSIZE (sizeof(struct efs_dentry) - 3 + 1) -#define EFS_MAXNAMELEN ((1 << (sizeof(char) * 8)) - 1) - -#define EFS_DIRBLK_HEADERSIZE 4 -#define EFS_DIRBLK_MAGIC 0xbeef /* moo */ - -struct efs_dir { - __be16 magic; - unsigned char firstused; - unsigned char slots; - - unsigned char space[EFS_DIRBSIZE - EFS_DIRBLK_HEADERSIZE]; -}; - -#define EFS_MAXENTS \ - ((EFS_DIRBSIZE - EFS_DIRBLK_HEADERSIZE) / \ - (EFS_DENTSIZE + sizeof(char))) - -#define EFS_SLOTAT(dir, slot) EFS_REALOFF((dir)->space[slot]) - -#define EFS_REALOFF(offset) ((offset << 1)) - - -static inline struct efs_inode_info *INODE_INFO(struct inode *inode) -{ - return container_of(inode, struct efs_inode_info, vfs_inode); -} - -static inline struct efs_sb_info *SUPER_INFO(struct super_block *sb) -{ - return sb->s_fs_info; -} - -struct statfs; -struct fid; - -extern const struct inode_operations efs_dir_inode_operations; -extern const struct file_operations efs_dir_operations; -extern const struct address_space_operations efs_symlink_aops; - -extern struct inode *efs_iget(struct super_block *, unsigned long); -extern efs_block_t efs_map_block(struct inode *, efs_block_t); -extern int efs_get_block(struct inode *, sector_t, struct buffer_head *, int); - -extern struct dentry *efs_lookup(struct inode *, struct dentry *, unsigned int); -extern struct dentry *efs_fh_to_dentry(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type); -extern struct dentry *efs_fh_to_parent(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type); -extern struct dentry *efs_get_parent(struct dentry *); -extern int efs_bmap(struct inode *, int); - -#endif /* _EFS_EFS_H_ */ diff --git a/fs/efs/file.c b/fs/efs/file.c deleted file mode 100644 index 9153dfe79bbc..000000000000 --- a/fs/efs/file.c +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * file.c - * - * Copyright (c) 1999 Al Smith - * - * Portions derived from work (c) 1995,1996 Christian Vogelgsang. - */ - -#include -#include "efs.h" - -int efs_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) -{ - int error = -EROFS; - long phys; - - if (create) - return error; - if (iblock >= inode->i_blocks) - return 0; - - phys = efs_map_block(inode, iblock); - if (phys) - map_bh(bh_result, inode->i_sb, phys); - return 0; -} - -int efs_bmap(struct inode *inode, efs_block_t block) { - - if (block < 0) { - pr_warn("%s(): block < 0\n", __func__); - return 0; - } - - /* are we about to read past the end of a file ? */ - if (!(block < inode->i_blocks)) - return 0; - - return efs_map_block(inode, block); -} diff --git a/fs/efs/inode.c b/fs/efs/inode.c deleted file mode 100644 index 4b132729e638..000000000000 --- a/fs/efs/inode.c +++ /dev/null @@ -1,315 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * inode.c - * - * Copyright (c) 1999 Al Smith - * - * Portions derived from work (c) 1995,1996 Christian Vogelgsang, - * and from work (c) 1998 Mike Shaver. - */ - -#include -#include -#include -#include "efs.h" -#include - -static int efs_read_folio(struct file *file, struct folio *folio) -{ - return block_read_full_folio(folio, efs_get_block); -} - -static sector_t _efs_bmap(struct address_space *mapping, sector_t block) -{ - return generic_block_bmap(mapping,block,efs_get_block); -} - -static const struct address_space_operations efs_aops = { - .read_folio = efs_read_folio, - .bmap = _efs_bmap -}; - -static inline void extent_copy(efs_extent *src, efs_extent *dst) { - /* - * this is slightly evil. it doesn't just copy - * efs_extent from src to dst, it also mangles - * the bits so that dst ends up in cpu byte-order. - */ - - dst->cooked.ex_magic = (unsigned int) src->raw[0]; - dst->cooked.ex_bn = ((unsigned int) src->raw[1] << 16) | - ((unsigned int) src->raw[2] << 8) | - ((unsigned int) src->raw[3] << 0); - dst->cooked.ex_length = (unsigned int) src->raw[4]; - dst->cooked.ex_offset = ((unsigned int) src->raw[5] << 16) | - ((unsigned int) src->raw[6] << 8) | - ((unsigned int) src->raw[7] << 0); - return; -} - -struct inode *efs_iget(struct super_block *super, unsigned long ino) -{ - int i, inode_index; - dev_t device; - u32 rdev; - struct buffer_head *bh; - struct efs_sb_info *sb = SUPER_INFO(super); - struct efs_inode_info *in; - efs_block_t block, offset; - struct efs_dinode *efs_inode; - struct inode *inode; - - inode = iget_locked(super, ino); - if (!inode) - return ERR_PTR(-ENOMEM); - if (!(inode_state_read_once(inode) & I_NEW)) - return inode; - - in = INODE_INFO(inode); - - /* - ** EFS layout: - ** - ** | cylinder group | cylinder group | cylinder group ..etc - ** |inodes|data |inodes|data |inodes|data ..etc - ** - ** work out the inode block index, (considering initially that the - ** inodes are stored as consecutive blocks). then work out the block - ** number of that inode given the above layout, and finally the - ** offset of the inode within that block. - */ - - inode_index = inode->i_ino / - (EFS_BLOCKSIZE / sizeof(struct efs_dinode)); - - block = sb->fs_start + sb->first_block + - (sb->group_size * (inode_index / sb->inode_blocks)) + - (inode_index % sb->inode_blocks); - - offset = (inode->i_ino % - (EFS_BLOCKSIZE / sizeof(struct efs_dinode))) * - sizeof(struct efs_dinode); - - bh = sb_bread(inode->i_sb, block); - if (!bh) { - pr_warn("%s() failed at block %d\n", __func__, block); - goto read_inode_error; - } - - efs_inode = (struct efs_dinode *) (bh->b_data + offset); - - inode->i_mode = be16_to_cpu(efs_inode->di_mode); - set_nlink(inode, be16_to_cpu(efs_inode->di_nlink)); - i_uid_write(inode, (uid_t)be16_to_cpu(efs_inode->di_uid)); - i_gid_write(inode, (gid_t)be16_to_cpu(efs_inode->di_gid)); - inode->i_size = be32_to_cpu(efs_inode->di_size); - inode_set_atime(inode, be32_to_cpu(efs_inode->di_atime), 0); - inode_set_mtime(inode, be32_to_cpu(efs_inode->di_mtime), 0); - inode_set_ctime(inode, be32_to_cpu(efs_inode->di_ctime), 0); - - /* this is the number of blocks in the file */ - if (inode->i_size == 0) { - inode->i_blocks = 0; - } else { - inode->i_blocks = ((inode->i_size - 1) >> EFS_BLOCKSIZE_BITS) + 1; - } - - rdev = be16_to_cpu(efs_inode->di_u.di_dev.odev); - if (rdev == 0xffff) { - rdev = be32_to_cpu(efs_inode->di_u.di_dev.ndev); - if (sysv_major(rdev) > 0xfff) - device = 0; - else - device = MKDEV(sysv_major(rdev), sysv_minor(rdev)); - } else - device = old_decode_dev(rdev); - - /* get the number of extents for this object */ - in->numextents = be16_to_cpu(efs_inode->di_numextents); - in->lastextent = 0; - - /* copy the extents contained within the inode to memory */ - for(i = 0; i < EFS_DIRECTEXTENTS; i++) { - extent_copy(&(efs_inode->di_u.di_extents[i]), &(in->extents[i])); - if (i < in->numextents && in->extents[i].cooked.ex_magic != 0) { - pr_warn("extent %d has bad magic number in inode %llu\n", - i, inode->i_ino); - brelse(bh); - goto read_inode_error; - } - } - - brelse(bh); - pr_debug("efs_iget(): inode %llu, extents %d, mode %o\n", - inode->i_ino, in->numextents, inode->i_mode); - switch (inode->i_mode & S_IFMT) { - case S_IFDIR: - inode->i_op = &efs_dir_inode_operations; - inode->i_fop = &efs_dir_operations; - break; - case S_IFREG: - inode->i_fop = &generic_ro_fops; - inode->i_data.a_ops = &efs_aops; - break; - case S_IFLNK: - inode->i_op = &page_symlink_inode_operations; - inode_nohighmem(inode); - inode->i_data.a_ops = &efs_symlink_aops; - break; - case S_IFCHR: - case S_IFBLK: - case S_IFIFO: - init_special_inode(inode, inode->i_mode, device); - break; - default: - pr_warn("unsupported inode mode %o\n", inode->i_mode); - goto read_inode_error; - break; - } - - unlock_new_inode(inode); - return inode; - -read_inode_error: - pr_warn("failed to read inode %llu\n", inode->i_ino); - iget_failed(inode); - return ERR_PTR(-EIO); -} - -static inline efs_block_t -efs_extent_check(efs_extent *ptr, efs_block_t block, struct efs_sb_info *sb) { - efs_block_t start; - efs_block_t length; - efs_block_t offset; - - /* - * given an extent and a logical block within a file, - * can this block be found within this extent ? - */ - start = ptr->cooked.ex_bn; - length = ptr->cooked.ex_length; - offset = ptr->cooked.ex_offset; - - if ((block >= offset) && (block < offset+length)) { - return(sb->fs_start + start + block - offset); - } else { - return 0; - } -} - -efs_block_t efs_map_block(struct inode *inode, efs_block_t block) { - struct efs_sb_info *sb = SUPER_INFO(inode->i_sb); - struct efs_inode_info *in = INODE_INFO(inode); - struct buffer_head *bh = NULL; - - int cur, last, first = 1; - int ibase, ioffset, dirext, direxts, indext, indexts; - efs_block_t iblock, result = 0, lastblock = 0; - efs_extent ext, *exts; - - last = in->lastextent; - - if (in->numextents <= EFS_DIRECTEXTENTS) { - /* first check the last extent we returned */ - if ((result = efs_extent_check(&in->extents[last], block, sb))) - return result; - - /* if we only have one extent then nothing can be found */ - if (in->numextents == 1) { - pr_err("%s() failed to map (1 extent)\n", __func__); - return 0; - } - - direxts = in->numextents; - - /* - * check the stored extents in the inode - * start with next extent and check forwards - */ - for(dirext = 1; dirext < direxts; dirext++) { - cur = (last + dirext) % in->numextents; - if ((result = efs_extent_check(&in->extents[cur], block, sb))) { - in->lastextent = cur; - return result; - } - } - - pr_err("%s() failed to map block %u (dir)\n", __func__, block); - return 0; - } - - pr_debug("%s(): indirect search for logical block %u\n", - __func__, block); - direxts = in->extents[0].cooked.ex_offset; - indexts = in->numextents; - - for(indext = 0; indext < indexts; indext++) { - cur = (last + indext) % indexts; - - /* - * work out which direct extent contains `cur'. - * - * also compute ibase: i.e. the number of the first - * indirect extent contained within direct extent `cur'. - * - */ - ibase = 0; - for(dirext = 0; cur < ibase && dirext < direxts; dirext++) { - ibase += in->extents[dirext].cooked.ex_length * - (EFS_BLOCKSIZE / sizeof(efs_extent)); - } - - if (dirext == direxts) { - /* should never happen */ - pr_err("couldn't find direct extent for indirect extent %d (block %u)\n", - cur, block); - if (bh) brelse(bh); - return 0; - } - - /* work out block number and offset of this indirect extent */ - iblock = sb->fs_start + in->extents[dirext].cooked.ex_bn + - (cur - ibase) / - (EFS_BLOCKSIZE / sizeof(efs_extent)); - ioffset = (cur - ibase) % - (EFS_BLOCKSIZE / sizeof(efs_extent)); - - if (first || lastblock != iblock) { - if (bh) brelse(bh); - - bh = sb_bread(inode->i_sb, iblock); - if (!bh) { - pr_err("%s() failed at block %d\n", - __func__, iblock); - return 0; - } - pr_debug("%s(): read indirect extent block %d\n", - __func__, iblock); - first = 0; - lastblock = iblock; - } - - exts = (efs_extent *) bh->b_data; - - extent_copy(&(exts[ioffset]), &ext); - - if (ext.cooked.ex_magic != 0) { - pr_err("extent %d has bad magic number in block %d\n", - cur, iblock); - if (bh) brelse(bh); - return 0; - } - - if ((result = efs_extent_check(&ext, block, sb))) { - if (bh) brelse(bh); - in->lastextent = cur; - return result; - } - } - if (bh) brelse(bh); - pr_err("%s() failed to map block %u (indir)\n", __func__, block); - return 0; -} - -MODULE_DESCRIPTION("Extent File System (efs)"); -MODULE_LICENSE("GPL"); diff --git a/fs/efs/namei.c b/fs/efs/namei.c deleted file mode 100644 index 38961ee1d1af..000000000000 --- a/fs/efs/namei.c +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * namei.c - * - * Copyright (c) 1999 Al Smith - * - * Portions derived from work (c) 1995,1996 Christian Vogelgsang. - */ - -#include -#include -#include -#include "efs.h" - - -static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) -{ - struct buffer_head *bh; - - int slot, namelen; - char *nameptr; - struct efs_dir *dirblock; - struct efs_dentry *dirslot; - efs_ino_t inodenum; - efs_block_t block; - - if (inode->i_size & (EFS_DIRBSIZE-1)) - pr_warn("%s(): directory size not a multiple of EFS_DIRBSIZE\n", - __func__); - - for(block = 0; block < inode->i_blocks; block++) { - - bh = sb_bread(inode->i_sb, efs_bmap(inode, block)); - if (!bh) { - pr_err("%s(): failed to read dir block %d\n", - __func__, block); - return 0; - } - - dirblock = (struct efs_dir *) bh->b_data; - - if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) { - pr_err("%s(): invalid directory block\n", __func__); - brelse(bh); - return 0; - } - - for (slot = 0; slot < dirblock->slots; slot++) { - dirslot = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot)); - - namelen = dirslot->namelen; - nameptr = dirslot->name; - - if ((namelen == len) && (!memcmp(name, nameptr, len))) { - inodenum = be32_to_cpu(dirslot->inode); - brelse(bh); - return inodenum; - } - } - brelse(bh); - } - return 0; -} - -struct dentry *efs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) -{ - efs_ino_t inodenum; - struct inode *inode = NULL; - - inodenum = efs_find_entry(dir, dentry->d_name.name, dentry->d_name.len); - if (inodenum) - inode = efs_iget(dir->i_sb, inodenum); - - return d_splice_alias(inode, dentry); -} - -static struct inode *efs_nfs_get_inode(struct super_block *sb, u64 ino, - u32 generation) -{ - struct inode *inode; - - if (ino == 0) - return ERR_PTR(-ESTALE); - inode = efs_iget(sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - - if (generation && inode->i_generation != generation) { - iput(inode); - return ERR_PTR(-ESTALE); - } - - return inode; -} - -struct dentry *efs_fh_to_dentry(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type) -{ - return generic_fh_to_dentry(sb, fid, fh_len, fh_type, - efs_nfs_get_inode); -} - -struct dentry *efs_fh_to_parent(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type) -{ - return generic_fh_to_parent(sb, fid, fh_len, fh_type, - efs_nfs_get_inode); -} - -struct dentry *efs_get_parent(struct dentry *child) -{ - struct dentry *parent = ERR_PTR(-ENOENT); - efs_ino_t ino; - - ino = efs_find_entry(d_inode(child), "..", 2); - if (ino) - parent = d_obtain_alias(efs_iget(child->d_sb, ino)); - - return parent; -} diff --git a/fs/efs/super.c b/fs/efs/super.c deleted file mode 100644 index 11fea3bbce7c..000000000000 --- a/fs/efs/super.c +++ /dev/null @@ -1,368 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * super.c - * - * Copyright (c) 1999 Al Smith - * - * Portions derived from work (c) 1995,1996 Christian Vogelgsang. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "efs.h" -#include -#include - -static int efs_statfs(struct dentry *dentry, struct kstatfs *buf); -static int efs_init_fs_context(struct fs_context *fc); - -static void efs_kill_sb(struct super_block *s) -{ - struct efs_sb_info *sbi = SUPER_INFO(s); - kill_block_super(s); - kfree(sbi); -} - -static struct pt_types sgi_pt_types[] = { - {0x00, "SGI vh"}, - {0x01, "SGI trkrepl"}, - {0x02, "SGI secrepl"}, - {0x03, "SGI raw"}, - {0x04, "SGI bsd"}, - {SGI_SYSV, "SGI sysv"}, - {0x06, "SGI vol"}, - {SGI_EFS, "SGI efs"}, - {0x08, "SGI lv"}, - {0x09, "SGI rlv"}, - {0x0A, "SGI xfs"}, - {0x0B, "SGI xfslog"}, - {0x0C, "SGI xlv"}, - {0x82, "Linux swap"}, - {0x83, "Linux native"}, - {0, NULL} -}; - -/* - * File system definition and registration. - */ -static struct file_system_type efs_fs_type = { - .owner = THIS_MODULE, - .name = "efs", - .kill_sb = efs_kill_sb, - .fs_flags = FS_REQUIRES_DEV, - .init_fs_context = efs_init_fs_context, -}; -MODULE_ALIAS_FS("efs"); - -static struct kmem_cache * efs_inode_cachep; - -static struct inode *efs_alloc_inode(struct super_block *sb) -{ - struct efs_inode_info *ei; - ei = alloc_inode_sb(sb, efs_inode_cachep, GFP_KERNEL); - if (!ei) - return NULL; - return &ei->vfs_inode; -} - -static void efs_free_inode(struct inode *inode) -{ - kmem_cache_free(efs_inode_cachep, INODE_INFO(inode)); -} - -static void init_once(void *foo) -{ - struct efs_inode_info *ei = (struct efs_inode_info *) foo; - - inode_init_once(&ei->vfs_inode); -} - -static int __init init_inodecache(void) -{ - efs_inode_cachep = kmem_cache_create("efs_inode_cache", - sizeof(struct efs_inode_info), 0, - SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT, - init_once); - if (efs_inode_cachep == NULL) - return -ENOMEM; - return 0; -} - -static void destroy_inodecache(void) -{ - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ - rcu_barrier(); - kmem_cache_destroy(efs_inode_cachep); -} - -static const struct super_operations efs_superblock_operations = { - .alloc_inode = efs_alloc_inode, - .free_inode = efs_free_inode, - .statfs = efs_statfs, -}; - -static const struct export_operations efs_export_ops = { - .encode_fh = generic_encode_ino32_fh, - .fh_to_dentry = efs_fh_to_dentry, - .fh_to_parent = efs_fh_to_parent, - .get_parent = efs_get_parent, -}; - -static int __init init_efs_fs(void) { - int err; - pr_info(EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n"); - err = init_inodecache(); - if (err) - goto out1; - err = register_filesystem(&efs_fs_type); - if (err) - goto out; - return 0; -out: - destroy_inodecache(); -out1: - return err; -} - -static void __exit exit_efs_fs(void) { - unregister_filesystem(&efs_fs_type); - destroy_inodecache(); -} - -module_init(init_efs_fs) -module_exit(exit_efs_fs) - -static efs_block_t efs_validate_vh(struct volume_header *vh) { - int i; - __be32 cs, *ui; - int csum; - efs_block_t sblock = 0; /* shuts up gcc */ - struct pt_types *pt_entry; - int pt_type, slice = -1; - - if (be32_to_cpu(vh->vh_magic) != VHMAGIC) { - /* - * assume that we're dealing with a partition and allow - * read_super() to try and detect a valid superblock - * on the next block. - */ - return 0; - } - - ui = ((__be32 *) (vh + 1)) - 1; - for(csum = 0; ui >= ((__be32 *) vh);) { - cs = *ui--; - csum += be32_to_cpu(cs); - } - if (csum) { - pr_warn("SGI disklabel: checksum bad, label corrupted\n"); - return 0; - } - -#ifdef DEBUG - pr_debug("bf: \"%16s\"\n", vh->vh_bootfile); - - for(i = 0; i < NVDIR; i++) { - int j; - char name[VDNAMESIZE+1]; - - for(j = 0; j < VDNAMESIZE; j++) { - name[j] = vh->vh_vd[i].vd_name[j]; - } - name[j] = (char) 0; - - if (name[0]) { - pr_debug("vh: %8s block: 0x%08x size: 0x%08x\n", - name, (int) be32_to_cpu(vh->vh_vd[i].vd_lbn), - (int) be32_to_cpu(vh->vh_vd[i].vd_nbytes)); - } - } -#endif - - for(i = 0; i < NPARTAB; i++) { - pt_type = (int) be32_to_cpu(vh->vh_pt[i].pt_type); - for(pt_entry = sgi_pt_types; pt_entry->pt_name; pt_entry++) { - if (pt_type == pt_entry->pt_type) break; - } -#ifdef DEBUG - if (be32_to_cpu(vh->vh_pt[i].pt_nblks)) { - pr_debug("pt %2d: start: %08d size: %08d type: 0x%02x (%s)\n", - i, (int)be32_to_cpu(vh->vh_pt[i].pt_firstlbn), - (int)be32_to_cpu(vh->vh_pt[i].pt_nblks), - pt_type, (pt_entry->pt_name) ? - pt_entry->pt_name : "unknown"); - } -#endif - if (IS_EFS(pt_type)) { - sblock = be32_to_cpu(vh->vh_pt[i].pt_firstlbn); - slice = i; - } - } - - if (slice == -1) { - pr_notice("partition table contained no EFS partitions\n"); -#ifdef DEBUG - } else { - pr_info("using slice %d (type %s, offset 0x%x)\n", slice, - (pt_entry->pt_name) ? pt_entry->pt_name : "unknown", - sblock); -#endif - } - return sblock; -} - -static int efs_validate_super(struct efs_sb_info *sb, struct efs_super *super) { - - if (!IS_EFS_MAGIC(be32_to_cpu(super->fs_magic))) - return -1; - - sb->fs_magic = be32_to_cpu(super->fs_magic); - sb->total_blocks = be32_to_cpu(super->fs_size); - sb->first_block = be32_to_cpu(super->fs_firstcg); - sb->group_size = be32_to_cpu(super->fs_cgfsize); - sb->data_free = be32_to_cpu(super->fs_tfree); - sb->inode_free = be32_to_cpu(super->fs_tinode); - sb->inode_blocks = be16_to_cpu(super->fs_cgisize); - sb->total_groups = be16_to_cpu(super->fs_ncg); - - return 0; -} - -static int efs_fill_super(struct super_block *s, struct fs_context *fc) -{ - struct efs_sb_info *sb; - struct buffer_head *bh; - struct inode *root; - - sb = kzalloc_obj(struct efs_sb_info); - if (!sb) - return -ENOMEM; - s->s_fs_info = sb; - s->s_time_min = 0; - s->s_time_max = U32_MAX; - - s->s_magic = EFS_SUPER_MAGIC; - if (!sb_set_blocksize(s, EFS_BLOCKSIZE)) { - pr_err("device does not support %d byte blocks\n", - EFS_BLOCKSIZE); - return invalf(fc, "device does not support %d byte blocks\n", - EFS_BLOCKSIZE); - } - - /* read the vh (volume header) block */ - bh = sb_bread(s, 0); - - if (!bh) { - pr_err("cannot read volume header\n"); - return -EIO; - } - - /* - * if this returns zero then we didn't find any partition table. - * this isn't (yet) an error - just assume for the moment that - * the device is valid and go on to search for a superblock. - */ - sb->fs_start = efs_validate_vh((struct volume_header *) bh->b_data); - brelse(bh); - - if (sb->fs_start == -1) { - return -EINVAL; - } - - bh = sb_bread(s, sb->fs_start + EFS_SUPER); - if (!bh) { - pr_err("cannot read superblock\n"); - return -EIO; - } - - if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) { -#ifdef DEBUG - pr_warn("invalid superblock at block %u\n", - sb->fs_start + EFS_SUPER); -#endif - brelse(bh); - return -EINVAL; - } - brelse(bh); - - if (!sb_rdonly(s)) { -#ifdef DEBUG - pr_info("forcing read-only mode\n"); -#endif - s->s_flags |= SB_RDONLY; - } - s->s_op = &efs_superblock_operations; - s->s_export_op = &efs_export_ops; - root = efs_iget(s, EFS_ROOTINODE); - if (IS_ERR(root)) { - pr_err("get root inode failed\n"); - return PTR_ERR(root); - } - - s->s_root = d_make_root(root); - if (!(s->s_root)) { - pr_err("get root dentry failed\n"); - return -ENOMEM; - } - - return 0; -} - -static int efs_get_tree(struct fs_context *fc) -{ - return get_tree_bdev(fc, efs_fill_super); -} - -static int efs_reconfigure(struct fs_context *fc) -{ - sync_filesystem(fc->root->d_sb); - fc->sb_flags |= SB_RDONLY; - - return 0; -} - -static const struct fs_context_operations efs_context_opts = { - .get_tree = efs_get_tree, - .reconfigure = efs_reconfigure, -}; - -/* - * Set up the filesystem mount context. - */ -static int efs_init_fs_context(struct fs_context *fc) -{ - fc->ops = &efs_context_opts; - - return 0; -} - -static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct super_block *sb = dentry->d_sb; - struct efs_sb_info *sbi = SUPER_INFO(sb); - u64 id = huge_encode_dev(sb->s_bdev->bd_dev); - - buf->f_type = EFS_SUPER_MAGIC; /* efs magic number */ - buf->f_bsize = EFS_BLOCKSIZE; /* blocksize */ - buf->f_blocks = sbi->total_groups * /* total data blocks */ - (sbi->group_size - sbi->inode_blocks); - buf->f_bfree = sbi->data_free; /* free data blocks */ - buf->f_bavail = sbi->data_free; /* free blocks for non-root */ - buf->f_files = sbi->total_groups * /* total inodes */ - sbi->inode_blocks * - (EFS_BLOCKSIZE / sizeof(struct efs_dinode)); - buf->f_ffree = sbi->inode_free; /* free inodes */ - buf->f_fsid = u64_to_fsid(id); - buf->f_namelen = EFS_MAXNAMELEN; /* max filename length */ - - return 0; -} - diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c deleted file mode 100644 index 7749feded722..000000000000 --- a/fs/efs/symlink.c +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * symlink.c - * - * Copyright (c) 1999 Al Smith - * - * Portions derived from work (c) 1995,1996 Christian Vogelgsang. - */ - -#include -#include -#include -#include "efs.h" - -static int efs_symlink_read_folio(struct file *file, struct folio *folio) -{ - char *link = folio_address(folio); - struct buffer_head *bh; - struct inode *inode = folio->mapping->host; - efs_block_t size = inode->i_size; - int err; - - err = -ENAMETOOLONG; - if (size > 2 * EFS_BLOCKSIZE) - goto fail; - - /* read first 512 bytes of link target */ - err = -EIO; - bh = sb_bread(inode->i_sb, efs_bmap(inode, 0)); - if (!bh) - goto fail; - memcpy(link, bh->b_data, (size > EFS_BLOCKSIZE) ? EFS_BLOCKSIZE : size); - brelse(bh); - if (size > EFS_BLOCKSIZE) { - bh = sb_bread(inode->i_sb, efs_bmap(inode, 1)); - if (!bh) - goto fail; - memcpy(link + EFS_BLOCKSIZE, bh->b_data, size - EFS_BLOCKSIZE); - brelse(bh); - } - link[size] = '\0'; - err = 0; -fail: - folio_end_read(folio, err == 0); - return err; -} - -const struct address_space_operations efs_symlink_aops = { - .read_folio = efs_symlink_read_folio -}; diff --git a/include/linux/efs_vh.h b/include/linux/efs_vh.h deleted file mode 100644 index 206c5270f7b8..000000000000 --- a/include/linux/efs_vh.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * efs_vh.h - * - * Copyright (c) 1999 Al Smith - * - * Portions derived from IRIX header files (c) 1985 MIPS Computer Systems, Inc. - */ - -#ifndef __EFS_VH_H__ -#define __EFS_VH_H__ - -#define VHMAGIC 0xbe5a941 /* volume header magic number */ -#define NPARTAB 16 /* 16 unix partitions */ -#define NVDIR 15 /* max of 15 directory entries */ -#define BFNAMESIZE 16 /* max 16 chars in boot file name */ -#define VDNAMESIZE 8 - -struct volume_directory { - char vd_name[VDNAMESIZE]; /* name */ - __be32 vd_lbn; /* logical block number */ - __be32 vd_nbytes; /* file length in bytes */ -}; - -struct partition_table { /* one per logical partition */ - __be32 pt_nblks; /* # of logical blks in partition */ - __be32 pt_firstlbn; /* first lbn of partition */ - __be32 pt_type; /* use of partition */ -}; - -struct volume_header { - __be32 vh_magic; /* identifies volume header */ - __be16 vh_rootpt; /* root partition number */ - __be16 vh_swappt; /* swap partition number */ - char vh_bootfile[BFNAMESIZE]; /* name of file to boot */ - char pad[48]; /* device param space */ - struct volume_directory vh_vd[NVDIR]; /* other vol hdr contents */ - struct partition_table vh_pt[NPARTAB]; /* device partition layout */ - __be32 vh_csum; /* volume header checksum */ - __be32 vh_fill; /* fill out to 512 bytes */ -}; - -/* partition type sysv is used for EFS format CD-ROM partitions */ -#define SGI_SYSV 0x05 -#define SGI_EFS 0x07 -#define IS_EFS(x) (((x) == SGI_EFS) || ((x) == SGI_SYSV)) - -struct pt_types { - int pt_type; - char *pt_name; -}; - -#endif /* __EFS_VH_H__ */ - diff --git a/include/uapi/linux/efs_fs_sb.h b/include/uapi/linux/efs_fs_sb.h deleted file mode 100644 index 6bad29a10faa..000000000000 --- a/include/uapi/linux/efs_fs_sb.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * efs_fs_sb.h - * - * Copyright (c) 1999 Al Smith - * - * Portions derived from IRIX header files (c) 1988 Silicon Graphics - */ - -#ifndef __EFS_FS_SB_H__ -#define __EFS_FS_SB_H__ - -#include -#include - -/* EFS superblock magic numbers */ -#define EFS_MAGIC 0x072959 -#define EFS_NEWMAGIC 0x07295a - -#define IS_EFS_MAGIC(x) ((x == EFS_MAGIC) || (x == EFS_NEWMAGIC)) - -#define EFS_SUPER 1 -#define EFS_ROOTINODE 2 - -/* efs superblock on disk */ -struct efs_super { - __be32 fs_size; /* size of filesystem, in sectors */ - __be32 fs_firstcg; /* bb offset to first cg */ - __be32 fs_cgfsize; /* size of cylinder group in bb's */ - __be16 fs_cgisize; /* bb's of inodes per cylinder group */ - __be16 fs_sectors; /* sectors per track */ - __be16 fs_heads; /* heads per cylinder */ - __be16 fs_ncg; /* # of cylinder groups in filesystem */ - __be16 fs_dirty; /* fs needs to be fsck'd */ - __be32 fs_time; /* last super-block update */ - __be32 fs_magic; /* magic number */ - char fs_fname[6]; /* file system name */ - char fs_fpack[6]; /* file system pack name */ - __be32 fs_bmsize; /* size of bitmap in bytes */ - __be32 fs_tfree; /* total free data blocks */ - __be32 fs_tinode; /* total free inodes */ - __be32 fs_bmblock; /* bitmap location. */ - __be32 fs_replsb; /* Location of replicated superblock. */ - __be32 fs_lastialloc; /* last allocated inode */ - char fs_spare[20]; /* space for expansion - MUST BE ZERO */ - __be32 fs_checksum; /* checksum of volume portion of fs */ -}; - -/* efs superblock information in memory */ -struct efs_sb_info { - __u32 fs_magic; /* superblock magic number */ - __u32 fs_start; /* first block of filesystem */ - __u32 first_block; /* first data block in filesystem */ - __u32 total_blocks; /* total number of blocks in filesystem */ - __u32 group_size; /* # of blocks a group consists of */ - __u32 data_free; /* # of free data blocks */ - __u32 inode_free; /* # of free inodes */ - __u16 inode_blocks; /* # of blocks used for inodes in every grp */ - __u16 total_groups; /* # of groups */ -}; - -#endif /* __EFS_FS_SB_H__ */ - -- cgit From b4e124d16855213409f5dfa6aa18b81cd00fdbba Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 17 Jun 2026 13:18:27 +0200 Subject: fs: Add bpf_sock_read_xattr() kfunc to read socket xattrs In c8db08110cbe ("Merge tag 'vfs-7.1-rc1.xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs") we added support for extended attributes for sockets. This comes in two flavors: sockfs and non-sockfs/filesystem sockets. Filesystem sockets are actual filesystem objects so reading xattrs must use dedicated fs helpers such as bpf_get_dentry_xattr() and bpf_get_file_xattr(). Those are inherently sleeping operations. Sockfs sockets on the other hand don't need to use sleeping operations as the underlying data structure is lockless. In addition, retrieval of sockfs extended attributes often happens from LSM hooks that only provide struct socket and it's completely nonsensical to grab a reference to a file, then force a sleeping operation to retrieve the xattr and drop the reference. We know that the sockfs file cannot go away while the LSM hook runs. This series adds a bpf_sock_read_xattr() kfunc that, given a struct socket, reads a user.* extended attribute from the socket's sockfs inode into a bpf_dynptr. Together with fsetxattr() from userspace this lets a process label a socket with a user.* xattr and have a BPF LSM program retrieve that label locklessly. The kfunc mirrors the existing bpf_cgroup_read_xattr(), including the restriction to the user.* namespace. systemd uses user.* xattrs on sockets to implement socket rate limiting and to tag sockets for other purposes [1] such as implementing a varlink registry. There is currently no efficient way for a BPF program to read those labels back. The new helper allows a listening socket marked with an extended attribute to be read back during bind/connect and then act on the connect()ing socket. Extended attributes make it possible to allow an unprivileged user manager such as systemd --user to mark sockets from userspace and then rediscover them or implement policies. The kfunc is registered KF_RCU and only for BPF LSM programs. A struct socket is only guaranteed to live in sockfs when an LSM socket hook hands it out, which is what keeps SOCK_INODE() valid. Sockets that embed struct socket outside sockfs (tun, tap) are only reachable from tracing programs and are excluded by the registration. (Btw, for consistency it would be nice to force allocation of struct socket from sockfs instead of simply embedding it in e.g., struct tun_file which makes the SOCKFS_I() pattern a hazard - at least outside of sockfs functions.) The read never sleeps and takes no lock. For sockfs the value lives in the inode's in-memory xattr store and simple_xattr_get() resolves it with an RCU-protected rhashtable lookup, taking neither the inode lock nor any xattr lock. The kfunc is therefore usable from both sleepable and non-sleepable LSM hooks. Link: https://github.com/systemd/systemd/pull/40559 [1] Link: https://patch.msgid.link/20260617-work-bpf-sock-xattr-v1-1-a1276f7c9da3@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/bpf_fs_kfuncs.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/net.h | 1 + net/socket.c | 25 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+) (limited to 'include') diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 768aca2dc0f0..9a4ea5c9b0c9 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -11,6 +11,7 @@ #include #include #include +#include #include __bpf_kfunc_start_defs(); @@ -359,6 +360,39 @@ __bpf_kfunc int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__s } #endif /* CONFIG_CGROUPS */ +#ifdef CONFIG_NET +/** + * bpf_sock_read_xattr - read xattr of a socket's inode in sockfs + * @sock: socket to get xattr from + * @name__str: name of the xattr + * @value_p: output buffer of the xattr value + * + * Get xattr *name__str* of *sock* and store the output in *value_p*. + * + * For security reasons, only *name__str* with prefix "user." is allowed. + * + * Return: length of the xattr value on success, a negative value on error. + */ +__bpf_kfunc int bpf_sock_read_xattr(struct socket *sock, const char *name__str, + struct bpf_dynptr *value_p) +{ + struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p; + u32 value_len; + void *value; + + /* Only allow reading "user.*" xattrs */ + if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + return -EPERM; + + value_len = __bpf_dynptr_size(value_ptr); + value = __bpf_dynptr_data_rw(value_ptr, value_len); + if (!value) + return -EINVAL; + + return sock_read_xattr(sock, name__str, value, value_len); +} +#endif /* CONFIG_NET */ + /** * bpf_real_inode - get the real inode backing a dentry * @dentry: dentry to resolve @@ -385,6 +419,9 @@ BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL) +#ifdef CONFIG_NET +BTF_ID_FLAGS(func, bpf_sock_read_xattr, KF_RCU) +#endif BTF_KFUNCS_END(bpf_fs_kfunc_set_ids) static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id) diff --git a/include/linux/net.h b/include/linux/net.h index f268f395ce47..fdcf9956805c 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -285,6 +285,7 @@ int sock_recvmsg(struct socket *sock, struct msghdr *msg, int flags); struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname); struct socket *sockfd_lookup(int fd, int *err); struct socket *sock_from_file(struct file *file); +int sock_read_xattr(struct socket *sock, const char *name, void *value, size_t size); #define sockfd_put(sock) fput(sock->file) int net_ratelimit(void); diff --git a/net/socket.c b/net/socket.c index 63c69a0fa74e..b0256cd222f8 100644 --- a/net/socket.c +++ b/net/socket.c @@ -465,6 +465,31 @@ static const struct xattr_handler sockfs_user_xattr_handler = { .set = sockfs_user_xattr_set, }; +/** + * sock_read_xattr - read a user.* xattr from a socket's sockfs inode + * @sock: socket whose inode holds the xattr + * @name: full xattr name, e.g. "user.bpf_test" + * @value: output buffer + * @size: size of @value in bytes + * + * SOCK_INODE() is valid only for sockfs sockets; sock_from_file() rejects + * anything else (e.g. tun, tap). + * Lockless: simple_xattr_get() looks up the value under RCU, no inode lock. + * + * Return: length of the value on success, a negative errno on error. + */ +int sock_read_xattr(struct socket *sock, const char *name, void *value, size_t size) +{ + struct file *file = sock->file; + struct sockfs_inode *si; + + if (!file || sock_from_file(file) != sock) + return -EOPNOTSUPP; + + si = SOCKFS_I(SOCK_INODE(sock)); + return simple_xattr_get(&sockfs_xa_cache, &si->xattrs, name, value, size); +} + static const struct xattr_handler * const sockfs_xattr_handlers[] = { &sockfs_xattr_handler, &sockfs_security_xattr_handler, -- cgit From ea4e4cc263011910eb7c62f3bb4fa094a1573c61 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 13:58:14 +0200 Subject: block: allow making a block device unfreezable Add bdev_deny_freeze() and bdev_allow_freeze(), modeled on deny_write_access()/allow_write_access(). bd_fsfreeze_count becomes a signed counter: > 0 counts active freezes, < 0 counts deniers, and the two regimes are mutually exclusive. bdev_freeze() refuses with -EBUSY while a deny is held, and bdev_deny_freeze() refuses while the device is frozen. A filesystem that mutates a device's membership (a btrfs device add, remove or replace) denies freezing on the device for the duration, so a claim a freeze walk might act on is never added or torn down behind the freezer's back. The deny/allow helpers are a single atomic on bd_fsfreeze_count and take no lock, so they can be called while holding s_umount without inverting against bdev_freeze()'s bd_fsfreeze_mutex -> s_umount order. Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260616-work-super-freeze_deny_upstream-v2-1-b3567c7f994b@kernel.org Signed-off-by: Christian Brauner (Amutable) --- block/bdev.c | 63 +++++++++++++++++++++++++++++++++++++++-------- include/linux/blk_types.h | 2 +- include/linux/blkdev.h | 2 ++ 3 files changed, 56 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/block/bdev.c b/block/bdev.c index 85ce57bd2ae4..9b73487a91ca 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -304,7 +304,12 @@ int bdev_freeze(struct block_device *bdev) mutex_lock(&bdev->bd_fsfreeze_mutex); - if (atomic_inc_return(&bdev->bd_fsfreeze_count) > 1) { + /* A device being removed from its filesystem refuses freezes. */ + if (!atomic_inc_unless_negative(&bdev->bd_fsfreeze_count)) { + mutex_unlock(&bdev->bd_fsfreeze_mutex); + return -EBUSY; + } + if (atomic_read(&bdev->bd_fsfreeze_count) > 1) { mutex_unlock(&bdev->bd_fsfreeze_mutex); return 0; } @@ -340,18 +345,18 @@ int bdev_thaw(struct block_device *bdev) mutex_lock(&bdev->bd_fsfreeze_mutex); - /* - * If this returns < 0 it means that @bd_fsfreeze_count was - * already 0 and no decrement was performed. - */ - nr_freeze = atomic_dec_if_positive(&bdev->bd_fsfreeze_count); - if (nr_freeze < 0) + /* <= 0: not frozen (0) or a freeze deny is held (< 0); leave it. */ + nr_freeze = atomic_read(&bdev->bd_fsfreeze_count); + if (nr_freeze <= 0) goto out; error = 0; - if (nr_freeze > 0) + if (nr_freeze > 1) { + atomic_dec(&bdev->bd_fsfreeze_count); goto out; + } + /* Keep the count positive across the thaw so a deny is refused. */ mutex_lock(&bdev->bd_holder_lock); if (bdev->bd_holder_ops && bdev->bd_holder_ops->thaw) { error = bdev->bd_holder_ops->thaw(bdev); @@ -360,14 +365,52 @@ int bdev_thaw(struct block_device *bdev) mutex_unlock(&bdev->bd_holder_lock); } - if (error) - atomic_inc(&bdev->bd_fsfreeze_count); + if (!error) + atomic_dec(&bdev->bd_fsfreeze_count); out: mutex_unlock(&bdev->bd_fsfreeze_mutex); return error; } EXPORT_SYMBOL(bdev_thaw); +/** + * bdev_deny_freeze - make a block device unfreezable + * @bdev: block device + * + * Reserve @bdev against bdev_freeze() the way deny_write_access() reserves a + * file against writers. bd_fsfreeze_count is sign-encoded: > 0 counts active + * freezes, < 0 counts deniers, so a deny succeeds only while no freeze is in + * progress. While held, bdev_freeze() returns -EBUSY. Pair with + * bdev_allow_freeze(). + * + * A filesystem removing, adding or replacing a member device denies freezes on + * it for the duration, so a claim a freeze walk might act on is never torn down + * behind the freezer's back. The deny is device-scoped, not (device, + * superblock)-scoped: a device shared by several superblocks is refused for all + * of them. No in-tree filesystem removes a shared claim from a live superblock. + * + * Return: 0, or -EBUSY if the device is currently frozen. + */ +int bdev_deny_freeze(struct block_device *bdev) +{ + return atomic_dec_unless_positive(&bdev->bd_fsfreeze_count) ? 0 : -EBUSY; +} +EXPORT_SYMBOL_GPL(bdev_deny_freeze); + +/** + * bdev_allow_freeze - allow freezing a block device again + * @bdev: block device + * + * Undo one bdev_deny_freeze(). + */ +void bdev_allow_freeze(struct block_device *bdev) +{ + /* A deny must be held, i.e. the count must be negative. */ + WARN_ON_ONCE(atomic_read(&bdev->bd_fsfreeze_count) >= 0); + atomic_inc(&bdev->bd_fsfreeze_count); +} +EXPORT_SYMBOL_GPL(bdev_allow_freeze); + /* * pseudo-fs */ diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 8808ee76e73c..5a725a0cd35f 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -66,7 +66,7 @@ struct block_device { int bd_holders; struct kobject *bd_holder_dir; - atomic_t bd_fsfreeze_count; /* number of freeze requests */ + atomic_t bd_fsfreeze_count; /* >0 freeze requests, <0 freeze deniers */ struct mutex bd_fsfreeze_mutex; /* serialize freeze/thaw */ struct partition_meta_info *bd_meta_info; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9213a5716f95..c419117be083 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1837,6 +1837,8 @@ static inline int early_lookup_bdev(const char *pathname, dev_t *dev) int bdev_freeze(struct block_device *bdev); int bdev_thaw(struct block_device *bdev); +int bdev_deny_freeze(struct block_device *bdev); +void bdev_allow_freeze(struct block_device *bdev); void bdev_fput(struct file *bdev_file); struct io_comp_batch { -- cgit From 822d87bc520fee8d95448c0aa3c728a4c1a595af Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 13:58:15 +0200 Subject: block: split bdev_yield_claim() out of bdev_fput() bdev_fput() yields the holder claim and then closes the file, which is a deferred operation. Split the yield half into bdev_yield_claim() so a caller can give up the holder while the file - and therefore the block device - is still open, act on the device, and only then bdev_fput(). A filesystem that made a device unfreezable for a membership change with bdev_deny_freeze() undoes the deny on release with bdev_yield_claim(bdev_file); bdev_allow_freeze(file_bdev(bdev_file)); bdev_fput(bdev_file); Re-allowing only after the holder is yielded avoids stranding the filesystem on a racing freeze, and doing it while the file is still open avoids touching the block device after bdev_fput(). bdev_fput() yields again, which is a no-op once the claim has already been given up. Link: https://patch.msgid.link/20260616-work-super-freeze_deny_upstream-v2-2-b3567c7f994b@kernel.org Reviewed-by: Jan Kara Reviewd-by: Johannes Thumshirn Signed-off-by: Christian Brauner (Amutable) --- block/bdev.c | 50 ++++++++++++++++++++++++++++++++++---------------- include/linux/blkdev.h | 1 + 2 files changed, 35 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/block/bdev.c b/block/bdev.c index 9b73487a91ca..28b0d40c362f 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -1195,6 +1195,39 @@ put_no_open: blkdev_put_no_open(bdev); } +/** + * bdev_yield_claim - give up the holder claim on an open block device + * @bdev_file: open block device + * + * Yield the holder and any write access for @bdev_file without closing it, so + * the caller can still act on the device - e.g. bdev_allow_freeze() it - before + * the final bdev_fput(). bdev_fput() yields too, so calling it afterwards is + * safe. + */ +void bdev_yield_claim(struct file *bdev_file) +{ + struct block_device *bdev; + struct gendisk *disk; + + if (!bdev_file->private_data) + return; + + bdev = file_bdev(bdev_file); + disk = bdev->bd_disk; + + mutex_lock(&disk->open_mutex); + bdev_yield_write_access(bdev_file); + bd_yield_claim(bdev_file); + /* + * Tell release we already gave up our hold on the + * device and if write restrictions are available that + * we already gave up write access to the device. + */ + bdev_file->private_data = BDEV_I(bdev_file->f_mapping->host); + mutex_unlock(&disk->open_mutex); +} +EXPORT_SYMBOL_GPL(bdev_yield_claim); + /** * bdev_fput - yield claim to the block device and put the file * @bdev_file: open block device @@ -1208,22 +1241,7 @@ void bdev_fput(struct file *bdev_file) if (WARN_ON_ONCE(bdev_file->f_op != &def_blk_fops)) return; - if (bdev_file->private_data) { - struct block_device *bdev = file_bdev(bdev_file); - struct gendisk *disk = bdev->bd_disk; - - mutex_lock(&disk->open_mutex); - bdev_yield_write_access(bdev_file); - bd_yield_claim(bdev_file); - /* - * Tell release we already gave up our hold on the - * device and if write restrictions are available that - * we already gave up write access to the device. - */ - bdev_file->private_data = BDEV_I(bdev_file->f_mapping->host); - mutex_unlock(&disk->open_mutex); - } - + bdev_yield_claim(bdev_file); fput(bdev_file); } EXPORT_SYMBOL(bdev_fput); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index c419117be083..f4e5eca5a91f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1840,6 +1840,7 @@ int bdev_thaw(struct block_device *bdev); int bdev_deny_freeze(struct block_device *bdev); void bdev_allow_freeze(struct block_device *bdev); void bdev_fput(struct file *bdev_file); +void bdev_yield_claim(struct file *bdev_file); struct io_comp_batch { struct rq_list req_list; -- cgit From 3ec9800c2d33c783dd3b27d4cc3bb22b9385f828 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 16:08:18 +0200 Subject: super: convert s_count to refcount_t s_passive The superblock carries two counters: s_active, the active reference count that keeps the filesystem usable, and s_count, the passive reference count that merely keeps the structure itself alive. Turn the passive count into a refcount_t and rename it to s_passive to make the pairing with s_active obvious. Everything is still serialized by sb_lock, so there is no functional change; the conversion buys the usual refcount_t saturation and underflow checking. The following patches start dropping passive references without holding sb_lock and make the device-to-superblock table hold one passive reference per registered entry, which a plain integer cannot support. Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-2-7df6b864028e@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/super.c | 18 +++++++++--------- include/linux/fs/super_types.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index a8fd61136aaf..25dd72b550e0 100644 --- a/fs/super.c +++ b/fs/super.c @@ -102,7 +102,7 @@ static bool super_flags(const struct super_block *sb, unsigned int flags) * creation will succeed and SB_BORN is set by vfs_get_tree() or we're * woken and we'll see SB_DYING. * - * The caller must have acquired a temporary reference on @sb->s_count. + * The caller must have acquired a temporary reference on @sb->s_passive. * * Return: The function returns true if SB_BORN was set and with * s_umount held. The function returns false if SB_DYING was @@ -367,7 +367,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, spin_lock_init(&s->s_inode_wblist_lock); fserror_mount(s); - s->s_count = 1; + refcount_set(&s->s_passive, 1); atomic_set(&s->s_active, 1); mutex_init(&s->s_vfs_rename_mutex); lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key); @@ -407,7 +407,7 @@ fail: */ static void __put_super(struct super_block *s) { - if (!--s->s_count) { + if (refcount_dec_and_test(&s->s_passive)) { list_del_init(&s->s_list); WARN_ON(s->s_dentry_lru.node); WARN_ON(s->s_inode_lru.node); @@ -529,7 +529,7 @@ static bool grab_super(struct super_block *sb) { bool locked; - sb->s_count++; + refcount_inc(&sb->s_passive); spin_unlock(&sb_lock); locked = super_lock_excl(sb); if (locked) { @@ -556,7 +556,7 @@ static bool grab_super(struct super_block *sb) * lock held in read mode in case of success. On successful return, * the caller must drop the s_umount lock when done. * - * Note that unlike get_super() et.al. this one does *not* bump ->s_count. + * Note that unlike get_super() et.al. this one does *not* bump ->s_passive. * The reason why it's safe is that we are OK with doing trylock instead * of down_read(). There's a couple of places that are OK with that, but * it's very much not a general-purpose interface. @@ -858,7 +858,7 @@ static void __iterate_supers(void (*f)(struct super_block *, void *), void *arg, sb = next_super(sb, flags)) { if (super_flags(sb, SB_DYING)) continue; - sb->s_count++; + refcount_inc(&sb->s_passive); spin_unlock(&sb_lock); if (flags & SUPER_ITER_UNLOCKED) { @@ -903,7 +903,7 @@ void iterate_supers_type(struct file_system_type *type, if (super_flags(sb, SB_DYING)) continue; - sb->s_count++; + refcount_inc(&sb->s_passive); spin_unlock(&sb_lock); locked = super_lock_shared(sb); @@ -935,7 +935,7 @@ struct super_block *user_get_super(dev_t dev, bool excl) if (sb->s_dev != dev) continue; - sb->s_count++; + refcount_inc(&sb->s_passive); spin_unlock(&sb_lock); locked = super_lock(sb, excl); @@ -1369,7 +1369,7 @@ static struct super_block *bdev_super_lock(struct block_device *bdev, bool excl) /* Make sure sb doesn't go away from under us */ spin_lock(&sb_lock); - sb->s_count++; + refcount_inc(&sb->s_passive); spin_unlock(&sb_lock); mutex_unlock(&bdev->bd_holder_lock); diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h index ef7941e9dc79..68747182abf9 100644 --- a/include/linux/fs/super_types.h +++ b/include/linux/fs/super_types.h @@ -145,7 +145,7 @@ struct super_block { unsigned long s_magic; struct dentry *s_root; struct rw_semaphore s_umount; - int s_count; + refcount_t s_passive; atomic_t s_active; #ifdef CONFIG_SECURITY void *s_security; -- cgit From abc410fc6d8ff4af5b37038c2db5e3b451dd2244 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 16:08:20 +0200 Subject: fs, block: move blk_mode_t and fop_flags_t into blk_mode_t and fop_flags_t are both plain 'unsigned int __bitwise' flag typedefs, exactly like the gfp_t, slab_flags_t and fmode_t that already live in . Move them there so they are available everywhere without having to drag in a subsystem header. Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-4-7df6b864028e@kernel.org Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Christian Brauner (Amutable) --- include/linux/blkdev.h | 2 -- include/linux/fs.h | 2 -- include/linux/types.h | 2 ++ 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f4e5eca5a91f..9e395d95067e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -126,8 +126,6 @@ struct blk_integrity { unsigned char pi_tuple_size; }; -typedef unsigned int __bitwise blk_mode_t; - /* open for reading */ #define BLK_OPEN_READ ((__force blk_mode_t)(1 << 0)) /* open for writing */ diff --git a/include/linux/fs.h b/include/linux/fs.h index d10897b3a1e3..33d7dffda752 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1916,8 +1916,6 @@ struct dir_context { struct io_uring_cmd; struct offset_ctx; -typedef unsigned int __bitwise fop_flags_t; - struct file_operations { struct module *owner; fop_flags_t fop_flags; diff --git a/include/linux/types.h b/include/linux/types.h index 93166b0b0617..bc5dda2a3d86 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -163,6 +163,8 @@ typedef u32 dma_addr_t; typedef unsigned int __bitwise gfp_t; typedef unsigned int __bitwise slab_flags_t; typedef unsigned int __bitwise fmode_t; +typedef unsigned int __bitwise blk_mode_t; +typedef unsigned int __bitwise fop_flags_t; #ifdef CONFIG_PHYS_ADDR_T_64BIT typedef u64 phys_addr_t; -- cgit From 9ee5f161a4dbad4bf388fe25321eb14c253eb248 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 16:08:23 +0200 Subject: fs: maintain a global device-to-superblock table fs_holder_ops recovers the owning superblock from bdev->bd_holder, which forces the holder to be exactly one superblock and prevents several superblocks from sharing one block device. That's what erofs is doing. As a first step introduce a global dev_t-keyed rhltable mapping each device to the superblock(s) using it. The entry is preallocated in alloc_super() and registered under sb->s_dev by the set callback through set_anon_super() and set_bdev_super(), the two helpers every set callback assigns s_dev through. Registration is the final fallible act of a set callback, so an insert failure unwinds through sget_fc()'s existing set-failure path: the fs_context keeps ownership of s_fs_info and the callers' error paths stay correct. set_anon_super() releases the anonymous dev it allocated when registration fails. Unwinding through deactivate_locked_super() instead would run kill_sb() and free s_fs_info behind the caller's back: nfs and ceph free that object through a local pointer when sget_fc() fails and would double-free. The superblock stashes the entry in sb->s_super_dev and kill_super_notify() drops the claim through it, so teardown doesn't depend on s_dev staying stable; an entry that was never registered is freed together with the superblock in destroy_super_work(). Each table entry holds a passive reference (s_passive) on its superblock, so the struct stays valid for as long as the entry is reachable. Entries are claim-counted through sd_ref: additional claims on the same (device, superblock) pair share the entry, and the unlink is deferred to the last put, so a later iteration cursor never resumes from a removed node. The table is initialized from mnt_init(): the first superblocks (the tmpfs shm mount and rootfs) are created from start_kernel() long before any initcall runs, so an initcall would be too late. The table has no readers yet; the fs_holder_ops callbacks are switched over once all devices a filesystem claims are registered. Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-7-7df6b864028e@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/internal.h | 1 + fs/namespace.c | 2 + fs/super.c | 102 ++++++++++++++++++++++++++++++++++++++++- include/linux/fs/super_types.h | 2 + 4 files changed, 105 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/internal.h b/fs/internal.h index 355d93f92208..174f06357555 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -137,6 +137,7 @@ extern int reconfigure_super(struct fs_context *); extern bool super_trylock_shared(struct super_block *sb); struct super_block *user_get_super(dev_t, bool excl); void put_super(struct super_block *sb); +void __init super_dev_init(void); extern bool mount_capable(struct fs_context *); /* diff --git a/fs/namespace.c b/fs/namespace.c index 3d5cd5bf3b05..7cef6dae0854 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -6262,6 +6262,8 @@ void __init mnt_init(void) if (!mount_hashtable || !mountpoint_hashtable) panic("Failed to allocate mount hash table\n"); + super_dev_init(); + kernfs_init(); err = sysfs_init(); diff --git a/fs/super.c b/fs/super.c index a771a0ad4c9a..ff5e305d0ab4 100644 --- a/fs/super.c +++ b/fs/super.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include /* for the emergency remount stuff */ @@ -272,6 +273,8 @@ static unsigned long super_cache_count(struct shrinker *shrink, return total_objects; } +static struct super_dev *super_dev_alloc(dev_t dev, struct super_block *sb); + static void destroy_super_work(struct work_struct *work) { struct super_block *s = container_of(work, struct super_block, @@ -279,6 +282,8 @@ static void destroy_super_work(struct work_struct *work) fsnotify_sb_free(s); security_sb_free(s); put_user_ns(s->s_user_ns); + /* Only an unregistered entry is still owned by the superblock. */ + kfree(s->s_super_dev); kfree(s->s_subtype); for (int i = 0; i < SB_FREEZE_LEVELS; i++) percpu_free_rwsem(&s->s_writers.rw_sem[i]); @@ -392,6 +397,10 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, goto fail; if (list_lru_init_memcg(&s->s_inode_lru, s->s_shrink)) goto fail; + s->s_super_dev = super_dev_alloc(0, s); + if (!s->s_super_dev) + goto fail; + s->s_min_writeback_pages = MIN_WRITEBACK_PAGES; return s; @@ -421,6 +430,77 @@ void put_super(struct super_block *s) } } +struct super_dev { + dev_t sd_dev; + struct super_block *sd_sb; + refcount_t sd_ref; + struct rhlist_head sd_node; + struct rcu_head sd_rcu; +}; + +static struct rhltable super_dev_table; +static const struct rhashtable_params super_dev_params = { + .key_len = sizeof(dev_t), + .key_offset = offsetof(struct super_dev, sd_dev), + .head_offset = offsetof(struct super_dev, sd_node), +}; + +static struct super_dev *super_dev_alloc(dev_t dev, struct super_block *sb) +{ + struct super_dev *fsd; + + fsd = kzalloc_obj(*fsd); + if (!fsd) + return NULL; + fsd->sd_dev = dev; + fsd->sd_sb = sb; + refcount_set(&fsd->sd_ref, 1); + return fsd; +} + +static void super_dev_put(struct super_dev *fsd) +{ + /* Unlink only once unpinned, so a cursor never resumes from a removed node. */ + if (fsd && refcount_dec_and_test(&fsd->sd_ref)) { + rhltable_remove(&super_dev_table, &fsd->sd_node, super_dev_params); + put_super(fsd->sd_sb); + kfree_rcu(fsd, sd_rcu); + } +} + +void __init super_dev_init(void) +{ + if (rhltable_init(&super_dev_table, &super_dev_params)) + panic("VFS: Cannot initialise super_dev_table\n"); +} + +static int super_dev_insert(struct super_dev *fsd) +{ + int err; + + err = rhltable_insert(&super_dev_table, &fsd->sd_node, super_dev_params); + if (!err) + refcount_inc(&fsd->sd_sb->s_passive); + return err; +} + +/* Register @sb under @sb->s_dev as the final fallible act of a set callback. */ +static int super_dev_register(struct super_block *sb) +{ + struct super_dev *fsd = sb->s_super_dev; + int err; + + lockdep_assert_held(&sb_lock); + VFS_WARN_ON_ONCE(!sb->s_dev); + VFS_WARN_ON_ONCE(!fsd || fsd->sd_dev); + + fsd->sd_dev = sb->s_dev; + err = super_dev_insert(fsd); + if (err) + fsd->sd_dev = 0; + return err; +} + static void kill_super_notify(struct super_block *sb) { lockdep_assert_not_held(&sb->s_umount); @@ -440,6 +520,12 @@ static void kill_super_notify(struct super_block *sb) hlist_del_init(&sb->s_instances); spin_unlock(&sb_lock); + /* Drop sget_fc()'s claim; a never-registered entry stays with the sb. */ + if (sb->s_super_dev->sd_dev) { + super_dev_put(sb->s_super_dev); + sb->s_super_dev = NULL; + } + /* * Let concurrent mounts know that this thing is really dead. * We don't need @sb->s_umount here as every concurrent caller @@ -750,6 +836,7 @@ retry: } if (!s) { spin_unlock(&sb_lock); + s = alloc_super(fc->fs_type, fc->sb_flags, user_ns); if (!s) return ERR_PTR(-ENOMEM); @@ -759,11 +846,13 @@ retry: s->s_fs_info = fc->s_fs_info; err = set(s, fc); if (err) { + VFS_WARN_ON_ONCE(s->s_super_dev->sd_dev); s->s_fs_info = NULL; spin_unlock(&sb_lock); destroy_unused_super(s); return ERR_PTR(err); } + VFS_WARN_ON_ONCE(!s->s_super_dev->sd_dev); fc->s_fs_info = NULL; s->s_type = fc->fs_type; s->s_iflags |= fc->s_iflags; @@ -1217,7 +1306,16 @@ EXPORT_SYMBOL(free_anon_bdev); int set_anon_super(struct super_block *s, void *data) { - return get_anon_bdev(&s->s_dev); + int error; + + error = get_anon_bdev(&s->s_dev); + if (error) + return error; + + error = super_dev_register(s); + if (error) + free_anon_bdev(s->s_dev); + return error; } EXPORT_SYMBOL(set_anon_super); @@ -1303,7 +1401,7 @@ EXPORT_SYMBOL(get_tree_keyed); static int set_bdev_super(struct super_block *s, void *data) { s->s_dev = *(dev_t *)data; - return 0; + return super_dev_register(s); } static int super_s_dev_set(struct super_block *s, struct fs_context *fc) diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h index 68747182abf9..c8172558750f 100644 --- a/include/linux/fs/super_types.h +++ b/include/linux/fs/super_types.h @@ -30,6 +30,7 @@ struct mount; struct mtd_info; struct quotactl_ops; struct shrinker; +struct super_dev; struct unicode_map; struct user_namespace; struct workqueue_struct; @@ -132,6 +133,7 @@ struct super_operations { struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ + struct super_dev *s_super_dev; /* sget_fc()'s device table claim */ unsigned char s_blocksize_bits; unsigned long s_blocksize; loff_t s_maxbytes; /* Max file size */ -- cgit From 875c4965a77b34214cf43a68e10c4ae179575814 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 16:08:24 +0200 Subject: fs: add dedicated block device open helpers for filesystems Add fs_bdev_file_open_by_{dev,path}() and fs_bdev_file_release(). They open the device with fs_holder_ops and register a claim in the device-to-superblock table. Claims on the same (device, superblock) pair share one entry, so when a filesystem claims a device it already uses (xfs with its log on the data device), no second entry is added and each superblock will be acted on once. The holder argument remains purely the block layer's exclusivity token: a superblock, or a file_system_type for a device shared by several superblocks of that type. The shared case only becomes usable once the fs_holder_ops callbacks resolve superblocks through the table instead of bdev->bd_holder. Convert the main device, setup_bdev_super() and kill_block_super(), over: the open finds the entry registered by sget_fc() and claims it again. cramfs and romfs bypass kill_block_super() so they can handle MTD mounts and release the main device with a plain bdev_fput(), which would leave the claim behind: the (dev, sb) entry would never be unregistered and the passive reference it holds would keep the superblock alive forever. Convert their release paths in the same step. The frozen-device check stays in setup_bdev_super() for the primary device and is added to fs_bdev_register() for new claims, i.e. every additional device a filesystem opens through the helpers. Only a (device, superblock) pair the superblock claimed earlier may be reopened while frozen (xfs with its log on the data device): the freeze already covers that superblock through the existing claim, so nothing escapes it. Without the setup_bdev_super() check a device frozen before the mount even started (dm lock_fs, loop) could be mounted and written to (journal replay) under an active freeze, because the primary open reuses the entry registered by sget_fc() and never takes the new-claim path. Both checks read bd_fsfreeze_count only after the entry is published (by sget_fc() for the primary, by fs_bdev_register() for new claims) and pair with bdev_freeze() incrementing the count before walking the table: either the mount sees the elevated freeze count and fails with EBUSY, or the freeze finds the published entry and converges once SB_BORN is set. Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-8-7df6b864028e@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/cramfs/inode.c | 2 +- fs/romfs/super.c | 2 +- fs/super.c | 154 ++++++++++++++++++++++++++++++++++++++++++++--- include/linux/fs/super.h | 7 +++ 4 files changed, 155 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 4edbfccd0bbe..d4cd03f4f60d 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -504,7 +504,7 @@ static void cramfs_kill_sb(struct super_block *sb) sb->s_mtd = NULL; } else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV) && sb->s_bdev) { sync_blockdev(sb->s_bdev); - bdev_fput(sb->s_bdev_file); + fs_bdev_file_release(sb->s_bdev_file, sb); } kfree(sbi); } diff --git a/fs/romfs/super.c b/fs/romfs/super.c index ac55193bf398..43eb897197c0 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -587,7 +587,7 @@ static void romfs_kill_sb(struct super_block *sb) #ifdef CONFIG_ROMFS_ON_BLOCK if (sb->s_bdev) { sync_blockdev(sb->s_bdev); - bdev_fput(sb->s_bdev_file); + fs_bdev_file_release(sb->s_bdev_file, sb); } #endif } diff --git a/fs/super.c b/fs/super.c index ff5e305d0ab4..3d166c7f578a 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1633,6 +1633,145 @@ const struct blk_holder_ops fs_holder_ops = { }; EXPORT_SYMBOL_GPL(fs_holder_ops); +static struct super_dev *super_dev_lookup(dev_t dev, struct super_block *sb) +{ + struct super_dev *it; + struct rhlist_head *list, *pos; + + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "suspicious super_dev_lookup() usage"); + VFS_WARN_ON_ONCE(!dev); + VFS_WARN_ON_ONCE(!sb); + + list = rhltable_lookup(&super_dev_table, &dev, super_dev_params); + rhl_for_each_entry_rcu(it, pos, list, sd_node) { + if (it->sd_sb == sb) + return it; + } + + return NULL; +} + +static int fs_bdev_register(struct file *bdev_file, struct super_block *sb) +{ + struct super_dev *sb_dev __free(kfree) = NULL; + dev_t dev = file_bdev(bdev_file)->bd_dev; + int err; + + scoped_guard(rcu) { + sb_dev = super_dev_lookup(dev, sb); + if (sb_dev && refcount_inc_not_zero(&sb_dev->sd_ref)) { + retain_and_null_ptr(sb_dev); + return 0; + } + } + + sb_dev = super_dev_alloc(dev, sb); + if (!sb_dev) + return -ENOMEM; + + err = super_dev_insert(sb_dev); + if (err) + return err; + + /* Publish the entry before reading the count; pairs with bdev_freeze(). */ + smp_mb(); + if (atomic_read(&file_bdev(bdev_file)->bd_fsfreeze_count) > 0) { + err = -EBUSY; + super_dev_put(sb_dev); + } + + retain_and_null_ptr(sb_dev); + return err; +} + +/** + * fs_bdev_file_open_by_dev - claim a block device on behalf of a superblock + * @dev: block device number + * @mode: open mode + * @holder: block-layer exclusivity token (a superblock, or the file_system_type + * when the device may be shared by several superblocks of that type) + * @sb: superblock to drive fs_holder_ops events for + * + * Open @dev with &fs_holder_ops and register that @sb uses it, so device + * removal/sync/freeze/thaw are propagated to @sb (and any other superblock + * sharing @dev). Must be paired with fs_bdev_file_release(). + * + * Return: an opened block-device file or an ERR_PTR(). + */ +struct file *fs_bdev_file_open_by_dev(dev_t dev, blk_mode_t mode, void *holder, + struct super_block *sb) +{ + struct file *bdev_file; + int err; + + bdev_file = bdev_file_open_by_dev(dev, mode, holder, &fs_holder_ops); + if (IS_ERR(bdev_file)) + return bdev_file; + + err = fs_bdev_register(bdev_file, sb); + if (err) { + bdev_fput(bdev_file); + return ERR_PTR(err); + } + return bdev_file; +} +EXPORT_SYMBOL_GPL(fs_bdev_file_open_by_dev); + +/** + * fs_bdev_file_open_by_path - claim a block device on behalf of a superblock + * @path: path to the block device + * @mode: open mode + * @holder: block-layer exclusivity token (a superblock, or the file_system_type + * when the device may be shared by several superblocks of that type) + * @sb: superblock to drive fs_holder_ops events for + * + * Open the block device at @path with &fs_holder_ops and register that @sb + * uses it, so device removal/sync/freeze/thaw are propagated to @sb (and any + * other superblock sharing the device). Must be paired with + * fs_bdev_file_release(). + * + * Return: an opened block-device file or an ERR_PTR(). + */ +struct file *fs_bdev_file_open_by_path(const char *path, blk_mode_t mode, + void *holder, struct super_block *sb) +{ + struct file *bdev_file; + int err; + + bdev_file = bdev_file_open_by_path(path, mode, holder, &fs_holder_ops); + if (IS_ERR(bdev_file)) + return bdev_file; + + err = fs_bdev_register(bdev_file, sb); + if (err) { + bdev_fput(bdev_file); + return ERR_PTR(err); + } + return bdev_file; +} +EXPORT_SYMBOL_GPL(fs_bdev_file_open_by_path); + +/** + * fs_bdev_file_release - release a block device claimed for a superblock + * @bdev_file: file returned by fs_bdev_file_open_by_{dev,path}() + * @sb: superblock the device was claimed for + * + * Drop one claim on the {dev, @sb} entry; the last claim unregisters it (a + * pinning cursor defers the actual unlink). Then close the block device. + */ +void fs_bdev_file_release(struct file *bdev_file, struct super_block *sb) +{ + dev_t dev = file_bdev(bdev_file)->bd_dev; + struct super_dev *sb_dev; + + rcu_read_lock(); + sb_dev = super_dev_lookup(dev, sb); + rcu_read_unlock(); + super_dev_put(sb_dev); + bdev_fput(bdev_file); +} +EXPORT_SYMBOL_GPL(fs_bdev_file_release); + int setup_bdev_super(struct super_block *sb, int sb_flags, struct fs_context *fc) { @@ -1640,7 +1779,7 @@ int setup_bdev_super(struct super_block *sb, int sb_flags, struct file *bdev_file; struct block_device *bdev; - bdev_file = bdev_file_open_by_dev(sb->s_dev, mode, sb, &fs_holder_ops); + bdev_file = fs_bdev_file_open_by_dev(sb->s_dev, mode, sb, sb); if (IS_ERR(bdev_file)) { if (fc) errorf(fc, "%s: Can't open blockdev", fc->source); @@ -1654,20 +1793,19 @@ int setup_bdev_super(struct super_block *sb, int sb_flags, * writable from userspace even for a read-only block device. */ if ((mode & BLK_OPEN_WRITE) && bdev_read_only(bdev)) { - bdev_fput(bdev_file); + fs_bdev_file_release(bdev_file, sb); return -EACCES; } - /* - * It is enough to check bdev was not frozen before we set - * s_bdev as freezing will wait until SB_BORN is set. - */ + /* The sget_fc() entry is already published; pairs with bdev_freeze(). */ + smp_mb(); if (atomic_read(&bdev->bd_fsfreeze_count) > 0) { if (fc) warnf(fc, "%pg: Can't mount, blockdev is frozen", bdev); - bdev_fput(bdev_file); + fs_bdev_file_release(bdev_file, sb); return -EBUSY; } + spin_lock(&sb_lock); sb->s_bdev_file = bdev_file; sb->s_bdev = bdev; @@ -1756,7 +1894,7 @@ void kill_block_super(struct super_block *sb) generic_shutdown_super(sb); if (bdev) { sync_blockdev(bdev); - bdev_fput(sb->s_bdev_file); + fs_bdev_file_release(sb->s_bdev_file, sb); } } diff --git a/include/linux/fs/super.h b/include/linux/fs/super.h index 405612678115..caf358483144 100644 --- a/include/linux/fs/super.h +++ b/include/linux/fs/super.h @@ -237,4 +237,11 @@ int thaw_super(struct super_block *super, enum freeze_holder who, int sb_init_dio_done_wq(struct super_block *sb); +struct file; +struct file *fs_bdev_file_open_by_dev(dev_t dev, blk_mode_t mode, void *holder, + struct super_block *sb); +struct file *fs_bdev_file_open_by_path(const char *path, blk_mode_t mode, + void *holder, struct super_block *sb); +void fs_bdev_file_release(struct file *bdev_file, struct super_block *sb); + #endif /* _LINUX_FS_SUPER_H */ -- cgit From cdb5146f8d5f938ec624d78d8ff001f1a60c17cf Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 16:08:28 +0200 Subject: fs: look up superblocks via the device table in fs_holder_ops Switch the fs_holder_ops callbacks from recovering the single owning superblock out of bdev->bd_holder to walking the device-to-superblock table and acting on every superblock registered for the device. The holder argument becomes purely the block layer's exclusivity token and is no longer needed by the fs specific callbacks. All devices opened with fs_holder_ops are registered by now: the main device since setup_bdev_super() switched to fs_bdev_file_open_by_dev() and the extra devices (xfs log and realtime devices, btrfs member devices, the ext4 external journal) since the preceding per-filesystem conversions. So no event is lost in the switchover. The walk uses a refcount-pinning cursor: each step takes a reference on the entry via sd_ref and resumes from its sd_node. Unlinking an entry is deferred to the last unpin, so a cursor never resumes from a removed node. mark_dead and sync only need the passive reference the entry holds plus s_umount, which they take with super_lock_shared(). freeze and thaw additionally need an active reference and acquire it with get_active_super(), which waits for the superblock to be born before taking s_active. Taking s_active before the superblock is born would pin a still-mounting superblock so a racing mount that aborts could never drop s_active to zero and reach SB_DYING, deadlocking the wait for SB_BORN. This is how filesystems_freeze() and filesystems_thaw() acquire it too. One semantic change: when no live superblock uses the device anymore (the holder is dying or was never registered), fs_bdev_freeze() and fs_bdev_thaw() now return 0 - freeze after syncing the block device - where they used to return -EINVAL. The freeze-deny release path moves to the table in the same switchover. A device made unfreezable for a btrfs membership change must drop its table entry before re-allowing freezing; otherwise a freeze racing the release reaches the superblock through the still-registered entry and is stranded once the release unlinks it. Split fs_bdev_unregister() out of fs_bdev_file_release() - the inverse of fs_bdev_register() - so btrfs_release_device_allow_freeze() can drop the {dev, sb} entry, re-allow freezing on the still-open device, then close it. Re-allowing only after the entry is gone keeps a racing freeze from reaching the superblock, and doing it while the file is still open avoids touching the block device after the close. btrfs previously yielded bd_holder before re-allowing, which this commit makes irrelevant to freeze resolution. Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-12-7df6b864028e@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/btrfs/volumes.c | 6 +- fs/super.c | 269 +++++++++++++++++++++++------------------------ include/linux/fs/super.h | 1 + 3 files changed, 138 insertions(+), 138 deletions(-) (limited to 'include') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 02abbfce5ea3..d827d83722c1 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1137,10 +1137,10 @@ void btrfs_release_device_allow_freeze(struct file *bdev_file) { struct super_block *sb = bdev_file->private_data; - /* Yield before allow (strand-safe); file still open for the allow (UAF-safe). */ - bdev_yield_claim(bdev_file); + /* Unregister before re-allowing (strand-safe); file still open (UAF-safe). */ + fs_bdev_unregister(bdev_file, sb); bdev_allow_freeze(file_bdev(bdev_file)); - fs_bdev_file_release(bdev_file, sb); + bdev_fput(bdev_file); } static void btrfs_close_bdev(struct btrfs_device *device, bool allow_freeze) diff --git a/fs/super.c b/fs/super.c index 3d166c7f578a..236e868209a4 100644 --- a/fs/super.c +++ b/fs/super.c @@ -501,6 +501,42 @@ static int super_dev_register(struct super_block *sb) return err; } +#ifdef CONFIG_BLOCK +static struct super_dev *super_dev_get(struct rhlist_head *pos) +{ + struct super_dev *sb_dev; + + for (; pos; pos = rcu_dereference_all(pos->next)) { + sb_dev = container_of(pos, struct super_dev, sd_node); + if (refcount_inc_not_zero(&sb_dev->sd_ref)) + return sb_dev; + } + return NULL; +} + +static struct super_dev *super_dev_first(dev_t dev) +{ + struct super_dev *sb_dev; + + rcu_read_lock(); + sb_dev = super_dev_get(rhltable_lookup(&super_dev_table, &dev, super_dev_params)); + rcu_read_unlock(); + return sb_dev; +} + +static struct super_dev *super_dev_next(struct super_dev *prev) +{ + struct super_dev *sb_dev; + + rcu_read_lock(); + sb_dev = super_dev_get(rcu_dereference_all(prev->sd_node.next)); + rcu_read_unlock(); + + super_dev_put(prev); + return sb_dev; +} +#endif + static void kill_super_notify(struct super_block *sb) { lockdep_assert_not_held(&sb->s_umount); @@ -1443,185 +1479,131 @@ struct super_block *sget_dev(struct fs_context *fc, dev_t dev) EXPORT_SYMBOL(sget_dev); #ifdef CONFIG_BLOCK -/* - * Lock the superblock that is holder of the bdev. Returns the superblock - * pointer if we successfully locked the superblock and it is alive. Otherwise - * we return NULL and just unlock bdev->bd_holder_lock. - * - * The function must be called with bdev->bd_holder_lock and releases it. - */ -static struct super_block *bdev_super_lock(struct block_device *bdev, bool excl) - __releases(&bdev->bd_holder_lock) +static int fs_super_freeze(struct super_block *sb) { - struct super_block *sb = bdev->bd_holder; - bool locked; - - lockdep_assert_held(&bdev->bd_holder_lock); - lockdep_assert_not_held(&sb->s_umount); - lockdep_assert_not_held(&bdev->bd_disk->open_mutex); - - /* Make sure sb doesn't go away from under us */ - refcount_inc(&sb->s_passive); - - mutex_unlock(&bdev->bd_holder_lock); - - locked = super_lock(sb, excl); - - /* - * If the superblock wasn't already SB_DYING then we hold - * s_umount and can safely drop our temporary reference. - */ - put_super(sb); - - if (!locked) - return NULL; - - if (!sb->s_root || !(sb->s_flags & SB_ACTIVE)) { - super_unlock(sb, excl); - return NULL; - } + if (sb->s_op->freeze_super) + return sb->s_op->freeze_super(sb, + FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); + return freeze_super(sb, FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); +} - return sb; +static int fs_super_thaw(struct super_block *sb) +{ + if (sb->s_op->thaw_super) + return sb->s_op->thaw_super(sb, + FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); + return thaw_super(sb, FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); } static void fs_bdev_mark_dead(struct block_device *bdev, bool surprise) { - struct super_block *sb; + struct super_dev *sb_dev; + dev_t dev = bdev->bd_dev; - sb = bdev_super_lock(bdev, false); - if (!sb) - return; + mutex_unlock(&bdev->bd_holder_lock); - if (sb->s_op->remove_bdev) { - int ret; + for (sb_dev = super_dev_first(dev); sb_dev; sb_dev = super_dev_next(sb_dev)) { + struct super_block *sb = sb_dev->sd_sb; - ret = sb->s_op->remove_bdev(sb, bdev); - if (!ret) { - super_unlock_shared(sb); - return; + if (!super_lock_shared(sb)) + continue; + if (sb->s_root && (sb->s_flags & SB_ACTIVE)) { + if (!sb->s_op->remove_bdev || + sb->s_op->remove_bdev(sb, bdev)) { + if (!surprise) + sync_filesystem(sb); + shrink_dcache_sb(sb); + evict_inodes(sb); + if (sb->s_op->shutdown) + sb->s_op->shutdown(sb); + } } - /* Fallback to shutdown. */ + super_unlock_shared(sb); } - - if (!surprise) - sync_filesystem(sb); - shrink_dcache_sb(sb); - evict_inodes(sb); - if (sb->s_op->shutdown) - sb->s_op->shutdown(sb); - - super_unlock_shared(sb); } static void fs_bdev_sync(struct block_device *bdev) { - struct super_block *sb; - - sb = bdev_super_lock(bdev, false); - if (!sb) - return; + struct super_dev *sb_dev; + dev_t dev = bdev->bd_dev; - sync_filesystem(sb); - super_unlock_shared(sb); -} + mutex_unlock(&bdev->bd_holder_lock); -static struct super_block *get_bdev_super(struct block_device *bdev) -{ - bool active = false; - struct super_block *sb; + for (sb_dev = super_dev_first(dev); sb_dev; sb_dev = super_dev_next(sb_dev)) { + struct super_block *sb = sb_dev->sd_sb; - sb = bdev_super_lock(bdev, true); - if (sb) { - active = atomic_inc_not_zero(&sb->s_active); - super_unlock_excl(sb); + if (!super_lock_shared(sb)) + continue; + if (sb->s_root && (sb->s_flags & SB_ACTIVE)) + sync_filesystem(sb); + super_unlock_shared(sb); } - if (!active) - return NULL; - return sb; } /** - * fs_bdev_freeze - freeze owning filesystem of block device + * fs_bdev_freeze - freeze every superblock using a block device * @bdev: block device * - * Freeze the filesystem that owns this block device if it is still - * active. - * - * A filesystem that owns multiple block devices may be frozen from each - * block device and won't be unfrozen until all block devices are - * unfrozen. Each block device can only freeze the filesystem once as we - * nest freezes for block devices in the block layer. + * Freeze each live superblock using @bdev. A superblock owning several block + * devices is frozen once per device and stays frozen until all are thawed; the + * block layer nests these freezes so the count stays balanced. * - * Return: If the freeze was successful zero is returned. If the freeze - * failed a negative error code is returned. + * Return: 0, or the first error from freezing a superblock or syncing the + * block device. */ static int fs_bdev_freeze(struct block_device *bdev) { - struct super_block *sb; - int error = 0; + dev_t dev = bdev->bd_dev; + struct super_dev *sb_dev; + int error = 0, err; lockdep_assert_held(&bdev->bd_fsfreeze_mutex); - sb = get_bdev_super(bdev); - if (!sb) - return -EINVAL; + mutex_unlock(&bdev->bd_holder_lock); + + for (sb_dev = super_dev_first(dev); sb_dev; sb_dev = super_dev_next(sb_dev)) { + if (!get_active_super(sb_dev->sd_sb)) + continue; + err = fs_super_freeze(sb_dev->sd_sb); + if (err && !error) + error = err; + deactivate_super(sb_dev->sd_sb); + } - if (sb->s_op->freeze_super) - error = sb->s_op->freeze_super(sb, - FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); - else - error = freeze_super(sb, - FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); if (!error) error = sync_blockdev(bdev); - deactivate_super(sb); return error; } /** - * fs_bdev_thaw - thaw owning filesystem of block device + * fs_bdev_thaw - thaw every superblock using a block device * @bdev: block device * - * Thaw the filesystem that owns this block device. + * The counterpart to fs_bdev_freeze(): thaw each live superblock using @bdev. + * A zero return does not imply a superblock is fully unfrozen; it may have been + * frozen more than once (by the kernel or via another device). * - * A filesystem that owns multiple block devices may be frozen from each - * block device and won't be unfrozen until all block devices are - * unfrozen. Each block device can only freeze the filesystem once as we - * nest freezes for block devices in the block layer. - * - * Return: If the thaw was successful zero is returned. If the thaw - * failed a negative error code is returned. If this function - * returns zero it doesn't mean that the filesystem is unfrozen - * as it may have been frozen multiple times (kernel may hold a - * freeze or might be frozen from other block devices). + * Return: 0, or the first error from thawing a superblock. */ static int fs_bdev_thaw(struct block_device *bdev) { - struct super_block *sb; - int error; + dev_t dev = bdev->bd_dev; + struct super_dev *sb_dev; + int error = 0, err; lockdep_assert_held(&bdev->bd_fsfreeze_mutex); - /* - * The block device may have been frozen before it was claimed by a - * filesystem. Concurrently another process might try to mount that - * frozen block device and has temporarily claimed the block device for - * that purpose causing a concurrent fs_bdev_thaw() to end up here. The - * mounter is already about to abort mounting because they still saw an - * elevanted bdev->bd_fsfreeze_count so get_bdev_super() will return - * NULL in that case. - */ - sb = get_bdev_super(bdev); - if (!sb) - return -EINVAL; + mutex_unlock(&bdev->bd_holder_lock); + + for (sb_dev = super_dev_first(dev); sb_dev; sb_dev = super_dev_next(sb_dev)) { + if (!get_active_super(sb_dev->sd_sb)) + continue; + err = fs_super_thaw(sb_dev->sd_sb); + if (err && !error) + error = err; + deactivate_super(sb_dev->sd_sb); + } - if (sb->s_op->thaw_super) - error = sb->s_op->thaw_super(sb, - FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); - else - error = thaw_super(sb, - FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE, NULL); - deactivate_super(sb); return error; } @@ -1752,14 +1734,18 @@ struct file *fs_bdev_file_open_by_path(const char *path, blk_mode_t mode, EXPORT_SYMBOL_GPL(fs_bdev_file_open_by_path); /** - * fs_bdev_file_release - release a block device claimed for a superblock + * fs_bdev_unregister - drop a superblock's claim on a block device * @bdev_file: file returned by fs_bdev_file_open_by_{dev,path}() * @sb: superblock the device was claimed for * - * Drop one claim on the {dev, @sb} entry; the last claim unregisters it (a - * pinning cursor defers the actual unlink). Then close the block device. + * The inverse of fs_bdev_register(): drop one claim on the {dev, @sb} entry + * (the last claim unregisters it; a pinning cursor defers the actual unlink) + * without closing the device. A caller that must act on the still-open device + * between unregistering and closing - e.g. re-allow freezing one denied for a + * membership change - pairs this with bdev_fput(). fs_bdev_file_release() is + * the common unregister-and-close. */ -void fs_bdev_file_release(struct file *bdev_file, struct super_block *sb) +void fs_bdev_unregister(struct file *bdev_file, struct super_block *sb) { dev_t dev = file_bdev(bdev_file)->bd_dev; struct super_dev *sb_dev; @@ -1768,6 +1754,19 @@ void fs_bdev_file_release(struct file *bdev_file, struct super_block *sb) sb_dev = super_dev_lookup(dev, sb); rcu_read_unlock(); super_dev_put(sb_dev); +} +EXPORT_SYMBOL_GPL(fs_bdev_unregister); + +/** + * fs_bdev_file_release - release a block device claimed for a superblock + * @bdev_file: file returned by fs_bdev_file_open_by_{dev,path}() + * @sb: superblock the device was claimed for + * + * Unregister the {dev, @sb} entry, then close the block device. + */ +void fs_bdev_file_release(struct file *bdev_file, struct super_block *sb) +{ + fs_bdev_unregister(bdev_file, sb); bdev_fput(bdev_file); } EXPORT_SYMBOL_GPL(fs_bdev_file_release); diff --git a/include/linux/fs/super.h b/include/linux/fs/super.h index caf358483144..733d439f01ed 100644 --- a/include/linux/fs/super.h +++ b/include/linux/fs/super.h @@ -242,6 +242,7 @@ struct file *fs_bdev_file_open_by_dev(dev_t dev, blk_mode_t mode, void *holder, struct super_block *sb); struct file *fs_bdev_file_open_by_path(const char *path, blk_mode_t mode, void *holder, struct super_block *sb); +void fs_bdev_unregister(struct file *bdev_file, struct super_block *sb); void fs_bdev_file_release(struct file *bdev_file, struct super_block *sb); #endif /* _LINUX_FS_SUPER_H */ -- cgit From 41fda7804af4931df056f74f91661edf7f696777 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 16 Jun 2026 16:08:32 +0200 Subject: super: make fs_holder_ops private Now that filesystems open and claim their block devices through fs_bdev_file_open_by_{dev,path}(), nothing outside fs/super.c references fs_holder_ops. Make it static and drop its declaration from blkdev.h. Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260616-work-super-bdev_holder_global-v2-16-7df6b864028e@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/super.c | 3 +-- include/linux/blkdev.h | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index a83f58755cf8..2d0a07861bfc 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1624,13 +1624,12 @@ static int fs_bdev_thaw(struct block_device *bdev) return error; } -const struct blk_holder_ops fs_holder_ops = { +static const struct blk_holder_ops fs_holder_ops = { .mark_dead = fs_bdev_mark_dead, .sync = fs_bdev_sync, .freeze = fs_bdev_freeze, .thaw = fs_bdev_thaw, }; -EXPORT_SYMBOL_GPL(fs_holder_ops); static struct super_dev *super_dev_lookup(dev_t dev, struct super_block *sb) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9e395d95067e..dbb549cdfb77 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1768,13 +1768,6 @@ struct blk_holder_ops { __releases(&bdev->bd_holder_lock); }; -/* - * For filesystems using @fs_holder_ops, the @holder argument passed to - * helpers used to open and claim block devices via - * bd_prepare_to_claim() must point to a superblock. - */ -extern const struct blk_holder_ops fs_holder_ops; - /* * Return the correct open flags for blkdev_get_by_* for super block flags * as stored in sb->s_flags. -- cgit From 21bcea3ef2025796a29ba88f2747d864ed535758 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 1 Jun 2026 15:56:34 +0200 Subject: fs: add switch_fs_struct() Don't open-code the guts of replacing current's fs struct. Link: https://patch.msgid.link/20260601-work-kthread-nullfs-v4-1-77ee053060e0@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/fs_struct.c | 18 ++++++++++++++++++ include/linux/fs_struct.h | 2 ++ kernel/fork.c | 22 ++++++---------------- 3 files changed, 26 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/fs_struct.c b/fs/fs_struct.c index 394875d06fd6..c441586537e7 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -147,6 +147,24 @@ int unshare_fs_struct(void) } EXPORT_SYMBOL_GPL(unshare_fs_struct); +struct fs_struct *switch_fs_struct(struct fs_struct *new_fs) +{ + struct fs_struct *fs; + + scoped_guard(task_lock, current) { + fs = current->fs; + read_seqlock_excl(&fs->seq); + current->fs = new_fs; + if (--fs->users) + new_fs = NULL; + else + new_fs = fs; + read_sequnlock_excl(&fs->seq); + } + + return new_fs; +} + /* to be mentioned only in INIT_TASK */ struct fs_struct init_fs = { .users = 1, diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h index 0070764b790a..ade459383f92 100644 --- a/include/linux/fs_struct.h +++ b/include/linux/fs_struct.h @@ -40,6 +40,8 @@ static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd) read_sequnlock_excl(&fs->seq); } +struct fs_struct *switch_fs_struct(struct fs_struct *new_fs); + extern bool current_chrooted(void); static inline int current_umask(void) diff --git a/kernel/fork.c b/kernel/fork.c index 13e38e89a1f3..27f775113be6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -3215,7 +3215,7 @@ static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp */ int ksys_unshare(unsigned long unshare_flags) { - struct fs_struct *fs, *new_fs = NULL; + struct fs_struct *new_fs = NULL; struct files_struct *new_fd = NULL; struct cred *new_cred = NULL; struct nsproxy *new_nsproxy = NULL; @@ -3293,23 +3293,13 @@ int ksys_unshare(unsigned long unshare_flags) new_nsproxy = NULL; } - task_lock(current); + if (new_fs) + new_fs = switch_fs_struct(new_fs); - if (new_fs) { - fs = current->fs; - read_seqlock_excl(&fs->seq); - current->fs = new_fs; - if (--fs->users) - new_fs = NULL; - else - new_fs = fs; - read_sequnlock_excl(&fs->seq); - } - - if (new_fd) + if (new_fd) { + guard(task_lock)(current); swap(current->files, new_fd); - - task_unlock(current); + } if (new_cred) { /* Install the new user namespace */ -- cgit From 67c54d1d730a8b7a43f2560dcea9b7dc95fba1cd Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 1 Jun 2026 15:56:36 +0200 Subject: fs: add scoped_with_init_fs() Similar to scoped_with_kernel_creds() allow a temporary override of current->fs to serve the few places where lookup is performed from kthread context or needs init's filesytem state. Link: https://patch.msgid.link/20260601-work-kthread-nullfs-v4-3-77ee053060e0@kernel.org Signed-off-by: Christian Brauner (Amutable) --- include/linux/fs_struct.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'include') diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h index ade459383f92..e11d0e57168f 100644 --- a/include/linux/fs_struct.h +++ b/include/linux/fs_struct.h @@ -6,6 +6,7 @@ #include #include #include +#include struct fs_struct { int users; @@ -49,4 +50,34 @@ static inline int current_umask(void) return current->fs->umask; } +/* + * Temporarily use userspace_init_fs for path resolution in kthreads. + * Callers should use scoped_with_init_fs() which automatically + * restores the original fs_struct at scope exit. + */ +static inline struct fs_struct *__override_init_fs(void) +{ + struct fs_struct *fs; + + fs = current->fs; + WRITE_ONCE(current->fs, fs); + return fs; +} + +static inline void __revert_init_fs(struct fs_struct *revert_fs) +{ + VFS_WARN_ON_ONCE(current->fs != revert_fs); + WRITE_ONCE(current->fs, revert_fs); +} + +DEFINE_CLASS(__override_init_fs, + struct fs_struct *, + __revert_init_fs(_T), + __override_init_fs(), void) + +#define scoped_with_init_fs() \ + scoped_class(__override_init_fs, __UNIQUE_ID(label)) + +void __init init_userspace_fs(void); + #endif /* _LINUX_FS_STRUCT_H */ -- cgit From 1d4ee94a51bdb98610bf7283e6297b133f8c1025 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 1 Jun 2026 15:56:37 +0200 Subject: fs: add real_fs to track task's actual fs_struct Add a real_fs field to task_struct that always mirrors the fs field. This lays the groundwork for distinguishing between a task's permanent fs_struct and one that is temporarily overridden via scoped_with_init_fs(). When a kthread temporarily overrides current->fs for path lookup, we need to know the original fs_struct for operations like exit_fs() and unshare_fs_struct() that must operate on the real, permanent fs. For now real_fs is always equal to fs. It is maintained alongside fs in all the relevant paths: exit_fs(), unshare_fs_struct(), switch_fs_struct(), and copy_fs(). Link: https://patch.msgid.link/20260601-work-kthread-nullfs-v4-4-77ee053060e0@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/fs_struct.c | 11 ++++++++--- fs/proc/array.c | 4 ++-- fs/proc/base.c | 8 ++++---- fs/proc_namespace.c | 4 ++-- include/linux/sched.h | 1 + init/init_task.c | 1 + kernel/fork.c | 8 +++++++- kernel/kcmp.c | 2 +- 8 files changed, 26 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/fs/fs_struct.c b/fs/fs_struct.c index fcecf209f1a9..c03a574ed65a 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -61,7 +61,7 @@ void chroot_fs_refs(const struct path *old_root, const struct path *new_root) read_lock(&tasklist_lock); for_each_process_thread(g, p) { task_lock(p); - fs = p->fs; + fs = p->real_fs; if (fs) { int hits = 0; write_seqlock(&fs->seq); @@ -89,12 +89,13 @@ void free_fs_struct(struct fs_struct *fs) void exit_fs(struct task_struct *tsk) { - struct fs_struct *fs = tsk->fs; + struct fs_struct *fs = tsk->real_fs; if (fs) { int kill; task_lock(tsk); read_seqlock_excl(&fs->seq); + tsk->real_fs = NULL; tsk->fs = NULL; kill = !--fs->users; read_sequnlock_excl(&fs->seq); @@ -126,7 +127,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) int unshare_fs_struct(void) { - struct fs_struct *fs = current->fs; + struct fs_struct *fs = current->real_fs; struct fs_struct *new_fs = copy_fs_struct(fs); int kill; @@ -135,8 +136,10 @@ int unshare_fs_struct(void) task_lock(current); read_seqlock_excl(&fs->seq); + VFS_WARN_ON_ONCE(fs != current->fs); kill = !--fs->users; current->fs = new_fs; + current->real_fs = new_fs; read_sequnlock_excl(&fs->seq); task_unlock(current); @@ -177,8 +180,10 @@ struct fs_struct *switch_fs_struct(struct fs_struct *new_fs) scoped_guard(task_lock, current) { fs = current->fs; + VFS_WARN_ON_ONCE(fs != current->real_fs); read_seqlock_excl(&fs->seq); current->fs = new_fs; + current->real_fs = new_fs; if (--fs->users) new_fs = NULL; else diff --git a/fs/proc/array.c b/fs/proc/array.c index 479ea8cb4ef4..f6f75d206762 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -168,8 +168,8 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, cred = get_task_cred(p); task_lock(p); - if (p->fs) - umask = p->fs->umask; + if (p->real_fs) + umask = p->real_fs->umask; if (p->files) max_fds = files_fdtable(p->files)->max_fds; task_unlock(p); diff --git a/fs/proc/base.c b/fs/proc/base.c index 780f81259052..6a39de424f62 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -211,8 +211,8 @@ static int get_task_root(struct task_struct *task, struct path *root) int result = -ENOENT; task_lock(task); - if (task->fs) { - get_fs_root(task->fs, root); + if (task->real_fs) { + get_fs_root(task->real_fs, root); result = 0; } task_unlock(task); @@ -225,8 +225,8 @@ static int proc_cwd_link(struct dentry *dentry, struct path *path, int result = -ENOENT; task_lock(task); - if (task->fs) { - get_fs_pwd(task->fs, path); + if (task->real_fs) { + get_fs_pwd(task->real_fs, path); result = 0; } task_unlock(task); diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 5c555db68aa2..036356c0a55b 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -254,13 +254,13 @@ static int mounts_open_common(struct inode *inode, struct file *file, } ns = nsp->mnt_ns; get_mnt_ns(ns); - if (!task->fs) { + if (!task->real_fs) { task_unlock(task); put_task_struct(task); ret = -ENOENT; goto err_put_ns; } - get_fs_root(task->fs, &root); + get_fs_root(task->real_fs, &root); task_unlock(task); put_task_struct(task); diff --git a/include/linux/sched.h b/include/linux/sched.h index 373bcc0598d1..1e4136c2b2a3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1191,6 +1191,7 @@ struct task_struct { unsigned long last_switch_time; #endif /* Filesystem information: */ + struct fs_struct *real_fs; struct fs_struct *fs; /* Open file information: */ diff --git a/init/init_task.c b/init/init_task.c index b67ef6040a65..ba5c2523f7e0 100644 --- a/init/init_task.c +++ b/init/init_task.c @@ -162,6 +162,7 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = { RCU_POINTER_INITIALIZER(cred, &init_cred), .comm = INIT_TASK_COMM, .thread = INIT_THREAD, + .real_fs = &init_fs, .fs = &init_fs, .files = &init_files, #ifdef CONFIG_IO_URING diff --git a/kernel/fork.c b/kernel/fork.c index 27f775113be6..69b522fc0179 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1616,6 +1616,8 @@ static int copy_exec_state(u64 clone_flags, struct task_struct *tsk) static int copy_fs(u64 clone_flags, struct task_struct *tsk) { struct fs_struct *fs = current->fs; + + VFS_WARN_ON_ONCE(current->fs != current->real_fs); if (clone_flags & CLONE_FS) { /* tsk->fs is already what we want */ read_seqlock_excl(&fs->seq); @@ -1628,7 +1630,7 @@ static int copy_fs(u64 clone_flags, struct task_struct *tsk) read_sequnlock_excl(&fs->seq); return 0; } - tsk->fs = copy_fs_struct(fs); + tsk->real_fs = tsk->fs = copy_fs_struct(fs); if (!tsk->fs) return -ENOMEM; return 0; @@ -3246,6 +3248,10 @@ int ksys_unshare(unsigned long unshare_flags) if (unshare_flags & CLONE_NEWNS) unshare_flags |= CLONE_FS; + /* No unsharing with overriden fs state */ + VFS_WARN_ON_ONCE(unshare_flags & (CLONE_NEWNS | CLONE_FS) && + current->fs != current->real_fs); + err = check_unshare_flags(unshare_flags); if (err) goto bad_unshare_out; diff --git a/kernel/kcmp.c b/kernel/kcmp.c index 7c1a65bd5f8d..76476aeee067 100644 --- a/kernel/kcmp.c +++ b/kernel/kcmp.c @@ -186,7 +186,7 @@ SYSCALL_DEFINE5(kcmp, pid_t, pid1, pid_t, pid2, int, type, ret = kcmp_ptr(task1->files, task2->files, KCMP_FILES); break; case KCMP_FS: - ret = kcmp_ptr(task1->fs, task2->fs, KCMP_FS); + ret = kcmp_ptr(task1->real_fs, task2->real_fs, KCMP_FS); break; case KCMP_SIGHAND: ret = kcmp_ptr(task1->sighand, task2->sighand, KCMP_SIGHAND); -- cgit From 9a8e296958884b807a02759975170b6559901242 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 1 Jun 2026 15:56:38 +0200 Subject: fs: make userspace_init_fs a dynamically-initialized pointer Change userspace_init_fs from a declared-but-unused extern struct to a dynamically initialized pointer. Add init_userspace_fs() which is called early in kernel_init() (PID 1) to record PID 1's fs_struct as the canonical userspace filesystem state. Wire up __override_init_fs() and __revert_init_fs() to actually swap current->fs to/from userspace_init_fs. Previously these were no-ops that stored current->fs back to itself. Fix nullfs_userspace_init() to compare against userspace_init_fs instead of &init_fs. When PID 1 unshares its filesystem state, revert userspace_init_fs to init_fs's root (nullfs) so that stale filesystem state is not silently inherited by kworkers and usermodehelpers. At this stage PID 1's fs still points to rootfs (set by init_mount_tree), so userspace_init_fs points to rootfs and scoped_with_init_fs() is functionally equivalent to its previous no-op behavior. Link: https://patch.msgid.link/20260601-work-kthread-nullfs-v4-5-77ee053060e0@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/fs_struct.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++- include/linux/fs_struct.h | 15 ++++++++------- include/linux/init_task.h | 1 + init/main.c | 3 +++ 4 files changed, 59 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/fs_struct.c b/fs/fs_struct.c index c03a574ed65a..f44e43ce6d93 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -8,6 +8,7 @@ #include #include #include "internal.h" +#include "mount.h" /* * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values. @@ -163,15 +164,34 @@ EXPORT_SYMBOL_GPL(unshare_fs_struct); * fs_struct state. Breaking that contract sucks for both sides. * So just don't bother with extra work for this. No sane init * system should ever do this. + * + * On older kernels if PID 1 unshared its filesystem state with us the + * kernel simply used the stale fs_struct state implicitly pinning + * anything that PID 1 had last used. Even if PID 1 might've moved on to + * some completely different fs_struct state and might've even unmounted + * the old root. + * + * This has hilarious consequences: Think continuing to dump coredump + * state into an implicitly pinned directory somewhere. Calling random + * binaries in the old rootfs via usermodehelpers. + * + * Be aggressive about this: We simply reject operating on stale + * fs_struct state by reverting to nullfs. Every kworker that does + * lookups after this point will fail. Every usermodehelper call will + * fail. Tough luck but let's be kind and emit a warning to userspace. */ static inline void validate_fs_switch(struct fs_struct *old_fs) { + might_sleep(); + if (likely(current->pid != 1)) return; /* @old_fs may be dangling but for comparison it's fine */ - if (old_fs != &init_fs) + if (old_fs != userspace_init_fs) return; pr_warn("VFS: Pid 1 stopped sharing filesystem state\n"); + set_fs_root(userspace_init_fs, &init_fs.root); + set_fs_pwd(userspace_init_fs, &init_fs.root); } struct fs_struct *switch_fs_struct(struct fs_struct *new_fs) @@ -201,3 +221,29 @@ struct fs_struct init_fs = { .seq = __SEQLOCK_UNLOCKED(init_fs.seq), .umask = 0022, }; + +struct fs_struct *userspace_init_fs __ro_after_init; +EXPORT_SYMBOL_GPL(userspace_init_fs); + +void __init init_userspace_fs(void) +{ + struct mount *m; + struct path root; + + /* Move PID 1 from nullfs into the initramfs. */ + m = topmost_overmount(current->nsproxy->mnt_ns->root); + root.mnt = &m->mnt; + root.dentry = root.mnt->mnt_root; + + VFS_WARN_ON_ONCE(current->pid != 1); + + set_fs_root(current->fs, &root); + set_fs_pwd(current->fs, &root); + + /* Hold a reference for the global pointer. */ + read_seqlock_excl(¤t->fs->seq); + current->fs->users++; + read_sequnlock_excl(¤t->fs->seq); + + userspace_init_fs = current->fs; +} diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h index e11d0e57168f..97eef8d3863d 100644 --- a/include/linux/fs_struct.h +++ b/include/linux/fs_struct.h @@ -17,6 +17,7 @@ struct fs_struct { } __randomize_layout; extern struct kmem_cache *fs_cachep; +extern struct fs_struct *userspace_init_fs; extern void exit_fs(struct task_struct *); extern void set_fs_root(struct fs_struct *, const struct path *); @@ -57,17 +58,17 @@ static inline int current_umask(void) */ static inline struct fs_struct *__override_init_fs(void) { - struct fs_struct *fs; + struct fs_struct *old_fs; - fs = current->fs; - WRITE_ONCE(current->fs, fs); - return fs; + old_fs = current->fs; + WRITE_ONCE(current->fs, userspace_init_fs); + return old_fs; } -static inline void __revert_init_fs(struct fs_struct *revert_fs) +static inline void __revert_init_fs(struct fs_struct *old_fs) { - VFS_WARN_ON_ONCE(current->fs != revert_fs); - WRITE_ONCE(current->fs, revert_fs); + VFS_WARN_ON_ONCE(current->fs != userspace_init_fs); + WRITE_ONCE(current->fs, old_fs); } DEFINE_CLASS(__override_init_fs, diff --git a/include/linux/init_task.h b/include/linux/init_task.h index a6cb241ea00c..61536be773f5 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -24,6 +24,7 @@ extern struct files_struct init_files; extern struct fs_struct init_fs; +extern struct fs_struct *userspace_init_fs; extern struct nsproxy init_nsproxy; #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE diff --git a/init/main.c b/init/main.c index e363232b428b..9af754e33209 100644 --- a/init/main.c +++ b/init/main.c @@ -103,6 +103,7 @@ #include #include #include +#include #include #include #include @@ -1540,6 +1541,8 @@ static int __ref kernel_init(void *unused) { int ret; + init_userspace_fs(); + /* * Wait until kthreadd is all set-up. */ -- cgit From ed4b1672529018b2013c62235c4bf1ab0ae0e3d4 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 1 Jun 2026 15:56:53 +0200 Subject: fs: add umh argument to struct kernel_clone_args Add a umh field to struct kernel_clone_args. When set, copy_fs() copies from pid 1's fs_struct instead of the kthread's fs_struct. This ensures usermodehelper threads always get init's filesystem state regardless of their parent's (kthreadd's) fs. Usermodehelper threads are not allowed to create mount namespaces (CLONE_NEWNS), share filesystem state (CLONE_FS), or be started from a non-initial mount namespace. No usermodehelper currently does this so we don't need to worry about this restriction. Set .umh = 1 in user_mode_thread(). At this stage pid 1's fs points to rootfs which is the same as kthreadd's fs, so this is functionally equivalent. Link: https://patch.msgid.link/20260601-work-kthread-nullfs-v4-20-77ee053060e0@kernel.org Signed-off-by: Christian Brauner (Amutable) --- include/linux/sched/task.h | 1 + kernel/fork.c | 25 +++++++++++++++++++++---- kernel/umh.c | 6 ++---- 3 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h index 41ed884cffc9..e0c1ca8c6a18 100644 --- a/include/linux/sched/task.h +++ b/include/linux/sched/task.h @@ -31,6 +31,7 @@ struct kernel_clone_args { u32 io_thread:1; u32 user_worker:1; u32 no_files:1; + u32 umh:1; unsigned long stack; unsigned long stack_size; unsigned long tls; diff --git a/kernel/fork.c b/kernel/fork.c index 69b522fc0179..b85b649c710d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1613,11 +1613,27 @@ static int copy_exec_state(u64 clone_flags, struct task_struct *tsk) return task_exec_state_copy(tsk); } -static int copy_fs(u64 clone_flags, struct task_struct *tsk) +static int copy_fs(u64 clone_flags, struct task_struct *tsk, bool umh) { - struct fs_struct *fs = current->fs; + struct fs_struct *fs; + + /* + * Usermodehelper may copy userspace_init_fs filesystem state but + * they don't get to create mount namespaces, share the + * filesystem state, or be started from a non-initial mount + * namespace. + */ + if (umh) { + if (clone_flags & (CLONE_NEWNS | CLONE_FS)) + return -EINVAL; + if (current->nsproxy->mnt_ns != &init_mnt_ns) + return -EINVAL; + fs = userspace_init_fs; + } else { + fs = current->fs; + VFS_WARN_ON_ONCE(current->fs != current->real_fs); + } - VFS_WARN_ON_ONCE(current->fs != current->real_fs); if (clone_flags & CLONE_FS) { /* tsk->fs is already what we want */ read_seqlock_excl(&fs->seq); @@ -2278,7 +2294,7 @@ __latent_entropy struct task_struct *copy_process( retval = copy_files(clone_flags, p, args->no_files); if (retval) goto bad_fork_cleanup_semundo; - retval = copy_fs(clone_flags, p); + retval = copy_fs(clone_flags, p, args->umh); if (retval) goto bad_fork_cleanup_files; retval = copy_sighand(clone_flags, p); @@ -2820,6 +2836,7 @@ pid_t user_mode_thread(int (*fn)(void *), void *arg, unsigned long flags) .exit_signal = (flags & CSIGNAL), .fn = fn, .fn_arg = arg, + .umh = 1, }; return kernel_clone(&args); diff --git a/kernel/umh.c b/kernel/umh.c index 48117c569e1a..6e2c7bb315c6 100644 --- a/kernel/umh.c +++ b/kernel/umh.c @@ -71,10 +71,8 @@ static int call_usermodehelper_exec_async(void *data) spin_unlock_irq(¤t->sighand->siglock); /* - * Initial kernel threads share ther FS with init, in order to - * get the init root directory. But we've now created a new - * thread that is going to execve a user process and has its own - * 'struct fs_struct'. Reset umask to the default. + * Usermodehelper threads get a copy of userspace init's + * fs_struct. Reset umask to the default. */ current->fs->umask = 0022; -- cgit From d425596035b34f0119a688e38d0e65bf43fa73c6 Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Tue, 23 Jun 2026 10:55:33 +0800 Subject: dt-bindings: clock: Add Amlogic A9 AO clock controller Add the Always-On clock controller dt-bindings for the Amlogic A9 SoC family. Acked-by: Conor Dooley Signed-off-by: Jian Hu Link: https://patch.msgid.link/20260623-a9_aoclk-v5-1-c7cb1ff9ebf1@amlogic.com Signed-off-by: Jerome Brunet --- .../bindings/clock/amlogic,a9-aoclkc.yaml | 76 ++++++++++++++++++++++ include/dt-bindings/clock/amlogic,a9-aoclkc.h | 76 ++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a9-aoclkc.yaml create mode 100644 include/dt-bindings/clock/amlogic,a9-aoclkc.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/amlogic,a9-aoclkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,a9-aoclkc.yaml new file mode 100644 index 000000000000..1fa9b3a32fbb --- /dev/null +++ b/Documentation/devicetree/bindings/clock/amlogic,a9-aoclkc.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2026 Amlogic, Inc. All rights reserved +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/amlogic,a9-aoclkc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic A9 Series Always-On Clock Controller + +maintainers: + - Neil Armstrong + - Jerome Brunet + - Jian Hu + - Xianwei Zhao + +properties: + compatible: + const: amlogic,a9-aoclkc + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clocks: + minItems: 5 + items: + - description: input oscillator + - description: input fclk div 3 + - description: input fclk div 4 + - description: input fclk div 5 + - description: input sys clk + - description: external fixed 32k (optional) + + clock-names: + minItems: 5 + items: + - const: xtal + - const: fdiv3 + - const: fdiv4 + - const: fdiv5 + - const: sys + - const: ext_32k + +required: + - compatible + - reg + - '#clock-cells' + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + clock-controller@0 { + compatible = "amlogic,a9-aoclkc"; + reg = <0x0 0x0 0x0 0x58>; + #clock-cells = <1>; + clocks = <&xtal>, + <&scmi_clk 14>, + <&scmi_clk 16>, + <&scmi_clk 18>, + <&scmi_clk 21>; + clock-names = "xtal", + "fdiv3", + "fdiv4", + "fdiv5", + "sys"; + }; + }; diff --git a/include/dt-bindings/clock/amlogic,a9-aoclkc.h b/include/dt-bindings/clock/amlogic,a9-aoclkc.h new file mode 100644 index 000000000000..a7d704d4b58e --- /dev/null +++ b/include/dt-bindings/clock/amlogic,a9-aoclkc.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (C) 2026 Amlogic, Inc. All rights reserved. + */ + +#ifndef __AMLOGIC_A9_AO_CLKC_H +#define __AMLOGIC_A9_AO_CLKC_H + +#define CLKID_AO_XTAL_IN 0 +#define CLKID_AO_XTAL 1 +#define CLKID_AO_SYS 2 +#define CLKID_AO_SYS_I3C 3 +#define CLKID_AO_SYS_RTC_REG 4 +#define CLKID_AO_SYS_CLKTREE 5 +#define CLKID_AO_SYS_RST_CTRL 6 +#define CLKID_AO_SYS_PAD 7 +#define CLKID_AO_SYS_RTC_DIG 8 +#define CLKID_AO_SYS_IRQ 9 +#define CLKID_AO_SYS_PWRCTRL 10 +#define CLKID_AO_SYS_PWM_A 11 +#define CLKID_AO_SYS_PWM_B 12 +#define CLKID_AO_SYS_PWM_C 13 +#define CLKID_AO_SYS_PWM_D 14 +#define CLKID_AO_SYS_PWM_E 15 +#define CLKID_AO_SYS_PWM_F 16 +#define CLKID_AO_SYS_PWM_G 17 +#define CLKID_AO_SYS_I2C_A 18 +#define CLKID_AO_SYS_I2C_B 19 +#define CLKID_AO_SYS_I2C_C 20 +#define CLKID_AO_SYS_I2C_D 21 +#define CLKID_AO_SYS_SED 22 +#define CLKID_AO_SYS_IR_CTRL 23 +#define CLKID_AO_SYS_UART_B 24 +#define CLKID_AO_SYS_UART_C 25 +#define CLKID_AO_SYS_UART_D 26 +#define CLKID_AO_SYS_UART_E 27 +#define CLKID_AO_SYS_SPISG_0 28 +#define CLKID_AO_SYS_RTC_SECURE 29 +#define CLKID_AO_SYS_CEC 30 +#define CLKID_AO_SYS_AOCPU 31 +#define CLKID_AO_SYS_SRAM 32 +#define CLKID_AO_SYS_SPISG_1 33 +#define CLKID_AO_SYS_SPISG_2 34 +#define CLKID_AO_PWM_A_SEL 35 +#define CLKID_AO_PWM_A_DIV 36 +#define CLKID_AO_PWM_A 37 +#define CLKID_AO_PWM_B_SEL 38 +#define CLKID_AO_PWM_B_DIV 39 +#define CLKID_AO_PWM_B 40 +#define CLKID_AO_PWM_C_SEL 41 +#define CLKID_AO_PWM_C_DIV 42 +#define CLKID_AO_PWM_C 43 +#define CLKID_AO_PWM_D_SEL 44 +#define CLKID_AO_PWM_D_DIV 45 +#define CLKID_AO_PWM_D 46 +#define CLKID_AO_PWM_E_SEL 47 +#define CLKID_AO_PWM_E_DIV 48 +#define CLKID_AO_PWM_E 49 +#define CLKID_AO_PWM_F_SEL 50 +#define CLKID_AO_PWM_F_DIV 51 +#define CLKID_AO_PWM_F 52 +#define CLKID_AO_PWM_G_SEL 53 +#define CLKID_AO_PWM_G_DIV 54 +#define CLKID_AO_PWM_G 55 +#define CLKID_AO_RTC_DUALDIV_IN 56 +#define CLKID_AO_RTC_DUALDIV_DIV 57 +#define CLKID_AO_RTC_DUALDIV_SEL 58 +#define CLKID_AO_RTC_DUALDIV 59 +#define CLKID_AO_RTC 60 +#define CLKID_AO_CEC_DUALDIV_IN 61 +#define CLKID_AO_CEC_DUALDIV_DIV 62 +#define CLKID_AO_CEC_DUALDIV_SEL 63 +#define CLKID_AO_CEC_DUALDIV 64 +#define CLKID_AO_CEC 65 + +#endif /* __AMLOGIC_A9_AO_CLKC_H */ -- cgit From 7b06ff772080919fdb194c95af6b1e3acb079b71 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Jun 2026 14:58:27 +0200 Subject: ARM: s3c: Replace __ASSEMBLY__ with __ASSEMBLER__ in header files While the GCC and Clang compilers already define __ASSEMBLER__ automatically when compiling assembly code, __ASSEMBLY__ is a macro that only gets defined by the Makefiles in the kernel. This can be very confusing when switching between userspace and kernelspace coding, or when dealing with uapi headers that rather should use __ASSEMBLER__ instead. So let's standardize now on the __ASSEMBLER__ macro that is provided by the compilers. This is a completely mechanical patch (done with a simple "sed -i" statement). Signed-off-by: Thomas Huth Link: https://patch.msgid.link/20260619125827.215977-1-thuth@redhat.com Signed-off-by: Krzysztof Kozlowski --- arch/arm/mach-s3c/map-base.h | 2 +- include/linux/serial_s3c.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-s3c/map-base.h b/arch/arm/mach-s3c/map-base.h index 463a995b399b..beb58e6f12e1 100644 --- a/arch/arm/mach-s3c/map-base.h +++ b/arch/arm/mach-s3c/map-base.h @@ -20,7 +20,7 @@ #define S3C_ADDR_BASE 0xF6000000 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x)) #else #define S3C_ADDR(x) (S3C_ADDR_BASE + (x)) diff --git a/include/linux/serial_s3c.h b/include/linux/serial_s3c.h index 102aa33d956c..f54cb6e23f85 100644 --- a/include/linux/serial_s3c.h +++ b/include/linux/serial_s3c.h @@ -269,7 +269,7 @@ #define APPLE_S5L_UTRSTAT_RXTO BIT(9) #define APPLE_S5L_UTRSTAT_ALL_FLAGS GENMASK(9, 3) -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -294,7 +294,7 @@ struct s3c2410_uartcfg { unsigned long ufcon; /* value of ufcon for port */ }; -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __ASM_ARM_REGS_SERIAL_H */ -- cgit From c0933934d5d96fc641e8d0025f9099e554a7b47a Mon Sep 17 00:00:00 2001 From: "Nícolas F. R. A. Prado" Date: Fri, 26 Jun 2026 11:47:22 -0400 Subject: ALSA: hda: Force resume if acomp notified during system suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if an HDMI cable is connected while the system is suspended, the HDMI audio jack status stays off after resume. This is due to the jack state not being synced by the HDA HDMI codec device's runtime resume, as that never happens if the device was runtime suspended before the system suspended, or by the acomp notification triggered from the DRM side if that happens before the HDA HDMI codec device has resumed. To fix this, if snd_hda_hdmi_acomp_pin_eld_notify() gets called before the HDA HDMI codec device has resumed, mark it to be forcefully runtime resumed at the next PM complete time. Do this using a separate acomp_requested_resume flag that can be temporarily set without overwriting forced_resume for drivers that always want to force resume. Assisted-by: Copilot:claude-sonnet-4.6 Signed-off-by: Nícolas F. R. A. Prado Link: https://patch.msgid.link/20260626-hda-force-resume-eld-notify-v1-1-a92cb01393e0@collabora.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 1 + sound/hda/codecs/hdmi/hdmi.c | 4 +++- sound/hda/common/codec.c | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 17945ab5e6e2..c05b6d44c491 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -256,6 +256,7 @@ struct hda_codec { unsigned int link_down_at_suspend:1; /* link down at runtime suspend */ unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ unsigned int forced_resume:1; /* forced resume for jack */ + unsigned int acomp_requested_resume:1; /* resume requested by acomp */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ unsigned int ctl_dev_id:1; /* old control element id build behaviour */ unsigned int eld_jack_detect:1; /* Machine jack-detection by ELD */ diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 1f4d646724ed..0b6816018a42 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -2233,8 +2233,10 @@ void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) + if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) { + codec->acomp_requested_resume = 1; return; + } snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id); } diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index ef533770179b..b9ded149ea11 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -2967,8 +2967,11 @@ static void hda_codec_pm_complete(struct device *dev) dev->power.power_state = PMSG_RESUME; if (pm_runtime_suspended(dev) && (codec->jackpoll_interval || - hda_codec_need_resume(codec) || codec->forced_resume)) + hda_codec_need_resume(codec) || codec->forced_resume || + codec->acomp_requested_resume)) { + codec->acomp_requested_resume = 0; pm_request_resume(dev); + } } static int hda_codec_pm_suspend(struct device *dev) -- cgit From fe8c977b35fd9e945ff281382c808026d41731e5 Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Thu, 14 May 2026 19:11:58 -0700 Subject: firmware: stratix10-rsu: Add flash device info retrieval via SMC Extend the Intel Remote System Update (RSU) driver to retrieve the device info table through an ARM SMC call to the service layer. The table reports flash size and erase size for multiple devices. Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-rsu.c | 200 ++++++++++++++++++++- drivers/firmware/stratix10-svc.c | 94 +++++++++- include/linux/firmware/intel/stratix10-smc.h | 25 ++- .../linux/firmware/intel/stratix10-svc-client.h | 12 +- 4 files changed, 315 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index daddb5224794..d887c74b9821 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -7,17 +7,24 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include #include #include -#include -#define RSU_ERASE_SIZE_MASK GENMASK_ULL(63, 32) +/* + * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO packs each flash word as: + * [63:32] erase_size, [31:0] size (see stratix10-smc.h). + */ +#define RSU_DEVICE_INFO_SIZE_MASK GENMASK_ULL(31, 0) +#define RSU_DEVICE_INFO_ERASE_SIZE_MASK GENMASK_ULL(63, 32) + #define RSU_DCMF0_MASK GENMASK_ULL(31, 0) #define RSU_DCMF1_MASK GENMASK_ULL(63, 32) #define RSU_DCMF2_MASK GENMASK_ULL(31, 0) @@ -33,11 +40,31 @@ #define INVALID_DCMF_VERSION 0xFF #define INVALID_DCMF_STATUS 0xFFFFFFFF #define INVALID_SPT_ADDRESS 0x0 +#define INVALID_DEVICE_INFO (~0U) #define RSU_RETRY_SLEEP_MS (1U) #define RSU_ASYNC_MSG_RETRY (3U) #define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int)) +struct flash_device_info { + unsigned int size; + unsigned int erase_size; +}; + +/** + * rsu_device_info_set_from_packed() - Decode one RSU device-info SMC word + * @di: slot to fill + * @packed: register value: [63:32] erase_size, [31:0] size + * (INTEL_SIP_SMC_RSU_GET_DEVICE_INFO) + */ +static void rsu_device_info_set_from_packed(struct flash_device_info *di, + unsigned long packed) +{ + di->size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_SIZE_MASK, packed); + di->erase_size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_ERASE_SIZE_MASK, + packed); +} + typedef void (*rsu_callback)(struct stratix10_svc_client *client, struct stratix10_svc_cb_data *data); /** @@ -60,6 +87,8 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @dcmf_status.dcmf1: dcmf1 status * @dcmf_status.dcmf2: dcmf2 status * @dcmf_status.dcmf3: dcmf3 status + * @device_info: per-device flash information array; each entry contains + * size and erase size for one flash device * @retry_counter: the current image's retry counter * @max_retry: the preset max retry value * @spt0_address: address of spt0 @@ -93,6 +122,8 @@ struct stratix10_rsu_priv { unsigned int dcmf3; } dcmf_status; + struct flash_device_info device_info[4]; + unsigned int retry_counter; unsigned int max_retry; @@ -100,6 +131,20 @@ struct stratix10_rsu_priv { unsigned long spt1_address; }; +/** + * rsu_device_info_invalidate() - Mark all cached QSPI device slots invalid + * @priv: RSU private data + */ +static void rsu_device_info_invalidate(struct stratix10_rsu_priv *priv) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(priv->device_info); i++) { + priv->device_info[i].size = INVALID_DEVICE_INFO; + priv->device_info[i].erase_size = INVALID_DEVICE_INFO; + } +} + typedef void (*rsu_async_callback)(struct device *dev, struct stratix10_rsu_priv *priv, struct stratix10_svc_cb_data *data); @@ -229,8 +274,57 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client, } /** - * rsu_async_get_spt_table_callback() - Callback to be used by the rsu_async_send() - * to retrieve the SPT table information. + * rsu_get_device_info_callback() - Callback from Intel service layer for + * getting the QSPI device info + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for QSPI device info. + * @data->kaddr1 points to struct arm_smccc_1_2_regs on SVC_STATUS_OK or + * SVC_STATUS_ERROR; it is NULL on SVC_STATUS_NO_SUPPORT (unsupported command). + */ +static void rsu_get_device_info_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + struct arm_smccc_1_2_regs *res = data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) { + if (!res) { + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO: missing result payload\n"); + rsu_device_info_invalidate(priv); + complete(&priv->completion); + return; + } + + rsu_device_info_set_from_packed(&priv->device_info[0], res->a1); + rsu_device_info_set_from_packed(&priv->device_info[1], res->a2); + rsu_device_info_set_from_packed(&priv->device_info[2], res->a3); + rsu_device_info_set_from_packed(&priv->device_info[3], res->a4); + + } else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) { + dev_warn(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO not supported by firmware\n"); + rsu_device_info_invalidate(priv); + } else { + if (res) + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO returned 0x%lX\n", + res->a0); + else + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO failed with status 0x%X\n", + data->status); + rsu_device_info_invalidate(priv); + } + + complete(&priv->completion); +} + +/** + * rsu_async_get_spt_table_callback() - Callback to be used by the + * rsu_async_send() to retrieve the SPT table information. * @dev: pointer to device object * @priv: pointer to priv object * @data: pointer to callback data structure @@ -698,6 +792,75 @@ static ssize_t notify_store(struct device *dev, return count; } +static ssize_t rsu_device_info_show(struct device *dev, char *buf, + unsigned int index, bool erase_size) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + unsigned int value; + + if (!priv) + return -ENODEV; + + if (index >= ARRAY_SIZE(priv->device_info)) + return -EINVAL; + + value = erase_size ? priv->device_info[index].erase_size : + priv->device_info[index].size; + + if (value == INVALID_DEVICE_INFO) + return -EIO; + + return sysfs_emit(buf, "0x%08x\n", value); +} + +static ssize_t size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, false); +} + +static ssize_t size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, false); +} + +static ssize_t size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, false); +} + +static ssize_t size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, false); +} + +static ssize_t erase_size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, true); +} + +static ssize_t erase_size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, true); +} + +static ssize_t erase_size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, true); +} + +static ssize_t erase_size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, true); +} + static ssize_t spt0_address_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -742,6 +905,14 @@ static DEVICE_ATTR_RO(dcmf0_status); static DEVICE_ATTR_RO(dcmf1_status); static DEVICE_ATTR_RO(dcmf2_status); static DEVICE_ATTR_RO(dcmf3_status); +static DEVICE_ATTR_RO(size0); +static DEVICE_ATTR_RO(size1); +static DEVICE_ATTR_RO(size2); +static DEVICE_ATTR_RO(size3); +static DEVICE_ATTR_RO(erase_size0); +static DEVICE_ATTR_RO(erase_size1); +static DEVICE_ATTR_RO(erase_size2); +static DEVICE_ATTR_RO(erase_size3); static DEVICE_ATTR_WO(reboot_image); static DEVICE_ATTR_WO(notify); static DEVICE_ATTR_RO(spt0_address); @@ -764,6 +935,14 @@ static struct attribute *rsu_attrs[] = { &dev_attr_dcmf1_status.attr, &dev_attr_dcmf2_status.attr, &dev_attr_dcmf3_status.attr, + &dev_attr_size0.attr, + &dev_attr_size1.attr, + &dev_attr_size2.attr, + &dev_attr_size3.attr, + &dev_attr_erase_size0.attr, + &dev_attr_erase_size1.attr, + &dev_attr_erase_size2.attr, + &dev_attr_erase_size3.attr, &dev_attr_reboot_image.attr, &dev_attr_notify.attr, &dev_attr_spt0_address.attr, @@ -796,6 +975,7 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; /* spt0/1_address and status fields default to 0 from kzalloc */ + rsu_device_info_invalidate(priv); mutex_init(&priv->lock); init_completion(&priv->completion); @@ -846,6 +1026,16 @@ static int stratix10_rsu_probe(struct platform_device *pdev) goto remove_async_client; } + /* get QSPI device info from firmware */ + ret = rsu_send_msg(priv, COMMAND_RSU_GET_DEVICE_INFO, 0, + rsu_get_device_info_callback); + if (ret) { + dev_err(dev, "Error, getting QSPI Device Info %i\n", ret); + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); + return ret; + } + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0, rsu_async_get_spt_table_callback); if (ret) { diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index c24ca5823078..de938ab2db0b 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -445,13 +446,15 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl, * svc_thread_recv_status_ok() - handle the successful status * @p_data: pointer to service data structure * @cb_data: pointer to callback data structure to service client - * @res: result from SMC or HVC call + * @res: result from SMC or HVC call (a0-a3; used for routing and most commands) + * @res12: full v1.2 result for %COMMAND_RSU_GET_DEVICE_INFO, else NULL * * Send back the correspond status to the service clients. */ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, struct stratix10_svc_cb_data *cb_data, - struct arm_smccc_res res) + struct arm_smccc_res res, + struct arm_smccc_1_2_regs *res12) { cb_data->kaddr1 = NULL; cb_data->kaddr2 = NULL; @@ -513,6 +516,16 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, res.a2 = res.a2 * BYTE_TO_WORD_SIZE; cb_data->kaddr2 = &res.a2; break; + case COMMAND_RSU_GET_DEVICE_INFO: + if (WARN_ON(!res12)) { + cb_data->status = BIT(SVC_STATUS_ERROR); + break; + } + cb_data->status = BIT(SVC_STATUS_OK); + cb_data->kaddr1 = res12; + cb_data->kaddr2 = NULL; + cb_data->kaddr3 = NULL; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -522,6 +535,10 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data); } +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res); + /** * svc_normal_to_secure_thread() - the function to run in the kthread * @data: data pointer for kthread function @@ -539,6 +556,7 @@ static int svc_normal_to_secure_thread(void *data) struct stratix10_svc_data *pdata = NULL; struct stratix10_svc_cb_data *cbdata = NULL; struct arm_smccc_res res; + struct arm_smccc_1_2_regs res12 = { 0 }; unsigned long a0, a1, a2, a3, a4, a5, a6, a7; int ret_fifo = 0; @@ -727,6 +745,16 @@ static int svc_normal_to_secure_thread(void *data) a5 = (unsigned long)pdata->paddr_output; a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE; break; + case COMMAND_RSU_GET_DEVICE_INFO: + a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + a1 = 0; + a2 = 0; + a3 = 0; + a4 = 0; + a5 = 0; + a6 = 0; + a7 = 0; + break; default: pr_warn("it shouldn't happen\n"); mutex_unlock(&ctrl->sdm_lock); @@ -740,7 +768,18 @@ static int svc_normal_to_secure_thread(void *data) pr_debug(" a3=0x%016x\n", (unsigned int)a3); pr_debug(" a4=0x%016x\n", (unsigned int)a4); pr_debug(" a5=0x%016x\n", (unsigned int)a5); - ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + struct arm_smccc_1_2_regs args12 = { 0 }; + + args12.a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + svc_smccc_1_2_full(ctrl, &args12, &res12); + res.a0 = res12.a0; + res.a1 = res12.a1; + res.a2 = res12.a2; + res.a3 = res12.a3; + } else { + ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + } pr_debug("%s: %s: after SMC call -- res.a0=0x%016x", __func__, chan->name, (unsigned int)res.a0); @@ -763,9 +802,15 @@ static int svc_normal_to_secure_thread(void *data) } switch (res.a0) { - case INTEL_SIP_SMC_STATUS_OK: - svc_thread_recv_status_ok(pdata, cbdata, res); + case INTEL_SIP_SMC_STATUS_OK: { + struct arm_smccc_1_2_regs *devinfo_res = + (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) ? + &res12 : NULL; + + svc_thread_recv_status_ok(pdata, cbdata, res, + devinfo_res); break; + } case INTEL_SIP_SMC_STATUS_BUSY: switch (pdata->command) { case COMMAND_RECONFIG_DATA_SUBMIT: @@ -806,10 +851,16 @@ static int svc_normal_to_secure_thread(void *data) case INTEL_SIP_SMC_RSU_ERROR: pr_err("%s: STATUS_ERROR\n", __func__); cbdata->status = BIT(SVC_STATUS_ERROR); - cbdata->kaddr1 = &res.a1; - cbdata->kaddr2 = (res.a2) ? - svc_pa_to_va(res.a2) : NULL; - cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + cbdata->kaddr1 = &res12; + cbdata->kaddr2 = NULL; + cbdata->kaddr3 = NULL; + } else { + cbdata->kaddr1 = &res.a1; + cbdata->kaddr2 = (res.a2) ? + svc_pa_to_va(res.a2) : NULL; + cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + } pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); break; default: @@ -1025,6 +1076,31 @@ static void svc_smccc_hvc(unsigned long a0, unsigned long a1, arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); } +/** + * svc_smccc_1_2_full() - SMC/HVC v1.2 call matching the sync channel method + * @ctrl: service controller (selects SMC vs HVC) + * @args: arguments + * @res: full register-file result (a0-a17) + */ +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res) +{ + if (ctrl->invoke_fn == svc_smccc_smc) { + arm_smccc_1_2_smc(args, res); + } else if (ctrl->invoke_fn == svc_smccc_hvc) { + arm_smccc_1_2_hvc(args, res); + } else { + WARN_ON_ONCE(1); + /* + * INTEL_SIP_SMC_STATUS_OK is 0; zero-filled res would be misrouted + * as success. Force an error path and clear fabricated payload. + */ + memset(res, 0, sizeof(*res)); + res->a0 = INTEL_SIP_SMC_STATUS_ERROR; + } +} + /** * get_invoke_func() - invoke SMC or HVC call * @dev: pointer to device diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 9116512169dc..6e042943b6ce 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -429,6 +429,29 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) #define INTEL_SIP_SMC_RSU_DCMF_STATUS \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_DCMF_STATUS) +/** + * Request INTEL_SIP_SMC_RSU_GET_DEVICE_INFO + * + * Sync call used by service driver at EL1 to query QSPI device info from FW + * + * Call register usage: + * a0 INTEL_SIP_SMC_RSU_GET_DEVICE_INFO + * a1-7 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK + * a1 erasesize0 | size0 + * a2 erasesize1 | size1 + * a3 erasesize2 | size2 + * a4 erasesize3 | size3 + * Or + * + * a0 INTEL_SIP_SMC_RSU_ERROR + */ +#define INTEL_SIP_SMC_FUNCID_RSU_GET_DEVICE_INFO 22 +#define INTEL_SIP_SMC_RSU_GET_DEVICE_INFO \ + INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_GET_DEVICE_INFO) + /** * Request INTEL_SIP_SMC_SERVICE_COMPLETED * Sync call to check if the secure world have completed service request @@ -493,7 +516,7 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) * a3 not used */ #define INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD 60 - #define INTEL_SIP_SMC_MBOX_SEND_CMD \ +#define INTEL_SIP_SMC_MBOX_SEND_CMD \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD) /** diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h index 3edd93502bf8..af13dacdf5ac 100644 --- a/include/linux/firmware/intel/stratix10-svc-client.h +++ b/include/linux/firmware/intel/stratix10-svc-client.h @@ -128,6 +128,10 @@ struct stratix10_svc_chan; * @COMMAND_RSU_DCMF_STATUS: query firmware for the DCMF status * return status is SVC_STATUS_OK or SVC_STATUS_ERROR * + * @COMMAND_RSU_GET_DEVICE_INFO: query firmware for QSPI device info; + * return status is SVC_STATUS_OK, SVC_STATUS_ERROR, or SVC_STATUS_NO_SUPPORT + * (unsupported command / firmware compatibility path in the service layer). + * * @COMMAND_RSU_GET_SPT_TABLE: query firmware for SPT table * return status is SVC_STATUS_OK or SVC_STATUS_ERROR * @@ -174,6 +178,7 @@ enum stratix10_svc_command_code { COMMAND_RSU_MAX_RETRY, COMMAND_RSU_DCMF_VERSION, COMMAND_RSU_DCMF_STATUS, + COMMAND_RSU_GET_DEVICE_INFO, COMMAND_FIRMWARE_VERSION, COMMAND_RSU_GET_SPT_TABLE, /* for FCS */ @@ -224,7 +229,12 @@ struct stratix10_svc_command_config_type { /** * struct stratix10_svc_cb_data - callback data structure from service layer * @status: the status of sent command - * @kaddr1: address of 1st completed data block + * @kaddr1: address of 1st completed data block, or command-specific payload. + * For COMMAND_RSU_GET_DEVICE_INFO on SVC_STATUS_OK or SVC_STATUS_ERROR, + * points to struct arm_smccc_1_2_regs filled by the SMC/HVC return + * registers (a0 status, a1-a4 packed device words per + * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO). On SVC_STATUS_NO_SUPPORT (older + * firmware that does not handle this command), kaddr1 is NULL. * @kaddr2: address of 2nd completed data block * @kaddr3: address of 3rd completed data block */ -- cgit From 02475538bec2c47d1cce1211823c0453175af6a9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Jun 2026 12:06:00 +0200 Subject: vdso: Replace __ASSEMBLY__ with __ASSEMBLER__ in header files While the GCC and Clang compilers already define __ASSEMBLER__ automatically when compiling assembly code, __ASSEMBLY__ is a macro that only gets defined by the Makefiles in the kernel. This can be very confusing when switching between userspace and kernelspace coding, or when dealing with uapi headers that rather should use __ASSEMBLER__ instead. So standardize now on the __ASSEMBLER__ macro that is provided by the compilers. Signed-off-by: Thomas Huth Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://patch.msgid.link/20260619100600.121042-1-thuth@redhat.com --- include/asm-generic/vdso/vsyscall.h | 4 ++-- include/vdso/datapage.h | 6 +++--- include/vdso/helpers.h | 4 ++-- include/vdso/processor.h | 4 ++-- include/vdso/vsyscall.h | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vdso/vsyscall.h b/include/asm-generic/vdso/vsyscall.h index 5c6d9799f4e7..a6b03cfba0e2 100644 --- a/include/asm-generic/vdso/vsyscall.h +++ b/include/asm-generic/vdso/vsyscall.h @@ -2,7 +2,7 @@ #ifndef __ASM_GENERIC_VSYSCALL_H #define __ASM_GENERIC_VSYSCALL_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #ifndef __arch_get_vdso_u_time_data static __always_inline const struct vdso_time_data *__arch_get_vdso_u_time_data(void) @@ -30,6 +30,6 @@ static __always_inline void __arch_sync_vdso_time_data(struct vdso_time_data *vd } #endif /* __arch_sync_vdso_time_data */ -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __ASM_GENERIC_VSYSCALL_H */ diff --git a/include/vdso/datapage.h b/include/vdso/datapage.h index 5977723fb3b5..09897f76ae07 100644 --- a/include/vdso/datapage.h +++ b/include/vdso/datapage.h @@ -2,7 +2,7 @@ #ifndef __VDSO_DATAPAGE_H #define __VDSO_DATAPAGE_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include @@ -176,7 +176,7 @@ enum vdso_pages { VDSO_NR_PAGES }; -#else /* !__ASSEMBLY__ */ +#else /* !__ASSEMBLER__ */ #ifdef CONFIG_VDSO_GETRANDOM #define __vdso_u_rng_data PROVIDE(vdso_u_rng_data = vdso_u_data + 2 * PAGE_SIZE); @@ -197,6 +197,6 @@ enum vdso_pages { __vdso_u_arch_data \ -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __VDSO_DATAPAGE_H */ diff --git a/include/vdso/helpers.h b/include/vdso/helpers.h index a3bf4f1c0d37..65151b681c4f 100644 --- a/include/vdso/helpers.h +++ b/include/vdso/helpers.h @@ -2,7 +2,7 @@ #ifndef __VDSO_HELPERS_H #define __VDSO_HELPERS_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -111,6 +111,6 @@ static __always_inline void vdso_write_end(struct vdso_time_data *vd) vdso_write_seq_end(&vc[CS_RAW]); } -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __VDSO_HELPERS_H */ diff --git a/include/vdso/processor.h b/include/vdso/processor.h index fbe8265ea3c4..cc781912a696 100644 --- a/include/vdso/processor.h +++ b/include/vdso/processor.h @@ -5,10 +5,10 @@ #ifndef __VDSO_PROCESSOR_H #define __VDSO_PROCESSOR_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __VDSO_PROCESSOR_H */ diff --git a/include/vdso/vsyscall.h b/include/vdso/vsyscall.h index b0fdc9c6bf43..c5c2a2c07857 100644 --- a/include/vdso/vsyscall.h +++ b/include/vdso/vsyscall.h @@ -2,13 +2,13 @@ #ifndef __VDSO_VSYSCALL_H #define __VDSO_VSYSCALL_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include unsigned long vdso_update_begin(void); void vdso_update_end(unsigned long flags); -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLER__ */ #endif /* __VDSO_VSYSCALL_H */ -- cgit From a8374683868634012ac873d628fa581fcc452e9c Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Tue, 16 Jun 2026 16:28:44 +0300 Subject: mtd: spinand: add support for HeYangTek HYF1GQ4UDACAE The HeYangTek HYF1GQ4UDACAE is a 1 Gbit (128 MiB) SLC SPI-NAND with 2048 + 64 byte pages and on-die 4-bit / 512-byte ECC; its JEDEC manufacturer ID is 0xc9. The die is GD5F1GQ4-compatible, so the OOB layout is taken from the in-tree gd5fxgq4xa. The die exposes only a coarse 2-bit ECC status with no fine-grained bitflip-count register, so the status is decoded into a representative number of corrected bitflips. It is found, among others, on some Keenetic KN-3411 (Buddy 6) units. Datasheet: https://www.heyangtek.cn/previewfile.jsp?file=ABUIABA9GAAgwsvRnwYo-eDpsgc Signed-off-by: Aleksei Sviridkin Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/heyangtek.c | 132 +++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/heyangtek.c (limited to 'include') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index a47bd22cd309..b5ccb44860df 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 spinand-objs := core.o otp.o -spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o +spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index f86786344d52..35365b67dd8e 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1359,6 +1359,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &fmsh_spinand_manufacturer, &foresee_spinand_manufacturer, &gigadevice_spinand_manufacturer, + &heyangtek_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, ¶gon_spinand_manufacturer, diff --git a/drivers/mtd/nand/spi/heyangtek.c b/drivers/mtd/nand/spi/heyangtek.c new file mode 100644 index 000000000000..7fc50fd3de08 --- /dev/null +++ b/drivers/mtd/nand/spi/heyangtek.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Authors: + * Andrey Zolotarev - the main driver logic + * Aleksei Sviridkin - adaptation to the mainline Linux kernel + * + * Based on: + * https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7 + */ + +#include +#include +#include + +#define SPINAND_MFR_HEYANGTEK 0xc9 + +#define HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS (3 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +/* + * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken + * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each + * 16-byte section, the bad block marker sits in byte 0 and the remaining + * bytes are exposed as free. + */ +static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + + return 0; +} + +static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + if (section) { + region->offset = 16 * section; + region->length = 8; + } else { + /* section 0 has one byte reserved for the bad block marker */ + region->offset = 1; + region->length = 7; + } + + return 0; +} + +static const struct mtd_ooblayout_ops hyf1gq4_ooblayout = { + .ecc = hyf1gq4_ooblayout_ecc, + .free = hyf1gq4_ooblayout_free, +}; + +static int hyf1gq4_ecc_get_status(struct spinand_device *spinand, u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * The die exposes only a coarse 2-bit ECC status and has no + * register for the exact bitflip count. This code means + * "corrected, below the refresh threshold", so report half of + * the ECC strength as a representative value. + */ + return nanddev_get_ecc_conf(nand)->strength / 2; + + case HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS: + /* + * "Corrected, refresh recommended": report the full ECC + * strength so the upper layers relocate the data. + */ + return nanddev_get_ecc_conf(nand)->strength; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info heyangtek_spinand_table[] = { + SPINAND_INFO("HYF1GQ4UDACAE", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&hyf1gq4_ooblayout, + hyf1gq4_ecc_get_status)), +}; + +static const struct spinand_manufacturer_ops heyangtek_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer heyangtek_spinand_manufacturer = { + .id = SPINAND_MFR_HEYANGTEK, + .name = "HeYangTek", + .chips = heyangtek_spinand_table, + .nchips = ARRAY_SIZE(heyangtek_spinand_table), + .ops = &heyangtek_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index ec6efcfeef83..5f4c00ae72a7 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -437,6 +437,7 @@ extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; extern const struct spinand_manufacturer fmsh_spinand_manufacturer; extern const struct spinand_manufacturer foresee_spinand_manufacturer; extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; +extern const struct spinand_manufacturer heyangtek_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer paragon_spinand_manufacturer; -- cgit From 70cfc11cfcfb07ca8eeed1f667cc0eec9e82aa71 Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Fri, 19 Jun 2026 09:38:54 -0700 Subject: remoteproc: xlnx: Refactor start & stop ops Current _start and _stop ops are implemented using various APIs from the platform management firmware driver. Instead provide respective RPU start and stop API in the firmware driver and move the logic to interact with the PM firmware in the firmware driver. The remoteproc driver doesn't need to know actual logic, but only the final result i.e. RPU start/stop was success or not. This refactor keeps the remoteproc driver simple and moves firmware interaction logic to the firmware driver. Signed-off-by: Tanmay Shah Acked-by: Michal Simek Link: https://lore.kernel.org/r/20260619163854.410392-1-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier --- drivers/firmware/xilinx/zynqmp.c | 93 +++++++++++++++++++++++++++++++++ drivers/remoteproc/xlnx_r5_remoteproc.c | 68 ++---------------------- include/linux/firmware/xlnx-zynqmp.h | 12 +++++ 3 files changed, 110 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index af838b2dc327..f9a3a95b0638 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -1513,6 +1513,99 @@ int zynqmp_pm_request_wake(const u32 node, } EXPORT_SYMBOL_GPL(zynqmp_pm_request_wake); +/** + * zynqmp_pm_start_rpu - Boot Real-time Processing Unit (Cortex-R) on SoC + * + * @node: power-domains id of the core + * @bootaddr: Boot address of elf + * + * Return: status, either success or error+reason + */ +int zynqmp_pm_start_rpu(const u32 node, const u64 bootaddr) +{ + enum rpu_boot_mem bootmem; + int ret; + + /* + * The exception vector pointers (EVP) refer to the base-address of + * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector + * starts at the base-address and subsequent vectors are on 4-byte + * boundaries. + * + * Exception vectors can start either from 0x0000_0000 (LOVEC) or + * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip Memory) + * + * Usually firmware will put Exception vectors at LOVEC. + * + * It is not recommend that you change the exception vector. + * Changing the EVP to HIVEC will result in increased interrupt latency + * and jitter. Also, if the OCM is secured and the Cortex-R5F processor + * is non-secured, then the Cortex-R5F processor cannot access the + * HIVEC exception vectors in the OCM. + */ + bootmem = (bootaddr >= 0xFFFC0000) ? + PM_RPU_BOOTMEM_HIVEC : PM_RPU_BOOTMEM_LOVEC; + + pr_debug("RPU boot addr 0x%llx from %s.", bootaddr, + bootmem == PM_RPU_BOOTMEM_HIVEC ? "OCM" : "TCM"); + + /* Request node before starting RPU core if new version of API is supported */ + if (zynqmp_pm_feature(PM_REQUEST_NODE) > PM_API_VERSION_1) { + ret = zynqmp_pm_request_node(node, + ZYNQMP_PM_CAPABILITY_ACCESS, 0, + ZYNQMP_PM_REQUEST_ACK_BLOCKING); + if (ret < 0) { + pr_err("failed to request 0x%x", node); + return ret; + } + } + + ret = zynqmp_pm_request_wake(node, true, + bootmem, ZYNQMP_PM_REQUEST_ACK_NO); + if (ret) + pr_err("failed to start RPU = 0x%x\n", node); + return ret; +} +EXPORT_SYMBOL_GPL(zynqmp_pm_start_rpu); + +/** + * zynqmp_pm_stop_rpu - Stop Real-time Processing Unit (Cortex-R) on SoC + * + * @node: power-domains id of the core + * + * Return: status, either success or error+reason + */ +int zynqmp_pm_stop_rpu(const u32 node) +{ + int ret; + + /* Use release node API to stop core if new version of API is supported */ + if (zynqmp_pm_feature(PM_RELEASE_NODE) > PM_API_VERSION_1) { + ret = zynqmp_pm_release_node(node); + if (ret) + pr_err("failed to stop remoteproc RPU %d\n", ret); + return ret; + } + + /* + * Check expected version of EEMI call before calling it. This avoids + * any error or warning prints from firmware as it is expected that fw + * doesn't support it. + */ + if (zynqmp_pm_feature(PM_FORCE_POWERDOWN) != PM_API_VERSION_1) { + pr_debug("EEMI interface %d ver 1 not supported\n", + PM_FORCE_POWERDOWN); + return -EOPNOTSUPP; + } + + /* maintain force pwr down for backward compatibility */ + ret = zynqmp_pm_force_pwrdwn(node, ZYNQMP_PM_REQUEST_ACK_BLOCKING); + if (ret) + pr_err("core force power down failed\n"); + return ret; +} +EXPORT_SYMBOL_GPL(zynqmp_pm_stop_rpu); + /** * zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves * @node: Node ID of the slave diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 7000f08975f0..7014d48c1228 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -364,49 +364,12 @@ static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) static int zynqmp_r5_rproc_start(struct rproc *rproc) { struct zynqmp_r5_core *r5_core = rproc->priv; - enum rpu_boot_mem bootmem; int ret; - /* - * The exception vector pointers (EVP) refer to the base-address of - * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector - * starts at the base-address and subsequent vectors are on 4-byte - * boundaries. - * - * Exception vectors can start either from 0x0000_0000 (LOVEC) or - * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip Memory) - * - * Usually firmware will put Exception vectors at LOVEC. - * - * It is not recommend that you change the exception vector. - * Changing the EVP to HIVEC will result in increased interrupt latency - * and jitter. Also, if the OCM is secured and the Cortex-R5F processor - * is non-secured, then the Cortex-R5F processor cannot access the - * HIVEC exception vectors in the OCM. - */ - bootmem = (rproc->bootaddr >= 0xFFFC0000) ? - PM_RPU_BOOTMEM_HIVEC : PM_RPU_BOOTMEM_LOVEC; - - dev_dbg(r5_core->dev, "RPU boot addr 0x%llx from %s.", rproc->bootaddr, - bootmem == PM_RPU_BOOTMEM_HIVEC ? "OCM" : "TCM"); - - /* Request node before starting RPU core if new version of API is supported */ - if (zynqmp_pm_feature(PM_REQUEST_NODE) > 1) { - ret = zynqmp_pm_request_node(r5_core->pm_domain_id, - ZYNQMP_PM_CAPABILITY_ACCESS, 0, - ZYNQMP_PM_REQUEST_ACK_BLOCKING); - if (ret < 0) { - dev_err(r5_core->dev, "failed to request 0x%x", - r5_core->pm_domain_id); - return ret; - } - } - - ret = zynqmp_pm_request_wake(r5_core->pm_domain_id, 1, - bootmem, ZYNQMP_PM_REQUEST_ACK_NO); + ret = zynqmp_pm_start_rpu(r5_core->pm_domain_id, rproc->bootaddr); if (ret) - dev_err(r5_core->dev, - "failed to start RPU = 0x%x\n", r5_core->pm_domain_id); + dev_err(&rproc->dev, "failed to start RPU\n"); + return ret; } @@ -423,30 +386,9 @@ static int zynqmp_r5_rproc_stop(struct rproc *rproc) struct zynqmp_r5_core *r5_core = rproc->priv; int ret; - /* Use release node API to stop core if new version of API is supported */ - if (zynqmp_pm_feature(PM_RELEASE_NODE) > 1) { - ret = zynqmp_pm_release_node(r5_core->pm_domain_id); - if (ret) - dev_err(r5_core->dev, "failed to stop remoteproc RPU %d\n", ret); - return ret; - } - - /* - * Check expected version of EEMI call before calling it. This avoids - * any error or warning prints from firmware as it is expected that fw - * doesn't support it. - */ - if (zynqmp_pm_feature(PM_FORCE_POWERDOWN) != 1) { - dev_dbg(r5_core->dev, "EEMI interface %d ver 1 not supported\n", - PM_FORCE_POWERDOWN); - return -EOPNOTSUPP; - } - - /* maintain force pwr down for backward compatibility */ - ret = zynqmp_pm_force_pwrdwn(r5_core->pm_domain_id, - ZYNQMP_PM_REQUEST_ACK_BLOCKING); + ret = zynqmp_pm_stop_rpu(r5_core->pm_domain_id); if (ret) - dev_err(r5_core->dev, "core force power down failed\n"); + dev_err(&rproc->dev, "failed to stop RPU\n"); return ret; } diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 7e27b0f7bf7e..347df66ee176 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -644,6 +644,8 @@ int zynqmp_pm_get_node_status(const u32 node, u32 *const status, u32 *const requirements, u32 *const usage); int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, u32 *const requirements, u32 *const usage); +int zynqmp_pm_start_rpu(const u32 node, const u64 bootaddr); +int zynqmp_pm_stop_rpu(const u32 node); int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value); int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value); @@ -960,6 +962,16 @@ static inline int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const statu return -ENODEV; } +static inline int zynqmp_pm_start_rpu(const u32 node, const u64 bootaddr) +{ + return -ENODEV; +} + +static inline int zynqmp_pm_stop_rpu(const u32 node) +{ + return -ENODEV; +} + static inline int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) -- cgit From ae794aa6e88d43289ab9a9b682f5cc5f6ac92657 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:23 +0800 Subject: dt-bindings: clock: Add spread spectrum definition Per dt-schema, the modulation methods are: down-spread(3), up-spread(2), center-spread(1), no-spread(0). So define them in dt-bindings to avoid write the magic number in device tree. Reviewed-by: Brian Masney Acked-by: Rob Herring (Arm) Reviewed-by: Sebin Francis Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- include/dt-bindings/clock/clock.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 include/dt-bindings/clock/clock.h (limited to 'include') diff --git a/include/dt-bindings/clock/clock.h b/include/dt-bindings/clock/clock.h new file mode 100644 index 000000000000..155e2653a120 --- /dev/null +++ b/include/dt-bindings/clock/clock.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Copyright 2025 NXP + */ + +#ifndef __DT_BINDINGS_CLOCK_H +#define __DT_BINDINGS_CLOCK_H + +#define CLK_SSC_NO_SPREAD 0 +#define CLK_SSC_CENTER_SPREAD 1 +#define CLK_SSC_UP_SPREAD 2 +#define CLK_SSC_DOWN_SPREAD 3 + +#endif /* __DT_BINDINGS_CLOCK_H */ -- cgit From c86814e70390a48bd4323ba4318cd8c0246e019b Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 12 Jun 2026 16:46:24 +0800 Subject: clk: Introduce clk_hw_set_spread_spectrum Add clk_hw_set_spread_spectrum to configure a clock to enable spread spectrum feature. set_spread_spectrum ops is added for clk drivers to have their own hardware specific implementation. Reviewed-by: Brian Masney Reviewed-by: Sebin Francis Signed-off-by: Peng Fan Signed-off-by: Brian Masney --- drivers/clk/clk.c | 27 +++++++++++++++++++++++++++ include/linux/clk-provider.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 048adfa86a5d..8c78621cde25 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2774,6 +2774,33 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate) } EXPORT_SYMBOL_GPL(clk_set_max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, const struct clk_spread_spectrum *ss_conf) +{ + struct clk_core *core; + int ret; + + if (!hw) + return 0; + + core = hw->core; + + clk_prepare_lock(); + + ret = clk_pm_runtime_get(core); + if (ret) + goto fail; + + if (core->ops->set_spread_spectrum) + ret = core->ops->set_spread_spectrum(hw, ss_conf); + + clk_pm_runtime_put(core); + +fail: + clk_prepare_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(clk_hw_set_spread_spectrum); + /** * clk_get_parent - return the parent of a clk * @clk: the clk whose parent gets returned diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index b01a38fef8cf..7d3747378739 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -6,6 +6,7 @@ #ifndef __LINUX_CLK_PROVIDER_H #define __LINUX_CLK_PROVIDER_H +#include #include #include @@ -84,6 +85,26 @@ struct clk_duty { unsigned int den; }; +enum clk_ssc_method { + CLK_SPREAD_NO = CLK_SSC_NO_SPREAD, + CLK_SPREAD_CENTER = CLK_SSC_CENTER_SPREAD, + CLK_SPREAD_UP = CLK_SSC_UP_SPREAD, + CLK_SPREAD_DOWN = CLK_SSC_DOWN_SPREAD, +}; + +/** + * struct clk_spread_spectrum - Structure encoding spread spectrum of a clock + * + * @modfreq_hz: Modulation frequency + * @spread_bp: Modulation percent in permyriad + * @method: Modulation method + */ +struct clk_spread_spectrum { + u32 modfreq_hz; + u32 spread_bp; + enum clk_ssc_method method; +}; + /** * struct clk_ops - Callback operations for hardware clocks; these are to * be provided by the clock implementation, and will be called by drivers @@ -174,6 +195,12 @@ struct clk_duty { * separately via calls to .set_parent and .set_rate. * Returns 0 on success, -EERROR otherwise. * + * @set_spread_spectrum: Optional callback used to configure the spread + * spectrum modulation frequency, percentage, and method + * to reduce EMI by spreading the clock frequency over a + * wider range. + * Returns 0 on success, -EERROR otherwise. + * * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy * is expressed in ppb (parts per billion). The parent accuracy is * an input parameter. @@ -249,6 +276,8 @@ struct clk_ops { int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); + int (*set_spread_spectrum)(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); int (*get_phase)(struct clk_hw *hw); @@ -1436,6 +1465,8 @@ void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, unsigned long *max_rate); void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, unsigned long max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) { -- cgit From fc8c21a2b5247a56c3b5e2a40e3c59b6805c2212 Mon Sep 17 00:00:00 2001 From: Xuyang Dong Date: Fri, 5 Jun 2026 14:10:02 +0800 Subject: dt-bindings: clock: Add ESWIN eic7700 HSP clock and reset generator Add bindings for the high-speed peripherals clock and reset generator on the ESWIN EIC7700 HSP. Acked-by: Conor Dooley Signed-off-by: Xuyang Dong Signed-off-by: Brian Masney --- .../bindings/clock/eswin,eic7700-hspcrg.yaml | 63 ++++++++++++++++++++++ MAINTAINERS | 5 +- include/dt-bindings/clock/eswin,eic7700-hspcrg.h | 33 ++++++++++++ include/dt-bindings/reset/eswin,eic7700-hspcrg.h | 21 ++++++++ 4 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml create mode 100644 include/dt-bindings/clock/eswin,eic7700-hspcrg.h create mode 100644 include/dt-bindings/reset/eswin,eic7700-hspcrg.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml b/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml new file mode 100644 index 000000000000..43df689ae647 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/eswin,eic7700-hspcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN EIC7700 HSP Clock and Reset Generator + +maintainers: + - Xuyang Dong + +description: + Clock and reset generator for the ESWIN EIC7700 HSP (high-speed peripherals). + +properties: + compatible: + const: eswin,eic7700-hspcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: HSP configuration top clock + - description: MMC top clock + - description: SATA top clock + + clock-names: + items: + - const: cfg + - const: mmc + - const: sata + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + clock-controller@50440000 { + compatible = "eswin,eic7700-hspcrg"; + reg = <0x50440000 0x2000>; + clocks = <&clock 171>, <&clock 254>, <&clock 187>; + clock-names = "cfg", "mmc", "sata"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..2b8151f366ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9660,9 +9660,10 @@ ESWIN EIC7700 CLOCK DRIVER M: Yifeng Huang M: Xuyang Dong S: Maintained -F: Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml +F: Documentation/devicetree/bindings/clock/eswin,eic7700* F: drivers/clk/eswin/ -F: include/dt-bindings/clock/eswin,eic7700-clock.h +F: include/dt-bindings/clock/eswin,eic7700* +F: include/dt-bindings/reset/eswin,eic7700-hspcrg.h ET131X NETWORK DRIVER M: Mark Einon diff --git a/include/dt-bindings/clock/eswin,eic7700-hspcrg.h b/include/dt-bindings/clock/eswin,eic7700-hspcrg.h new file mode 100644 index 000000000000..1d1ff15c1154 --- /dev/null +++ b/include/dt-bindings/clock/eswin,eic7700-hspcrg.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Device Tree binding constants for EIC7700 HSP clock controller. + * + * Authors: Xuyang Dong + */ + +#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ +#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ + +#define EIC7700_HSP_CLK_FAC_CFG_DIV2 0 +#define EIC7700_HSP_CLK_FAC_CFG_DIV4 1 +#define EIC7700_HSP_CLK_FAC_MMC_DIV10 2 +#define EIC7700_HSP_CLK_MUX_EMMC_3MUX1 3 +#define EIC7700_HSP_CLK_MUX_SD0_3MUX1 4 +#define EIC7700_HSP_CLK_MUX_SD1_3MUX1 5 +#define EIC7700_HSP_CLK_MUX_EMMC_CQE_2MUX1 6 +#define EIC7700_HSP_CLK_MUX_SD0_CQE_2MUX1 7 +#define EIC7700_HSP_CLK_MUX_SD1_CQE_2MUX1 8 +#define EIC7700_HSP_CLK_GATE_MSHC0_TMR 9 +#define EIC7700_HSP_CLK_GATE_EMMC 10 +#define EIC7700_HSP_CLK_GATE_MSHC1_TMR 11 +#define EIC7700_HSP_CLK_GATE_SD0 12 +#define EIC7700_HSP_CLK_GATE_MSHC2_TMR 13 +#define EIC7700_HSP_CLK_GATE_SD1 14 +#define EIC7700_HSP_CLK_GATE_USB0 15 +#define EIC7700_HSP_CLK_GATE_USB1 16 +#define EIC7700_HSP_CLK_GATE_SATA 17 + +#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ */ diff --git a/include/dt-bindings/reset/eswin,eic7700-hspcrg.h b/include/dt-bindings/reset/eswin,eic7700-hspcrg.h new file mode 100644 index 000000000000..413fcd08c701 --- /dev/null +++ b/include/dt-bindings/reset/eswin,eic7700-hspcrg.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Device Tree binding constants for EIC7700 HSP reset controller. + * + * Authors: Xuyang Dong + */ + +#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ +#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ + +#define EIC7700_HSP_RST_SATA_P0 0 +#define EIC7700_HSP_RST_SATA_PHY 1 +#define EIC7700_HSP_RST_USB0 2 +#define EIC7700_HSP_RST_USB1 3 +#define EIC7700_HSP_RST_USB0_PHY 4 +#define EIC7700_HSP_RST_USB1_PHY 5 + +#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ */ -- cgit From e9f08e779976bbfef7d168c70350083878db7e2e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 15 Jun 2026 21:44:39 +0800 Subject: ASoC: SOF: add Intel UAOL sof_ipc_dai_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type will be used for Intel USB Audio Offload Link (UAOL) DAI. Signed-off-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260615134439.1044872-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 36809f712723..0b6a6ba6489a 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -90,6 +90,7 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ + SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ }; /* general purpose DAI configuration */ -- cgit From 2c599da8231ff45260d3267c6d334b80147f16c5 Mon Sep 17 00:00:00 2001 From: Alejandro Lucero Date: Mon, 29 Jun 2026 19:37:26 +0100 Subject: cxl: Support Type2 cxl regs mapping Export cxl core functions for a Type2 driver being able to discover and map the device registers. Signed-off-by: Alejandro Lucero Reviewed-by: Dan Williams Reviewed-by: Jonathan Cameron Reviewed-by: Dave Jiang Reviewed-by: Ben Cheatham Acked-by: Edward Cree Link: https://patch.msgid.link/20260629183727.51502-2-alejandro.lucero-palau@amd.com Signed-off-by: Dave Jiang --- drivers/cxl/core/pci.c | 1 + drivers/cxl/core/port.c | 1 + drivers/cxl/core/regs.c | 1 + drivers/cxl/cxlpci.h | 12 ------------ drivers/cxl/pci.c | 1 + include/cxl/pci.h | 22 ++++++++++++++++++++++ 6 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 include/cxl/pci.h (limited to 'include') diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index e4338fd7e01b..9d807c1a002c 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 1215ee4f4035..cb633e19151b 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c index 93710cf4f0a6..20c2d9fbcfe7 100644 --- a/drivers/cxl/core/regs.c +++ b/drivers/cxl/core/regs.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index b826eb53cf7b..110ec9c44f09 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -13,16 +13,6 @@ */ #define CXL_PCI_DEFAULT_MAX_VECTORS 16 -/* Register Block Identifier (RBI) */ -enum cxl_regloc_type { - CXL_REGLOC_RBI_EMPTY = 0, - CXL_REGLOC_RBI_COMPONENT, - CXL_REGLOC_RBI_VIRT, - CXL_REGLOC_RBI_MEMDEV, - CXL_REGLOC_RBI_PMU, - CXL_REGLOC_RBI_TYPES -}; - /* * Table Access DOE, CDAT Read Entry Response * @@ -112,6 +102,4 @@ static inline void devm_cxl_port_ras_setup(struct cxl_port *port) } #endif -int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, - struct cxl_register_map *map); #endif /* __CXL_PCI_H__ */ diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 267c679b0b3c..bb892dbfdd6d 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "cxlmem.h" #include "cxlpci.h" diff --git a/include/cxl/pci.h b/include/cxl/pci.h new file mode 100644 index 000000000000..3e0000015871 --- /dev/null +++ b/include/cxl/pci.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2020 Intel Corporation. All rights reserved. */ + +#ifndef __CXL_CXL_PCI_H__ +#define __CXL_CXL_PCI_H__ + +/* Register Block Identifier (RBI) */ +enum cxl_regloc_type { + CXL_REGLOC_RBI_EMPTY = 0, + CXL_REGLOC_RBI_COMPONENT, + CXL_REGLOC_RBI_VIRT, + CXL_REGLOC_RBI_MEMDEV, + CXL_REGLOC_RBI_PMU, + CXL_REGLOC_RBI_TYPES +}; + +struct cxl_register_map; +struct pci_dev; + +int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, + struct cxl_register_map *map); +#endif -- cgit From 96ddf1af34f5f9e29891a5bfb7a18dd0a5bab9d6 Mon Sep 17 00:00:00 2001 From: Alejandro Lucero Date: Mon, 29 Jun 2026 19:37:27 +0100 Subject: cxl: Support dpa without a mailbox Type3 relies on mailbox CXL_MBOX_OP_IDENTIFY command for initializing memdev state params which end up being used for DPA initialization. Allow a Type2 driver to initialize DPA simply by giving the size of its volatile hardware partition. Move related functions to memdev. Signed-off-by: Alejandro Lucero Reviewed-by: Dan Williams Reviewed-by: Dave Jiang Reviewed-by: Ben Cheatham Reviewed-by: Jonathan Cameron Acked-by: Edward Cree Link: https://patch.msgid.link/20260629183727.51502-3-alejandro.lucero-palau@amd.com Signed-off-by: Dave Jiang --- drivers/cxl/core/core.h | 2 ++ drivers/cxl/core/mbox.c | 51 +----------------------------------- drivers/cxl/core/memdev.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ include/cxl/cxl.h | 2 ++ 4 files changed, 72 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 07555ae63859..f7cebb026552 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -101,6 +101,8 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr, struct dentry *cxl_debugfs_create_dir(const char *dir); int cxl_dpa_set_part(struct cxl_endpoint_decoder *cxled, enum cxl_partition_mode mode); +struct cxl_memdev_state; +int cxl_mem_get_partition_info(struct cxl_memdev_state *mds); int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size); int cxl_dpa_free(struct cxl_endpoint_decoder *cxled); resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled); diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 7c6c5b7450a5..97b1e61ad018 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -1152,7 +1152,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_mem_get_event_records, "CXL"); * * See CXL @8.2.9.5.2.1 Get Partition Info */ -static int cxl_mem_get_partition_info(struct cxl_memdev_state *mds) +int cxl_mem_get_partition_info(struct cxl_memdev_state *mds) { struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; struct cxl_mbox_get_partition_info pi; @@ -1308,55 +1308,6 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd) return -EBUSY; } -static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode) -{ - int i = info->nr_partitions; - - if (size == 0) - return; - - info->part[i].range = (struct range) { - .start = start, - .end = start + size - 1, - }; - info->part[i].mode = mode; - info->nr_partitions++; -} - -int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info) -{ - struct cxl_dev_state *cxlds = &mds->cxlds; - struct device *dev = cxlds->dev; - int rc; - - if (!cxlds->media_ready) { - info->size = 0; - return 0; - } - - info->size = mds->total_bytes; - - if (mds->partition_align_bytes == 0) { - add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM); - add_part(info, mds->volatile_only_bytes, - mds->persistent_only_bytes, CXL_PARTMODE_PMEM); - return 0; - } - - rc = cxl_mem_get_partition_info(mds); - if (rc) { - dev_err(dev, "Failed to query partition information\n"); - return rc; - } - - add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM); - add_part(info, mds->active_volatile_bytes, mds->active_persistent_bytes, - CXL_PARTMODE_PMEM); - - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL"); - int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count) { struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 33a3d2e7b13a..2e457b1ebc7d 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -594,6 +594,73 @@ bool is_cxl_memdev(const struct device *dev) } EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "CXL"); +static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode) +{ + int i = info->nr_partitions; + + if (size == 0) + return; + + info->part[i].range = (struct range) { + .start = start, + .end = start + size - 1, + }; + info->part[i].mode = mode; + info->nr_partitions++; +} + +int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info) +{ + struct cxl_dev_state *cxlds = &mds->cxlds; + struct device *dev = cxlds->dev; + int rc; + + if (!cxlds->media_ready) { + info->size = 0; + return 0; + } + + info->size = mds->total_bytes; + + if (mds->partition_align_bytes == 0) { + add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM); + add_part(info, mds->volatile_only_bytes, + mds->persistent_only_bytes, CXL_PARTMODE_PMEM); + return 0; + } + + rc = cxl_mem_get_partition_info(mds); + if (rc) { + dev_err(dev, "Failed to query partition information\n"); + return rc; + } + + add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM); + add_part(info, mds->active_volatile_bytes, mds->active_persistent_bytes, + CXL_PARTMODE_PMEM); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL"); + + +/** + * cxl_set_capacity: initialize dpa by a driver without a mailbox. + * + * @cxlds: pointer to cxl_dev_state + * @capacity: device volatile memory size + */ +int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity) +{ + struct cxl_dpa_info range_info = { + .size = capacity, + }; + + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM); + return cxl_dpa_setup(cxlds, &range_info); +} +EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL"); + /** * set_exclusive_cxl_commands() - atomically disable user cxl commands * @mds: The device state to operate on diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h index 016c74fb747c..802b143de83d 100644 --- a/include/cxl/cxl.h +++ b/include/cxl/cxl.h @@ -226,4 +226,6 @@ struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev, struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds, struct range *range); + +int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity); #endif /* __CXL_CXL_H__ */ -- cgit From 19253cac2a9021733e047ab0c04594c7c21182a9 Mon Sep 17 00:00:00 2001 From: Jad Keskes Date: Wed, 17 Jun 2026 10:46:22 +0100 Subject: regulator: max14577: fix set_mode clobbering enable on MAX77836 LDOs So the PWRMD field in CNFG1_LDO is both the enable bit and the mode. You can't change one without stepping on the other. The problem is that enable() from the regulator core just writes enable_mask (which is PWRMD_NORMAL). If you'd called set_mode(LPM) then disabled and re-enabled, the mode gets reset to NORMAL. And set_mode updates the register through the same field, so it can accidentally enable a disabled regulator. Fix it by storing the mode in per-regulator data. A custom enable writes whatever mode was last set. set_mode only touches hardware if the regulator is already on; otherwise it just caches the value. Add of_map_mode while here so the initial mode can be wired from DT. Signed-off-by: Jad Keskes Acked-by: Lee Jones Link: https://patch.msgid.link/20260617094622.1846471-1-inasj268@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/max14577-regulator.c | 103 +++++++++++++++++++++++++++++++-- include/linux/mfd/max14577-private.h | 3 + 2 files changed, 102 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index c9d8d5e31cbd..cd592c5de148 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c @@ -123,15 +123,88 @@ static const struct regulator_desc max14577_supported_regulators[] = { [MAX14577_CHARGER] = MAX14577_CHARGER_REG, }; +struct max77836_ldo { + struct max14577 *max14577; + unsigned int mode; +}; + +static int max77836_ldo_enable(struct regulator_dev *rdev) +{ + struct max77836_ldo *ldo = rdev_get_drvdata(rdev); + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX77836_CNFG1_LDO_PWRMD_MASK, ldo->mode); +} + +static int max77836_ldo_disable(struct regulator_dev *rdev) +{ + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX77836_CNFG1_LDO_PWRMD_MASK, + MAX77836_CNFG1_LDO_PWRMD_OFF); +} + +static unsigned int max77836_ldo_get_mode(struct regulator_dev *rdev) +{ + struct max77836_ldo *ldo = rdev_get_drvdata(rdev); + + switch (ldo->mode) { + case MAX77836_CNFG1_LDO_PWRMD_LPM: + return REGULATOR_MODE_IDLE; + case MAX77836_CNFG1_LDO_PWRMD_NORMAL: + return REGULATOR_MODE_NORMAL; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int max77836_ldo_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct max77836_ldo *ldo = rdev_get_drvdata(rdev); + unsigned int val; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = MAX77836_CNFG1_LDO_PWRMD_NORMAL; + break; + case REGULATOR_MODE_IDLE: + val = MAX77836_CNFG1_LDO_PWRMD_LPM; + break; + default: + return -EINVAL; + } + + ldo->mode = val; + + /* Only touch hardware if the regulator is already on */ + if (regulator_is_enabled_regmap(rdev)) + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX77836_CNFG1_LDO_PWRMD_MASK, val); + + return 0; +} + +static unsigned int max77836_ldo_of_map_mode(unsigned int mode) +{ + switch (mode) { + case REGULATOR_MODE_NORMAL: + case REGULATOR_MODE_IDLE: + return mode; + default: + return REGULATOR_MODE_INVALID; + } +} + static const struct regulator_ops max77836_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, + .enable = max77836_ldo_enable, + .disable = max77836_ldo_disable, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - /* TODO: add .set_suspend_mode */ + .get_mode = max77836_ldo_get_mode, + .set_mode = max77836_ldo_set_mode, }; #define MAX77836_LDO_REG(num) { \ @@ -147,6 +220,7 @@ static const struct regulator_ops max77836_ldo_ops = { .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, \ .enable_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \ .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK, \ + .of_map_mode = max77836_ldo_of_map_mode, \ .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \ .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK, \ } @@ -205,7 +279,6 @@ static int max14577_regulator_probe(struct platform_device *pdev) } config.dev = max14577->dev; - config.driver_data = max14577; for (i = 0; i < supported_regulators_size; i++) { struct regulator_dev *regulator; @@ -217,6 +290,28 @@ static int max14577_regulator_probe(struct platform_device *pdev) config.init_data = pdata->regulators[i].initdata; config.of_node = pdata->regulators[i].of_node; } + + /* + * LDOs need per-regulator driver data to store their mode. + * The charger and safeout share the core MFD struct. + */ + if (dev_type == MAXIM_DEVICE_TYPE_MAX77836 && + (supported_regulators[i].id == MAX77836_LDO1 || + supported_regulators[i].id == MAX77836_LDO2)) { + struct max77836_ldo *ldo; + + ldo = devm_kzalloc(&pdev->dev, sizeof(*ldo), + GFP_KERNEL); + if (!ldo) + return -ENOMEM; + + ldo->max14577 = max14577; + ldo->mode = MAX77836_CNFG1_LDO_PWRMD_NORMAL; + config.driver_data = ldo; + } else { + config.driver_data = max14577; + } + config.regmap = max14577_get_regmap(max14577, supported_regulators[i].id); diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index dd51a37fa37f..5957e15b568e 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -350,6 +350,9 @@ enum max77836_pmic_reg { #define MAX77836_CNFG1_LDO_PWRMD_SHIFT 6 #define MAX77836_CNFG1_LDO_TV_SHIFT 0 #define MAX77836_CNFG1_LDO_PWRMD_MASK (0x3 << MAX77836_CNFG1_LDO_PWRMD_SHIFT) +#define MAX77836_CNFG1_LDO_PWRMD_OFF (0x0 << MAX77836_CNFG1_LDO_PWRMD_SHIFT) +#define MAX77836_CNFG1_LDO_PWRMD_LPM (0x1 << MAX77836_CNFG1_LDO_PWRMD_SHIFT) +#define MAX77836_CNFG1_LDO_PWRMD_NORMAL (0x3 << MAX77836_CNFG1_LDO_PWRMD_SHIFT) #define MAX77836_CNFG1_LDO_TV_MASK (0x3f << MAX77836_CNFG1_LDO_TV_SHIFT) /* LDO1/LDO2 CONFIG2 register */ -- cgit From 0efe609ef5b6ab286ec296369365efd2b9ce774f Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Fri, 26 Jun 2026 14:28:06 +0530 Subject: kunit,rust: Add ability to skip entire test suites Currently, KUnit provides mechanisms to skip individual test cases, but there is no way to skip an entire test suite based on runtime conditions checked during suite initialization. This limitation forces test suites to either fail or skip tests individually when certain prerequisites are not available. To address this limitation, the patch adds a 'status' field to struct kunit_suite that allows suite_init callbacks to mark the entire suite as KUNIT_SKIPPED. When a suite is marked as skipped, all test cases within that suite are bypassed without execution. The patch proposed changes to kunit_suite_has_succeeded() to Check suite status before evaluating individual test case results. Also kunit_run_tests() is updated to skip suite execution if kunit_suite's 'status' is KUNIT_SKIPPED, thats either set before suite_init or by the suite_init callback itself. kunit_init_suite() is updated to initialize the 'status' of kunit_suite to KUNIT_SUCCESS so that any skipped suite's can be restarted from debugfs. This enables test suites to perform runtime capability checks in their 'suite_init' callback and gracefully skip all tests when prerequisites are not met, rather than reporting failures or requiring each test case to perform redundant checks. In case a kunit-suite is skipped it can be re-run from the kunit's debugfs interface. Also update debugfs_print_results() to clearly log the kunit-suite as 'SKIP'. kunit_suite_has_succeeded() is also updated on which debugfs_print_results() depends to update 'kunit_suite.status' in case any of the kunit_case has failed. Finally, update KUnit Rust binding macro-rule 'kunit_unsafe_test_suite' to add and initialize the newly introduced 'kunit_suite.status'. Without this 'kunit_suite.status' field is never initialized which is an error for the Rust compiler. Link: https://patchwork.kernel.org/project/linux-kselftest/patch/20260626085811.151133-2-vaibhav@linux.ibm.com/mbox/ Reviewed-by: David Gow Signed-off-by: Vaibhav Jain Signed-off-by: Shuah Khan --- include/kunit/test.h | 1 + lib/kunit/debugfs.c | 30 +++++++++++++++++++++--------- lib/kunit/test.c | 17 ++++++++++++++++- rust/kernel/kunit.rs | 1 + 4 files changed, 39 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/kunit/test.h b/include/kunit/test.h index e52452e58305..da5312e0dfa5 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -285,6 +285,7 @@ struct kunit_suite { struct string_stream *log; int suite_init_err; bool is_init; + enum kunit_status status; }; /* Stores an array of suites, end points one past the end */ diff --git a/lib/kunit/debugfs.c b/lib/kunit/debugfs.c index 9c326f1837bd..442b2ceb955b 100644 --- a/lib/kunit/debugfs.c +++ b/lib/kunit/debugfs.c @@ -76,18 +76,30 @@ static int debugfs_print_results(struct seq_file *seq, void *v) seq_puts(seq, "KTAP version 1\n"); seq_puts(seq, "1..1\n"); - /* Print suite header because it is not stored in the test logs. */ - seq_puts(seq, KUNIT_SUBTEST_INDENT "KTAP version 1\n"); - seq_printf(seq, KUNIT_SUBTEST_INDENT "# Subtest: %s\n", suite->name); - seq_printf(seq, KUNIT_SUBTEST_INDENT "1..%zd\n", kunit_suite_num_test_cases(suite)); - - kunit_suite_for_each_test_case(suite, test_case) - debugfs_print_result(seq, test_case->log); + if (suite->status != KUNIT_SKIPPED) { + /* Print suite header because it is not stored in the test logs. */ + seq_puts(seq, + KUNIT_SUBTEST_INDENT "KTAP version 1\n"); + seq_printf(seq, + KUNIT_SUBTEST_INDENT "# Subtest: %s\n", + suite->name); + seq_printf(seq, + KUNIT_SUBTEST_INDENT "1..%zd\n", + kunit_suite_num_test_cases(suite)); + + kunit_suite_for_each_test_case(suite, test_case) + debugfs_print_result(seq, test_case->log); + } debugfs_print_result(seq, suite->log); - seq_printf(seq, "%s %d %s\n", - kunit_status_to_ok_not_ok(success), 1, suite->name); + if (suite->status != KUNIT_SKIPPED) + seq_printf(seq, "%s %d %s\n", + kunit_status_to_ok_not_ok(success), 1, suite->name); + else + seq_printf(seq, "%s %d %s # SKIP %s\n", + kunit_status_to_ok_not_ok(success), 1, suite->name, + suite->status_comment); return 0; } diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 99773e000e1b..09e3dabfac0c 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -214,12 +214,18 @@ enum kunit_status kunit_suite_has_succeeded(struct kunit_suite *suite) const struct kunit_case *test_case; enum kunit_status status = KUNIT_SKIPPED; + if (suite->status == KUNIT_SKIPPED) + return KUNIT_SKIPPED; + if (suite->suite_init_err) return KUNIT_FAILURE; kunit_suite_for_each_test_case(suite, test_case) { - if (test_case->status == KUNIT_FAILURE) + if (test_case->status == KUNIT_FAILURE) { + /* Update the kunit_suite status also */ + suite->status = KUNIT_FAILURE; return KUNIT_FAILURE; + } else if (test_case->status == KUNIT_SUCCESS) status = KUNIT_SUCCESS; } @@ -795,12 +801,20 @@ int kunit_run_tests(struct kunit_suite *suite) /* Taint the kernel so we know we've run tests. */ add_taint(TAINT_TEST, LOCKDEP_STILL_OK); + if (suite->status == KUNIT_SKIPPED) + goto suite_end; + if (suite->suite_init) { suite->suite_init_err = suite->suite_init(suite); if (suite->suite_init_err) { + suite->status = KUNIT_FAILURE; kunit_err(suite, KUNIT_SUBTEST_INDENT "# failed to initialize (%d)", suite->suite_init_err); goto suite_end; + + } else if (suite->status == KUNIT_SKIPPED) { + /* Skip this kunit suite */ + goto suite_end; } } @@ -825,6 +839,7 @@ static void kunit_init_suite(struct kunit_suite *suite) kunit_debugfs_create_suite(suite); suite->status_comment[0] = '\0'; suite->suite_init_err = 0; + suite->status = KUNIT_SUCCESS; if (suite->log) string_stream_clear(suite->log); diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index cdee5f27bd7f..91eaff8c186a 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -288,6 +288,7 @@ macro_rules! kunit_unsafe_test_suite { log: ::core::ptr::null_mut(), suite_init_err: 0, is_init: false, + status: kernel::bindings::kunit_status_KUNIT_SUCCESS, }; #[used(compiler)] -- cgit From ef9f74ee4ccc5f98108eb2dfbf293f64db61ccb1 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 11 May 2026 21:35:04 -0400 Subject: clk: add kernel docs for the core flags Let's add a DOC section for the clk core flags, and move the documentation for each flag into the doc header so that it can be easily referenced in the generated kernel documentation. Note: The comment about "Please update clk_flags..." is included as a separate comment so it doesn't show up in the generated documents. Reviewed-by: Maxime Ripard Signed-off-by: Brian Masney --- include/linux/clk-provider.h | 46 ++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7d3747378739..a2348b961538 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -10,28 +10,40 @@ #include #include -/* - * flags used across common struct clk. these flags should only affect the - * top-level framework. custom flags for dealing with hardware specifics - * belong in struct clk_foo +/** + * DOC: clk framework flags + * + * Flags used across common struct clk. These flags should only affect the + * top-level framework. Custom flags for dealing with hardware specifics + * belong in struct clk_foo. * - * Please update clk_flags[] in drivers/clk/clk.c when making changes here! + * * CLK_SET_RATE_GATE - must be gated across rate change + * * CLK_SET_PARENT_GATE - must be gated across re-parent + * * CLK_SET_RATE_PARENT - propagate rate change up one level + * * CLK_IGNORE_UNUSED - do not gate even if unused + * * CLK_GET_RATE_NOCACHE - do not use the cached clk rate + * * CLK_SET_RATE_NO_REPARENT - don't re-parent on rate change + * * CLK_GET_ACCURACY_NOCACHE - do not use the cached clk accuracy + * * CLK_RECALC_NEW_RATES - recalc rates after notifications + * * CLK_SET_RATE_UNGATE - clock needs to run to set rate + * * CLK_IS_CRITICAL - do not gate, ever + * * CLK_OPS_PARENT_ENABLE - parents need enable during gate/ungate, set rate and re-parent + * * CLK_DUTY_CYCLE_PARENT - duty cycle call may be forwarded to the parent clock */ -#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */ -#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */ -#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ -#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ +/* Please update clk_flags[] in drivers/clk/clk.c when making changes here! */ +#define CLK_SET_RATE_GATE BIT(0) +#define CLK_SET_PARENT_GATE BIT(1) +#define CLK_SET_RATE_PARENT BIT(2) +#define CLK_IGNORE_UNUSED BIT(3) /* unused */ /* unused */ -#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ -#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ -#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ -#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ -#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */ -#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ -/* parents need enable during gate/ungate, set rate and re-parent */ +#define CLK_GET_RATE_NOCACHE BIT(6) +#define CLK_SET_RATE_NO_REPARENT BIT(7) +#define CLK_GET_ACCURACY_NOCACHE BIT(8) +#define CLK_RECALC_NEW_RATES BIT(9) +#define CLK_SET_RATE_UNGATE BIT(10) +#define CLK_IS_CRITICAL BIT(11) #define CLK_OPS_PARENT_ENABLE BIT(12) -/* duty cycle call may be forwarded to the parent clock */ #define CLK_DUTY_CYCLE_PARENT BIT(13) struct clk; -- cgit From 862e0773f130d21a65c991f493b01f4ec040e821 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 5 May 2026 20:48:58 -0400 Subject: clk: add clk_determine_rate_noop() Add a new helper clk_determine_rate_noop() that's for clocks where the rate rounding is handled by the firmware/hardware, or the clock is capable of any rate. The requested rate is passed through unchanged, and the actual rate will be learned via recalc_rate() after the rate is set. This shared helper will be used to get rid of the driver-specific empty determine rate implementations that are present in the tree. Signed-off-by: Brian Masney --- drivers/clk/clk.c | 18 ++++++++++++++++++ include/linux/clk-provider.h | 1 + 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f97a7cecb200..fef87167a60b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -933,6 +933,24 @@ int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, } EXPORT_SYMBOL_GPL(clk_hw_determine_rate_no_reparent); +/** + * clk_determine_rate_noop - clk_ops::determine_rate noop implementation + * @hw: clk to determine rate on + * @req: rate request + * + * Noop determine rate for clocks where the rate rounding is handled by the + * firmware/hardware, or the clock is capable of any rate. The requested rate is + * passed through unchanged, and the actual rate will be learned via + * recalc_rate() after the rate is set. + * + * Returns: 0 always + */ +int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req) +{ + return 0; +} +EXPORT_SYMBOL_GPL(clk_determine_rate_noop); + /*** clk api ***/ static void clk_core_rate_unprotect(struct clk_core *core) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index a2348b961538..9d32e4a16eb8 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -1472,6 +1472,7 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long flags); int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, struct clk_rate_request *req); +int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req); void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent); void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, unsigned long *max_rate); -- cgit From b659b963206a9976a5ccc52a4b2aee1f674289cf Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:58:59 +0100 Subject: lib: kstrtox: add kstrtoudec64() and kstrtodec64() Add helpers that parses decimal numbers into 64-bit number, i.e., decimal point numbers with pre-defined scale are parsed into a 64-bit value (fixed precision). After the decimal point, digits beyond the specified scale are ignored. Signed-off-by: Rodrigo Alencar Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/kstrtox.h | 3 ++ lib/kstrtox.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) (limited to 'include') diff --git a/include/linux/kstrtox.h b/include/linux/kstrtox.h index 6c9282866770..b41b9736886f 100644 --- a/include/linux/kstrtox.h +++ b/include/linux/kstrtox.h @@ -97,6 +97,9 @@ int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); int __must_check kstrtobool(const char *s, bool *res); +int __must_check kstrtoudec64(const char *s, unsigned int scale, u64 *res); +int __must_check kstrtodec64(const char *s, unsigned int scale, s64 *res); + int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res); int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res); int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res); diff --git a/lib/kstrtox.c b/lib/kstrtox.c index 6d00162aea6c..bac1c057e1b0 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -394,6 +395,109 @@ int kstrtobool(const char *s, bool *res) } EXPORT_SYMBOL(kstrtobool); +static int _kstrtoudec64(const char *s, unsigned int scale, u64 *res) +{ + unsigned int rv_int, rv_frac; + u64 _res = 0; + + rv_int = _parse_integer(s, 10, &_res); + if (rv_int & KSTRTOX_OVERFLOW) + return -ERANGE; + s += rv_int; + + if (*s == '.') + s++; /* skip decimal point */ + + rv_frac = _parse_integer(s, 10, &_res, scale, _res); + if (rv_frac & KSTRTOX_OVERFLOW) + return -ERANGE; + s += rv_frac; + + /* + * Check input beyond rv_int and rv_frac to cover cases like ".5" with + * scale 0, which is considered a valid input, being parsed as 0. + */ + if (!rv_int && !rv_frac && !isdigit(*s)) + return -EINVAL; + + while (isdigit(*s)) /* truncate digits */ + s++; + + if (*s == '\n') + s++; + if (*s) + return -EINVAL; + + if (_res && ((scale - rv_frac) > 19 /* log10(2^64) = 19.26 */ || + check_mul_overflow(_res, int_pow(10, scale - rv_frac), &_res))) + return -ERANGE; + + *res = _res; + return 0; +} + +/** + * kstrtoudec64() - Convert a string to an unsigned 64-bit scaled decimal value. + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign, but not a minus sign. + * @scale: The number of digits to the right of the decimal point. + * @res: Where to write the result of the conversion on success. + * + * For example, a scale of 3 with input "123.45" results in 123450. Note that + * trailing zeros in the fractional part input to match the scale are not + * required. Also, digits beyond the specified scale are ignored. + * + * Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + */ +noinline +int kstrtoudec64(const char *s, unsigned int scale, u64 *res) +{ + if (s[0] == '+') + s++; + return _kstrtoudec64(s, scale, res); +} +EXPORT_SYMBOL(kstrtoudec64); + +/** + * kstrtodec64() - Convert a string to a signed 64-bit scaled decimal value. + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign or a minus sign. + * @scale: The number of digits to the right of the decimal point. + * @res: Where to write the result of the conversion on success. + * + * For example, a scale of 4 with input "-3.141592" results in -31415. Note + * that digits beyond the specified scale are ignored. Also, trailing zeros in + * the fractional part input to match the scale are not required. + * + * Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + */ +noinline +int kstrtodec64(const char *s, unsigned int scale, s64 *res) +{ + u64 tmp; + int rv; + + if (s[0] == '-') { + rv = _kstrtoudec64(s + 1, scale, &tmp); + if (rv < 0) + return rv; + if ((s64)-tmp > 0) + return -ERANGE; + *res = -tmp; + } else { + rv = kstrtoudec64(s, scale, &tmp); + if (rv < 0) + return rv; + if ((s64)tmp < 0) + return -ERANGE; + *res = tmp; + } + return 0; +} +EXPORT_SYMBOL(kstrtodec64); + /* * Since "base" would be a nonsense argument, this open-codes the * _from_user helper instead of using the helper macro below. -- cgit From b89c1a68a5db61e63af9fdd41acc2a675187266c Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:01 +0100 Subject: lib: math: div64: add div64_s64_rem() Add div64_s64_rem() function, with 32-bit implementation that uses div64_u64_rem() and a branchless approach to resolve the sign of the remainder and quotient (negation in two's complement). Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- include/linux/math64.h | 18 ++++++++++++++++++ lib/math/div64.c | 15 +++++++++++++++ lib/math/test_mul_u64_u64_div_u64.c | 1 + 3 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/linux/math64.h b/include/linux/math64.h index cc305206d89f..99189410d4bb 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -57,6 +57,20 @@ static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) return dividend / divisor; } +/** + * div64_s64_rem - signed 64bit divide with 64bit divisor and remainder + * @dividend: signed 64bit dividend + * @divisor: signed 64bit divisor + * @remainder: pointer to signed 64bit remainder + * + * Return: sets ``*remainder``, then returns dividend / divisor + */ +static inline s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + /** * div64_u64 - unsigned 64bit divide with 64bit divisor * @dividend: unsigned 64bit dividend @@ -102,6 +116,10 @@ extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); extern u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder); #endif +#ifndef div64_s64_rem +extern s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder); +#endif + #ifndef div64_u64 extern u64 div64_u64(u64 dividend, u64 divisor); #endif diff --git a/lib/math/div64.c b/lib/math/div64.c index d1e92ea24fce..0b10ded09a9b 100644 --- a/lib/math/div64.c +++ b/lib/math/div64.c @@ -158,6 +158,21 @@ u64 div64_u64(u64 dividend, u64 divisor) EXPORT_SYMBOL(div64_u64); #endif +#ifndef div64_s64_rem +s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + s64 quot, t, rem; + + quot = div64_u64_rem(abs(dividend), abs(divisor), (u64 *)&rem); + t = dividend >> 63; + *remainder = (rem ^ t) - t; + t = (dividend ^ divisor) >> 63; + + return (quot ^ t) - t; +} +EXPORT_SYMBOL(div64_s64_rem); +#endif + #ifndef div64_s64 s64 div64_s64(s64 dividend, s64 divisor) { diff --git a/lib/math/test_mul_u64_u64_div_u64.c b/lib/math/test_mul_u64_u64_div_u64.c index 338d014f0c73..d12dc05938fb 100644 --- a/lib/math/test_mul_u64_u64_div_u64.c +++ b/lib/math/test_mul_u64_u64_div_u64.c @@ -157,6 +157,7 @@ static void __exit test_exit(void) #define __div64_32 __div64_32 #define div_s64_rem div_s64_rem #define div64_u64_rem div64_u64_rem +#define div64_s64_rem div64_s64_rem #define div64_u64 div64_u64 #define div64_s64 div64_s64 #define iter_div_u64_rem iter_div_u64_rem -- cgit From 454f60336d59559de5b29c3f5a8ac3a4163ad42a Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:02 +0100 Subject: iio: core: add decimal value formatting into 64-bit value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create new format types for iio values (IIO_VAL_DECIMAL64_*), which defines the representation of fixed decimal point values into a single 64-bit number. This new format increases the range of represented values, allowing for integer parts greater than 2^32, as bits are not "wasted" in the fractional part, which can be seen in IIO_VAL_INT_PLUS_MICRO and IIO_VAL_INT_PLUS_NANO. Helpers are created to compose and decompose 64-bit decimals into integer values used in IIO formatting interfaces, which creates consistency and avoid error-prone manual assignments when using wordpart macros. When doing the parsing, kstrtodec64() is used with the scale defined by the specific decimal format type. Signed-off-by: Rodrigo Alencar Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 49 ++++++++++++++++++++++++++++++++--------- include/linux/iio/types.h | 20 +++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 7e3d3872e2e6..bdf3d4c06331 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include @@ -656,6 +656,7 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, int size, const int *vals) { int tmp0, tmp1; + int l = 0; s64 tmp2; bool scale_db = false; @@ -699,7 +700,6 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_INT_MULTIPLE: { int i; - int l = 0; for (i = 0; i < size; ++i) l += sysfs_emit_at(buf, offset + l, "%d ", vals[i]); @@ -708,8 +708,25 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_CHAR: return sysfs_emit_at(buf, offset, "%c", (char)vals[0]); case IIO_VAL_INT_64: - tmp2 = (s64)((((u64)vals[1]) << 32) | (u32)vals[0]); - return sysfs_emit_at(buf, offset, "%lld", tmp2); + return sysfs_emit_at(buf, offset, "%lld", + iio_val_s64_compose(vals[0], vals[1])); + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + { + int scale = type - IIO_VAL_DECIMAL64_BASE; + s64 frac; + + tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]), + int_pow(10, scale), &frac); + if (tmp2 == 0 && frac < 0) + l += sysfs_emit_at(buf, offset, "-"); + + l += sysfs_emit_at(buf, offset + l, "%lld.%0*lld", tmp2, scale, + abs(frac)); + return l; + } default: return 0; } @@ -979,6 +996,7 @@ static ssize_t iio_write_channel_info(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); int ret, fract_mult = 100000; + int type, dec_scale = 0; int integer, fract = 0; long long integer64; bool is_char = false; @@ -989,9 +1007,11 @@ static ssize_t iio_write_channel_info(struct device *dev, if (!indio_dev->info->write_raw) return -EINVAL; - if (indio_dev->info->write_raw_get_fmt) - switch (indio_dev->info->write_raw_get_fmt(indio_dev, - this_attr->c, this_attr->address)) { + if (indio_dev->info->write_raw_get_fmt) { + type = indio_dev->info->write_raw_get_fmt(indio_dev, + this_attr->c, + this_attr->address); + switch (type) { case IIO_VAL_INT: fract_mult = 0; break; @@ -1007,12 +1027,19 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_CHAR: is_char = true; break; + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + dec_scale = type - IIO_VAL_DECIMAL64_BASE; + fallthrough; case IIO_VAL_INT_64: is_64bit = true; break; default: return -EINVAL; } + } if (is_char) { char ch; @@ -1021,12 +1048,14 @@ static ssize_t iio_write_channel_info(struct device *dev, return -EINVAL; integer = ch; } else if (is_64bit) { - ret = kstrtoll(buf, 0, &integer64); + if (dec_scale) + ret = kstrtodec64(buf, dec_scale, &integer64); + else + ret = kstrtoll(buf, 0, &integer64); if (ret) return ret; - fract = upper_32_bits(integer64); - integer = lower_32_bits(integer64); + iio_val_s64_decompose(integer64, &integer, &fract); } else { ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract, scale_db); diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 4e3099defc1d..924ac9dc6893 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -7,6 +7,9 @@ #ifndef _IIO_TYPES_H_ #define _IIO_TYPES_H_ +#include +#include + #include enum iio_event_info { @@ -34,6 +37,23 @@ enum iio_event_info { #define IIO_VAL_FRACTIONAL_LOG2 11 #define IIO_VAL_CHAR 12 +#define IIO_VAL_DECIMAL64_BASE 32 +#define IIO_VAL_DECIMAL64_MILLI (IIO_VAL_DECIMAL64_BASE + 3) +#define IIO_VAL_DECIMAL64_MICRO (IIO_VAL_DECIMAL64_BASE + 6) +#define IIO_VAL_DECIMAL64_NANO (IIO_VAL_DECIMAL64_BASE + 9) +#define IIO_VAL_DECIMAL64_PICO (IIO_VAL_DECIMAL64_BASE + 12) + +static inline s64 iio_val_s64_compose(s32 val0, s32 val1) +{ + return (s64)(((u64)val1 << 32) | (u32)val0); +} + +static inline void iio_val_s64_decompose(s64 dec64, s32 *val0, s32 *val1) +{ + *val0 = lower_32_bits(dec64); + *val1 = upper_32_bits(dec64); +} + enum iio_available_type { IIO_AVAIL_LIST, IIO_AVAIL_RANGE, -- cgit From 1258f9c9ff1dba0d081e27a5f85597a916249988 Mon Sep 17 00:00:00 2001 From: Chenyu Chen Date: Tue, 26 May 2026 10:59:50 +0800 Subject: drm/edid: parse panel type from DisplayID 2.x Display Parameters Parse the Display Parameters Data Block (tag 0x21) defined in DisplayID v2.1a Section 4.2.6. Extract the Display Device Technology field from the color depth and device technology byte, which indicates whether the panel uses LCD or OLED technology. Add a panel_type field to struct drm_display_info and populate it during DisplayID iteration so downstream drivers can use it for panel-type-dependent behavior. Add DRM_MODE_PANEL_TYPE_LCD to the UAPI panel type property alongside the existing OLED value. Assisted-by: Copilot:Claude-Opus-4.6 Signed-off-by: Chenyu Chen Reviewed-by: Jani Nikula Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260526030254.1460480-3-chen-yu.chen@amd.com Signed-off-by: Mario Limonciello --- drivers/gpu/drm/drm_connector.c | 3 ++- drivers/gpu/drm/drm_displayid_internal.h | 24 +++++++++++++++++ drivers/gpu/drm/drm_edid.c | 45 ++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 6 +++++ include/uapi/drm/drm_mode.h | 1 + 5 files changed, 78 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index cbb067d02cb9..d194312cb6c5 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1189,6 +1189,7 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = { static const struct drm_prop_enum_list drm_panel_type_enum_list[] = { { DRM_MODE_PANEL_TYPE_UNKNOWN, "unknown" }, { DRM_MODE_PANEL_TYPE_OLED, "OLED" }, + { DRM_MODE_PANEL_TYPE_LCD, "LCD" }, }; /** @@ -1533,7 +1534,7 @@ EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name); * never read back the value of "DPMS" because it can be incorrect. * panel_type: * Immutable enum property to indicate the type of connected panel. - * Possible values are "unknown" (default) and "OLED". + * Possible values are "unknown" (default), "OLED", and "LCD". * PATH: * Connector path property to identify how this sink is physically * connected. Used by DP MST. This should be set by calling diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h index 5b1b32f73516..6f431aafafcf 100644 --- a/drivers/gpu/drm/drm_displayid_internal.h +++ b/drivers/gpu/drm/drm_displayid_internal.h @@ -142,6 +142,30 @@ struct displayid_formula_timing_block { struct displayid_formula_timings_9 timings[]; } __packed; +#define DISPLAYID_DEVICE_TECH_UNSPECIFIED 0 +#define DISPLAYID_DEVICE_TECH_LCD 1 +#define DISPLAYID_DEVICE_TECH_OLED 2 + +#define DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH GENMASK(6, 4) + +struct displayid_display_params_block { + struct displayid_block base; + __le16 horiz_image_size; + __le16 vert_image_size; + __le16 horiz_pixel_count; + __le16 vert_pixel_count; + u8 features; + u8 primary_color1[3]; + u8 primary_color2[3]; + u8 primary_color3[3]; + u8 white_point[3]; + __le16 max_luminance_full; + __le16 max_luminance_10; + __le16 min_luminance; + u8 color_depth_and_tech; /* [2:0] depth, [6:4] device tech, [7] theme */ + u8 gamma_eotf; +} __packed; + #define DISPLAYID_VESA_MSO_OVERLAP GENMASK(3, 0) #define DISPLAYID_VESA_MSO_MODE GENMASK(6, 5) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index aebbff8ac992..ae26618a9a57 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6713,6 +6713,8 @@ static void drm_reset_display_info(struct drm_connector *connector) info->source_physical_address = CEC_PHYS_ADDR_INVALID; memset(&info->amd_vsdb, 0, sizeof(info->amd_vsdb)); + + info->panel_type = DRM_MODE_PANEL_TYPE_UNKNOWN; } static void drm_displayid_process_base_section_header(struct drm_connector *connector, @@ -6731,6 +6733,45 @@ static void drm_displayid_process_base_section_header(struct drm_connector *conn info->non_desktop = true; } +static void +drm_displayid_parse_display_params(struct drm_connector *connector, + const struct displayid_block *block) +{ + struct drm_display_info *info = &connector->display_info; + const struct displayid_display_params_block *params = + (const struct displayid_display_params_block *)block; + u8 tech; + + if (block->num_bytes < sizeof(*params) - sizeof(params->base)) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID Display Parameters block too short (%u < %zu)\n", + connector->base.id, connector->name, + block->num_bytes, + sizeof(*params) - sizeof(params->base)); + return; + } + + tech = FIELD_GET(DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH, + params->color_depth_and_tech); + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID Display Parameters: device technology %s\n", + connector->base.id, connector->name, + tech == DISPLAYID_DEVICE_TECH_LCD ? "LCD" : + tech == DISPLAYID_DEVICE_TECH_OLED ? "OLED" : "unspecified"); + + switch (tech) { + case DISPLAYID_DEVICE_TECH_LCD: + info->panel_type = DRM_MODE_PANEL_TYPE_LCD; + break; + case DISPLAYID_DEVICE_TECH_OLED: + info->panel_type = DRM_MODE_PANEL_TYPE_OLED; + break; + default: + break; + } +} + static void update_displayid_info(struct drm_connector *connector, const struct drm_edid *drm_edid) { @@ -6744,6 +6785,10 @@ static void update_displayid_info(struct drm_connector *connector, drm_displayid_process_base_section_header(connector, &iter); base_section_header_processed = true; } + + if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 && + block->tag == DATA_BLOCK_2_DISPLAY_PARAMETERS) + drm_displayid_parse_display_params(connector, block); } displayid_iter_end(&iter); } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 4317166562cf..0cb72aa081d9 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -989,6 +989,12 @@ struct drm_display_info { * @amd_vsdb: AMD-specific VSDB information. */ struct drm_amd_vsdb_info amd_vsdb; + + /** + * @panel_type: Panel type from DisplayID Display Parameters + * Data Block (tag 0x21). Uses DRM_MODE_PANEL_TYPE_* constants. + */ + u8 panel_type; }; int drm_display_info_set_bus_formats(struct drm_display_info *info, diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 381a3e857d4e..bd435effdcee 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -155,6 +155,7 @@ extern "C" { /* Panel type property */ #define DRM_MODE_PANEL_TYPE_UNKNOWN 0 #define DRM_MODE_PANEL_TYPE_OLED 1 +#define DRM_MODE_PANEL_TYPE_LCD 2 /* * DRM_MODE_ROTATE_ -- cgit From 90ac22ffef48dbc6e7561434b6e01753a859bb51 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Mar 2026 15:42:35 +0100 Subject: sched/fair: Add cgroup_mode: max In order to avoid the average CPU fraction avg(F_g_n) becoming tiny '1/N', assume each cgroup is maximally concurrent and distrubute 'N*weight', such that: F_g_n' = N * F_g_n Giving: avg(F_g_n') = N*avg(F_g_n) ~ N * 1/N = 1 And while this sounds like it solves things, remember what that ~ meant. There is the corner case when a cgroup is minimally loaded, eg a single runnable task, therefore limit the CPU fraction to that of a nice -20 task to avoid getting too much load. This last bit is what makes it different from a previous proposal to allow raising cpu.weight to '100 * N', that would not limit the mininal concurrency case and results in a very large F_g_n. And just like F_g_n << 1 is problematic, so is F_g_n >> 1 for the exact same reasons (it would drown the kthreads, but it also risks overflowing the load values). So while this might appear to be a better scheme than the current default scheme, it doesn't really handle less than maximal concurrency nicely -- it clips and introduces artificially large weights. So where the traditional SMP mode works well when nr_tasks << nr_cpus, MAX doesn't work well in that regime and vice-versa. The meaning of "cpu.weight" would be: weight per allowed CPU. Included for completeness (and infrastructure). Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260605124051.589618504%40infradead.org --- include/linux/cpuset.h | 6 ++++++ kernel/cgroup/cpuset.c | 22 +++++++++++++++++++++ kernel/sched/debug.c | 1 + kernel/sched/fair.c | 52 +++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 76 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 65d76a38974b..9db2d4fcead1 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -80,6 +80,7 @@ extern void lockdep_assert_cpuset_lock_held(void); extern void cpuset_cpus_allowed_locked(struct task_struct *p, struct cpumask *mask); extern void cpuset_cpus_allowed(struct task_struct *p, struct cpumask *mask); extern bool cpuset_cpus_allowed_fallback(struct task_struct *p); +extern int cpuset_num_cpus(struct cgroup *cgroup); extern nodemask_t cpuset_mems_allowed(struct task_struct *p); #define cpuset_current_mems_allowed (current->mems_allowed) void cpuset_init_current_mems_allowed(void); @@ -216,6 +217,11 @@ static inline bool cpuset_cpus_allowed_fallback(struct task_struct *p) return false; } +static inline int cpuset_num_cpus(struct cgroup *cgroup) +{ + return num_online_cpus(); +} + static inline nodemask_t cpuset_mems_allowed(struct task_struct *p) { return node_possible_map; diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 591e3aa487fc..e2f3da71e03e 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -4124,6 +4124,28 @@ bool cpuset_cpus_allowed_fallback(struct task_struct *tsk) return changed; } +/* + * Returns the number of CPUs available for this cgroup. + * + * This only really works for cgroup-v2 where all the controllers are mounted + * in the same hierarchy. If not cgroup-v2 or no cpuset controller is + * configured it reverts to num_online_cpus(). + */ +int cpuset_num_cpus(struct cgroup *cgrp) +{ + int nr = num_online_cpus(); + struct cpuset *cs; + + if (is_in_v2_mode()) { + guard(rcu)(); + cs = css_cs(cgroup_e_css(cgrp, &cpuset_cgrp_subsys)); + if (cs) + nr = cpumask_weight(cs->effective_cpus); + } + + return nr; +} + void __init cpuset_init_current_mems_allowed(void) { nodes_setall(current->mems_allowed); diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 53b9e8218a2b..84e0ac40b6c0 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -640,6 +640,7 @@ static int cgroup_mode = 1; static const char *cgroup_mode_str[] = { "up", "smp", + "max", }; static int sched_cgroup_mode(const char *str) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 3f8a2801f23e..b556d53a191c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4801,12 +4801,10 @@ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq); * * hence icky! */ -static long calc_smp_shares(struct cfs_rq *cfs_rq) +static long __calc_smp_shares(struct cfs_rq *cfs_rq, long tg_shares, long shares_max) { - long tg_weight, tg_shares, load, shares; struct task_group *tg = cfs_rq->tg; - - tg_shares = READ_ONCE(tg->shares); + long tg_weight, load, shares; load = max(scale_load_down(cfs_rq->load.weight), cfs_rq->avg.load_avg); @@ -4832,7 +4830,48 @@ static long calc_smp_shares(struct cfs_rq *cfs_rq) * case no task is runnable on a CPU MIN_SHARES=2 should be returned * instead of 0. */ - return clamp_t(long, shares, MIN_SHARES, tg_shares); + return clamp_t(long, shares, MIN_SHARES, shares_max); +} + +static int tg_cpus(struct task_group *tg) +{ + int nr = num_online_cpus(); + + if (cpusets_enabled()) { + struct cgroup *cgrp = tg->css.cgroup; + if (cgrp) + nr = cpuset_num_cpus(cgrp); + } + + return nr; +} + +/* + * Func: min(fraction(nr_cpus * tg->shares), nice -20) + * + * Scale tg->shares by the maximal number of CPUs; but clip the max shares at + * nice -20, otherwise a single spinner on a 512 CPU machine would result in + * 512*NICE_0_LOAD, which is also crazy. + */ +static long calc_max_shares(struct cfs_rq *cfs_rq) +{ + struct task_group *tg = cfs_rq->tg; + int nr = tg_cpus(tg); + long tg_shares = READ_ONCE(tg->shares); + long max_shares = scale_load(sched_prio_to_weight[0]); + return __calc_smp_shares(cfs_rq, tg_shares * nr, max_shares); +} + +/* + * Func: fraction(tg->shares) + * + * This infamously results in tiny shares when you have many CPUs. + */ +static long calc_smp_shares(struct cfs_rq *cfs_rq) +{ + struct task_group *tg = cfs_rq->tg; + long tg_shares = READ_ONCE(tg->shares); + return __calc_smp_shares(cfs_rq, tg_shares, tg_shares); } /* @@ -4857,6 +4896,9 @@ void __sched_cgroup_mode_update(int mode) default: func = &calc_smp_shares; break; + case 2: + func = &calc_max_shares; + break; } static_call_update(calc_group_shares, func); } -- cgit From 85570f10a4c61372c1d437365b9d8dbad512ec6f Mon Sep 17 00:00:00 2001 From: "Peter Zijlstra (Intel)" Date: Sat, 6 Dec 2025 10:08:58 +0100 Subject: sched/eevdf: Move to a single runqueue Change fair/cgroup to a single runqueue. Infamously fair/cgroup isn't working for a number of people; typically the complaint is latencies and/or overhead. The latency issue is due to the intermediate entries that represent a combination of tasks and thereby obfuscate the runnability of tasks. The approach here is to leave the cgroup hierarchy as is; including the intermediate enqueue/dequeue but move the actual EEVDF runqueue outside. This means things like the shares_weight approximation are fully preserved. That is, given a hierarchy like: R | se--G1 / \ G2--se se--G3 / \ | T1--se se--T2 se--T3 This is fully maintained for load tracking, however the EEVDF parts of cfs_rq/se go unused for the intermediates and are instead connected like: _R_ / | \ T1 T2 T3 Since the effective weight of the entities is determined by the hierarchy, this gets recomputed on enqueue,set_next_task and tick. Notably, the effective weight (se->h_load) is computed from the hierarchical fraction: se->load / cfs_rq->load. Since EEVDF is now exclusively operating on rq->cfs, it needs to consider cfs_rq->h_nr_queued rather than cfs_rq->nr_queued. Similarly, only tasks can get delayed, simplifying some of the cgroup cleanup. One place where additional information was required was set_next_task() / put_prev_task(), where we need to track 'current' both in the hierarchical sense (cfs_rq->h_curr) and in the flat sense (cfs_rq->curr). As a result of only having a single level to pick from, much of the complications in pick_next_task() and preemption go away. Since many of the hierarchical operations are still there, this won't immediately fix the performance issues, but hopefully it will fix some of the latency issues. TODO: split struct cfs_rq / struct sched_entity TODO: try and get rid of h_curr Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260605124052.227463677%40infradead.org --- include/linux/sched.h | 1 + kernel/sched/core.c | 5 +- kernel/sched/debug.c | 9 +- kernel/sched/fair.c | 801 ++++++++++++++++++++++---------------------------- kernel/sched/pelt.c | 6 +- kernel/sched/sched.h | 26 +- 6 files changed, 374 insertions(+), 474 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 373bcc0598d1..12f633514ad2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -575,6 +575,7 @@ struct sched_statistics { struct sched_entity { /* For load-balancing: */ struct load_weight load; + struct load_weight h_load; struct rb_node run_node; u64 deadline; u64 min_vruntime; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 96226707c2f6..2e7cde033a31 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5657,11 +5657,8 @@ EXPORT_PER_CPU_SYMBOL(kernel_cpustat); */ static inline void prefetch_curr_exec_start(struct task_struct *p) { -#ifdef CONFIG_FAIR_GROUP_SCHED - struct sched_entity *curr = p->se.cfs_rq->curr; -#else struct sched_entity *curr = task_rq(p)->cfs.curr; -#endif + prefetch(curr); prefetch(&curr->exec_start); } diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index f54e732e6d97..c15291cbcbb3 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -975,10 +975,11 @@ print_task(struct seq_file *m, struct rq *rq, struct task_struct *p) else SEQ_printf(m, " %c", task_state_to_char(p)); - SEQ_printf(m, " %15s %5d %9Ld.%06ld %c %9Ld.%06ld %c %9Ld.%06ld %9Ld.%06ld %9Ld %5d ", + SEQ_printf(m, " %15s %5d %10ld %9Ld.%06ld %c %9Ld.%06ld %c %9Ld.%06ld %9Ld.%06ld %9Ld %5d ", p->comm, task_pid_nr(p), + p->se.h_load.weight, SPLIT_NS(p->se.vruntime), - entity_eligible(cfs_rq_of(&p->se), &p->se) ? 'E' : 'N', + entity_eligible(&rq->cfs, &p->se) ? 'E' : 'N', SPLIT_NS(p->se.deadline), p->se.custom_slice ? 'S' : ' ', SPLIT_NS(p->se.slice), @@ -1007,7 +1008,7 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu) SEQ_printf(m, "\n"); SEQ_printf(m, "runnable tasks:\n"); - SEQ_printf(m, " S task PID vruntime eligible " + SEQ_printf(m, " S task PID weight vruntime eligible " "deadline slice sum-exec switches " "prio wait-time sum-sleep sum-block" #ifdef CONFIG_NUMA_BALANCING @@ -1115,6 +1116,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) cfs_rq->tg_load_avg_contrib); SEQ_printf(m, " .%-30s: %ld\n", "tg_load_avg", atomic_long_read(&cfs_rq->tg->load_avg)); + SEQ_printf(m, " .%-30s: %lu\n", "h_load", + cfs_rq->h_load); #endif /* CONFIG_FAIR_GROUP_SCHED */ #ifdef CONFIG_CFS_BANDWIDTH SEQ_printf(m, " .%-30s: %d\n", "throttled", diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1608c01c36fb..cfaed2cb960e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -297,8 +297,8 @@ static u64 __calc_delta(u64 delta_exec, unsigned long weight, struct load_weight */ static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se) { - if (unlikely(se->load.weight != NICE_0_LOAD)) - delta = __calc_delta(delta, NICE_0_LOAD, &se->load); + if (se->h_load.weight != NICE_0_LOAD) + delta = __calc_delta(delta, NICE_0_LOAD, &se->h_load); return delta; } @@ -428,38 +428,6 @@ static inline struct sched_entity *parent_entity(const struct sched_entity *se) return se->parent; } -static void -find_matching_se(struct sched_entity **se, struct sched_entity **pse) -{ - int se_depth, pse_depth; - - /* - * preemption test can be made between sibling entities who are in the - * same cfs_rq i.e who have a common parent. Walk up the hierarchy of - * both tasks until we find their ancestors who are siblings of common - * parent. - */ - - /* First walk up until both entities are at same depth */ - se_depth = (*se)->depth; - pse_depth = (*pse)->depth; - - while (se_depth > pse_depth) { - se_depth--; - *se = parent_entity(*se); - } - - while (pse_depth > se_depth) { - pse_depth--; - *pse = parent_entity(*pse); - } - - while (!is_same_group(*se, *pse)) { - *se = parent_entity(*se); - *pse = parent_entity(*pse); - } -} - static int tg_is_idle(struct task_group *tg) { return tg->idle > 0; @@ -503,11 +471,6 @@ static inline struct sched_entity *parent_entity(struct sched_entity *se) return NULL; } -static inline void -find_matching_se(struct sched_entity **se, struct sched_entity **pse) -{ -} - static inline int tg_is_idle(struct task_group *tg) { return 0; @@ -686,7 +649,7 @@ static inline unsigned long avg_vruntime_weight(struct cfs_rq *cfs_rq, unsigned static inline void __sum_w_vruntime_add(struct cfs_rq *cfs_rq, struct sched_entity *se) { - unsigned long weight = avg_vruntime_weight(cfs_rq, se->load.weight); + unsigned long weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); s64 w_vruntime, key = entity_key(cfs_rq, se); w_vruntime = key * weight; @@ -703,7 +666,7 @@ sum_w_vruntime_add_paranoid(struct cfs_rq *cfs_rq, struct sched_entity *se) s64 key, tmp; again: - weight = avg_vruntime_weight(cfs_rq, se->load.weight); + weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); key = entity_key(cfs_rq, se); if (check_mul_overflow(key, weight, &key)) @@ -749,7 +712,7 @@ sum_w_vruntime_add(struct cfs_rq *cfs_rq, struct sched_entity *se) static void sum_w_vruntime_sub(struct cfs_rq *cfs_rq, struct sched_entity *se) { - unsigned long weight = avg_vruntime_weight(cfs_rq, se->load.weight); + unsigned long weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); s64 key = entity_key(cfs_rq, se); cfs_rq->sum_w_vruntime -= key * weight; @@ -791,7 +754,7 @@ u64 avg_vruntime(struct cfs_rq *cfs_rq) s64 runtime = cfs_rq->sum_w_vruntime; if (curr) { - unsigned long w = avg_vruntime_weight(cfs_rq, curr->load.weight); + unsigned long w = avg_vruntime_weight(cfs_rq, curr->h_load.weight); runtime += entity_key(cfs_rq, curr) * w; weight += w; @@ -862,8 +825,6 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) u64 avruntime = avg_vruntime(cfs_rq); s64 vlag = entity_lag(cfs_rq, se, avruntime); - WARN_ON_ONCE(!se->on_rq); - if (se->sched_delayed) { /* previous vlag < 0 otherwise se would not be delayed */ vlag = max(vlag, se->vlag); @@ -899,7 +860,7 @@ static int vruntime_eligible(struct cfs_rq *cfs_rq, u64 vruntime) long load = cfs_rq->sum_weight; if (curr && curr->on_rq) { - unsigned long weight = avg_vruntime_weight(cfs_rq, curr->load.weight); + unsigned long weight = avg_vruntime_weight(cfs_rq, curr->h_load.weight); avg += entity_key(cfs_rq, curr) * weight; load += weight; @@ -1040,6 +1001,9 @@ RB_DECLARE_CALLBACKS(static, min_vruntime_cb, struct sched_entity, */ static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(&rq_of(cfs_rq)->cfs != cfs_rq); + WARN_ON_ONCE(!entity_is_task(se)); + sum_w_vruntime_add(cfs_rq, se); se->min_vruntime = se->vruntime; se->min_slice = se->slice; @@ -1049,6 +1013,9 @@ static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(&rq_of(cfs_rq)->cfs != cfs_rq); + WARN_ON_ONCE(!entity_is_task(se)); + rb_erase_augmented_cached(&se->run_node, &cfs_rq->tasks_timeline, &min_vruntime_cb); sum_w_vruntime_sub(cfs_rq, se); @@ -1145,7 +1112,7 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq, bool protect) * We can safely skip eligibility check if there is only one entity * in this cfs_rq, saving some cycles. */ - if (cfs_rq->nr_queued == 1) + if (cfs_rq->h_nr_queued == 1) return curr && curr->on_rq ? curr : se; /* @@ -1395,8 +1362,6 @@ static s64 update_se(struct rq *rq, struct sched_entity *se) return delta_exec; } -static void set_next_buddy(struct sched_entity *se); - #ifdef CONFIG_SCHED_CACHE /* @@ -1991,7 +1956,7 @@ static void update_curr(struct cfs_rq *cfs_rq) * not necessarily be the actual task running * (rq->curr.se). This is easy to confuse! */ - struct sched_entity *curr = cfs_rq->curr; + struct sched_entity *curr = cfs_rq->h_curr; struct rq *rq = rq_of(cfs_rq); s64 delta_exec; bool resched; @@ -2003,26 +1968,29 @@ static void update_curr(struct cfs_rq *cfs_rq) if (unlikely(delta_exec <= 0)) return; + account_cfs_rq_runtime(cfs_rq, delta_exec); + + if (!entity_is_task(curr)) + return; + + cfs_rq = &rq->cfs; + curr->vruntime += calc_delta_fair(delta_exec, curr); resched = update_deadline(cfs_rq, curr); - if (entity_is_task(curr)) { - /* - * If the fair_server is active, we need to account for the - * fair_server time whether or not the task is running on - * behalf of fair_server or not: - * - If the task is running on behalf of fair_server, we need - * to limit its time based on the assigned runtime. - * - Fair task that runs outside of fair_server should account - * against fair_server such that it can account for this time - * and possibly avoid running this period. - */ - dl_server_update(&rq->fair_server, delta_exec); - } - - account_cfs_rq_runtime(cfs_rq, delta_exec); + /* + * If the fair_server is active, we need to account for the + * fair_server time whether or not the task is running on + * behalf of fair_server or not: + * - If the task is running on behalf of fair_server, we need + * to limit its time based on the assigned runtime. + * - Fair task that runs outside of fair_server should account + * against fair_server such that it can account for this time + * and possibly avoid running this period. + */ + dl_server_update(&rq->fair_server, delta_exec); - if (cfs_rq->nr_queued == 1) + if (cfs_rq->h_nr_queued == 1) return; if (resched || !protect_slice(curr)) { @@ -2033,7 +2001,10 @@ static void update_curr(struct cfs_rq *cfs_rq) static void update_curr_fair(struct rq *rq) { - update_curr(cfs_rq_of(&rq->donor->se)); + struct sched_entity *se = &rq->donor->se; + + for_each_sched_entity(se) + update_curr(cfs_rq_of(se)); } static inline void @@ -2109,7 +2080,7 @@ update_stats_enqueue_fair(struct cfs_rq *cfs_rq, struct sched_entity *se, int fl * Are we enqueueing a waiting task? (for current tasks * a dequeue/enqueue event is a NOP) */ - if (se != cfs_rq->curr) + if (se != cfs_rq->h_curr) update_stats_wait_start_fair(cfs_rq, se); if (flags & ENQUEUE_WAKEUP) @@ -2127,7 +2098,7 @@ update_stats_dequeue_fair(struct cfs_rq *cfs_rq, struct sched_entity *se, int fl * Mark the end of the wait period if dequeueing a * waiting task: */ - if (se != cfs_rq->curr) + if (se != cfs_rq->h_curr) update_stats_wait_end_fair(cfs_rq, se); if ((flags & DEQUEUE_SLEEP) && entity_is_task(se)) { @@ -4468,6 +4439,7 @@ static inline void update_scan_period(struct task_struct *p, int new_cpu) static void account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(cfs_rq != cfs_rq_of(se)); update_load_add(&cfs_rq->load, se->load.weight); if (entity_is_task(se)) { struct task_struct *p = task_of(se); @@ -4483,6 +4455,7 @@ account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se) static void account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(cfs_rq != cfs_rq_of(se)); update_load_sub(&cfs_rq->load, se->load.weight); if (entity_is_task(se)) { struct task_struct *p = task_of(se); @@ -4564,7 +4537,7 @@ dequeue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) static void rescale_entity(struct sched_entity *se, unsigned long weight, bool rel_vprot) { - unsigned long old_weight = se->load.weight; + long old_weight = se->h_load.weight; /* * VRUNTIME @@ -4664,16 +4637,17 @@ rescale_entity(struct sched_entity *se, unsigned long weight, bool rel_vprot) se->vprot = div64_long(se->vprot * old_weight, weight); } -static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, - unsigned long weight) +static void reweight_eevdf(struct cfs_rq *cfs_rq, struct sched_entity *se, + unsigned long weight, bool on_rq) { bool curr = cfs_rq->curr == se; bool rel_vprot = false; u64 avruntime = 0; - if (se->on_rq) { - /* commit outstanding execution time */ - update_curr(cfs_rq); + if (se->h_load.weight == weight) + return; + + if (on_rq) { avruntime = avg_vruntime(cfs_rq); se->vlag = entity_lag(cfs_rq, se, avruntime); se->deadline -= avruntime; @@ -4683,46 +4657,90 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, rel_vprot = true; } - cfs_rq->nr_queued--; + cfs_rq->h_nr_queued--; if (!curr) __dequeue_entity(cfs_rq, se); - update_load_sub(&cfs_rq->load, se->load.weight); } - dequeue_load_avg(cfs_rq, se); rescale_entity(se, weight, rel_vprot); - update_load_set(&se->load, weight); - - do { - u32 divider = get_pelt_divider(&se->avg); - se->avg.load_avg = div_u64(se_weight(se) * se->avg.load_sum, divider); - } while (0); + update_load_set(&se->h_load, weight); - enqueue_load_avg(cfs_rq, se); - if (se->on_rq) { + if (on_rq) { if (rel_vprot) se->vprot += avruntime; se->deadline += avruntime; se->rel_deadline = 0; se->vruntime = avruntime - se->vlag; - update_load_add(&cfs_rq->load, se->load.weight); if (!curr) __enqueue_entity(cfs_rq, se); - cfs_rq->nr_queued++; + cfs_rq->h_nr_queued++; } } +static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, + unsigned long weight) +{ + if (se->load.weight == weight) + return; + + if (se->on_rq) { + WARN_ON_ONCE(cfs_rq != cfs_rq_of(se)); + update_load_sub(&cfs_rq->load, se->load.weight); + } + dequeue_load_avg(cfs_rq, se); + + update_load_set(&se->load, weight); + + do { + u32 divider = get_pelt_divider(&se->avg); + se->avg.load_avg = div_u64(se_weight(se) * se->avg.load_sum, divider); + } while (0); + + enqueue_load_avg(cfs_rq, se); + + if (se->on_rq) + update_load_add(&cfs_rq->load, se->load.weight); +} + +/* + * weight = NICE_0_LOAD; + * for_each_entity_se(se) + * weight = __calc_prop_weight(cfs_rq_of(se), se, weight); + */ +static __always_inline +unsigned long __calc_prop_weight(struct cfs_rq *cfs_rq, struct sched_entity *se, + unsigned long weight) +{ + weight *= se->load.weight; + if (parent_entity(se)) + weight /= cfs_rq->load.weight; + else + weight /= NICE_0_LOAD; + + return max(weight, MIN_SHARES); +} + static void reweight_task_fair(struct rq *rq, struct task_struct *p, const struct load_weight *lw) { struct sched_entity *se = &p->se; - struct cfs_rq *cfs_rq = cfs_rq_of(se); - struct load_weight *load = &se->load; + unsigned long weight = NICE_0_LOAD; + + if (se->on_rq) + update_curr_fair(rq); - reweight_entity(cfs_rq, se, lw->weight); - load->inv_weight = lw->inv_weight; + reweight_entity(cfs_rq_of(se), se, lw->weight); + se->load.inv_weight = lw->inv_weight; + + if (!se->on_rq) + return; + + for_each_sched_entity(se) + weight = __calc_prop_weight(cfs_rq_of(se), se, weight); + + reweight_eevdf(&rq->cfs, &p->se, weight, p->se.on_rq); } static inline int throttled_hierarchy(struct cfs_rq *cfs_rq); @@ -4958,8 +4976,7 @@ static void update_cfs_group(struct sched_entity *se) return; shares = static_call(calc_group_shares)(gcfs_rq); - if (unlikely(se->load.weight != shares)) - reweight_entity(cfs_rq_of(se), se, shares); + reweight_entity(cfs_rq_of(se), se, shares); } #else /* !CONFIG_FAIR_GROUP_SCHED: */ @@ -5077,7 +5094,7 @@ static inline bool cfs_rq_is_decayed(struct cfs_rq *cfs_rq) * differential update where we store the last value we propagated. This in * turn allows skipping updates if the differential is 'small'. * - * Updating tg's load_avg is necessary before update_cfs_share(). + * Updating tg's load_avg is necessary before update_cfs_group(). */ static inline void update_tg_load_avg(struct cfs_rq *cfs_rq) { @@ -5545,7 +5562,7 @@ static void migrate_se_pelt_lag(struct sched_entity *se) {} * The cfs_rq avg is the direct sum of all its entities (blocked and runnable) * avg. The immediate corollary is that all (fair) tasks must be attached. * - * cfs_rq->avg is used for task_h_load() and update_cfs_share() for example. + * cfs_rq->avg is used for task_h_load() and update_cfs_group() for example. * * Return: true if the load decayed or we removed load. * @@ -6083,6 +6100,7 @@ static void place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { u64 vslice, vruntime = avg_vruntime(cfs_rq); + unsigned int nr_queued = cfs_rq->h_nr_queued; bool update_zero = false; s64 lag = 0; @@ -6090,6 +6108,9 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) se->slice = sysctl_sched_base_slice; vslice = calc_delta_fair(se->slice, se); + if (flags & ENQUEUE_QUEUED) + nr_queued -= 1; + /* * Due to how V is constructed as the weighted average of entities, * adding tasks with positive lag, or removing tasks with negative lag @@ -6098,7 +6119,7 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) * * EEVDF: placement strategy #1 / #2 */ - if (sched_feat(PLACE_LAG) && cfs_rq->nr_queued && se->vlag) { + if (sched_feat(PLACE_LAG) && nr_queued && se->vlag) { struct sched_entity *curr = cfs_rq->curr; long load, weight; @@ -6158,9 +6179,9 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) */ load = cfs_rq->sum_weight; if (curr && curr->on_rq) - load += avg_vruntime_weight(cfs_rq, curr->load.weight); + load += avg_vruntime_weight(cfs_rq, curr->h_load.weight); - weight = avg_vruntime_weight(cfs_rq, se->load.weight); + weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); lag *= load + weight; if (WARN_ON_ONCE(!load)) load = 1; @@ -6218,23 +6239,9 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) static void check_enqueue_throttle(struct cfs_rq *cfs_rq); static inline int cfs_rq_throttled(struct cfs_rq *cfs_rq); -static void -requeue_delayed_entity(struct sched_entity *se); - static void enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { - bool curr = cfs_rq->curr == se; - - /* - * If we're the current task, we must renormalise before calling - * update_curr(). - */ - if (curr) - place_entity(cfs_rq, se, flags); - - update_curr(cfs_rq); - /* * When enqueuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. @@ -6253,13 +6260,6 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) */ update_cfs_group(se); - /* - * XXX now that the entity has been re-weighted, and it's lag adjusted, - * we can place the entity. - */ - if (!curr) - place_entity(cfs_rq, se, flags); - account_entity_enqueue(cfs_rq, se); /* Entity has migrated, no longer consider this task hot */ @@ -6268,8 +6268,6 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) check_schedstat_required(); update_stats_enqueue_fair(cfs_rq, se, flags); - if (!curr) - __enqueue_entity(cfs_rq, se); se->on_rq = 1; if (cfs_rq->nr_queued == 1) { @@ -6287,21 +6285,19 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) } } -static void __clear_buddies_next(struct sched_entity *se) +static void set_next_buddy(struct cfs_rq *cfs_rq, struct sched_entity *se) { - for_each_sched_entity(se) { - struct cfs_rq *cfs_rq = cfs_rq_of(se); - if (cfs_rq->next != se) - break; - - cfs_rq->next = NULL; - } + if (WARN_ON_ONCE(!se->on_rq || se->sched_delayed)) + return; + if (se_is_idle(se)) + return; + cfs_rq->next = se; } static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se) { if (cfs_rq->next == se) - __clear_buddies_next(se); + cfs_rq->next = NULL; } static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq); @@ -6312,7 +6308,7 @@ static void set_delayed(struct sched_entity *se) /* * Delayed se of cfs_rq have no tasks queued on them. - * Do not adjust h_nr_runnable since dequeue_entities() + * Do not adjust h_nr_runnable since __dequeue_task() * will account it for blocked tasks. */ if (!entity_is_task(se)) @@ -6345,45 +6341,16 @@ static void clear_delayed(struct sched_entity *se) } } -static bool +static void dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { - bool sleep = flags & DEQUEUE_SLEEP; - int action = 0; + int action = UPDATE_TG; - update_curr(cfs_rq); - clear_buddies(cfs_rq, se); - - if (flags & DEQUEUE_DELAYED) { - WARN_ON_ONCE(!se->sched_delayed); - } else { - bool delay = sleep; - /* - * DELAY_DEQUEUE relies on spurious wakeups, special task - * states must not suffer spurious wakeups, excempt them. - */ - if (flags & (DEQUEUE_SPECIAL | DEQUEUE_THROTTLE)) - delay = false; - - WARN_ON_ONCE(delay && se->sched_delayed); - - if (sched_feat(DELAY_DEQUEUE) && delay && - !entity_eligible(cfs_rq, se)) { - if (entity_is_task(se)) - action |= UPDATE_UTIL_EST; - update_load_avg(cfs_rq, se, action); - update_entity_lag(cfs_rq, se); - set_delayed(se); - return false; - } - } - - action = UPDATE_TG; if (entity_is_task(se)) { if (task_on_rq_migrating(task_of(se))) action |= DO_DETACH; - if (sleep && !(flags & DEQUEUE_DELAYED)) + if ((flags & DEQUEUE_SLEEP) && !(flags & DEQUEUE_DELAYED)) action |= UPDATE_UTIL_EST; } @@ -6401,14 +6368,6 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) update_stats_dequeue_fair(cfs_rq, se, flags); - update_entity_lag(cfs_rq, se); - if (sched_feat(PLACE_REL_DEADLINE) && !sleep) { - se->deadline -= se->vruntime; - se->rel_deadline = 1; - } - - if (se != cfs_rq->curr) - __dequeue_entity(cfs_rq, se); se->on_rq = 0; account_entity_dequeue(cfs_rq, se); @@ -6417,9 +6376,6 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) update_cfs_group(se); - if (flags & DEQUEUE_DELAYED) - clear_delayed(se); - if (cfs_rq->nr_queued == 0) { update_idle_cfs_rq_clock_pelt(cfs_rq); #ifdef CONFIG_CFS_BANDWIDTH @@ -6432,15 +6388,11 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) } #endif } - - return true; } static void -set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, bool first) +set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { - clear_buddies(cfs_rq, se); - /* 'current' is not kept within the tree. */ if (se->on_rq) { /* @@ -6449,16 +6401,12 @@ set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, bool first) * runqueue. */ update_stats_wait_end_fair(cfs_rq, se); - __dequeue_entity(cfs_rq, se); update_load_avg(cfs_rq, se, UPDATE_TG); - - if (first) - set_protect_slice(cfs_rq, se); } update_stats_curr_start(cfs_rq, se); - WARN_ON_ONCE(cfs_rq->curr); - cfs_rq->curr = se; + WARN_ON_ONCE(cfs_rq->h_curr); + cfs_rq->h_curr = se; /* * Track our maximum slice length, if the CPU's load is at @@ -6478,23 +6426,17 @@ set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, bool first) se->prev_sum_exec_runtime = se->sum_exec_runtime; } -static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags); +static bool __dequeue_task(struct rq *rq, struct task_struct *p, int flags); -/* - * Pick the next process, keeping these things in mind, in this order: - * 1) keep things fair between processes/task groups - * 2) pick the "next" process, since someone really wants that to run - * 3) pick the "last" process, for cache locality - * 4) do not run the "skip" process, if something else is available - */ static struct sched_entity * -pick_next_entity(struct rq *rq, struct cfs_rq *cfs_rq, bool protect) +pick_next_entity(struct rq *rq, bool protect) { + struct cfs_rq *cfs_rq = &rq->cfs; struct sched_entity *se; se = pick_eevdf(cfs_rq, protect); if (se->sched_delayed) { - dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); + __dequeue_task(rq, task_of(se), DEQUEUE_SLEEP | DEQUEUE_DELAYED); /* * Must not reference @se again, see __block_task(). */ @@ -6514,13 +6456,11 @@ static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev) if (prev->on_rq) { update_stats_wait_start_fair(cfs_rq, prev); - /* Put 'current' back into the tree. */ - __enqueue_entity(cfs_rq, prev); /* in !on_rq case, update occurred at dequeue */ update_load_avg(cfs_rq, prev, 0); } - WARN_ON_ONCE(cfs_rq->curr != prev); - cfs_rq->curr = NULL; + WARN_ON_ONCE(cfs_rq->h_curr != prev); + cfs_rq->h_curr = NULL; } static void @@ -7075,7 +7015,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) assert_list_leaf_cfs_rq(rq); /* Determine whether we need to wake up potentially idle CPU: */ - if (rq->curr == rq->idle && rq->cfs.nr_queued) + if (rq->curr == rq->idle && rq->cfs.h_nr_queued) resched_curr(rq); } @@ -7410,7 +7350,7 @@ static void check_enqueue_throttle(struct cfs_rq *cfs_rq) return; /* an active group must be handled by the update_curr() path */ - if (!cfs_rq->runtime_enabled || cfs_rq->curr) + if (!cfs_rq->runtime_enabled || cfs_rq->h_curr) return; /* ensure the group is not already throttled */ @@ -7782,7 +7722,7 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p) resched_curr(rq); return; } - delta = (se->load.weight * vdelta) / NICE_0_LOAD; + delta = (se->h_load.weight * vdelta) / NICE_0_LOAD; /* * Correct for instantaneous load of other classes. @@ -7882,10 +7822,8 @@ static int choose_idle_cpu(int cpu, struct task_struct *p) } static void -requeue_delayed_entity(struct sched_entity *se) +requeue_delayed_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { - struct cfs_rq *cfs_rq = cfs_rq_of(se); - /* * se->sched_delayed should imply: se->on_rq == 1. * Because a delayed entity is one that is still on @@ -7895,19 +7833,58 @@ requeue_delayed_entity(struct sched_entity *se) WARN_ON_ONCE(!se->on_rq); if (update_entity_lag(cfs_rq, se)) { - cfs_rq->nr_queued--; + cfs_rq->h_nr_queued--; if (se != cfs_rq->curr) __dequeue_entity(cfs_rq, se); place_entity(cfs_rq, se, 0); if (se != cfs_rq->curr) __enqueue_entity(cfs_rq, se); - cfs_rq->nr_queued++; + cfs_rq->h_nr_queued++; } update_load_avg(cfs_rq, se, 0); clear_delayed(se); } +static unsigned long enqueue_hierarchy(struct task_struct *p, int flags) +{ + unsigned long weight = NICE_0_LOAD; + int task_new = !(flags & ENQUEUE_WAKEUP); + struct sched_entity *se = &p->se; + int h_nr_idle = task_has_idle_policy(p); + int h_nr_runnable = 1; + + if (task_new && se->sched_delayed) + h_nr_runnable = 0; + + for_each_sched_entity(se) { + struct cfs_rq *cfs_rq = cfs_rq_of(se); + + update_curr(cfs_rq); + + if (!se->on_rq) { + enqueue_entity(cfs_rq, se, flags); + } else { + update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); + update_cfs_group(se); + } + + cfs_rq->h_nr_runnable += h_nr_runnable; + cfs_rq->h_nr_queued++; + cfs_rq->h_nr_idle += h_nr_idle; + + if (cfs_rq_is_idle(cfs_rq)) + h_nr_idle = 1; + + weight = __calc_prop_weight(cfs_rq, se, weight); + + flags = ENQUEUE_WAKEUP; + } + + return weight; +} + /* * The enqueue_task method is called before nr_running is * increased. Here we update the fair scheduling stats and @@ -7916,13 +7893,12 @@ requeue_delayed_entity(struct sched_entity *se) static void enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) { - struct cfs_rq *cfs_rq; - struct sched_entity *se = &p->se; - int h_nr_idle = task_has_idle_policy(p); - int h_nr_runnable = 1; - int task_new = !(flags & ENQUEUE_WAKEUP); int rq_h_nr_queued = rq->cfs.h_nr_queued; - u64 slice = 0; + int task_new = !(flags & ENQUEUE_WAKEUP); + struct sched_entity *se = &p->se; + struct cfs_rq *cfs_rq = &rq->cfs; + unsigned long weight; + bool curr; if (task_is_throttled(p) && enqueue_throttled_task(p)) return; @@ -7934,10 +7910,10 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) * estimated utilization, before we update schedutil. */ if (!p->se.sched_delayed || (flags & ENQUEUE_DELAYED)) - util_est_enqueue(&rq->cfs, p); + util_est_enqueue(cfs_rq, p); if (flags & ENQUEUE_DELAYED) { - requeue_delayed_entity(se); + requeue_delayed_entity(cfs_rq, se); return; } @@ -7949,57 +7925,22 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) if (p->in_iowait) cpufreq_update_util(rq, SCHED_CPUFREQ_IOWAIT); - if (task_new && se->sched_delayed) - h_nr_runnable = 0; - - for_each_sched_entity(se) { - if (se->on_rq) { - if (se->sched_delayed) - requeue_delayed_entity(se); - break; - } - cfs_rq = cfs_rq_of(se); - - /* - * Basically set the slice of group entries to the min_slice of - * their respective cfs_rq. This ensures the group can service - * its entities in the desired time-frame. - */ - if (slice) { - se->slice = slice; - se->custom_slice = 1; - } - enqueue_entity(cfs_rq, se, flags); - slice = cfs_rq_min_slice(cfs_rq); - - cfs_rq->h_nr_runnable += h_nr_runnable; - cfs_rq->h_nr_queued++; - cfs_rq->h_nr_idle += h_nr_idle; - - if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = 1; - - flags = ENQUEUE_WAKEUP; - } - - for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); + /* + * XXX comment on the curr thing + */ + curr = (cfs_rq->curr == se); + if (curr) + place_entity(cfs_rq, se, flags); - update_load_avg(cfs_rq, se, UPDATE_TG); - se_update_runnable(se); - update_cfs_group(se); + if (se->on_rq && se->sched_delayed) + requeue_delayed_entity(cfs_rq, se); - se->slice = slice; - if (se != cfs_rq->curr) - min_vruntime_cb_propagate(&se->run_node, NULL); - slice = cfs_rq_min_slice(cfs_rq); + weight = enqueue_hierarchy(p, flags); - cfs_rq->h_nr_runnable += h_nr_runnable; - cfs_rq->h_nr_queued++; - cfs_rq->h_nr_idle += h_nr_idle; - - if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = 1; + if (!curr) { + reweight_eevdf(cfs_rq, se, weight, false); + place_entity(cfs_rq, se, flags | ENQUEUE_QUEUED); + __enqueue_entity(cfs_rq, se); } if (!rq_h_nr_queued && rq->cfs.h_nr_queued) @@ -8030,105 +7971,109 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) hrtick_update(rq); } -/* - * Basically dequeue_task_fair(), except it can deal with dequeue_entity() - * failing half-way through and resume the dequeue later. - * - * Returns: - * -1 - dequeue delayed - * 0 - dequeue throttled - * 1 - dequeue complete - */ -static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags) +static void dequeue_hierarchy(struct task_struct *p, int flags) { - bool was_sched_idle = sched_idle_rq(rq); + struct sched_entity *se = &p->se; bool task_sleep = flags & DEQUEUE_SLEEP; bool task_delayed = flags & DEQUEUE_DELAYED; bool task_throttled = flags & DEQUEUE_THROTTLE; - struct task_struct *p = NULL; - int h_nr_idle = 0; - int h_nr_queued = 0; int h_nr_runnable = 0; - struct cfs_rq *cfs_rq; - u64 slice = 0; + int h_nr_idle = task_has_idle_policy(p); + bool dequeue = true; - if (entity_is_task(se)) { - p = task_of(se); - h_nr_queued = 1; - h_nr_idle = task_has_idle_policy(p); - if (task_sleep || task_delayed || !se->sched_delayed) - h_nr_runnable = 1; - } + if (task_sleep || task_delayed || !se->sched_delayed) + h_nr_runnable = 1; for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); + struct cfs_rq *cfs_rq = cfs_rq_of(se); - if (!dequeue_entity(cfs_rq, se, flags)) { - if (p && &p->se == se) - return -1; + update_curr(cfs_rq); - slice = cfs_rq_min_slice(cfs_rq); - break; + if (dequeue) { + dequeue_entity(cfs_rq, se, flags); + /* Don't dequeue parent if it has other entities besides us */ + if (cfs_rq->load.weight) + dequeue = false; + } else { + update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); + update_cfs_group(se); } cfs_rq->h_nr_runnable -= h_nr_runnable; - cfs_rq->h_nr_queued -= h_nr_queued; + cfs_rq->h_nr_queued--; cfs_rq->h_nr_idle -= h_nr_idle; if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = h_nr_queued; + h_nr_idle = 1; if (throttled_hierarchy(cfs_rq) && task_throttled) record_throttle_clock(cfs_rq); - /* Don't dequeue parent if it has other entities besides us */ - if (cfs_rq->load.weight) { - slice = cfs_rq_min_slice(cfs_rq); - - /* Avoid re-evaluating load for this entity: */ - se = parent_entity(se); - /* - * Bias pick_next to pick a task from this cfs_rq, as - * p is sleeping when it is within its sched_slice. - */ - if (task_sleep && se) - set_next_buddy(se); - break; - } flags |= DEQUEUE_SLEEP; flags &= ~(DEQUEUE_DELAYED | DEQUEUE_SPECIAL); } +} - for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); +/* + * The part of dequeue_task_fair() that is needed to dequeue delayed tasks. + * + * Returns: + * true - dequeued + * false - delayed + */ +static bool __dequeue_task(struct rq *rq, struct task_struct *p, int flags) +{ + struct sched_entity *se = &p->se; + struct cfs_rq *cfs_rq = &rq->cfs; + bool was_sched_idle = sched_idle_rq(rq); + bool task_sleep = flags & DEQUEUE_SLEEP; + bool task_delayed = flags & DEQUEUE_DELAYED; - update_load_avg(cfs_rq, se, UPDATE_TG); - se_update_runnable(se); - update_cfs_group(se); + clear_buddies(cfs_rq, se); - se->slice = slice; - if (se != cfs_rq->curr) - min_vruntime_cb_propagate(&se->run_node, NULL); - slice = cfs_rq_min_slice(cfs_rq); + update_curr(cfs_rq_of(se)); + update_entity_lag(cfs_rq, se); - cfs_rq->h_nr_runnable -= h_nr_runnable; - cfs_rq->h_nr_queued -= h_nr_queued; - cfs_rq->h_nr_idle -= h_nr_idle; + if (flags & DEQUEUE_DELAYED) { + WARN_ON_ONCE(!se->sched_delayed); + } else { + bool delay = task_sleep; + /* + * DELAY_DEQUEUE relies on spurious wakeups, special task + * states must not suffer spurious wakeups, excempt them. + */ + if (flags & (DEQUEUE_SPECIAL | DEQUEUE_THROTTLE)) + delay = false; - if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = h_nr_queued; + WARN_ON_ONCE(delay && se->sched_delayed); - if (throttled_hierarchy(cfs_rq) && task_throttled) - record_throttle_clock(cfs_rq); + if (sched_feat(DELAY_DEQUEUE) && delay && + !entity_eligible(cfs_rq, se)) { + update_load_avg(cfs_rq_of(se), se, UPDATE_UTIL_EST); + set_delayed(se); + return false; + } } - sub_nr_running(rq, h_nr_queued); + dequeue_hierarchy(p, flags); + + if (sched_feat(PLACE_REL_DEADLINE) && !task_sleep) { + se->deadline -= se->vruntime; + se->rel_deadline = 1; + } + if (se != cfs_rq->curr) + __dequeue_entity(cfs_rq, se); + + sub_nr_running(rq, 1); /* balance early to pull high priority tasks */ if (unlikely(!was_sched_idle && sched_idle_rq(rq))) rq->next_balance = jiffies; - if (p && task_delayed) { + if (task_delayed) { + clear_delayed(se); + WARN_ON_ONCE(!task_sleep); WARN_ON_ONCE(p->on_rq != 1); @@ -8140,7 +8085,7 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags) __block_task(rq, p); } - return 1; + return true; } /* @@ -8158,11 +8103,11 @@ static bool dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) if (!p->se.sched_delayed) util_est_dequeue(&rq->cfs, p); - if (dequeue_entities(rq, &p->se, flags) < 0) + if (!__dequeue_task(rq, p, flags)) return false; /* - * Must not reference @p after dequeue_entities(DEQUEUE_DELAYED). + * Must not reference @p after __dequeue_task(DEQUEUE_DELAYED). */ return true; } @@ -9757,19 +9702,6 @@ static void migrate_task_rq_fair(struct task_struct *p, int new_cpu) static void task_dead_fair(struct task_struct *p) { struct sched_entity *se = &p->se; - - if (se->sched_delayed) { - struct rq_flags rf; - struct rq *rq; - - rq = task_rq_lock(p, &rf); - if (se->sched_delayed) { - update_rq_clock(rq); - dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); - } - task_rq_unlock(rq, p, &rf); - } - remove_entity_load_avg(se); } @@ -9803,21 +9735,10 @@ static void set_cpus_allowed_fair(struct task_struct *p, struct affinity_context set_task_max_allowed_capacity(p); } -static void set_next_buddy(struct sched_entity *se) -{ - for_each_sched_entity(se) { - if (WARN_ON_ONCE(!se->on_rq)) - return; - if (se_is_idle(se)) - return; - cfs_rq_of(se)->next = se; - } -} - enum preempt_wakeup_action { PREEMPT_WAKEUP_NONE, /* No preemption. */ PREEMPT_WAKEUP_SHORT, /* Ignore slice protection. */ - PREEMPT_WAKEUP_PICK, /* Let __pick_eevdf() decide. */ + PREEMPT_WAKEUP_PICK, /* Let pick_eevdf() decide. */ PREEMPT_WAKEUP_RESCHED, /* Force reschedule. */ }; @@ -9834,7 +9755,7 @@ set_preempt_buddy(struct cfs_rq *cfs_rq, int wake_flags, if (cfs_rq->next && entity_before(cfs_rq->next, pse)) return false; - set_next_buddy(pse); + set_next_buddy(cfs_rq, pse); return true; } @@ -9887,7 +9808,7 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f enum preempt_wakeup_action preempt_action = PREEMPT_WAKEUP_PICK; struct task_struct *donor = rq->donor; struct sched_entity *nse, *se = &donor->se, *pse = &p->se; - struct cfs_rq *cfs_rq = task_cfs_rq(donor); + struct cfs_rq *cfs_rq = &rq->cfs; int cse_is_idle, pse_is_idle; /* @@ -9925,7 +9846,6 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f if (!sched_feat(WAKEUP_PREEMPTION)) return; - find_matching_se(&se, &pse); WARN_ON_ONCE(!pse); cse_is_idle = se_is_idle(se); @@ -9953,8 +9873,7 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f if (unlikely(!normal_policy(p->policy))) return; - cfs_rq = cfs_rq_of(se); - update_curr(cfs_rq); + update_curr_fair(rq); /* * If @p has a shorter slice than current and @p is eligible, override * current's slice protection in order to allow preemption. @@ -9998,18 +9917,15 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f } pick: - nse = pick_next_entity(rq, cfs_rq, preempt_action != PREEMPT_WAKEUP_SHORT); - /* If @p has become the most eligible task, force preemption */ - if (nse == pse) - goto preempt; + if (cfs_rq->h_nr_queued) { + nse = pick_next_entity(rq, preempt_action != PREEMPT_WAKEUP_SHORT); + if (unlikely(!nse)) + goto pick; - /* - * Because p is enqueued, nse being null can only mean that we - * dequeued a delayed task. If there are still entities queued in - * cfs, check if the next one will be p. - */ - if (!nse && cfs_rq->nr_queued) - goto pick; + /* If @p has become the most eligible task, force preemption */ + if (nse == pse) + goto preempt; + } if (sched_feat(RUN_TO_PARITY)) update_protect_slice(cfs_rq, se); @@ -10028,33 +9944,24 @@ preempt: struct task_struct *pick_task_fair(struct rq *rq, struct rq_flags *rf) __must_hold(__rq_lockp(rq)) { + struct cfs_rq *cfs_rq = &rq->cfs; struct sched_entity *se; - struct cfs_rq *cfs_rq; struct task_struct *p; - bool throttled; int new_tasks; again: - cfs_rq = &rq->cfs; - if (!cfs_rq->nr_queued) + if (!cfs_rq->h_nr_queued) goto idle; - throttled = false; - - do { - /* Might not have done put_prev_entity() */ - if (cfs_rq->curr && cfs_rq->curr->on_rq) - update_curr(cfs_rq); + /* Might not have done put_prev_entity() */ + if (cfs_rq->curr && cfs_rq->curr->on_rq) + update_curr(cfs_rq); - se = pick_next_entity(rq, cfs_rq, true); - if (!se) - goto again; - cfs_rq = group_cfs_rq(se); - } while (cfs_rq); + se = pick_next_entity(rq, true); + if (!se) + goto again; p = task_of(se); - if (unlikely(throttled)) - task_throttle_setup_work(p); return p; idle: @@ -10091,7 +9998,7 @@ void fair_server_init(struct rq *rq) static void put_prev_task_fair(struct rq *rq, struct task_struct *prev, struct task_struct *next) { struct sched_entity *se = &prev->se; - struct cfs_rq *cfs_rq; + struct cfs_rq *cfs_rq = &rq->cfs; struct sched_entity *nse = NULL; #ifdef CONFIG_FAIR_GROUP_SCHED @@ -10101,7 +10008,7 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev, struct t while (se) { cfs_rq = cfs_rq_of(se); - if (!nse || cfs_rq->curr) + if (!nse || cfs_rq->h_curr) put_prev_entity(cfs_rq, se); #ifdef CONFIG_FAIR_GROUP_SCHED if (nse) { @@ -10120,6 +10027,14 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev, struct t #endif se = parent_entity(se); } + + /* Put 'current' back into the tree. */ + cfs_rq = &rq->cfs; + se = &prev->se; + WARN_ON_ONCE(cfs_rq->curr != se); + cfs_rq->curr = NULL; + if (se->on_rq) + __enqueue_entity(cfs_rq, se); } /* @@ -10128,8 +10043,8 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev, struct t static void yield_task_fair(struct rq *rq) { struct task_struct *curr = rq->donor; - struct cfs_rq *cfs_rq = task_cfs_rq(curr); struct sched_entity *se = &curr->se; + struct cfs_rq *cfs_rq = &rq->cfs; /* * Are we the only task in the tree? @@ -10170,11 +10085,11 @@ static bool yield_to_task_fair(struct rq *rq, struct task_struct *p) struct sched_entity *se = &p->se; /* !se->on_rq also covers throttled task */ - if (!se->on_rq) + if (!se->on_rq || se->sched_delayed) return false; /* Tell the scheduler that we'd really like se to run next. */ - set_next_buddy(se); + set_next_buddy(&task_rq(p)->cfs, se); yield_task_fair(rq); @@ -10513,15 +10428,10 @@ static inline long migrate_degrades_locality(struct task_struct *p, */ static inline int task_is_ineligible_on_dst_cpu(struct task_struct *p, int dest_cpu) { - struct cfs_rq *dst_cfs_rq; + struct cfs_rq *dst_cfs_rq = &cpu_rq(dest_cpu)->cfs; -#ifdef CONFIG_FAIR_GROUP_SCHED - dst_cfs_rq = tg_cfs_rq(task_group(p), dest_cpu); -#else - dst_cfs_rq = &cpu_rq(dest_cpu)->cfs; -#endif - if (sched_feat(PLACE_LAG) && dst_cfs_rq->nr_queued && - !entity_eligible(task_cfs_rq(p), &p->se)) + if (sched_feat(PLACE_LAG) && dst_cfs_rq->h_nr_queued && + !entity_eligible(&task_rq(p)->cfs, &p->se)) return 1; return 0; @@ -11304,7 +11214,7 @@ static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq) while ((se = READ_ONCE(cfs_rq->h_load_next)) != NULL) { load = cfs_rq->h_load; load = div64_ul(load * se->avg.load_avg, - cfs_rq_load_avg(cfs_rq) + 1); + cfs_rq_load_avg(cfs_rq) + 1); cfs_rq = group_cfs_rq(se); cfs_rq->h_load = load; cfs_rq->last_h_load_update = now; @@ -14696,7 +14606,7 @@ static inline void task_tick_core(struct rq *rq, struct task_struct *curr) * MIN_NR_TASKS_DURING_FORCEIDLE - 1 tasks and use that to check * if we need to give up the CPU. */ - if (rq->core->core_forceidle_count && rq->cfs.nr_queued == 1 && + if (rq->core->core_forceidle_count && rq->cfs.h_nr_queued == 1 && __entity_slice_used(&curr->se, MIN_NR_TASKS_DURING_FORCEIDLE)) resched_curr(rq); } @@ -14905,30 +14815,8 @@ bool cfs_prio_less(const struct task_struct *a, const struct task_struct *b, WARN_ON_ONCE(task_rq(b)->core != rq->core); -#ifdef CONFIG_FAIR_GROUP_SCHED - /* - * Find an se in the hierarchy for tasks a and b, such that the se's - * are immediate siblings. - */ - while (sea->cfs_rq->tg != seb->cfs_rq->tg) { - int sea_depth = sea->depth; - int seb_depth = seb->depth; - - if (sea_depth >= seb_depth) - sea = parent_entity(sea); - if (sea_depth <= seb_depth) - seb = parent_entity(seb); - } - - se_fi_update(sea, rq->core->core_forceidle_seq, in_fi); - se_fi_update(seb, rq->core->core_forceidle_seq, in_fi); - - cfs_rqa = sea->cfs_rq; - cfs_rqb = seb->cfs_rq; -#else /* !CONFIG_FAIR_GROUP_SCHED: */ cfs_rqa = &task_rq(a)->cfs; cfs_rqb = &task_rq(b)->cfs; -#endif /* !CONFIG_FAIR_GROUP_SCHED */ /* * Find delta after normalizing se's vruntime with its cfs_rq's @@ -14967,11 +14855,20 @@ static inline void task_tick_core(struct rq *rq, struct task_struct *curr) {} static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) { struct sched_entity *se = &curr->se; - struct cfs_rq *cfs_rq; - for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); - entity_tick(cfs_rq, se, queued); + if (se->on_rq) { + unsigned long weight = NICE_0_LOAD; + struct cfs_rq *cfs_rq; + + for_each_sched_entity(se) { + cfs_rq = cfs_rq_of(se); + entity_tick(cfs_rq, se, queued); + + weight = __calc_prop_weight(cfs_rq, se, weight); + } + + se = &curr->se; + reweight_eevdf(cfs_rq, se, weight, se->on_rq); } if (queued) @@ -15011,7 +14908,7 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, u64 oldprio) if (p->prio == oldprio) return; - if (rq->cfs.nr_queued == 1) + if (rq->cfs.h_nr_queued == 1) return; /* @@ -15140,33 +15037,44 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p) } } -/* - * Account for a task changing its policy or group. - * - * This routine is mostly called to set cfs_rq->curr field when a task - * migrates between groups/classes. - */ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first) { struct sched_entity *se = &p->se; bool throttled = false; + struct cfs_rq *cfs_rq = &rq->cfs; + unsigned long weight = NICE_0_LOAD; + bool on_rq = se->on_rq; + + clear_buddies(cfs_rq, se); + + if (on_rq) + __dequeue_entity(cfs_rq, se); for_each_sched_entity(se) { - struct cfs_rq *cfs_rq = cfs_rq_of(se); + cfs_rq = cfs_rq_of(se); - if (IS_ENABLED(CONFIG_FAIR_GROUP_SCHED) && - first && cfs_rq->curr) - break; + if (!IS_ENABLED(CONFIG_FAIR_GROUP_SCHED) || + !first || !cfs_rq->h_curr) + set_next_entity(cfs_rq, se); - set_next_entity(cfs_rq, se, first); /* ensure bandwidth has been allocated on our new cfs_rq */ throttled |= account_cfs_rq_runtime(cfs_rq, 0); + + if (on_rq) + weight = __calc_prop_weight(cfs_rq, se, weight); } if (throttled) task_throttle_setup_work(p); se = &p->se; + cfs_rq->curr = se; + + if (on_rq) { + reweight_eevdf(cfs_rq, se, weight, se->on_rq); + if (first) + set_protect_slice(cfs_rq, se); + } if (task_on_rq_queued(p)) { /* @@ -15279,17 +15187,8 @@ void unregister_fair_sched_group(struct task_group *tg) struct sched_entity *se = tg_se(tg, cpu); struct rq *rq = cpu_rq(cpu); - if (se) { - if (se->sched_delayed) { - guard(rq_lock_irqsave)(rq); - if (se->sched_delayed) { - update_rq_clock(rq); - dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); - } - list_del_leaf_cfs_rq(cfs_rq); - } + if (se) remove_entity_load_avg(se); - } /* * Only empty task groups can be destroyed; so we can speculatively diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index 897790889ba3..779eb58a4261 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -206,7 +206,7 @@ ___update_load_sum(u64 now, struct sched_avg *sa, /* * running is a subset of runnable (weight) so running can't be set if * runnable is clear. But there are some corner cases where the current - * se has been already dequeued but cfs_rq->curr still points to it. + * se has been already dequeued but cfs_rq->h_curr still points to it. * This means that weight will be 0 but not running for a sched_entity * but also for a cfs_rq if the latter becomes idle. As an example, * this happens during sched_balance_newidle() which calls @@ -307,7 +307,7 @@ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se) { if (___update_load_sum(now, &se->avg, !!se->on_rq, se_runnable(se), - cfs_rq->curr == se)) { + cfs_rq->h_curr == se)) { ___update_load_avg(&se->avg, se_weight(se)); cfs_se_util_change(&se->avg); @@ -323,7 +323,7 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) if (___update_load_sum(now, &cfs_rq->avg, scale_load_down(cfs_rq->load.weight), cfs_rq->h_nr_runnable, - cfs_rq->curr != NULL)) { + cfs_rq->h_curr != NULL)) { ___update_load_avg(&cfs_rq->avg, 1); trace_pelt_cfs_tp(cfs_rq); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index fd267bfd51af..e20823fbd2b8 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -530,21 +530,8 @@ struct task_group { }; -#ifdef CONFIG_GROUP_SCHED_WEIGHT #define ROOT_TASK_GROUP_LOAD NICE_0_LOAD -/* - * A weight of 0 or 1 can cause arithmetics problems. - * A weight of a cfs_rq is the sum of weights of which entities - * are queued on this cfs_rq, so a weight of a entity should not be - * too large, so as the shares value of a task group. - * (The default weight is 1024 - so there's no practical - * limitation from this.) - */ -#define MIN_SHARES (1UL << 1) -#define MAX_SHARES (1UL << 18) -#endif - typedef int (*tg_visitor)(struct task_group *, void *); extern int walk_tg_tree_from(struct task_group *from, @@ -631,6 +618,17 @@ static inline bool cfs_task_bw_constrained(struct task_struct *p) { return false #endif /* !CONFIG_CGROUP_SCHED */ +/* + * A weight of 0 or 1 can cause arithmetics problems. + * A weight of a cfs_rq is the sum of weights of which entities + * are queued on this cfs_rq, so a weight of a entity should not be + * too large, so as the shares value of a task group. + * (The default weight is 1024 - so there's no practical + * limitation from this.) + */ +#define MIN_SHARES (1UL << 1) +#define MAX_SHARES (1UL << 18) + extern void unregister_rt_sched_group(struct task_group *tg); extern void free_rt_sched_group(struct task_group *tg); extern int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent); @@ -709,6 +707,7 @@ struct cfs_rq { /* * CFS load tracking */ + struct sched_entity *h_curr; struct sched_avg avg; #ifndef CONFIG_64BIT u64 last_update_time_copy; @@ -2575,6 +2574,7 @@ extern const u32 sched_prio_to_wmult[40]; #define ENQUEUE_MIGRATED 0x00040000 #define ENQUEUE_INITIAL 0x00080000 #define ENQUEUE_RQ_SELECTED 0x00100000 +#define ENQUEUE_QUEUED 0x00200000 #define RETRY_TASK ((void *)-1UL) -- cgit From b2463ebf2674ddec62f0f0e63061670bc2c75346 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Thu, 25 Jun 2026 18:16:25 +0530 Subject: sched/debug: Remove unused schedstats nr_migrations_cold, nr_wakeups_passive and nr_wakeups_idle are not being updated anywhere. So remove them. These are per process stats. So updating sched stats version isn't necessary. Signed-off-by: Shrikanth Hegde Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: K Prateek Nayak Tested-by: K Prateek Nayak Link: https://patch.msgid.link/20260625124648.802832-2-sshegde@linux.ibm.com --- include/linux/sched.h | 3 --- kernel/sched/debug.c | 3 --- 2 files changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 12f633514ad2..968b18a7f470 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -550,7 +550,6 @@ struct sched_statistics { s64 exec_max; u64 slice_max; - u64 nr_migrations_cold; u64 nr_failed_migrations_affine; u64 nr_failed_migrations_running; u64 nr_failed_migrations_hot; @@ -563,8 +562,6 @@ struct sched_statistics { u64 nr_wakeups_remote; u64 nr_wakeups_affine; u64 nr_wakeups_affine_attempts; - u64 nr_wakeups_passive; - u64 nr_wakeups_idle; #ifdef CONFIG_SCHED_CORE u64 core_forceidle_sum; diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index c15291cbcbb3..72236db67983 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -1442,7 +1442,6 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns, P_SCHEDSTAT(wait_count); PN_SCHEDSTAT(iowait_sum); P_SCHEDSTAT(iowait_count); - P_SCHEDSTAT(nr_migrations_cold); P_SCHEDSTAT(nr_failed_migrations_affine); P_SCHEDSTAT(nr_failed_migrations_running); P_SCHEDSTAT(nr_failed_migrations_hot); @@ -1454,8 +1453,6 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns, P_SCHEDSTAT(nr_wakeups_remote); P_SCHEDSTAT(nr_wakeups_affine); P_SCHEDSTAT(nr_wakeups_affine_attempts); - P_SCHEDSTAT(nr_wakeups_passive); - P_SCHEDSTAT(nr_wakeups_idle); avg_atom = p->se.sum_exec_runtime; if (nr_switches) -- cgit From 4f4230ff5d0aec3a5f3b3d9bab39b3db13800a44 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 18 Jun 2026 19:19:45 +0100 Subject: clk: renesas: rzv2h-cpg: Use per-SoC PLL reference frequency for calculations Introduce a per-SoC PLL reference input frequency parameter to avoid relying on a hardcoded 24MHz constant during PLL configuration math. Add an input_fref member to struct rzv2h_pll_limits. In the core calculation helper rzv2h_get_pll_pars(), derive the base input clock rate from limits->input_fref, utilizing the conditional ternary operator to fall back to 24MHz if the struct field is left uninitialized (0), and drop the obsolete macro RZ_V2H_OSC_CLK_IN_MEGA. This abstraction permits the reuse of the common PLL divider logic on newer SoC platforms like the RZ/T2H, which feature a 48 MHz PLL reference clock input instead of the 24 MHz signal used by RZ/V2H(P), without disrupting existing platforms. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260618181949.3036280-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzv2h-cpg.c | 8 ++++---- include/linux/clk/renesas.h | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c index e271c04cee34..fff89f2bdc0b 100644 --- a/drivers/clk/renesas/rzv2h-cpg.c +++ b/drivers/clk/renesas/rzv2h-cpg.c @@ -218,7 +218,6 @@ struct rzv2h_plldsi_div_clk { #define to_plldsi_div_clk(_hw) \ container_of(_hw, struct rzv2h_plldsi_div_clk, hw) -#define RZ_V2H_OSC_CLK_IN_MEGA (24 * MEGA) #define RZV2H_MAX_DIV_TABLES (16) /** @@ -242,6 +241,7 @@ struct rzv2h_plldsi_div_clk { bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz) { + unsigned long input_fref = limits->input_fref ?: (24 * MEGA); u64 fout_min_millihz = mul_u32_u32(limits->fout.min, MILLI); u64 fout_max_millihz = mul_u32_u32(limits->fout.max, MILLI); struct rzv2h_pll_pars p, best; @@ -254,7 +254,7 @@ bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, best.error_millihz = S64_MAX; for (p.p = limits->p.min; p.p <= limits->p.max; p.p++) { - u32 fref = RZ_V2H_OSC_CLK_IN_MEGA / p.p; + u32 fref = input_fref / p.p; u16 divider; for (divider = 1 << limits->s.min, p.s = limits->s.min; @@ -335,9 +335,9 @@ bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, continue; /* PLL_M component of (output * 65536 * PLL_P) */ - output = mul_u32_u32(p.m * 65536, RZ_V2H_OSC_CLK_IN_MEGA); + output = mul_u32_u32(p.m * 65536, input_fref); /* PLL_K component of (output * 65536 * PLL_P) */ - output += p.k * RZ_V2H_OSC_CLK_IN_MEGA; + output += p.k * input_fref; /* Make it in mHz */ output *= MILLI; output = DIV_U64_ROUND_CLOSEST(output, 65536 * p.p * divider); diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 0949400f44de..798bb0b54bab 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -53,6 +53,9 @@ static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { } * various parameters used to configure a PLL. These limits ensure * the PLL operates within valid and stable ranges. * + * @input_fref: Reference input frequency to the PLL (in Hz). If set + * to 0, a default value of 24MHz is used. + * * @fout: Output frequency range (in MHz) * @fout.min: Minimum allowed output frequency * @fout.max: Maximum allowed output frequency @@ -78,6 +81,8 @@ static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { } * @k.max: Maximum delta-sigma value */ struct rzv2h_pll_limits { + u32 input_fref; + struct { u32 min; u32 max; -- cgit From 73c360100dec2ecb0e905ceb58a01df45fda8988 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 18 Jun 2026 19:19:48 +0100 Subject: clk: renesas: rzv2h-cpg: Extract PLL calculation helpers into shared library Move the RZ/V2H PLL and divider parameter calculation helpers from rzv2h-cpg.c into a new reusable library. Introduce the CLK_RZV2H_CPG_LIB Kconfig symbol and add rzv2h-cpg-lib.c to host the PLL parameter search algorithms currently implemented by rzv2h_get_pll_pars() and rzv2h_get_pll_divs_pars(). Export the helpers as rzv2h_cpg_get_pll_pars() and rzv2h_cpg_get_pll_divs_pars() for use by other drivers. Update the public clock header to expose the new interfaces and provide compatibility aliases for the existing helper names, avoiding build breakage for current users while allowing future conversions to the new API. This prepares for reuse of the PLL and divider calculation logic by other Renesas clock drivers, including upcoming RZ/T2H and RZ/N2H CPG support, without duplicating the implementation. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260618181949.3036280-5-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/Kconfig | 4 + drivers/clk/renesas/Makefile | 1 + drivers/clk/renesas/rzv2h-cpg-lib.c | 217 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rzv2h-cpg.c | 203 --------------------------------- include/linux/clk/renesas.h | 29 ++--- 5 files changed, 238 insertions(+), 216 deletions(-) create mode 100644 drivers/clk/renesas/rzv2h-cpg-lib.c (limited to 'include') diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 0203ecbb3882..7659550b8566 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -260,8 +260,12 @@ config CLK_RZG2L config CLK_RZV2H bool "RZ/{G3E,V2H(P)} family clock support" if COMPILE_TEST + select CLK_RZV2H_CPG_LIB select RESET_CONTROLLER +config CLK_RZV2H_CPG_LIB + bool "RZV2H CPG library functions" if COMPILE_TEST + config CLK_RENESAS_VBATTB tristate "Renesas VBATTB clock controller" depends on ARCH_RZG2L || COMPILE_TEST diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index bd2bed91ab29..ac790e56034b 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_CLK_RCAR_GEN3_CPG) += rcar-gen3-cpg.o obj-$(CONFIG_CLK_RCAR_GEN4_CPG) += rcar-gen4-cpg.o obj-$(CONFIG_CLK_RCAR_USB2_CLOCK_SEL) += rcar-usb2-clock-sel.o obj-$(CONFIG_CLK_RZG2L) += rzg2l-cpg.o +obj-$(CONFIG_CLK_RZV2H_CPG_LIB) += rzv2h-cpg-lib.o obj-$(CONFIG_CLK_RZV2H) += rzv2h-cpg.o # Generic diff --git a/drivers/clk/renesas/rzv2h-cpg-lib.c b/drivers/clk/renesas/rzv2h-cpg-lib.c new file mode 100644 index 000000000000..124239c7327e --- /dev/null +++ b/drivers/clk/renesas/rzv2h-cpg-lib.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZV2H CPG Library. This library provides common functions to calculate + * PLL parameters for the RZV2H SoC. + * + * Copyright (C) 2026 Renesas Electronics Corp. + * + */ + +#include +#include +#include +#include +#include + +/** + * rzv2h_cpg_get_pll_pars - Finds the best combination of PLL parameters + * for a given frequency. + * + * @limits: Pointer to the structure containing the limits for the PLL parameters + * @pars: Pointer to the structure where the best calculated PLL parameters values + * will be stored + * @freq_millihz: Target output frequency in millihertz + * + * This function calculates the best set of PLL parameters (M, K, P, S) to achieve + * the desired frequency. + * There is no direct formula to calculate the PLL parameters, as it's an open + * system of equations, therefore this function uses an iterative approach to + * determine the best solution. The best solution is one that minimizes the error + * (desired frequency - actual frequency). + * + * Return: true if a valid set of parameters values is found, false otherwise. + */ +bool rzv2h_cpg_get_pll_pars(const struct rzv2h_pll_limits *limits, + struct rzv2h_pll_pars *pars, u64 freq_millihz) +{ + unsigned long input_fref = limits->input_fref ?: (24 * MEGA); + u64 fout_min_millihz = mul_u32_u32(limits->fout.min, MILLI); + u64 fout_max_millihz = mul_u32_u32(limits->fout.max, MILLI); + struct rzv2h_pll_pars p, best; + + if (freq_millihz > fout_max_millihz || + freq_millihz < fout_min_millihz) + return false; + + /* Initialize best error to maximum possible value */ + best.error_millihz = S64_MAX; + + for (p.p = limits->p.min; p.p <= limits->p.max; p.p++) { + u32 fref = input_fref / p.p; + u16 divider; + + for (divider = 1 << limits->s.min, p.s = limits->s.min; + p.s <= limits->s.max; p.s++, divider <<= 1) { + for (p.m = limits->m.min; p.m <= limits->m.max; p.m++) { + u64 output_m, output_k_range; + s64 pll_k, output_k; + u64 fvco, output; + + /* + * The frequency generated by the PLL + divider + * is calculated as follows: + * + * With: + * Freq = Ffout = Ffvco / 2^(pll_s) + * Ffvco = (pll_m + (pll_k / 65536)) * Ffref + * Ffref = 24MHz / pll_p + * + * Freq can also be rewritten as: + * Freq = Ffvco / 2^(pll_s) + * = ((pll_m + (pll_k / 65536)) * Ffref) / 2^(pll_s) + * = (pll_m * Ffref) / 2^(pll_s) + ((pll_k / 65536) * Ffref) / 2^(pll_s) + * = output_m + output_k + * + * Every parameter has been determined at this + * point, but pll_k. + * + * Considering that: + * limits->k.min <= pll_k <= limits->k.max + * Then: + * -0.5 <= (pll_k / 65536) < 0.5 + * Therefore: + * -Ffref / (2 * 2^(pll_s)) <= output_k < Ffref / (2 * 2^(pll_s)) + */ + + /* Compute output M component (in mHz) */ + output_m = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(p.m, fref) * MILLI, + divider); + /* Compute range for output K (in mHz) */ + output_k_range = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(fref, MILLI), + 2 * divider); + /* + * No point in continuing if we can't achieve + * the desired frequency + */ + if (freq_millihz < (output_m - output_k_range) || + freq_millihz >= (output_m + output_k_range)) { + continue; + } + + /* + * Compute the K component + * + * Since: + * Freq = output_m + output_k + * Then: + * output_k = Freq - output_m + * = ((pll_k / 65536) * Ffref) / 2^(pll_s) + * Therefore: + * pll_k = (output_k * 65536 * 2^(pll_s)) / Ffref + */ + output_k = freq_millihz - output_m; + pll_k = div_s64(output_k * 65536ULL * divider, + fref); + pll_k = DIV_S64_ROUND_CLOSEST(pll_k, MILLI); + + /* Validate K value within allowed limits */ + if (pll_k < limits->k.min || + pll_k > limits->k.max) + continue; + + p.k = pll_k; + + /* Compute (Ffvco * 65536) */ + fvco = mul_u32_u32(p.m * 65536 + p.k, fref); + if (fvco < mul_u32_u32(limits->fvco.min, 65536) || + fvco > mul_u32_u32(limits->fvco.max, 65536)) + continue; + + /* PLL_M component of (output * 65536 * PLL_P) */ + output = mul_u32_u32(p.m * 65536, input_fref); + /* PLL_K component of (output * 65536 * PLL_P) */ + output += p.k * input_fref; + /* Make it in mHz */ + output *= MILLI; + output = DIV_U64_ROUND_CLOSEST(output, 65536 * p.p * divider); + + /* Check output frequency against limits */ + if (output < fout_min_millihz || + output > fout_max_millihz) + continue; + + p.error_millihz = freq_millihz - output; + p.freq_millihz = output; + + /* If an exact match is found, return immediately */ + if (p.error_millihz == 0) { + *pars = p; + return true; + } + + /* Update best match if error is smaller */ + if (abs(best.error_millihz) > abs(p.error_millihz)) + best = p; + } + } + } + + /* If no valid parameters were found, return false */ + if (best.error_millihz == S64_MAX) + return false; + + *pars = best; + return true; +} +EXPORT_SYMBOL_NS_GPL(rzv2h_cpg_get_pll_pars, "RZV2H_CPG"); + +/* + * rzv2h_cpg_get_pll_divs_pars - Finds the best combination of PLL parameters + * and divider value for a given frequency. + * + * @limits: Pointer to the structure containing the limits for the PLL parameters + * @pars: Pointer to the structure where the best calculated PLL parameters and + * divider values will be stored + * @table: Pointer to the array of valid divider values + * @table_size: Size of the divider values array + * @freq_millihz: Target output frequency in millihertz + * + * This function calculates the best set of PLL parameters (M, K, P, S) and divider + * value to achieve the desired frequency. See rzv2h_cpg_get_pll_pars() for more + * details on how the PLL parameters are calculated. + * + * freq_millihz is the desired frequency generated by the PLL followed by a + * a gear. + */ +bool rzv2h_cpg_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, + struct rzv2h_pll_div_pars *pars, + const u8 *table, u8 table_size, u64 freq_millihz) +{ + struct rzv2h_pll_div_pars p, best; + + best.div.error_millihz = S64_MAX; + p.div.error_millihz = S64_MAX; + for (unsigned int i = 0; i < table_size; i++) { + if (!rzv2h_cpg_get_pll_pars(limits, &p.pll, freq_millihz * table[i])) + continue; + + p.div.divider_value = table[i]; + p.div.freq_millihz = DIV_U64_ROUND_CLOSEST(p.pll.freq_millihz, table[i]); + p.div.error_millihz = freq_millihz - p.div.freq_millihz; + + if (p.div.error_millihz == 0) { + *pars = p; + return true; + } + + if (abs(best.div.error_millihz) > abs(p.div.error_millihz)) + best = p; + } + + if (best.div.error_millihz == S64_MAX) + return false; + + *pars = best; + return true; +} +EXPORT_SYMBOL_NS_GPL(rzv2h_cpg_get_pll_divs_pars, "RZV2H_CPG"); diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c index fff89f2bdc0b..738dfafc6d9c 100644 --- a/drivers/clk/renesas/rzv2h-cpg.c +++ b/drivers/clk/renesas/rzv2h-cpg.c @@ -220,209 +220,6 @@ struct rzv2h_plldsi_div_clk { #define RZV2H_MAX_DIV_TABLES (16) -/** - * rzv2h_get_pll_pars - Finds the best combination of PLL parameters - * for a given frequency. - * - * @limits: Pointer to the structure containing the limits for the PLL parameters - * @pars: Pointer to the structure where the best calculated PLL parameters values - * will be stored - * @freq_millihz: Target output frequency in millihertz - * - * This function calculates the best set of PLL parameters (M, K, P, S) to achieve - * the desired frequency. - * There is no direct formula to calculate the PLL parameters, as it's an open - * system of equations, therefore this function uses an iterative approach to - * determine the best solution. The best solution is one that minimizes the error - * (desired frequency - actual frequency). - * - * Return: true if a valid set of parameters values is found, false otherwise. - */ -bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, - struct rzv2h_pll_pars *pars, u64 freq_millihz) -{ - unsigned long input_fref = limits->input_fref ?: (24 * MEGA); - u64 fout_min_millihz = mul_u32_u32(limits->fout.min, MILLI); - u64 fout_max_millihz = mul_u32_u32(limits->fout.max, MILLI); - struct rzv2h_pll_pars p, best; - - if (freq_millihz > fout_max_millihz || - freq_millihz < fout_min_millihz) - return false; - - /* Initialize best error to maximum possible value */ - best.error_millihz = S64_MAX; - - for (p.p = limits->p.min; p.p <= limits->p.max; p.p++) { - u32 fref = input_fref / p.p; - u16 divider; - - for (divider = 1 << limits->s.min, p.s = limits->s.min; - p.s <= limits->s.max; p.s++, divider <<= 1) { - for (p.m = limits->m.min; p.m <= limits->m.max; p.m++) { - u64 output_m, output_k_range; - s64 pll_k, output_k; - u64 fvco, output; - - /* - * The frequency generated by the PLL + divider - * is calculated as follows: - * - * With: - * Freq = Ffout = Ffvco / 2^(pll_s) - * Ffvco = (pll_m + (pll_k / 65536)) * Ffref - * Ffref = 24MHz / pll_p - * - * Freq can also be rewritten as: - * Freq = Ffvco / 2^(pll_s) - * = ((pll_m + (pll_k / 65536)) * Ffref) / 2^(pll_s) - * = (pll_m * Ffref) / 2^(pll_s) + ((pll_k / 65536) * Ffref) / 2^(pll_s) - * = output_m + output_k - * - * Every parameter has been determined at this - * point, but pll_k. - * - * Considering that: - * limits->k.min <= pll_k <= limits->k.max - * Then: - * -0.5 <= (pll_k / 65536) < 0.5 - * Therefore: - * -Ffref / (2 * 2^(pll_s)) <= output_k < Ffref / (2 * 2^(pll_s)) - */ - - /* Compute output M component (in mHz) */ - output_m = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(p.m, fref) * MILLI, - divider); - /* Compute range for output K (in mHz) */ - output_k_range = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(fref, MILLI), - 2 * divider); - /* - * No point in continuing if we can't achieve - * the desired frequency - */ - if (freq_millihz < (output_m - output_k_range) || - freq_millihz >= (output_m + output_k_range)) { - continue; - } - - /* - * Compute the K component - * - * Since: - * Freq = output_m + output_k - * Then: - * output_k = Freq - output_m - * = ((pll_k / 65536) * Ffref) / 2^(pll_s) - * Therefore: - * pll_k = (output_k * 65536 * 2^(pll_s)) / Ffref - */ - output_k = freq_millihz - output_m; - pll_k = div_s64(output_k * 65536ULL * divider, - fref); - pll_k = DIV_S64_ROUND_CLOSEST(pll_k, MILLI); - - /* Validate K value within allowed limits */ - if (pll_k < limits->k.min || - pll_k > limits->k.max) - continue; - - p.k = pll_k; - - /* Compute (Ffvco * 65536) */ - fvco = mul_u32_u32(p.m * 65536 + p.k, fref); - if (fvco < mul_u32_u32(limits->fvco.min, 65536) || - fvco > mul_u32_u32(limits->fvco.max, 65536)) - continue; - - /* PLL_M component of (output * 65536 * PLL_P) */ - output = mul_u32_u32(p.m * 65536, input_fref); - /* PLL_K component of (output * 65536 * PLL_P) */ - output += p.k * input_fref; - /* Make it in mHz */ - output *= MILLI; - output = DIV_U64_ROUND_CLOSEST(output, 65536 * p.p * divider); - - /* Check output frequency against limits */ - if (output < fout_min_millihz || - output > fout_max_millihz) - continue; - - p.error_millihz = freq_millihz - output; - p.freq_millihz = output; - - /* If an exact match is found, return immediately */ - if (p.error_millihz == 0) { - *pars = p; - return true; - } - - /* Update best match if error is smaller */ - if (abs(best.error_millihz) > abs(p.error_millihz)) - best = p; - } - } - } - - /* If no valid parameters were found, return false */ - if (best.error_millihz == S64_MAX) - return false; - - *pars = best; - return true; -} -EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_pars, "RZV2H_CPG"); - -/* - * rzv2h_get_pll_divs_pars - Finds the best combination of PLL parameters - * and divider value for a given frequency. - * - * @limits: Pointer to the structure containing the limits for the PLL parameters - * @pars: Pointer to the structure where the best calculated PLL parameters and - * divider values will be stored - * @table: Pointer to the array of valid divider values - * @table_size: Size of the divider values array - * @freq_millihz: Target output frequency in millihertz - * - * This function calculates the best set of PLL parameters (M, K, P, S) and divider - * value to achieve the desired frequency. See rzv2h_get_pll_pars() for more details - * on how the PLL parameters are calculated. - * - * freq_millihz is the desired frequency generated by the PLL followed by a - * a gear. - */ -bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, - struct rzv2h_pll_div_pars *pars, - const u8 *table, u8 table_size, u64 freq_millihz) -{ - struct rzv2h_pll_div_pars p, best; - - best.div.error_millihz = S64_MAX; - p.div.error_millihz = S64_MAX; - for (unsigned int i = 0; i < table_size; i++) { - if (!rzv2h_get_pll_pars(limits, &p.pll, freq_millihz * table[i])) - continue; - - p.div.divider_value = table[i]; - p.div.freq_millihz = DIV_U64_ROUND_CLOSEST(p.pll.freq_millihz, table[i]); - p.div.error_millihz = freq_millihz - p.div.freq_millihz; - - if (p.div.error_millihz == 0) { - *pars = p; - return true; - } - - if (abs(best.div.error_millihz) > abs(p.div.error_millihz)) - best = p; - } - - if (best.div.error_millihz == S64_MAX) - return false; - - *pars = best; - return true; -} -EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG"); - /** * struct rzv2h_plldsi_mux_clk - PLL DSI MUX clock * diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 798bb0b54bab..c9495558cd5c 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -189,28 +189,31 @@ struct rzv2h_pll_div_pars { .k = { .min = -32768, .max = 32767 }, \ } \ -#ifdef CONFIG_CLK_RZV2H -bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, - struct rzv2h_pll_pars *pars, u64 freq_millihz); +#ifdef CONFIG_CLK_RZV2H_CPG_LIB +bool rzv2h_cpg_get_pll_pars(const struct rzv2h_pll_limits *limits, + struct rzv2h_pll_pars *pars, u64 freq_millihz); -bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, - struct rzv2h_pll_div_pars *pars, - const u8 *table, u8 table_size, u64 freq_millihz); +bool rzv2h_cpg_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, + struct rzv2h_pll_div_pars *pars, + const u8 *table, u8 table_size, u64 freq_millihz); #else -static inline bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, - struct rzv2h_pll_pars *pars, - u64 freq_millihz) +static inline bool rzv2h_cpg_get_pll_pars(const struct rzv2h_pll_limits *limits, + struct rzv2h_pll_pars *pars, + u64 freq_millihz) { return false; } -static inline bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, - struct rzv2h_pll_div_pars *pars, - const u8 *table, u8 table_size, - u64 freq_millihz) +static inline bool rzv2h_cpg_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, + struct rzv2h_pll_div_pars *pars, + const u8 *table, u8 table_size, + u64 freq_millihz) { return false; } #endif +#define rzv2h_get_pll_pars rzv2h_cpg_get_pll_pars +#define rzv2h_get_pll_divs_pars rzv2h_cpg_get_pll_divs_pars + #endif -- cgit From 796bdb33e86aec8504bf8868e0665f120638ac72 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 21 May 2026 11:32:47 -0400 Subject: dmaengine: Add API to combine configuration and preparation (sg and single) Previously, configuration and preparation required two separate calls. This works well when configuration is done only once during initialization. However, in cases where the burst length or source/destination address must be adjusted for each transfer, calling two functions is verbose and requires additional locking to ensure both steps complete atomically. Add a new API dmaengine_prep_config_single() and dmaengine_prep_config_sg() and callback device_prep_config_sg() that combines configuration and preparation into a single operation. If the configuration argument is passed as NULL, fall back to the existing implementation. Tested-by: Niklas Cassel Acked-by: Manivannan Sadhasivam Signed-off-by: Frank Li Link: https://patch.msgid.link/20260521-dma_prep_config-v7-1-1f73f4899883@nxp.com Signed-off-by: Vinod Koul --- Documentation/driver-api/dmaengine/client.rst | 9 ++++ include/linux/dmaengine.h | 63 +++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst index d491e385d61a..5ee5d4a3596d 100644 --- a/Documentation/driver-api/dmaengine/client.rst +++ b/Documentation/driver-api/dmaengine/client.rst @@ -80,6 +80,10 @@ The details of these operations are: - slave_sg: DMA a list of scatter gather buffers from/to a peripheral + - config_sg: Similar with slave_sg, just pass down dma_slave_config + struct to avoid calling dmaengine_slave_config() every time adjusting the + burst length or the FIFO address is needed. + - peripheral_dma_vec: DMA an array of scatter gather buffers from/to a peripheral. Similar to slave_sg, but uses an array of dma_vec structures instead of a scatterlist. @@ -106,6 +110,11 @@ The details of these operations are: unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); + struct dma_async_tx_descriptor *dmaengine_prep_config_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags, struct dma_slave_config *config); + struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec( struct dma_chan *chan, const struct dma_vec *vecs, size_t nents, enum dma_data_direction direction, diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index b3d251c9734e..defa377d2ef5 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -835,6 +835,7 @@ struct dma_filter { * where the address and size of each segment is located in one entry of * the dma_vec array. * @device_prep_slave_sg: prepares a slave dma operation + * @device_prep_config_sg: prepares a slave DMA operation with dma_slave_config * @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio. * The function takes a buffer of size buf_len. The callback function will * be called after period_len bytes have been transferred. @@ -934,6 +935,10 @@ struct dma_device { struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags, void *context); + struct dma_async_tx_descriptor *(*device_prep_config_sg)( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, struct dma_slave_config *config); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, @@ -974,22 +979,44 @@ static inline bool is_slave_direction(enum dma_transfer_direction direction) (direction == DMA_DEV_TO_DEV); } -static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( - struct dma_chan *chan, dma_addr_t buf, size_t len, - enum dma_transfer_direction dir, unsigned long flags) +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_single(struct dma_chan *chan, dma_addr_t buf, size_t len, + enum dma_transfer_direction dir, + unsigned long flags, + struct dma_slave_config *config) { struct scatterlist sg; + + if (!chan || !chan->device) + return NULL; + sg_init_table(&sg, 1); sg_dma_address(&sg) = buf; sg_dma_len(&sg) = len; - if (!chan || !chan->device || !chan->device->device_prep_slave_sg) + if (chan->device->device_prep_config_sg) + return chan->device->device_prep_config_sg(chan, &sg, 1, dir, + flags, config); + + if (config) + if (dmaengine_slave_config(chan, config)) + return NULL; + + if (!chan->device->device_prep_slave_sg) return NULL; return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags, NULL); } +static inline struct dma_async_tx_descriptor * +dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len, + enum dma_transfer_direction dir, + unsigned long flags) +{ + return dmaengine_prep_config_single(chan, buf, len, dir, flags, NULL); +} + /** * dmaengine_prep_peripheral_dma_vec() - Prepare a DMA scatter-gather descriptor * @chan: The channel to be used for this descriptor @@ -1010,17 +1037,37 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec( dir, flags); } -static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg( - struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction dir, unsigned long flags) +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags, struct dma_slave_config *config) { - if (!chan || !chan->device || !chan->device->device_prep_slave_sg) + if (!chan || !chan->device) + return NULL; + + if (chan->device->device_prep_config_sg) + return chan->device->device_prep_config_sg(chan, sgl, sg_len, + dir, flags, config); + + if (config) + if (dmaengine_slave_config(chan, config)) + return NULL; + + if (!chan->device->device_prep_slave_sg) return NULL; return chan->device->device_prep_slave_sg(chan, sgl, sg_len, dir, flags, NULL); } +static inline struct dma_async_tx_descriptor * +dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags) +{ + return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL); +} + #ifdef CONFIG_RAPIDIO_DMA_ENGINE struct rio_dma_ext; static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg( -- cgit From af900b7dc1e1cdac571ac38e7fee80f1a1776a62 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 21 May 2026 11:32:48 -0400 Subject: dmaengine: Add safe API to combine configuration and preparation Introduce dmaengine_prep_config_single_safe() and dmaengine_prep_config_sg_safe() to provide a reentrant-safe way to combine slave configuration and transfer preparation. Drivers may implement the new device_prep_config_sg() callback to perform both steps atomically. If the callback is not provided, the helpers fall back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg() under per-channel spinlock protection. Tested-by: Niklas Cassel Signed-off-by: Frank Li Link: https://patch.msgid.link/20260521-dma_prep_config-v7-2-1f73f4899883@nxp.com Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 2 ++ include/linux/dmaengine.h | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) (limited to 'include') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 9049171df857..23e3bb18c166 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -1100,6 +1100,8 @@ static int __dma_async_device_channel_register(struct dma_device *device, chan->dev->device.parent = device->dev; chan->dev->chan = chan; chan->dev->dev_id = device->dev_id; + spin_lock_init(&chan->lock); + if (!name) dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id); else diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index defa377d2ef5..6fe46c0c9452 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -322,6 +322,8 @@ struct dma_router { * @slave: ptr to the device using this channel * @cookie: last cookie value returned to client * @completed_cookie: last completed cookie for this channel + * @lock: protect between config and prepare transfer when driver have not + * implemented callback device_prep_config_sg(). * @chan_id: channel ID for sysfs * @dev: class device for sysfs * @name: backlink name for sysfs @@ -341,6 +343,12 @@ struct dma_chan { dma_cookie_t cookie; dma_cookie_t completed_cookie; + /* + * protect between config and prepare transfer because *_prep() may be + * called from complete callback, which is in GFP_NOSLEEP context. + */ + spinlock_t lock; + /* sysfs */ int chan_id; struct dma_chan_dev *dev; @@ -1068,6 +1076,84 @@ dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL); } +/** + * dmaengine_prep_config_sg_safe - prepare a scatter-gather DMA transfer + * with atomic slave configuration update + * @chan: DMA channel + * @sgl: scatterlist for the transfer + * @sg_len: number of entries in @sgl + * @dir: DMA transfer direction + * @flags: transfer preparation flags + * @config: DMA slave configuration for this transfer + * + * Prepare a DMA scatter-gather transfer together with a corresponding slave + * configuration update in a re-entrant and race-safe manner. + * + * DMA engine drivers may implement the optional + * device_prep_config_sg() callback to perform both the slave configuration + * and descriptor preparation atomically. In this case, the operation is + * fully handled by the DMA engine driver. + * + * If the DMA engine driver does not implement device_prep_config_sg(), falls + * back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg(). + * The fallback path is protected by a per-channel spinlock to ensure that + * concurrent callers cannot interleave configuration and descriptor preparation + * on the same DMA channel. + * + * Return: Pointer to a prepared DMA async transaction descriptor on success, + * or %NULL if the transfer could not be prepared. + */ +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_sg_safe(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, + enum dma_transfer_direction dir, + unsigned long flags, + struct dma_slave_config *config) +{ + struct dma_async_tx_descriptor *tx; + unsigned long spinlock_flags; + + if (!chan || !chan->device) + return NULL; + + if (!chan->device->device_prep_config_sg) + spin_lock_irqsave(&chan->lock, spinlock_flags); + + tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config); + + if (!chan->device->device_prep_config_sg) + spin_unlock_irqrestore(&chan->lock, spinlock_flags); + + return tx; +} + +/** + * dmaengine_prep_config_single_safe - prepare a single-buffer DMA transfer + * with atomic slave configuration update + * @chan: DMA channel + * @buf: DMA buffer address + * @len: length of the transfer in bytes + * @dir: DMA transfer direction + * @flags: transfer preparation flags + * @config: DMA slave configuration for this transfer + * + * Detail see dmaengine_prep_config_sg_safe(). + */ +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf, + size_t len, enum dma_transfer_direction dir, + unsigned long flags, + struct dma_slave_config *config) +{ + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_dma_address(&sg) = buf; + sg_dma_len(&sg) = len; + + return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config); +} + #ifdef CONFIG_RAPIDIO_DMA_ENGINE struct rio_dma_ext; static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg( -- cgit From c97f0bf5f705b16d150f2b0d5ce0ee24eee4f68a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:10 +0000 Subject: ASoC: sdw_utils: tidyup .count_sidecar count_sidecar() is not using *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87jyrlety1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 28 ++++++++++++++-------------- sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c | 4 +--- sound/soc/sdw_utils/soc_sdw_utils.c | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 79c21966220b..443d63dc6ea3 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -44,6 +44,18 @@ struct asoc_sdw_codec_info; +struct asoc_sdw_mc_private { + struct snd_soc_card card; + struct snd_soc_jack sdw_headset; + struct device *headset_codec_dev; /* only one headset per card */ + struct device *amp_dev1, *amp_dev2; + bool append_dai_type; + bool ignore_internal_dmic; + void *private; + unsigned long mc_quirk; + int codec_info_list_count; +}; + struct asoc_sdw_dai_info { const bool direction[2]; /* playback & capture support */ const char *codec_name; @@ -88,25 +100,13 @@ struct asoc_sdw_codec_info { int (*codec_card_late_probe)(struct snd_soc_card *card); - int (*count_sidecar)(struct snd_soc_card *card, + int (*count_sidecar)(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int (*add_sidecar)(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, struct snd_soc_codec_conf **codec_conf); }; -struct asoc_sdw_mc_private { - struct snd_soc_card card; - struct snd_soc_jack sdw_headset; - struct device *headset_codec_dev; /* only one headset per card */ - struct device *amp_dev1, *amp_dev2; - bool append_dai_type; - bool ignore_internal_dmic; - void *private; - unsigned long mc_quirk; - int codec_info_list_count; -}; - struct asoc_sdw_endpoint { struct list_head list; @@ -235,7 +235,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card, int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* CS AMP support */ -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c index e0e32a279787..129a437ae397 100644 --- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c @@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), }; -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs) { - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) { (*num_dais)++; (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d8db8fc5313e..073f3f9205a7 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -2045,7 +2045,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; if (codec_info->count_sidecar && codec_info->add_sidecar) { - ret = codec_info->count_sidecar(card, &num_dais, num_devs); + ret = codec_info->count_sidecar(ctx, &num_dais, num_devs); if (ret) return ret; -- cgit From a1332be2a07090cf422507ec812ce2b9ba0a558a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:15 +0000 Subject: ASoC: sdw_utils: tidyup asoc_sdw_parse_sdw_endpoints() We can avoid to use *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/87ik75etxw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 3 ++- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- sound/soc/amd/acp/acp-sdw-sof-mach.c | 2 +- sound/soc/intel/boards/sof_sdw.c | 2 +- sound/soc/sdw_utils/soc_sdw_utils.c | 5 ++--- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 443d63dc6ea3..9b28e9aef4f1 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks const struct snd_soc_acpi_endpoint *new); int asoc_sdw_get_dai_type(u32 type); -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index e8b6819cc4b4..9726a9d33ec6 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -432,7 +432,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) if (!soc_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); if (ret < 0) return ret; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a423853f3a97..963ce6fd4012 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -303,7 +303,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); if (ret < 0) return ret; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d43daf9b025d..24226a387cc2 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1285,7 +1285,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_dai; } - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); if (ret < 0) goto err_end; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 073f3f9205a7..dd2cc57059d6 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1976,14 +1976,13 @@ put_device: return ret; } -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, int *num_devs) { - struct device *dev = card->dev; - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; -- cgit From 5beabef0cffaa1ea6e27e85dbd526b7a28e0e7c7 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 29 Jun 2026 17:47:39 +0800 Subject: crash: Add crash_prepare_headers() to exclude crash kernel memory The crash memory alloc, and the exclude of crashk_res, crashk_low_res and crashk_cma memory are almost identical across different architectures, handling them in the crash core would eliminate a lot of duplication, so add crash_prepare_headers() helper to handle them in the common code. To achieve the above goal, three architecture-specific functions are introduced: - arch_get_system_nr_ranges(). Pre-counts the max number of memory ranges. - arch_crash_populate_cmem(). Collects the memory ranges and fills them into cmem. - arch_crash_exclude_ranges(). Architecture's additional crash memory ranges exclusion, defaulting to empty. Reviewed-by: Sourabh Jain Acked-by: Baoquan He Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Jinjie Ruan Link: https://patch.msgid.link/20260629094746.191843-4-ruanjinjie@huawei.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/crash_core.h | 5 +++ kernel/crash_core.c | 82 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h index c1dee3f971a9..583ffcc703d4 100644 --- a/include/linux/crash_core.h +++ b/include/linux/crash_core.h @@ -59,6 +59,8 @@ extern int crash_exclude_mem_range(struct crash_mem *mem, unsigned long long mend); extern int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map, void **addr, unsigned long *sz); +extern int crash_prepare_headers(int need_kernel_map, void **addr, + unsigned long *sz, unsigned long *nr_mem_ranges); struct kimage; struct kexec_segment; @@ -76,6 +78,9 @@ int kexec_should_crash(struct task_struct *p); int kexec_crash_loaded(void); void crash_save_cpu(struct pt_regs *regs, int cpu); extern int kimage_crash_copy_vmcoreinfo(struct kimage *image); +extern unsigned int arch_get_system_nr_ranges(void); +extern int arch_crash_populate_cmem(struct crash_mem *cmem); +extern int arch_crash_exclude_ranges(struct crash_mem *cmem); #else /* !CONFIG_CRASH_DUMP*/ struct pt_regs; diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 4f21fc3b108b..481babc29131 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -168,9 +168,6 @@ static inline resource_size_t crash_resource_size(const struct resource *res) return !res->end ? 0 : resource_size(res); } - - - int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map, void **addr, unsigned long *sz) { @@ -272,6 +269,85 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map, return 0; } +static struct crash_mem *alloc_cmem(unsigned int nr_ranges) +{ + struct crash_mem *cmem; + + cmem = kvzalloc_flex(*cmem, ranges, nr_ranges); + if (!cmem) + return NULL; + + cmem->max_nr_ranges = nr_ranges; + return cmem; +} + +unsigned int __weak arch_get_system_nr_ranges(void) { return 0; } +int __weak arch_crash_populate_cmem(struct crash_mem *cmem) { return -1; } +int __weak arch_crash_exclude_ranges(struct crash_mem *cmem) { return 0; } + +static int crash_exclude_core_ranges(struct crash_mem *cmem) +{ + int ret, i; + + /* Exclude crashkernel region */ + ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end); + if (ret) + return ret; + + if (crashk_low_res.end) { + ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end); + if (ret) + return ret; + } + + for (i = 0; i < crashk_cma_cnt; ++i) { + ret = crash_exclude_mem_range(cmem, crashk_cma_ranges[i].start, + crashk_cma_ranges[i].end); + if (ret) + return ret; + } + + return 0; +} + +int crash_prepare_headers(int need_kernel_map, void **addr, unsigned long *sz, + unsigned long *nr_mem_ranges) +{ + unsigned int max_nr_ranges; + struct crash_mem *cmem; + int ret; + + max_nr_ranges = arch_get_system_nr_ranges(); + if (!max_nr_ranges) + return -ENOMEM; + + cmem = alloc_cmem(max_nr_ranges); + if (!cmem) + return -ENOMEM; + + ret = arch_crash_populate_cmem(cmem); + if (ret) + goto out; + + ret = crash_exclude_core_ranges(cmem); + if (ret) + goto out; + + ret = arch_crash_exclude_ranges(cmem); + if (ret) + goto out; + + /* Return the computed number of memory ranges, for hotplug usage */ + if (nr_mem_ranges) + *nr_mem_ranges = cmem->nr_ranges; + + ret = crash_prepare_elf64_headers(cmem, need_kernel_map, addr, sz); + +out: + kvfree(cmem); + return ret; +} + /** * crash_exclude_mem_range - exclude a mem range for existing ranges * @mem: mem->range contains an array of ranges sorted in ascending order -- cgit From b0e06c5a30742bc6bc8523fe9c71c1a043a7661d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 29 Jun 2026 17:47:44 +0800 Subject: powerpc/kexec_file: Use crash_exclude_core_ranges() helper The crash memory exclude of crashk_res and crashk_cma memory on powerpc are almost identical to the generic crash_exclude_core_ranges(). By introducing the architecture-specific arch_crash_exclude_mem_range() function with a default implementation of crash_exclude_mem_range(), and using crash_exclude_mem_range_guarded as powerpc's separate implementation, the generic crash_exclude_core_ranges() helper function can be reused. Cc: Andrew Morton Cc: Hari Bathini Cc: Madhavan Srinivasan Cc: Mahesh Salgaonkar Cc: Michael Ellerman Cc: Ritesh Harjani (IBM) Cc: Shivang Upadhyay Acked-by: Breno leitao Acked-by: Baoquan He Reviewed-by: Sourabh Jain Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Jinjie Ruan Link: https://patch.msgid.link/20260629094746.191843-9-ruanjinjie@huawei.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/powerpc/include/asm/kexec_ranges.h | 3 --- arch/powerpc/kexec/crash.c | 2 +- arch/powerpc/kexec/ranges.c | 16 ++++------------ include/linux/crash_core.h | 4 ++++ kernel/crash_core.c | 19 +++++++++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/arch/powerpc/include/asm/kexec_ranges.h b/arch/powerpc/include/asm/kexec_ranges.h index ad95e3792d10..8489e844b447 100644 --- a/arch/powerpc/include/asm/kexec_ranges.h +++ b/arch/powerpc/include/asm/kexec_ranges.h @@ -7,9 +7,6 @@ void sort_memory_ranges(struct crash_mem *mrngs, bool merge); struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges); int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size); -int crash_exclude_mem_range_guarded(struct crash_mem **mem_ranges, - unsigned long long mstart, - unsigned long long mend); int get_exclude_memory_ranges(struct crash_mem **mem_ranges); int get_reserved_memory_ranges(struct crash_mem **mem_ranges); int get_crash_memory_ranges(struct crash_mem **mem_ranges); diff --git a/arch/powerpc/kexec/crash.c b/arch/powerpc/kexec/crash.c index 2e88ec5c4356..60a917a6beaa 100644 --- a/arch/powerpc/kexec/crash.c +++ b/arch/powerpc/kexec/crash.c @@ -513,7 +513,7 @@ static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify * base_addr = PFN_PHYS(mn->start_pfn); size = mn->nr_pages * PAGE_SIZE; end = base_addr + size - 1; - ret = crash_exclude_mem_range_guarded(&cmem, base_addr, end); + ret = arch_crash_exclude_mem_range(&cmem, base_addr, end); if (ret) { pr_err("Failed to remove hot-unplugged memory from crash memory ranges\n"); goto out; diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c index 6c58bcc3e130..e5fea23b191b 100644 --- a/arch/powerpc/kexec/ranges.c +++ b/arch/powerpc/kexec/ranges.c @@ -553,9 +553,9 @@ out: #endif /* CONFIG_KEXEC_FILE */ #ifdef CONFIG_CRASH_DUMP -int crash_exclude_mem_range_guarded(struct crash_mem **mem_ranges, - unsigned long long mstart, - unsigned long long mend) +int arch_crash_exclude_mem_range(struct crash_mem **mem_ranges, + unsigned long long mstart, + unsigned long long mend) { struct crash_mem *tmem = *mem_ranges; @@ -604,18 +604,10 @@ int get_crash_memory_ranges(struct crash_mem **mem_ranges) sort_memory_ranges(*mem_ranges, true); } - /* Exclude crashkernel region */ - ret = crash_exclude_mem_range_guarded(mem_ranges, crashk_res.start, crashk_res.end); + ret = crash_exclude_core_ranges(mem_ranges); if (ret) goto out; - for (i = 0; i < crashk_cma_cnt; ++i) { - ret = crash_exclude_mem_range_guarded(mem_ranges, crashk_cma_ranges[i].start, - crashk_cma_ranges[i].end); - if (ret) - goto out; - } - /* * FIXME: For now, stay in parity with kexec-tools but if RTAS/OPAL * regions are exported to save their context at the time of diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h index 583ffcc703d4..bc087124cd78 100644 --- a/include/linux/crash_core.h +++ b/include/linux/crash_core.h @@ -61,6 +61,7 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_ma void **addr, unsigned long *sz); extern int crash_prepare_headers(int need_kernel_map, void **addr, unsigned long *sz, unsigned long *nr_mem_ranges); +extern int crash_exclude_core_ranges(struct crash_mem **cmem); struct kimage; struct kexec_segment; @@ -81,6 +82,9 @@ extern int kimage_crash_copy_vmcoreinfo(struct kimage *image); extern unsigned int arch_get_system_nr_ranges(void); extern int arch_crash_populate_cmem(struct crash_mem *cmem); extern int arch_crash_exclude_ranges(struct crash_mem *cmem); +extern int arch_crash_exclude_mem_range(struct crash_mem **mem, + unsigned long long mstart, + unsigned long long mend); #else /* !CONFIG_CRASH_DUMP*/ struct pt_regs; diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 481babc29131..2b36aa9fade0 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -285,24 +285,31 @@ unsigned int __weak arch_get_system_nr_ranges(void) { return 0; } int __weak arch_crash_populate_cmem(struct crash_mem *cmem) { return -1; } int __weak arch_crash_exclude_ranges(struct crash_mem *cmem) { return 0; } -static int crash_exclude_core_ranges(struct crash_mem *cmem) +int __weak arch_crash_exclude_mem_range(struct crash_mem **mem, + unsigned long long mstart, + unsigned long long mend) +{ + return crash_exclude_mem_range(*mem, mstart, mend); +} + +int crash_exclude_core_ranges(struct crash_mem **cmem) { int ret, i; /* Exclude crashkernel region */ - ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end); + ret = arch_crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end); if (ret) return ret; if (crashk_low_res.end) { - ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end); + ret = arch_crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end); if (ret) return ret; } for (i = 0; i < crashk_cma_cnt; ++i) { - ret = crash_exclude_mem_range(cmem, crashk_cma_ranges[i].start, - crashk_cma_ranges[i].end); + ret = arch_crash_exclude_mem_range(cmem, crashk_cma_ranges[i].start, + crashk_cma_ranges[i].end); if (ret) return ret; } @@ -329,7 +336,7 @@ int crash_prepare_headers(int need_kernel_map, void **addr, unsigned long *sz, if (ret) goto out; - ret = crash_exclude_core_ranges(cmem); + ret = crash_exclude_core_ranges(&cmem); if (ret) goto out; -- cgit From c12c63d05dfe7088aa8b1b872fe261e25537c823 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 29 Jun 2026 17:47:45 +0800 Subject: arm64: kexec_file: Add support for crashkernel CMA reservation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 35c18f2933c5 ("Add a new optional ",cma" suffix to the crashkernel= command line option") and commit ab475510e042 ("kdump: implement reserve_crashkernel_cma") added CMA support for kdump crashkernel reservation. Crash kernel memory reservation wastes production resources if too large, risks kdump failure if too small, and faces allocation difficulties on fragmented systems due to contiguous block constraints. The new CMA-based crashkernel reservation scheme splits the "large fixed reservation" into a "small fixed region + large CMA dynamic region": the CMA memory is available to userspace during normal operation to avoid waste, and is reclaimed for kdump upon crash—saving memory while improving reliability. So extend crashkernel CMA reservation support to arm64. The following changes are made to enable CMA reservation: - Parse and obtain the CMA reservation size along with other crashkernel parameters. - Call reserve_crashkernel_cma() to allocate the CMA region for kdump. - Include the CMA-reserved ranges for kdump kernel to use. - Exclude the CMA-reserved ranges from the crash kernel memory to prevent them from being exported through /proc/vmcore, which is already done in the crash core. Update kernel-parameters.txt to document CMA support for crashkernel on arm64 architecture. Tested-by: Breno Leitao Acked-by: Catalin Marinas Acked-by: Rob Herring (Arm) Acked-by: Baoquan He Acked-by: Mike Rapoport (Microsoft) Acked-by: Ard Biesheuvel Signed-off-by: Jinjie Ruan Link: https://patch.msgid.link/20260629094746.191843-10-ruanjinjie@huawei.com Signed-off-by: Mike Rapoport (Microsoft) --- Documentation/admin-guide/kernel-parameters.txt | 2 +- arch/arm64/kernel/machine_kexec_file.c | 2 +- arch/arm64/mm/init.c | 5 +++-- drivers/of/fdt.c | 9 +++++---- drivers/of/kexec.c | 9 +++++++++ include/linux/crash_reserve.h | 4 +++- 6 files changed, 22 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b5493a7f8f22..6774223c53b0 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1089,7 +1089,7 @@ Kernel parameters It will be ignored when crashkernel=X,high is not used or memory reserved is below 4G. crashkernel=size[KMG],cma - [KNL, X86, ppc] Reserve additional crash kernel memory from + [KNL, X86, ARM64, PPC] Reserve additional crash kernel memory from CMA. This reservation is usable by the first system's userspace memory and kernel movable allocations (memory balloon, zswap). Pages allocated from this memory range diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index b019b31df48c..854d872dfd0f 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -42,7 +42,7 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image) #ifdef CONFIG_CRASH_DUMP unsigned int arch_get_system_nr_ranges(void) { - unsigned int nr_ranges = 2; /* for exclusion of crashkernel region */ + unsigned int nr_ranges = 2 + crashk_cma_cnt; /* for exclusion of crashkernel region */ phys_addr_t start, end; u64 i; diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 97987f850a33..227f58522dad 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -96,8 +96,8 @@ phys_addr_t __ro_after_init arm64_dma_phys_limit; static void __init arch_reserve_crashkernel(void) { + unsigned long long crash_base, crash_size, cma_size = 0; unsigned long long low_size = 0; - unsigned long long crash_base, crash_size; bool high = false; int ret; @@ -106,11 +106,12 @@ static void __init arch_reserve_crashkernel(void) ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(), &crash_size, &crash_base, - &low_size, NULL, &high); + &low_size, &cma_size, &high); if (ret) return; reserve_crashkernel_generic(crash_size, crash_base, low_size, high); + reserve_crashkernel_cma(cma_size); } static phys_addr_t __init max_zone_phys(phys_addr_t zone_limit) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 26f66046cc32..a64afc3ded3d 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -878,11 +878,12 @@ static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND; /* * The main usage of linux,usable-memory-range is for crash dump kernel. * Originally, the number of usable-memory regions is one. Now there may - * be two regions, low region and high region. - * To make compatibility with existing user-space and older kdump, the low - * region is always the last range of linux,usable-memory-range if exist. + * be 2 + CRASHK_CMA_RANGES_MAX regions, low region, high region and cma + * regions. To make compatibility with existing user-space and older kdump, + * the high and low region are always the first two ranges of + * linux,usable-memory-range if exist. */ -#define MAX_USABLE_RANGES 2 +#define MAX_USABLE_RANGES (2 + CRASHK_CMA_RANGES_MAX) /** * early_init_dt_check_for_usable_mem_range - Decode usable memory range diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c index b6837e299e7f..029903b986cb 100644 --- a/drivers/of/kexec.c +++ b/drivers/of/kexec.c @@ -458,6 +458,15 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image, if (ret) goto out; } + + for (int i = 0; i < crashk_cma_cnt; i++) { + ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, + "linux,usable-memory-range", + crashk_cma_ranges[i].start, + crashk_cma_ranges[i].end - crashk_cma_ranges[i].start + 1); + if (ret) + goto out; + } #endif } diff --git a/include/linux/crash_reserve.h b/include/linux/crash_reserve.h index f0dc03d94ca2..30864d90d7f5 100644 --- a/include/linux/crash_reserve.h +++ b/include/linux/crash_reserve.h @@ -14,9 +14,11 @@ extern struct resource crashk_res; extern struct resource crashk_low_res; extern struct range crashk_cma_ranges[]; + +#define CRASHK_CMA_RANGES_MAX 4 #if defined(CONFIG_CMA) && defined(CONFIG_ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION) #define CRASHKERNEL_CMA -#define CRASHKERNEL_CMA_RANGES_MAX 4 +#define CRASHKERNEL_CMA_RANGES_MAX (CRASHK_CMA_RANGES_MAX) extern int crashk_cma_cnt; #else #define crashk_cma_cnt 0 -- cgit From c0d73c09381e1c95d9262550e057c2f3dc82f98d Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Fri, 12 Jun 2026 11:14:36 -0300 Subject: audit: add missing syscalls to PERM class tables Add missing file metadata syscalls to the audit PERM class tables, addressing gaps where certain file operations were not properly classified for audit rule matching. Changes: - audit_change_attr.h: Add file_setattr - audit_read.h: Add quotactl_fd, file_getattr, stat, stat64, lstat, lstat64, fstat, fstat64, newfstatat, fstatat64, and statx - audit_write.h: Add quotactl_fd Architecture-specific and conditionally-compiled syscalls are guarded with #ifdef. Signed-off-by: Steve Grubb Signed-off-by: Ricardo Robaina Signed-off-by: Paul Moore --- include/asm-generic/audit_change_attr.h | 3 +++ include/asm-generic/audit_read.h | 31 +++++++++++++++++++++++++++++++ include/asm-generic/audit_write.h | 3 +++ 3 files changed, 37 insertions(+) (limited to 'include') diff --git a/include/asm-generic/audit_change_attr.h b/include/asm-generic/audit_change_attr.h index ddd90bbe40df..94388da3490c 100644 --- a/include/asm-generic/audit_change_attr.h +++ b/include/asm-generic/audit_change_attr.h @@ -40,3 +40,6 @@ __NR_link, #ifdef __NR_linkat __NR_linkat, #endif +#ifdef __NR_file_setattr +__NR_file_setattr, +#endif diff --git a/include/asm-generic/audit_read.h b/include/asm-generic/audit_read.h index fb9991f53fb6..d8dc3dd6bf63 100644 --- a/include/asm-generic/audit_read.h +++ b/include/asm-generic/audit_read.h @@ -3,6 +3,9 @@ __NR_readlink, #endif __NR_quotactl, +#ifdef __NR_quotactl_fd +__NR_quotactl_fd, +#endif __NR_listxattr, #ifdef __NR_listxattrat __NR_listxattrat, @@ -18,3 +21,31 @@ __NR_fgetxattr, #ifdef __NR_readlinkat __NR_readlinkat, #endif +#ifdef __NR_file_getattr +__NR_file_getattr, +#endif +#ifdef __NR_stat +__NR_stat, +#endif +#ifdef __NR_stat64 +__NR_stat64, +#endif +#ifdef __NR_lstat +__NR_lstat, +#endif +#ifdef __NR_lstat64 +__NR_lstat64, +#endif +#ifdef __NR_fstat +__NR_fstat, +#endif +#ifdef __NR_fstat64 +__NR_fstat64, +#endif +#ifdef __NR_newfstatat +__NR_newfstatat, +#endif +#ifdef __NR_fstatat64 +__NR_fstatat64, +#endif +__NR_statx, diff --git a/include/asm-generic/audit_write.h b/include/asm-generic/audit_write.h index f9f1d0ae11d9..378128dc31e3 100644 --- a/include/asm-generic/audit_write.h +++ b/include/asm-generic/audit_write.h @@ -5,6 +5,9 @@ __NR_acct, __NR_swapon, #endif __NR_quotactl, +#ifdef __NR_quotactl_fd +__NR_quotactl_fd, +#endif #ifdef __NR_truncate __NR_truncate, #endif -- cgit From 5370facb7b4461166a4610d456fefeb92ef50a82 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 24 Jun 2026 10:55:51 +0100 Subject: media: keymaps: Remove obsolete RC_MAP_RC5_TV keymap define Since commit 206241069ecf ("[media] rc/keymaps: Remove the obsolete rc-rc5-tv keymap"), the rc-rc5-tv keymap is no longer in the tree. Fixes: 206241069ecf ("[media] rc/keymaps: Remove the obsolete rc-rc5-tv keymap") Signed-off-by: Sean Young Acked-by: Mauro Carvalho Chehab --- include/media/rc-map.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/media/rc-map.h b/include/media/rc-map.h index d90e4611b066..950d702aee3b 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -309,7 +309,6 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_PROTEUS_2309 "rc-proteus-2309" #define RC_MAP_PURPLETV "rc-purpletv" #define RC_MAP_PV951 "rc-pv951" -#define RC_MAP_RC5_TV "rc-rc5-tv" #define RC_MAP_RC6_MCE "rc-rc6-mce" #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" #define RC_MAP_REDDO "rc-reddo" -- cgit From 6e5deb2923b0d1b73c77a1a77c30b0da43d9e022 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 24 Jun 2026 11:05:47 +0100 Subject: media: keymaps: Remove obsolete RC_MAP_HAUPPAUGE_NEW keymap define Since commit af86ce79f020 ("[media] remove the old RC_MAP_HAUPPAUGE_NEW RC map"), the RC_MAP_HAUPPAUGE_NEW define is no longer used. Fixes: af86ce79f020 ("[media] remove the old RC_MAP_HAUPPAUGE_NEW RC map") Signed-off-by: Sean Young Acked-by: Mauro Carvalho Chehab --- include/media/rc-map.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 950d702aee3b..d95ed3e96de2 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -262,7 +262,6 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_GENIUS_TVGO_A11MCE "rc-genius-tvgo-a11mce" #define RC_MAP_GOTVIEW7135 "rc-gotview7135" #define RC_MAP_HAUPPAUGE "rc-hauppauge" -#define RC_MAP_HAUPPAUGE_NEW "rc-hauppauge" #define RC_MAP_HISI_POPLAR "rc-hisi-poplar" #define RC_MAP_HISI_TV_DEMO "rc-hisi-tv-demo" #define RC_MAP_IMON_MCE "rc-imon-mce" -- cgit From c905736a46892e4776efc7f50888d67715d6ec08 Mon Sep 17 00:00:00 2001 From: Robert Femmer Date: Wed, 24 Jun 2026 11:01:46 +0200 Subject: io_uring: annotate remote tasks for kcoverage Fuzzers use coverage information to guide generation of test cases towards new or interesting code paths. Syzkaller, specifically, makes use kcoverage (CONFIG_KCOV). Coverage information is not collected for kernel tasks unless annotated by kcov_remote_start and kcov_remote_stop. This patch annotates io-uring's work queue and sqpoll tasks. Depends-On: 20260430-kcov-refactor-common-handle-v1-1-23a0c7a0ba38@google.com Signed-off-by: Robert Femmer Signed-off-by: Jens Axboe --- include/linux/io_uring_types.h | 2 ++ io_uring/io-wq.c | 5 +++++ io_uring/io_uring.c | 2 ++ io_uring/sqpoll.c | 7 ++++++- 4 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h index 87151a5b62c1..a2c623a67a25 100644 --- a/include/linux/io_uring_types.h +++ b/include/linux/io_uring_types.h @@ -534,6 +534,8 @@ struct io_ring_ctx { struct io_mapped_region ring_region; /* used for optimised request parameter and wait argument passing */ struct io_mapped_region param_region; + + struct kcov_common_handle_id kcov_handle; }; /* diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index 2e14880eef92..be8d75731d24 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "io-wq.h" #include "slist.h" @@ -643,13 +644,17 @@ static void io_worker_handle_work(struct io_wq_acct *acct, unsigned int hash = __io_wq_is_hashed(work_flags) ? __io_get_work_hash(work_flags) : -1U; + struct io_kiocb *req; next_hashed = wq_next_work(work); if (do_kill && (work_flags & IO_WQ_WORK_UNBOUND)) atomic_or(IO_WQ_WORK_CANCEL, &work->flags); + req = container_of(work, struct io_kiocb, work); + kcov_remote_start_common(req->ctx->kcov_handle); io_wq_submit_work(work); + kcov_remote_stop(); io_assign_current_work(worker, NULL); linked = io_wq_free_work(work); diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 1ea2fca34a36..1279e27c2c6d 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -59,6 +59,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -293,6 +294,7 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p) INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd); io_napi_init(ctx); mutex_init(&ctx->mmap_lock); + ctx->kcov_handle = kcov_common_handle(); return ctx; diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c index 2460bd605266..ad42e8eb1002 100644 --- a/io_uring/sqpoll.c +++ b/io_uring/sqpoll.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -332,10 +333,14 @@ static int io_sq_thread(void *data) cap_entries = !list_is_singular(&sqd->ctx_list); list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) { - int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist); + int ret; + + kcov_remote_start_common(ctx->kcov_handle); + ret = __io_sq_thread(ctx, sqd, cap_entries, &ist); if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list))) sqt_spin = true; + kcov_remote_stop(); } if (io_sq_tw(IORING_TW_CAP_ENTRIES_VALUE)) sqt_spin = true; -- cgit From 317cefdcaacc409dfa371f086392ddbef914c99d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 29 Jun 2026 17:32:00 +0000 Subject: bonding: no longer rely on RTNL in bond_fill_info() Add READ_ONCE()/WRITE_ONCE() annotations on port->is_enabled. While this field is written under bond->mode_lock protection, is is read without this lock being held. Change bond_fill_info() to acquire RCU and use READ_ONCE() to read bond->params fields that can be updated concurrently from sysfs/procfs/rtnetlink. Add const qualifiers to bond_uses_primary(), __agg_active_ports(), bond_option_active_slave_get_rcu(), bond_3ad_get_active_agg_info(), __bond_3ad_get_active_agg_info() helpers. Signed-off-by: Eric Dumazet Cc: Jay Vosburgh Cc: Andrew Lunn Reviewed-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260629173200.469953-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_3ad.c | 24 ++++---- drivers/net/bonding/bond_netlink.c | 109 +++++++++++++++++++++---------------- drivers/net/bonding/bond_options.c | 8 +-- include/net/bond_3ad.h | 4 +- include/net/bonding.h | 8 +-- 5 files changed, 85 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index acbba08dbdfa..b8e4b4d68dd6 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -760,14 +760,14 @@ static int __agg_usable_ports(struct aggregator *agg) return valid; } -static int __agg_active_ports(struct aggregator *agg) +static int __agg_active_ports(const struct aggregator *agg) { - struct port *port; + const struct port *port; int active = 0; for (port = agg->lag_ports; port; port = port->next_port_in_aggregator) { - if (port->is_enabled) + if (READ_ONCE(port->is_enabled)) active++; } @@ -2801,11 +2801,11 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) * some of he adaptors(ce1000.lan) report. */ if (link == BOND_LINK_UP) { - port->is_enabled = true; + WRITE_ONCE(port->is_enabled, true); ad_update_actor_keys(port, false); } else { /* link has failed */ - port->is_enabled = false; + WRITE_ONCE(port->is_enabled, false); ad_update_actor_keys(port, true); } agg = __get_first_agg(port); @@ -2878,16 +2878,20 @@ out: * Returns: 0 on success * < 0 on error */ -int __bond_3ad_get_active_agg_info(struct bonding *bond, +int __bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info) { - struct aggregator *aggregator = NULL, *tmp; + const struct aggregator *aggregator = NULL, *tmp; + struct ad_slave_info *ad_slave_info; + const struct port *port; struct list_head *iter; struct slave *slave; - struct port *port; bond_for_each_slave_rcu(bond, slave, iter) { - port = &(SLAVE_AD_INFO(slave)->port); + ad_slave_info = SLAVE_AD_INFO(slave); + if (!ad_slave_info) + continue; + port = &ad_slave_info->port; tmp = rcu_dereference(port->aggregator); if (tmp && tmp->is_active) { aggregator = tmp; @@ -2907,7 +2911,7 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond, return 0; } -int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) +int bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info) { int ret; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 4a11572f663d..55d2f8a539d4 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -686,53 +686,58 @@ static size_t bond_get_size(const struct net_device *bond_dev) 0; } -static int bond_option_active_slave_get_ifindex(struct bonding *bond) +static int bond_option_active_slave_get_ifindex_rcu(const struct bonding *bond) { - const struct net_device *slave; - int ifindex; + const struct net_device *dev = NULL; + const struct slave *slave; - rcu_read_lock(); - slave = bond_option_active_slave_get_rcu(bond); - ifindex = slave ? slave->ifindex : 0; - rcu_read_unlock(); - return ifindex; + slave = rcu_dereference(bond->curr_active_slave); + if (slave) + dev = slave->dev; + return dev ? dev->ifindex : 0; } static int bond_fill_info(struct sk_buff *skb, const struct net_device *bond_dev) { - struct bonding *bond = netdev_priv(bond_dev); - unsigned int packets_per_slave; - int ifindex, i, targets_added; + const struct bonding *bond = netdev_priv(bond_dev); + int i, targets_added, miimon, mode; + const struct slave *primary; struct nlattr *targets; - struct slave *primary; - if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond))) + rcu_read_lock(); + mode = READ_ONCE(bond->params.mode); + if (nla_put_u8(skb, IFLA_BOND_MODE, mode)) goto nla_put_failure; - ifindex = bond_option_active_slave_get_ifindex(bond); - if (ifindex && nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, ifindex)) - goto nla_put_failure; + if (bond_mode_uses_primary(mode)) { + int ifindex = bond_option_active_slave_get_ifindex_rcu(bond); + + if (ifindex && nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, ifindex)) + goto nla_put_failure; + } - if (nla_put_u32(skb, IFLA_BOND_MIIMON, bond->params.miimon)) + miimon = READ_ONCE(bond->params.miimon); + if (nla_put_u32(skb, IFLA_BOND_MIIMON, miimon)) goto nla_put_failure; if (nla_put_u32(skb, IFLA_BOND_UPDELAY, - bond->params.updelay * bond->params.miimon)) + READ_ONCE(bond->params.updelay) * miimon)) goto nla_put_failure; if (nla_put_u32(skb, IFLA_BOND_DOWNDELAY, - bond->params.downdelay * bond->params.miimon)) + READ_ONCE(bond->params.downdelay) * miimon)) goto nla_put_failure; if (nla_put_u32(skb, IFLA_BOND_PEER_NOTIF_DELAY, - bond->params.peer_notif_delay * bond->params.miimon)) + READ_ONCE(bond->params.peer_notif_delay) * miimon)) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_USE_CARRIER, 1)) goto nla_put_failure; - if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, bond->params.arp_interval)) + if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, + READ_ONCE(bond->params.arp_interval))) goto nla_put_failure; targets = nla_nest_start_noflag(skb, IFLA_BOND_ARP_IP_TARGET); @@ -741,8 +746,10 @@ static int bond_fill_info(struct sk_buff *skb, targets_added = 0; for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { - if (bond->params.arp_targets[i]) { - if (nla_put_be32(skb, i, bond->params.arp_targets[i])) + __be32 t = READ_ONCE(bond->params.arp_targets[i]); + + if (t) { + if (nla_put_be32(skb, i, t)) goto nla_put_failure; targets_added = 1; } @@ -753,11 +760,12 @@ static int bond_fill_info(struct sk_buff *skb, else nla_nest_cancel(skb, targets); - if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE, bond->params.arp_validate)) + if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE, + READ_ONCE(bond->params.arp_validate))) goto nla_put_failure; if (nla_put_u32(skb, IFLA_BOND_ARP_ALL_TARGETS, - bond->params.arp_all_targets)) + READ_ONCE(bond->params.arp_all_targets))) goto nla_put_failure; #if IS_ENABLED(CONFIG_IPV6) @@ -767,6 +775,9 @@ static int bond_fill_info(struct sk_buff *skb, targets_added = 0; for (i = 0; i < BOND_MAX_NS_TARGETS; i++) { + /* Note: IPv6 addresses can not be read in an atomic READ_ONCE() yet. + * We accept this minor race for the moment. + */ if (!ipv6_addr_any(&bond->params.ns_targets[i])) { if (nla_put_in6_addr(skb, i, &bond->params.ns_targets[i])) goto nla_put_failure; @@ -780,97 +791,97 @@ static int bond_fill_info(struct sk_buff *skb, nla_nest_cancel(skb, targets); #endif - primary = rtnl_dereference(bond->primary_slave); + primary = rcu_dereference(bond->primary_slave); if (primary && nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex)) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT, - bond->params.primary_reselect)) + READ_ONCE(bond->params.primary_reselect))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_FAIL_OVER_MAC, - bond->params.fail_over_mac)) + READ_ONCE(bond->params.fail_over_mac))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_XMIT_HASH_POLICY, - bond->params.xmit_policy)) + READ_ONCE(bond->params.xmit_policy))) goto nla_put_failure; if (nla_put_u32(skb, IFLA_BOND_RESEND_IGMP, - bond->params.resend_igmp)) + READ_ONCE(bond->params.resend_igmp))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_NUM_PEER_NOTIF, - bond->params.num_peer_notif)) + READ_ONCE(bond->params.num_peer_notif))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_ALL_SLAVES_ACTIVE, - bond->params.all_slaves_active)) + READ_ONCE(bond->params.all_slaves_active))) goto nla_put_failure; if (nla_put_u32(skb, IFLA_BOND_MIN_LINKS, - bond->params.min_links)) + READ_ONCE(bond->params.min_links))) goto nla_put_failure; if (nla_put_u32(skb, IFLA_BOND_LP_INTERVAL, - bond->params.lp_interval)) + READ_ONCE(bond->params.lp_interval))) goto nla_put_failure; - packets_per_slave = bond->params.packets_per_slave; if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE, - packets_per_slave)) + READ_ONCE(bond->params.packets_per_slave))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_AD_LACP_ACTIVE, - bond->params.lacp_active)) + READ_ONCE(bond->params.lacp_active))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_AD_LACP_RATE, - bond->params.lacp_fast)) + READ_ONCE(bond->params.lacp_fast))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_AD_SELECT, - bond->params.ad_select)) + READ_ONCE(bond->params.ad_select))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_TLB_DYNAMIC_LB, - bond->params.tlb_dynamic_lb)) + READ_ONCE(bond->params.tlb_dynamic_lb))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_MISSED_MAX, - bond->params.missed_max)) + READ_ONCE(bond->params.missed_max))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_COUPLED_CONTROL, - bond->params.coupled_control)) + READ_ONCE(bond->params.coupled_control))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_BROADCAST_NEIGH, - bond->params.broadcast_neighbor)) + READ_ONCE(bond->params.broadcast_neighbor))) goto nla_put_failure; if (nla_put_u8(skb, IFLA_BOND_LACP_STRICT, - bond->params.lacp_strict)) + READ_ONCE(bond->params.lacp_strict))) goto nla_put_failure; - if (BOND_MODE(bond) == BOND_MODE_8023AD) { + if (mode == BOND_MODE_8023AD) { struct ad_info info; if (capable(CAP_NET_ADMIN)) { if (nla_put_u16(skb, IFLA_BOND_AD_ACTOR_SYS_PRIO, - bond->params.ad_actor_sys_prio)) + READ_ONCE(bond->params.ad_actor_sys_prio))) goto nla_put_failure; if (nla_put_u16(skb, IFLA_BOND_AD_USER_PORT_KEY, - bond->params.ad_user_port_key)) + READ_ONCE(bond->params.ad_user_port_key))) goto nla_put_failure; + /* Small race here, this is a minor trade off. */ if (nla_put(skb, IFLA_BOND_AD_ACTOR_SYSTEM, ETH_ALEN, &bond->params.ad_actor_system)) goto nla_put_failure; } - if (!bond_3ad_get_active_agg_info(bond, &info)) { + if (!__bond_3ad_get_active_agg_info(bond, &info)) { struct nlattr *nest; nest = nla_nest_start_noflag(skb, IFLA_BOND_AD_INFO); @@ -898,9 +909,11 @@ static int bond_fill_info(struct sk_buff *skb, } } + rcu_read_unlock(); return 0; nla_put_failure: + rcu_read_unlock(); return -EMSGSIZE; } diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index e590c8dee86e..36b8d89387ee 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -934,14 +934,14 @@ static int bond_option_mode_set(struct bonding *bond, /* don't cache arp_validate between modes */ WRITE_ONCE(bond->params.arp_validate, BOND_ARP_VALIDATE_NONE); - bond->params.mode = newval->value; + WRITE_ONCE(bond->params.mode, newval->value); /* When changing mode, the bond device is down, we may reduce * the bond_bcast_neigh_enabled in bond_close() if broadcast_neighbor * enabled in 8023ad mode. Therefore, only clear broadcast_neighbor * to 0. */ - bond->params.broadcast_neighbor = 0; + WRITE_ONCE(bond->params.broadcast_neighbor, 0); if (bond->dev->reg_state == NETREG_REGISTERED) { bool update = false; @@ -1706,7 +1706,7 @@ static int bond_option_lacp_strict_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n", newval->string, newval->value); - bond->params.lacp_strict = newval->value; + WRITE_ONCE(bond->params.lacp_strict, newval->value); bond_3ad_set_carrier(bond); return 0; @@ -1927,7 +1927,7 @@ static int bond_option_broadcast_neigh_set(struct bonding *bond, if (bond->params.broadcast_neighbor == newval->value) return 0; - bond->params.broadcast_neighbor = newval->value; + WRITE_ONCE(bond->params.broadcast_neighbor, newval->value); if (bond->dev->flags & IFF_UP) { if (bond->params.broadcast_neighbor) static_branch_inc(&bond_bcast_neigh_enabled); diff --git a/include/net/bond_3ad.h b/include/net/bond_3ad.h index 05572c19e14b..ef667dff2972 100644 --- a/include/net/bond_3ad.h +++ b/include/net/bond_3ad.h @@ -302,8 +302,8 @@ void bond_3ad_state_machine_handler(struct work_struct *); void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout); void bond_3ad_adapter_speed_duplex_changed(struct slave *slave); void bond_3ad_handle_link_change(struct slave *slave, char link); -int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info); -int __bond_3ad_get_active_agg_info(struct bonding *bond, +int bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info); +int __bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info); int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); diff --git a/include/net/bonding.h b/include/net/bonding.h index 2c54a36a8477..598d56b1bc97 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -345,14 +345,14 @@ static inline bool bond_mode_uses_primary(int mode) mode == BOND_MODE_ALB; } -static inline bool bond_uses_primary(struct bonding *bond) +static inline bool bond_uses_primary(const struct bonding *bond) { return bond_mode_uses_primary(BOND_MODE(bond)); } -static inline struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond) +static inline struct net_device *bond_option_active_slave_get_rcu(const struct bonding *bond) { - struct slave *slave = rcu_dereference_rtnl(bond->curr_active_slave); + const struct slave *slave = rcu_dereference_rtnl(bond->curr_active_slave); return bond_uses_primary(bond) && slave ? slave->dev : NULL; } @@ -703,7 +703,7 @@ void bond_setup(struct net_device *bond_dev); unsigned int bond_get_num_tx_queues(void); int bond_netlink_init(void); void bond_netlink_fini(void); -struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond); +struct net_device *bond_option_active_slave_get_rcu(const struct bonding *bond); const char *bond_slave_link_status(s8 link); struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev, struct net_device *end_dev, -- cgit From 6a673b6c419a5b5bf7457f33b80a77f842057066 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Jun 2026 18:06:54 +0200 Subject: kexec: Replace __ASSEMBLY__ with __ASSEMBLER__ in header file While the GCC and Clang compilers already define __ASSEMBLER__ automatically when compiling assembly code, __ASSEMBLY__ is a macro that only gets defined by the Makefiles in the kernel. This can be very confusing when switching between userspace and kernelspace coding, or when dealing with uapi headers that rather should use __ASSEMBLER__ instead. So let's standardize now on the __ASSEMBLER__ macro that is provided by the compilers. Signed-off-by: Thomas Huth Acked-by: Pratyush Yadav Link: https://patch.msgid.link/20260619160654.75980-1-thuth@redhat.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/kexec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 8a22bc9b8c6c..0af8ae4fdd08 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -13,7 +13,7 @@ #define IND_SOURCE (1 << IND_SOURCE_BIT) #define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE) -#if !defined(__ASSEMBLY__) +#if !defined(__ASSEMBLER__) #include #include -- cgit From 9370a5c664e8d95561cc9a418e499a15bf2bc1a3 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 23 Jun 2026 17:23:04 +0200 Subject: dma-buf: rename dma_fence_enable_sw_signaling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dropping the _sw_ part from the names was proposed multiple times now and IIRC people generally agreed with the idea already. The function requests a fence to signal and triggers some sort of HW interaction on most backends. So this is not really software related at all and the callback is already just named enable_signaling as well. Just streamline that and use a consistent name everywhere. Assisted-by: Claude Sonet 4 Signed-off-by: Christian König Reviewed-by: Matthew Brost Reviewed-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260624122917.2483-2-christian.koenig@amd.com --- drivers/dma-buf/dma-fence.c | 8 ++--- drivers/dma-buf/st-dma-fence-chain.c | 4 +-- drivers/dma-buf/st-dma-fence-unwrap.c | 42 ++++++++++++------------ drivers/dma-buf/st-dma-fence.c | 16 ++++----- drivers/dma-buf/st-dma-resv.c | 10 +++--- drivers/gpu/drm/i915/i915_active.c | 2 +- drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 2 +- drivers/gpu/drm/xe/xe_bo.c | 2 +- drivers/gpu/drm/xe/xe_sched_job.c | 2 +- drivers/gpu/drm/xe/xe_svm.c | 2 +- drivers/gpu/drm/xe/xe_userptr.c | 2 +- drivers/gpu/drm/xe/xe_vm.c | 4 +-- include/linux/dma-fence.h | 4 +-- 14 files changed, 51 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index c7ea1e75d38a..0ec81a568bbd 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -534,7 +534,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout) __dma_fence_might_wait(); - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); rcu_read_lock(); ops = rcu_dereference(fence->ops); @@ -656,14 +656,14 @@ static bool __dma_fence_enable_signaling(struct dma_fence *fence) } /** - * dma_fence_enable_sw_signaling - enable signaling on fence + * dma_fence_enable_signaling - enable signaling on fence * @fence: the fence to enable * * This will request for sw signaling to be enabled, to make the fence * complete as soon as possible. This calls &dma_fence_ops.enable_signaling * internally. */ -void dma_fence_enable_sw_signaling(struct dma_fence *fence) +void dma_fence_enable_signaling(struct dma_fence *fence) { unsigned long flags; @@ -671,7 +671,7 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence) __dma_fence_enable_signaling(fence); dma_fence_unlock_irqrestore(fence, flags); } -EXPORT_SYMBOL(dma_fence_enable_sw_signaling); +EXPORT_SYMBOL(dma_fence_enable_signaling); /** * dma_fence_add_callback - add a callback to be called when the fence diff --git a/drivers/dma-buf/st-dma-fence-chain.c b/drivers/dma-buf/st-dma-fence-chain.c index a3023d3fedc9..e0d9b69bfa76 100644 --- a/drivers/dma-buf/st-dma-fence-chain.c +++ b/drivers/dma-buf/st-dma-fence-chain.c @@ -82,7 +82,7 @@ static void test_sanitycheck(struct kunit *test) chain = mock_chain(NULL, f, 1); if (chain) - dma_fence_enable_sw_signaling(chain); + dma_fence_enable_signaling(chain); else KUNIT_FAIL(test, "Failed to create chain"); @@ -139,7 +139,7 @@ static int fence_chains_init(struct fence_chains *fc, unsigned int count, fc->tail = fc->chains[i]; - dma_fence_enable_sw_signaling(fc->chains[i]); + dma_fence_enable_signaling(fc->chains[i]); } fc->chain_length = i; diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c index 4e7ee25372ba..4d9d313b460c 100644 --- a/drivers/dma-buf/st-dma-fence-unwrap.c +++ b/drivers/dma-buf/st-dma-fence-unwrap.c @@ -103,7 +103,7 @@ static void test_sanitycheck(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); array = mock_array(1, f); KUNIT_ASSERT_NOT_NULL(test, array); @@ -122,7 +122,7 @@ static void test_unwrap_array(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -131,7 +131,7 @@ static void test_unwrap_array(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); array = mock_array(2, f1, f2); KUNIT_ASSERT_NOT_NULL(test, array); @@ -160,7 +160,7 @@ static void test_unwrap_chain(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -169,7 +169,7 @@ static void test_unwrap_chain(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); chain = mock_chain(f1, f2); KUNIT_ASSERT_NOT_NULL(test, chain); @@ -198,7 +198,7 @@ static void test_unwrap_chain_array(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -207,7 +207,7 @@ static void test_unwrap_chain_array(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); array = mock_array(2, f1, f2); KUNIT_ASSERT_NOT_NULL(test, array); @@ -239,7 +239,7 @@ static void test_unwrap_merge(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -247,7 +247,7 @@ static void test_unwrap_merge(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = dma_fence_unwrap_merge(f1, f2); if (!f3) { @@ -285,7 +285,7 @@ static void test_unwrap_merge_duplicate(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = dma_fence_unwrap_merge(f1, f1); if (!f2) { @@ -322,7 +322,7 @@ static void test_unwrap_merge_seqno(struct kunit *test) f1 = __mock_fence(ctx[1], 1); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = __mock_fence(ctx[1], 2); if (!f2) { @@ -330,7 +330,7 @@ static void test_unwrap_merge_seqno(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = __mock_fence(ctx[0], 1); if (!f3) { @@ -338,7 +338,7 @@ static void test_unwrap_merge_seqno(struct kunit *test) goto error_put_f2; } - dma_fence_enable_sw_signaling(f3); + dma_fence_enable_signaling(f3); f4 = dma_fence_unwrap_merge(f1, f2, f3); if (!f4) { @@ -378,7 +378,7 @@ static void test_unwrap_merge_order(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -387,7 +387,7 @@ static void test_unwrap_merge_order(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); a1 = mock_array(2, f1, f2); KUNIT_ASSERT_NOT_NULL(test, a1); @@ -442,7 +442,7 @@ static void test_unwrap_merge_complex(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -450,7 +450,7 @@ static void test_unwrap_merge_complex(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = dma_fence_unwrap_merge(f1, f2); if (!f3) { @@ -510,7 +510,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) f1 = __mock_fence(ctx[0], 2); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = __mock_fence(ctx[1], 1); if (!f2) { @@ -518,7 +518,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = __mock_fence(ctx[0], 1); if (!f3) { @@ -526,7 +526,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) goto error_put_f2; } - dma_fence_enable_sw_signaling(f3); + dma_fence_enable_signaling(f3); f4 = __mock_fence(ctx[1], 2); if (!f4) { @@ -534,7 +534,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) goto error_put_f3; } - dma_fence_enable_sw_signaling(f4); + dma_fence_enable_signaling(f4); f5 = mock_array(2, dma_fence_get(f1), dma_fence_get(f2)); if (!f5) { diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c index 499272229696..856d0d302a5d 100644 --- a/drivers/dma-buf/st-dma-fence.c +++ b/drivers/dma-buf/st-dma-fence.c @@ -42,7 +42,7 @@ static void test_sanitycheck(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_signal(f); dma_fence_put(f); @@ -55,7 +55,7 @@ static void test_signaling(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); if (dma_fence_is_signaled(f)) { KUNIT_FAIL(test, "Fence unexpectedly signaled on creation"); @@ -127,7 +127,7 @@ static void test_late_add_callback(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_signal(f); @@ -209,7 +209,7 @@ static void test_status(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); if (dma_fence_get_status(f)) { KUNIT_FAIL(test, "Fence unexpectedly has signaled status on creation"); @@ -233,7 +233,7 @@ static void test_error(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_set_error(f, -EIO); @@ -260,7 +260,7 @@ static void test_wait(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); if (dma_fence_wait_timeout(f, false, 0) != 0) { KUNIT_FAIL(test, "Wait reported complete before being signaled"); @@ -300,7 +300,7 @@ static void test_wait_timeout(struct kunit *test) wt.f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, wt.f); - dma_fence_enable_sw_signaling(wt.f); + dma_fence_enable_signaling(wt.f); if (dma_fence_wait_timeout(wt.f, false, 1) != 0) { KUNIT_FAIL(test, "Wait reported complete before being signaled"); @@ -379,7 +379,7 @@ static int thread_signal_callback(void *arg) break; } - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); rcu_assign_pointer(t->fences[t->id], f1); smp_wmb(); diff --git a/drivers/dma-buf/st-dma-resv.c b/drivers/dma-buf/st-dma-resv.c index 95a4becdb892..0b96136bbd54 100644 --- a/drivers/dma-buf/st-dma-resv.c +++ b/drivers/dma-buf/st-dma-resv.c @@ -48,7 +48,7 @@ static void test_sanitycheck(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_signal(f); dma_fence_put(f); @@ -73,7 +73,7 @@ static void test_signaling(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); @@ -117,7 +117,7 @@ static void test_for_each(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); @@ -176,7 +176,7 @@ static void test_for_each_unlocked(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); @@ -246,7 +246,7 @@ static void test_get_fences(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index 5cb7a72774a0..e7632c1ff4be 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -543,7 +543,7 @@ static void enable_signaling(struct i915_active_fence *active) if (!fence) return; - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_fence_put(fence); } diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c index 2db221f6fc3a..56ad8ef32584 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c @@ -69,7 +69,7 @@ static void dma_resv_kunit_active_fence_init(struct kunit *test, struct dma_fence *fence; fence = alloc_mock_fence(test); - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_resv_lock(resv, NULL); dma_resv_reserve_fences(resv, 1); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index bcd76f6bb7f0..3980f376e3ba 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -224,7 +224,7 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo) dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP); dma_resv_for_each_fence_unlocked(&cursor, fence) - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_resv_iter_end(&cursor); } diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 4c80bac67622..85e6d9a0f575 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -670,7 +670,7 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo, dma_resv_iter_begin(&cursor, bo->ttm.base.resv, DMA_RESV_USAGE_BOOKKEEP); dma_resv_for_each_fence_unlocked(&cursor, fence) - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_resv_iter_end(&cursor); } diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c index ae5b38b2a884..a4fa00632a30 100644 --- a/drivers/gpu/drm/xe/xe_sched_job.c +++ b/drivers/gpu/drm/xe/xe_sched_job.c @@ -214,7 +214,7 @@ void xe_sched_job_set_error(struct xe_sched_job *job, int error) trace_xe_sched_job_set_error(job); - dma_fence_enable_sw_signaling(job->fence); + dma_fence_enable_signaling(job->fence); xe_hw_fence_irq_run(job->q->fence_irq); } diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index e1651e70c8f0..dba73786d82a 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -1090,7 +1090,7 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, dma_resv_wait_timeout(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL, false, MAX_SCHEDULE_TIMEOUT); else if (pre_migrate_fence) - dma_fence_enable_sw_signaling(pre_migrate_fence); + dma_fence_enable_signaling(pre_migrate_fence); } drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm, diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c index 6761005c0b90..2e45e42c648f 100644 --- a/drivers/gpu/drm/xe/xe_userptr.c +++ b/drivers/gpu/drm/xe/xe_userptr.c @@ -180,7 +180,7 @@ xe_vma_userptr_invalidate_pass1(struct xe_vm *vm, struct xe_userptr_vma *uvma) dma_resv_iter_begin(&cursor, xe_vm_resv(vm), DMA_RESV_USAGE_BOOKKEEP); dma_resv_for_each_fence_unlocked(&cursor, fence) { - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); if (signaled && !dma_fence_is_signaled(fence)) signaled = false; } diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 080c2fff0e95..73ac031ffb04 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -256,7 +256,7 @@ int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) */ wait = __xe_vm_userptr_needs_repin(vm) || preempt_fences_waiting(vm); if (wait) - dma_fence_enable_sw_signaling(pfence); + dma_fence_enable_signaling(pfence); xe_svm_notifier_unlock(vm); @@ -287,7 +287,7 @@ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) --vm->preempt.num_exec_queues; } if (q->lr.pfence) { - dma_fence_enable_sw_signaling(q->lr.pfence); + dma_fence_enable_signaling(q->lr.pfence); dma_fence_put(q->lr.pfence); q->lr.pfence = NULL; } diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index b52ab692b22e..158cd609f103 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -448,7 +448,7 @@ int dma_fence_add_callback(struct dma_fence *fence, dma_fence_func_t func); bool dma_fence_remove_callback(struct dma_fence *fence, struct dma_fence_cb *cb); -void dma_fence_enable_sw_signaling(struct dma_fence *fence); +void dma_fence_enable_signaling(struct dma_fence *fence); /** * DOC: Safe external access to driver provided object members @@ -534,7 +534,7 @@ dma_fence_is_signaled_locked(struct dma_fence *fence) * Returns true if the fence was already signaled, false if not. Since this * function doesn't enable signaling, it is not guaranteed to ever return * true if dma_fence_add_callback(), dma_fence_wait() or - * dma_fence_enable_sw_signaling() haven't been called before. + * dma_fence_enable_signaling() haven't been called before. * * It's recommended for seqno fences to call dma_fence_signal when the * operation is complete, it makes it possible to prevent issues from -- cgit From 54fd3962c99df50056660747a9e783af27410126 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 29 Jun 2026 18:10:53 +0000 Subject: net: fib_rules: Make fib_rules_ops.delete() return void. Since commit d954a67a7dfa ("ipv4: fib_rule: Move fib4_rules_exit() to ->exit()."), both fib4_rule_delete() and fib6_rule_delete() always return 0. Let's change the return type to void. Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260629181226.1929658-2-kuniyu@google.com Reviewed-by: Ido Schimmel Signed-off-by: Paolo Abeni --- include/net/fib_rules.h | 2 +- net/core/fib_rules.c | 7 ++----- net/ipv4/fib_rules.c | 4 +--- net/ipv6/fib6_rules.c | 4 +--- 4 files changed, 5 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 7dee0ae616e3..f9a4bca51eda 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -82,7 +82,7 @@ struct fib_rules_ops { struct fib_rule_hdr *, struct nlattr **, struct netlink_ext_ack *); - int (*delete)(struct fib_rule *); + void (*delete)(struct fib_rule *); int (*compare)(struct fib_rule *, struct fib_rule_hdr *, struct nlattr **); diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index cf374c208732..961eb709f256 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -1055,11 +1055,8 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, goto errout_free; } - if (ops->delete) { - err = ops->delete(rule); - if (err) - goto errout_free; - } + if (ops->delete) + ops->delete(rule); if (rule->tun_id) ip_tunnel_unneed_metadata(); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index e068a5bace73..51d0ab423ed4 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -349,7 +349,7 @@ errout: return err; } -static int fib4_rule_delete(struct fib_rule *rule) +static void fib4_rule_delete(struct fib_rule *rule) { struct net *net = rule->fr_net; @@ -361,8 +361,6 @@ static int fib4_rule_delete(struct fib_rule *rule) if (net->ipv4.fib_rules_require_fldissect && fib_rule_requires_fldissect(rule)) net->ipv4.fib_rules_require_fldissect--; - - return 0; } static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index e1b2b4fa6e18..5ab4dde07225 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -480,15 +480,13 @@ errout: return err; } -static int fib6_rule_delete(struct fib_rule *rule) +static void fib6_rule_delete(struct fib_rule *rule) { struct net *net = rule->fr_net; if (net->ipv6.fib6_rules_require_fldissect && fib_rule_requires_fldissect(rule)) net->ipv6.fib6_rules_require_fldissect--; - - return 0; } static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, -- cgit From 4b8f5c974d14dc955b4252c02d5e4f185ddc9b23 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 29 Jun 2026 18:10:55 +0000 Subject: ipv4: fib: Protect fib_new_table() with spinlock. fib_newrule() will drop RTNL except for the first IPv4 rule. Then, fib4_rule_configure() could call fib_empty_table() and create a new IPv4 fib_table without RTNL. Currently, net->ipv4.fib_table_hash[] is only protected by RTNL. As a prep, let's protect net->ipv4.fib_table_hash[] with a dedicated spinlock. Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260629181226.1929658-4-kuniyu@google.com Reviewed-by: Ido Schimmel Signed-off-by: Paolo Abeni --- include/net/netns/ipv4.h | 1 + net/ipv4/fib_frontend.c | 25 +++++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 6e27c56514df..59506320558a 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -127,6 +127,7 @@ struct netns_ipv4 { atomic_t fib_num_tclassid_users; #endif struct hlist_head *fib_table_hash; + spinlock_t fib_table_hash_lock; struct sock *fibnl; struct hlist_head *fib_info_hash; unsigned int fib_info_hash_bits; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 42212970d735..336d70649eb9 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -76,7 +76,7 @@ fail: struct fib_table *fib_new_table(struct net *net, u32 id) { - struct fib_table *tb, *alias = NULL; + struct fib_table *tb, *new_tb, *alias = NULL; unsigned int h; if (id == 0) @@ -85,14 +85,27 @@ struct fib_table *fib_new_table(struct net *net, u32 id) if (tb) return tb; + if (!check_net(net)) + return NULL; + if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules) alias = fib_new_table(net, RT_TABLE_MAIN); - if (check_net(net)) - tb = fib_trie_table(id, alias); - if (!tb) + new_tb = fib_trie_table(id, alias); + if (!new_tb) return NULL; + spin_lock(&net->ipv4.fib_table_hash_lock); + + tb = fib_get_table(net, id); + if (tb) { + spin_unlock(&net->ipv4.fib_table_hash_lock); + fib_free_table(new_tb); + return tb; + } + + tb = new_tb; + switch (id) { case RT_TABLE_MAIN: rcu_assign_pointer(net->ipv4.fib_main, tb); @@ -106,6 +119,9 @@ struct fib_table *fib_new_table(struct net *net, u32 id) h = id & (FIB_TABLE_HASHSZ - 1); hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]); + + spin_unlock(&net->ipv4.fib_table_hash_lock); + return tb; } EXPORT_SYMBOL_GPL(fib_new_table); @@ -1565,6 +1581,7 @@ static int __net_init ip_fib_net_init(struct net *net) net->ipv4.sysctl_fib_multipath_hash_fields = FIB_MULTIPATH_HASH_FIELD_DEFAULT_MASK; #endif + spin_lock_init(&net->ipv4.fib_table_hash_lock); /* Avoid false sharing : Use at least a full cache line */ size = max_t(size_t, size, L1_CACHE_BYTES); -- cgit From 763a9437101b9f6210bcfbfd72ce51eb90b7a56e Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 29 Jun 2026 18:10:56 +0000 Subject: ipv4: fib: Drop RTNL annotation for net->ipv4.fib_table_hash[]. fib_newrule() will drop RTNL except for the first IPv4 rule. net->ipv4.fib_table_hash[] will be read with no protection, but this is fine because fib_table is not destroyed until netns dismantle except for the merged main/local table. fib_unmerge() will continue to be called under RTNL, so other readers (fib_flush() and fib_info_notify_update()) just have to care about the concurrent hlist_add(). IPv6 and IPMR/IP6MR also take this strategy and use RCU helpers to avoid data race against concurrent hlist_add(). Let's not use lockdep_rtnl_is_held() and rcu_dereference_rtnl() for net->ipv4.fib_table_hash[]. Note that commit a7e53531234d ("fib_trie: Make fib_table rcu safe") started to use the _safe version in fib_flush(), but it is not needed thanks to RTNL. Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260629181226.1929658-5-kuniyu@google.com Reviewed-by: Ido Schimmel Signed-off-by: Paolo Abeni --- include/net/ip_fib.h | 3 ++- net/ipv4/fib_frontend.c | 23 +++++++++++++---------- net/ipv4/fib_trie.c | 3 +-- 3 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index c63a3c4967ae..0a35355fb0f3 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -302,7 +302,8 @@ static inline struct fib_table *fib_get_table(struct net *net, u32 id) &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX] : &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]; - tb_hlist = rcu_dereference_rtnl(hlist_first_rcu(ptr)); + /* Only fib4_rules_init() adds fib_table. */ + tb_hlist = rcu_dereference_protected(hlist_first_rcu(ptr), true); return hlist_entry(tb_hlist, struct fib_table, tb_hlist); } diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 336d70649eb9..54eb72695093 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -126,24 +126,28 @@ struct fib_table *fib_new_table(struct net *net, u32 id) } EXPORT_SYMBOL_GPL(fib_new_table); -/* caller must hold either rtnl or rcu read lock */ struct fib_table *fib_get_table(struct net *net, u32 id) { - struct fib_table *tb; + struct fib_table *tb = NULL; struct hlist_head *head; unsigned int h; if (id == 0) id = RT_TABLE_MAIN; h = id & (FIB_TABLE_HASHSZ - 1); - head = &net->ipv4.fib_table_hash[h]; - hlist_for_each_entry_rcu(tb, head, tb_hlist, - lockdep_rtnl_is_held()) { + + /* fib_table is not destroyed until ip_fib_net_exit() + * except for the merged main/local table. + * fib_unmerge() is called under RTNL, so other readers + * under RTNL (e.g. fib_flush(), fib_info_notify_update()) + * can safely traverse the list with rcu_dereference_raw(). + */ + hlist_for_each_entry_rcu(tb, head, tb_hlist, true) if (tb->tb_id == id) - return tb; - } - return NULL; + break; + + return tb; } #endif /* CONFIG_IP_MULTIPLE_TABLES */ @@ -206,10 +210,9 @@ void fib_flush(struct net *net) for (h = 0; h < FIB_TABLE_HASHSZ; h++) { struct hlist_head *head = &net->ipv4.fib_table_hash[h]; - struct hlist_node *tmp; struct fib_table *tb; - hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) + hlist_for_each_entry_rcu(tb, head, tb_hlist, true) flushed += fib_table_flush(net, tb, false); } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index e11dc86ceda0..d1d342d7148e 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2137,8 +2137,7 @@ void fib_info_notify_update(struct net *net, struct nl_info *info) struct hlist_head *head = &net->ipv4.fib_table_hash[h]; struct fib_table *tb; - hlist_for_each_entry_rcu(tb, head, tb_hlist, - lockdep_rtnl_is_held()) + hlist_for_each_entry_rcu(tb, head, tb_hlist, true) __fib_info_notify_update(net, tb, info); } } -- cgit From 8e133ba99cd83e70495554f5e51b8062ffe5ba6b Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 29 Jun 2026 18:10:57 +0000 Subject: net: fib_rules: Add fib_rules_ops.lock. We will no longer hold RTNL for RTM_NEWRULE and RMT_DELRULE except for the first IPv4 RTM_NEWRULE. Let's add per-fib_rules_ops mutex inside RTNL. Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260629181226.1929658-6-kuniyu@google.com Reviewed-by: Ido Schimmel Signed-off-by: Paolo Abeni --- include/net/fib_rules.h | 1 + net/core/fib_rules.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index f9a4bca51eda..7636ef4da5ad 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -98,6 +98,7 @@ struct fib_rules_ops { struct list_head rules_list; struct module *owner; struct net *fro_net; + struct mutex lock; struct rcu_head rcu; }; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 961eb709f256..8b9dac1bd4a7 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -172,6 +172,7 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&ops->rules_list); + mutex_init(&ops->lock); ops->fro_net = net; err = __fib_rules_register(ops); @@ -392,6 +393,7 @@ static int call_fib_rule_notifiers(struct net *net, }; ASSERT_RTNL_NET(net); + lockdep_assert_held(&ops->lock); /* Paired with READ_ONCE() in fib_rules_seq() */ WRITE_ONCE(ops->fib_rules_seq, ops->fib_rules_seq + 1); @@ -910,6 +912,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (!rtnl_held) rtnl_net_lock(net); + mutex_lock(&ops->lock); err = fib_nl2rule_rtnl(rule, ops, tb, extack); if (err) @@ -978,6 +981,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, fib_rule_get(rule); + mutex_unlock(&ops->lock); if (!rtnl_held) rtnl_net_unlock(net); @@ -988,6 +992,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, return 0; errout_free: + mutex_unlock(&ops->lock); if (!rtnl_held) rtnl_net_unlock(net); kfree(rule); @@ -1039,6 +1044,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (!rtnl_held) rtnl_net_lock(net); + mutex_lock(&ops->lock); err = fib_nl2rule_rtnl(nlrule, ops, tb, extack); if (err) @@ -1093,6 +1099,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, NULL); + mutex_unlock(&ops->lock); if (!rtnl_held) rtnl_net_unlock(net); @@ -1104,6 +1111,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, return 0; errout_free: + mutex_unlock(&ops->lock); if (!rtnl_held) rtnl_net_unlock(net); kfree(nlrule); @@ -1403,20 +1411,28 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event, switch (event) { case NETDEV_REGISTER: - list_for_each_entry(ops, &net->rules_ops, list) + list_for_each_entry(ops, &net->rules_ops, list) { + mutex_lock(&ops->lock); attach_rules(&ops->rules_list, dev); + mutex_unlock(&ops->lock); + } break; case NETDEV_CHANGENAME: list_for_each_entry(ops, &net->rules_ops, list) { + mutex_lock(&ops->lock); detach_rules(&ops->rules_list, dev); attach_rules(&ops->rules_list, dev); + mutex_unlock(&ops->lock); } break; case NETDEV_UNREGISTER: - list_for_each_entry(ops, &net->rules_ops, list) + list_for_each_entry(ops, &net->rules_ops, list) { + mutex_lock(&ops->lock); detach_rules(&ops->rules_list, dev); + mutex_unlock(&ops->lock); + } break; } -- cgit From eef9bddc3313b01679c60892825afd2a7a83fba6 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 29 Jun 2026 18:11:01 +0000 Subject: net: fib_rules: Only hold RTNL for the first IPv4 RTM_NEWRULE. Now, RTM_DELRULE no longer needs RTNL, and the only RTNL dependant in RTM_NEWRULE is fib_unmerge(), which is called for the first IPv4 rule. Let's add fib_rules_ops.need_rtnl() and hold RTNL only for the first IPv4 rule. Tested: The script below creates 1K rules in parallel in 4K netns, and it got 20x/30x faster for IPv4/IPv6. #!/bin/bash N=4096 F=rules.txt for i in $(seq $N); do ip netns add ns-$i; done printf 'rule add from all table %d\n' {1..1024} > $F for v in 4 6; do echo "=== IPv${v} ===" time { for i in $(seq $N); do nsenter \ --net=/var/run/netns/ns-$i ip -$v -batch $F & done; wait; } done for i in $(seq $N); do ip netns del ns-$i; done rm -f $F Without this series: # ./test.sh === IPv4 === real 0m22.752s user 0m7.834s sys 92m46.721s === IPv6 === real 0m35.181s user 0m8.635s sys 142m30.479s With this series: # ./test.sh === IPv4 === real 0m0.918s user 0m5.675s sys 2m7.024s === IPv6 === real 0m1.214s user 0m7.917s sys 4m19.489s Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260629181226.1929658-10-kuniyu@google.com Reviewed-by: Ido Schimmel Signed-off-by: Paolo Abeni --- include/net/fib_rules.h | 1 + net/core/fib_rules.c | 15 ++++++--------- net/ipv4/fib_rules.c | 6 ++++++ 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 7636ef4da5ad..c6b94790fa81 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -93,6 +93,7 @@ struct fib_rules_ops { /* Called after modifications to the rules set, must flush * the route cache if one exists. */ void (*flush_cache)(struct fib_rules_ops *ops); + bool (*need_rtnl)(struct net *net); int nlgroup; struct list_head rules_list; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 2b652dd83241..22e5e5e1a9c4 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -881,6 +881,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr *tb[FRA_MAX + 1]; bool user_priority = false; struct fib_rule_hdr *frh; + bool unlock_rtnl = false; frh = nlmsg_payload(nlh, sizeof(*frh)); if (!frh) { @@ -906,8 +907,10 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (err) goto errout; - if (!rtnl_held) + if (!rtnl_held && ops->need_rtnl && ops->need_rtnl(net)) { + unlock_rtnl = true; rtnl_net_lock(net); + } mutex_lock(&ops->lock); err = fib_nl2rule_locked(rule, ops, tb, extack); @@ -978,7 +981,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, fib_rule_get(rule); mutex_unlock(&ops->lock); - if (!rtnl_held) + if (unlock_rtnl) rtnl_net_unlock(net); notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid); @@ -989,7 +992,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, errout_free: mutex_unlock(&ops->lock); - if (!rtnl_held) + if (unlock_rtnl) rtnl_net_unlock(net); kfree(rule); errout: @@ -1038,8 +1041,6 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (err) goto errout; - if (!rtnl_held) - rtnl_net_lock(net); mutex_lock(&ops->lock); err = fib_nl2rule_locked(nlrule, ops, tb, extack); @@ -1096,8 +1097,6 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, NULL); mutex_unlock(&ops->lock); - if (!rtnl_held) - rtnl_net_unlock(net); notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid); fib_rule_put(rule); @@ -1108,8 +1107,6 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, errout_free: mutex_unlock(&ops->lock); - if (!rtnl_held) - rtnl_net_unlock(net); kfree(nlrule); errout: rules_ops_put(ops); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 16d202246a36..4edb0dca7be8 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -460,6 +460,11 @@ static void fib4_rule_flush_cache(struct fib_rules_ops *ops) rt_cache_flush(ops->fro_net); } +static bool fib4_rule_need_rtnl(struct net *net) +{ + return !net->ipv4.fib_has_custom_rules; +} + static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = { .family = AF_INET, .rule_size = sizeof(struct fib4_rule), @@ -473,6 +478,7 @@ static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = { .fill = fib4_rule_fill, .nlmsg_payload = fib4_rule_nlmsg_payload, .flush_cache = fib4_rule_flush_cache, + .need_rtnl = fib4_rule_need_rtnl, .nlgroup = RTNLGRP_IPV4_RULE, .owner = THIS_MODULE, }; -- cgit From 5911f6d6e7cce5f35bcaabc1895616e10a6d0aa2 Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Mon, 15 Jun 2026 08:36:46 +0800 Subject: RDMA/nldev: Add resource summary max values for usage display Add RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX netlink attribute to expose device resource limits (max_qp, max_cq, max_mr, max_pd, max_srq) in the resource summary alongside the existing current count. This allows userspace tools like iproute2's rdma to display resource usage in curr/max format. Expected output from "rdma resource show": Before: 0: mlx5_0: qp 123 cq 45 mr 200 pd 10 After: 0: mlx5_0: qp 123/131072 cq 45/65536 mr 200/1000000 pd 10/32768 In JSON output, both "curr" and "max" fields will be provided so that scripts can compute percentages if needed. The new attribute is optional and backward compatible - old userspace tools will simply ignore it. Signed-off-by: Tao Cui Link: https://patch.msgid.link/20260615003646.168704-1-cui.tao@linux.dev Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/nldev.c | 29 ++++++++++++++++++++++++++--- include/uapi/rdma/rdma_netlink.h | 5 +++++ 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 02a0a9c0a4a6..f599c24b34e8 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -188,6 +188,7 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD] = { .type = NLA_U32 }, [RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES] = { .type = NLA_U32 }, [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX] = { .type = NLA_U64 }, }; static int put_driver_name_print_type(struct sk_buff *msg, const char *name, @@ -413,7 +414,7 @@ out: } static int fill_res_info_entry(struct sk_buff *msg, - const char *name, u64 curr) + const char *name, u64 curr, u64 max) { struct nlattr *entry_attr; @@ -427,6 +428,9 @@ static int fill_res_info_entry(struct sk_buff *msg, if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR, curr, RDMA_NLDEV_ATTR_PAD)) goto err; + if (max && nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX, + max, RDMA_NLDEV_ATTR_PAD)) + goto err; nla_nest_end(msg, entry_attr); return 0; @@ -450,7 +454,7 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, }; struct nlattr *table_attr; - int ret, i, curr; + int ret, i, curr, max; if (fill_nldev_handle(msg, device)) return -EMSGSIZE; @@ -463,7 +467,26 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, if (!names[i]) continue; curr = rdma_restrack_count(device, i, show_details); - ret = fill_res_info_entry(msg, names[i], curr); + switch (i) { + case RDMA_RESTRACK_QP: + max = device->attrs.max_qp; + break; + case RDMA_RESTRACK_CQ: + max = device->attrs.max_cq; + break; + case RDMA_RESTRACK_MR: + max = device->attrs.max_mr; + break; + case RDMA_RESTRACK_PD: + max = device->attrs.max_pd; + break; + case RDMA_RESTRACK_SRQ: + max = device->attrs.max_srq; + break; + default: + max = 0; + } + ret = fill_res_info_entry(msg, names[i], curr, max); if (ret) goto err; } diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index aac9782ddc09..3af946ecbac3 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -604,6 +604,11 @@ enum rdma_nldev_attr { RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */ RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */ + /* + * Resource summary entry maximum value. + */ + RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX, /* u64 */ + /* * Always the end */ -- cgit From 7cf9cd98cf6f0df3befc167ca6b54c07014d71de Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Wed, 24 Jun 2026 23:51:14 +0800 Subject: bpf: Copy per-CPU map value padding in copy_map_value_long() In kernel, per-CPU map elements are stored with round_up(map->value_size, 8) bytes. On UAPI lookup paths, it copies the rounded size for each CPU into a temporary buffer. However, copy_map_value_long() passes 'map->value_size' to bpf_obj_memcpy(). When the map has special fields, bpf_obj_memcpy() copies around those fields with memcpy(), and does not copy the tail padding between 'map->value_size' and round_up(map->value_size, 8). The temporary UAPI lookup buffers are allocated without __GFP_ZERO. As a result, when the per-CPU map's value size is not equal to round_up(map->value_size, 8), UAPI LOOKUP_ELEM and its variants can return stale heap contents from that padding to user space. The same issue applies to bpf_iter for per-CPU maps. Pass round_up(map->value_size, 8) to bpf_obj_memcpy() from copy_map_value_long(), so per-CPU maps both with and without special fields copy the entire per-CPU slot. Remove the now redundant round_up() from bpf_obj_memcpy()'s long_memcpy path. Fixes: 448325199f57 ("bpf: Add copy_map_value_long to copy to remote percpu memory") Signed-off-by: Leon Hwang Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20260624155115.85196-2-leon.hwang@linux.dev --- include/linux/bpf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 7719f6528445..ba09795e0bfd 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -570,7 +570,7 @@ static inline void bpf_obj_memcpy(struct btf_record *rec, if (IS_ERR_OR_NULL(rec)) { if (long_memcpy) - bpf_long_memcpy(dst, src, round_up(size, 8)); + bpf_long_memcpy(dst, src, size); else memcpy(dst, src, size); return; @@ -593,7 +593,7 @@ static inline void copy_map_value(struct bpf_map *map, void *dst, void *src) static inline void copy_map_value_long(struct bpf_map *map, void *dst, void *src) { - bpf_obj_memcpy(map->record, dst, src, map->value_size, true); + bpf_obj_memcpy(map->record, dst, src, round_up(map->value_size, 8), true); } static inline void bpf_obj_swap_uptrs(const struct btf_record *rec, void *dst, void *src) -- cgit From 859055e07697c46f6964109981aa1cd23d6bde47 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 29 Jun 2026 23:22:06 +0200 Subject: bpf: Add tracing_multi link info support Adding BPF_OBJ_GET_INFO_BY_FD support for tracing_multi links. We expose following tracing_multi link data: - attach_type of the program - number of ids - array of BTF ids - array of its related kernel addresses - array of cookies The change follows the kprobe_multi and uprobe_multi link-info convention of optional output arrays with an in/out count, On top of standard tracing link data we also expose addresses, because they are useful info for user (especially when the attachment was done via pattern). This data is hidden when kallsyms does not allow exposing kernel pointer values. Assisted-by: Codex:GPT-5 Signed-off-by: Jiri Olsa Signed-off-by: Andrii Nakryiko Acked-by: Leon Hwang Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20260629212208.895962-2-jolsa@kernel.org --- include/uapi/linux/bpf.h | 9 +++++++ kernel/trace/bpf_trace.c | 55 ++++++++++++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 9 +++++++ 3 files changed, 73 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index c91b5a4bda03..2f1d24fef857 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -6842,6 +6842,15 @@ struct bpf_link_info { __u32 flags; __u32 pid; } uprobe_multi; + struct { + __u32 attach_type; + __u32 count; /* in/out: tracing_multi target count */ + __u32 btf_obj_id; + __u32 :32; + __aligned_u64 ids; + __aligned_u64 addrs; + __aligned_u64 cookies; + } tracing_multi; struct { __u32 type; /* enum bpf_perf_event_type */ __u32 :32; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 75495a5c3507..76ab51deaa6b 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -3700,6 +3700,60 @@ static void bpf_tracing_multi_link_dealloc(struct bpf_link *link) kvfree(tr_link); } +static int bpf_tracing_multi_link_fill_link_info(const struct bpf_link *link, + struct bpf_link_info *info) +{ + u64 __user *ucookies = u64_to_user_ptr(info->tracing_multi.cookies); + u64 __user *uaddrs = u64_to_user_ptr(info->tracing_multi.addrs); + u32 __user *uids = u64_to_user_ptr(info->tracing_multi.ids); + struct bpf_tracing_multi_link *tr_link; + u32 ucount = info->tracing_multi.count; + bool has_cookies, show_addrs; + int err = 0; + + if ((uids || ucookies || uaddrs) && !ucount) + return -EINVAL; + + tr_link = container_of(link, struct bpf_tracing_multi_link, link); + + info->tracing_multi.attach_type = tr_link->link.attach_type; + info->tracing_multi.count = tr_link->nodes_cnt; + info->tracing_multi.btf_obj_id = btf_obj_id(tr_link->link.prog->aux->attach_btf); + + if (!uids && !ucookies && !uaddrs) + return 0; + + if (ucount < tr_link->nodes_cnt) + err = -ENOSPC; + else + ucount = tr_link->nodes_cnt; + + has_cookies = !!tr_link->cookies; + show_addrs = kallsyms_show_value(current_cred()); + + for (int i = 0; i < ucount; i++) { + struct bpf_tracing_multi_node *mnode = &tr_link->nodes[i]; + u64 addr, cookie; + u32 id; + + bpf_trampoline_unpack_key(mnode->trampoline->key, NULL, &id); + + addr = show_addrs ? mnode->trampoline->ip : 0; + cookie = has_cookies ? tr_link->cookies[i] : 0; + + if (uids && put_user(id, uids + i)) + return -EFAULT; + if (uaddrs && put_user(addr, uaddrs + i)) + return -EFAULT; + if (ucookies && put_user(cookie, ucookies + i)) + return -EFAULT; + + cond_resched(); + } + + return err; +} + #ifdef CONFIG_PROC_FS static void bpf_tracing_multi_show_fdinfo(const struct bpf_link *link, struct seq_file *seq) @@ -3730,6 +3784,7 @@ static void bpf_tracing_multi_show_fdinfo(const struct bpf_link *link, static const struct bpf_link_ops bpf_tracing_multi_link_lops = { .release = bpf_tracing_multi_link_release, .dealloc_deferred = bpf_tracing_multi_link_dealloc, + .fill_link_info = bpf_tracing_multi_link_fill_link_info, #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_tracing_multi_show_fdinfo, #endif diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index c91b5a4bda03..2f1d24fef857 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -6842,6 +6842,15 @@ struct bpf_link_info { __u32 flags; __u32 pid; } uprobe_multi; + struct { + __u32 attach_type; + __u32 count; /* in/out: tracing_multi target count */ + __u32 btf_obj_id; + __u32 :32; + __aligned_u64 ids; + __aligned_u64 addrs; + __aligned_u64 cookies; + } tracing_multi; struct { __u32 type; /* enum bpf_perf_event_type */ __u32 :32; -- cgit From 7ddc04d1bd08f80ffc1e2fb97f3fc6cacab0ffc0 Mon Sep 17 00:00:00 2001 From: "Mike Marciniszyn (Meta)" Date: Wed, 20 May 2026 16:03:35 -0400 Subject: leds: trigger: netdev: Extend speeds up to 100G Add 25G, 40G, 50G, and 100G as available speeds to the netdev LED trigger. Signed-off-by: Mike Marciniszyn (Meta) Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260520200337.204431-2-mike.marciniszyn@gmail.com Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-netdev.c | 46 ++++++++++++++++++++++++++++++++++- include/linux/leds.h | 4 +++ 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 64c078e997f2..5b4e92c14dbb 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -129,6 +129,22 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) trigger_data->link_speed == SPEED_10000) blink_on = true; + if (test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) && + trigger_data->link_speed == SPEED_25000) + blink_on = true; + + if (test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) && + trigger_data->link_speed == SPEED_40000) + blink_on = true; + + if (test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) && + trigger_data->link_speed == SPEED_50000) + blink_on = true; + + if (test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) && + trigger_data->link_speed == SPEED_100000) + blink_on = true; + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && trigger_data->duplex == DUPLEX_HALF) blink_on = true; @@ -342,6 +358,10 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf, case TRIGGER_NETDEV_LINK_2500: case TRIGGER_NETDEV_LINK_5000: case TRIGGER_NETDEV_LINK_10000: + case TRIGGER_NETDEV_LINK_25000: + case TRIGGER_NETDEV_LINK_40000: + case TRIGGER_NETDEV_LINK_50000: + case TRIGGER_NETDEV_LINK_100000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: @@ -378,6 +398,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, case TRIGGER_NETDEV_LINK_2500: case TRIGGER_NETDEV_LINK_5000: case TRIGGER_NETDEV_LINK_10000: + case TRIGGER_NETDEV_LINK_25000: + case TRIGGER_NETDEV_LINK_40000: + case TRIGGER_NETDEV_LINK_50000: + case TRIGGER_NETDEV_LINK_100000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: @@ -401,7 +425,11 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || - test_bit(TRIGGER_NETDEV_LINK_10000, &mode))) + test_bit(TRIGGER_NETDEV_LINK_10000, &mode) || + test_bit(TRIGGER_NETDEV_LINK_25000, &mode) || + test_bit(TRIGGER_NETDEV_LINK_40000, &mode) || + test_bit(TRIGGER_NETDEV_LINK_50000, &mode) || + test_bit(TRIGGER_NETDEV_LINK_100000, &mode))) return -EINVAL; cancel_delayed_work_sync(&trigger_data->work); @@ -438,6 +466,10 @@ DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000); DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500); DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000); DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000); +DEFINE_NETDEV_TRIGGER(link_25000, TRIGGER_NETDEV_LINK_25000); +DEFINE_NETDEV_TRIGGER(link_40000, TRIGGER_NETDEV_LINK_40000); +DEFINE_NETDEV_TRIGGER(link_50000, TRIGGER_NETDEV_LINK_50000); +DEFINE_NETDEV_TRIGGER(link_100000, TRIGGER_NETDEV_LINK_100000); DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); @@ -526,6 +558,10 @@ static umode_t netdev_trig_link_speed_visible(struct kobject *kobj, CHECK_LINK_MODE_ATTR(2500); CHECK_LINK_MODE_ATTR(5000); CHECK_LINK_MODE_ATTR(10000); + CHECK_LINK_MODE_ATTR(25000); + CHECK_LINK_MODE_ATTR(40000); + CHECK_LINK_MODE_ATTR(50000); + CHECK_LINK_MODE_ATTR(100000); } return 0; @@ -538,6 +574,10 @@ static struct attribute *netdev_trig_link_speed_attrs[] = { &dev_attr_link_2500.attr, &dev_attr_link_5000.attr, &dev_attr_link_10000.attr, + &dev_attr_link_25000.attr, + &dev_attr_link_40000.attr, + &dev_attr_link_50000.attr, + &dev_attr_link_100000.attr, NULL }; @@ -673,6 +713,10 @@ static void netdev_trig_work(struct work_struct *work) test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode); interval = jiffies_to_msecs( diff --git a/include/linux/leds.h b/include/linux/leds.h index b16b803cc1ac..bf31c246d9e2 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -607,6 +607,10 @@ enum led_trigger_netdev_modes { TRIGGER_NETDEV_LINK_2500, TRIGGER_NETDEV_LINK_5000, TRIGGER_NETDEV_LINK_10000, + TRIGGER_NETDEV_LINK_25000, + TRIGGER_NETDEV_LINK_40000, + TRIGGER_NETDEV_LINK_50000, + TRIGGER_NETDEV_LINK_100000, TRIGGER_NETDEV_HALF_DUPLEX, TRIGGER_NETDEV_FULL_DUPLEX, TRIGGER_NETDEV_TX, -- cgit From 44d19b8a7548aa25cbc6ebd5f27e958f7142c36b Mon Sep 17 00:00:00 2001 From: Shahyan Soltani Date: Tue, 30 Jun 2026 12:04:01 -0400 Subject: dma_buf: change unsigned int and int types into size_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The num_fences, count, i, and j variables in dma_fence_dedup_array() and __dma_fence_unwrap_merge() have inconsistent integer types, mixing both unsigned int and int. Use type size_t consistently for these instead, and update the return type of dma_fence_dedup_array() accordingly. Signed-off-by: Shahyan Soltani Suggested-by: Philipp Stanner Link: https://lore.kernel.org/r/20260630160401.67544-1-shahyan.soltani@amd.com Reviewed-by: Philipp Stanner Reviewed-by: Christian König Signed-off-by: Christian König --- drivers/dma-buf/dma-fence-unwrap.c | 8 ++++---- include/linux/dma-fence-unwrap.h | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c index 53bb40e70b27..65e87d263c3a 100644 --- a/drivers/dma-buf/dma-fence-unwrap.c +++ b/drivers/dma-buf/dma-fence-unwrap.c @@ -93,9 +93,9 @@ static int fence_cmp(const void *_a, const void *_b) * * Return: Number of unique fences remaining in the array. */ -int dma_fence_dedup_array(struct dma_fence **fences, int num_fences) +size_t dma_fence_dedup_array(struct dma_fence **fences, size_t num_fences) { - int i, j; + size_t i, j; sort(fences, num_fences, sizeof(*fences), fence_cmp, NULL); @@ -115,14 +115,14 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences) EXPORT_SYMBOL_GPL(dma_fence_dedup_array); /* Implementation for the dma_fence_merge() marco, don't use directly */ -struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences, +struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences, struct dma_fence **fences, struct dma_fence_unwrap *iter) { struct dma_fence *tmp, *unsignaled = NULL, **array; struct dma_fence_array *result; ktime_t timestamp; - int i, count; + size_t i, count; count = 0; timestamp = ns_to_ktime(0); diff --git a/include/linux/dma-fence-unwrap.h b/include/linux/dma-fence-unwrap.h index 62df222fe0f1..7bfacdf79de2 100644 --- a/include/linux/dma-fence-unwrap.h +++ b/include/linux/dma-fence-unwrap.h @@ -8,6 +8,8 @@ #ifndef __LINUX_DMA_FENCE_UNWRAP_H #define __LINUX_DMA_FENCE_UNWRAP_H +#include + struct dma_fence; /** @@ -48,11 +50,11 @@ struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor); for (fence = dma_fence_unwrap_first(head, cursor); fence; \ fence = dma_fence_unwrap_next(cursor)) -struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences, +struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences, struct dma_fence **fences, struct dma_fence_unwrap *cursors); -int dma_fence_dedup_array(struct dma_fence **array, int num_fences); +size_t dma_fence_dedup_array(struct dma_fence **array, size_t num_fences); /** * dma_fence_unwrap_merge - unwrap and merge fences -- cgit From 2ebce860bdd7ae5e13002811bc9bbbf33fcfc221 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Wed, 1 Jul 2026 18:16:13 -0400 Subject: mm/mm_init: handle alloc_percpu failure in free_area_init_core_hotplug We miss a failed allocation check for pgdat->per_cpu_nodestats, which results in a NULL deref when we offset into the per-cpu area. Propagate -ENOMEM up the stack and leave per_cpu_nodestats pointing at boot_nodestats so a later online can retry the allocation. hotadd_init_pgdat() returns NULL on failure, which __try_online_node() already maps to -ENOMEM. On failure nothing needs to be unwound: - the node is never marked online - per_cpu_nodestats is left pointing at boot_nodestats - __add_memory_resource() cleans up pending memblock resources - later online attempts retry the per_cpu_nodestats allocation Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260627202243.758289-1-gourry%40gourry.net Fixes: 75ef71840539 ("mm, vmstat: add infrastructure for per-node vmstats") Signed-off-by: Gregory Price Acked-by: David Hildenbrand (Arm) Link: https://patch.msgid.link/20260701221613.2818148-1-gourry@gourry.net Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/memory_hotplug.h | 2 +- mm/memory_hotplug.c | 3 ++- mm/mm_init.c | 14 +++++++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 7c9d66729c60..06c58cb05779 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -289,7 +289,7 @@ static inline void __remove_memory(u64 start, u64 size) {} /* Default online_type (MMOP_*) when new memory blocks are added. */ extern enum mmop mhp_get_default_online_type(void); extern void mhp_set_default_online_type(enum mmop online_type); -extern void __ref free_area_init_core_hotplug(struct pglist_data *pgdat); +int __ref free_area_init_core_hotplug(struct pglist_data *pgdat); extern int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags); extern int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags); extern int add_memory_resource(int nid, struct resource *resource, diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 7ac19fab2263..8b137328dcf0 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1263,7 +1263,8 @@ static pg_data_t *hotadd_init_pgdat(int nid) pgdat = NODE_DATA(nid); /* init node's zones as empty zones, we don't have any present pages.*/ - free_area_init_core_hotplug(pgdat); + if (free_area_init_core_hotplug(pgdat)) + return NULL; /* * The node we allocated has no zone fallback lists. For avoiding diff --git a/mm/mm_init.c b/mm/mm_init.c index 0d2eb82fa068..1ba1181d8ef9 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1535,7 +1535,7 @@ void __init set_pageblock_order(void) * NOTE: this function is only called during memory hotplug */ #ifdef CONFIG_MEMORY_HOTPLUG -void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) +int __ref free_area_init_core_hotplug(struct pglist_data *pgdat) { int nid = pgdat->node_id; enum zone_type z; @@ -1543,8 +1543,14 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) pgdat_init_internals(pgdat); - if (pgdat->per_cpu_nodestats == &boot_nodestats) - pgdat->per_cpu_nodestats = alloc_percpu(struct per_cpu_nodestat); + if (pgdat->per_cpu_nodestats == &boot_nodestats) { + struct per_cpu_nodestat __percpu *p; + + p = alloc_percpu(struct per_cpu_nodestat); + if (!p) + return -ENOMEM; + pgdat->per_cpu_nodestats = p; + } /* * Reset the nr_zones, order and highest_zoneidx before reuse. @@ -1575,6 +1581,8 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) zone->present_pages = 0; zone_init_internals(zone, z, nid, 0); } + + return 0; } #endif -- cgit From b010e2a4a9ac2bcd0db2c3a41877d59d827a8a80 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Fri, 20 Mar 2026 16:19:39 +0100 Subject: netfilter: nfnetlink_hook: Dump nat type chains These chains are indirectly attached to the hook since they are not called for packets belonging to an established connection. Introduce NF_HOOK_OP_NAT to identify the container and dump attached entries instead of the container itself. Dump these entries with the dispatcher's priority value since their own priority merely defines ordering within the dispatcher's list. Signed-off-by: Phil Sutter Signed-off-by: Florian Westphal --- include/linux/netfilter.h | 7 +++++++ net/netfilter/nf_nat_core.c | 6 ------ net/netfilter/nf_nat_proto.c | 8 ++++++++ net/netfilter/nfnetlink_hook.c | 37 +++++++++++++++++++++++++++++++++---- 4 files changed, 48 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index efbbfa770d66..e99afc1414cd 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -93,6 +93,7 @@ enum nf_hook_ops_type { NF_HOOK_OP_NF_TABLES, NF_HOOK_OP_BPF, NF_HOOK_OP_NFT_FT, + NF_HOOK_OP_NAT, }; struct nf_hook_ops { @@ -140,6 +141,12 @@ struct nf_hook_entries { */ }; +struct nf_nat_lookup_hook_priv { + struct nf_hook_entries __rcu *entries; + + struct rcu_head rcu_head; +}; + #ifdef CONFIG_NETFILTER static inline struct nf_hook_ops **nf_hook_entries_get_hook_ops(const struct nf_hook_entries *e) { diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 63ff6b4d5d21..8ac326e1eb5b 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -39,12 +39,6 @@ static struct hlist_head *nf_nat_bysource __read_mostly; static unsigned int nf_nat_htable_size __read_mostly; static siphash_aligned_key_t nf_nat_hash_rnd; -struct nf_nat_lookup_hook_priv { - struct nf_hook_entries __rcu *entries; - - struct rcu_head rcu_head; -}; - struct nf_nat_hooks_net { struct nf_hook_ops *nat_hook_ops; unsigned int users; diff --git a/net/netfilter/nf_nat_proto.c b/net/netfilter/nf_nat_proto.c index 07f51fe75fbe..64b9bac228ea 100644 --- a/net/netfilter/nf_nat_proto.c +++ b/net/netfilter/nf_nat_proto.c @@ -770,6 +770,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_NAT_DST, + .hook_ops_type = NF_HOOK_OP_NAT, }, /* After packet filtering, change source */ { @@ -777,6 +778,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_NAT_SRC, + .hook_ops_type = NF_HOOK_OP_NAT, }, /* Before packet filtering, change destination */ { @@ -784,6 +786,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_NAT_DST, + .hook_ops_type = NF_HOOK_OP_NAT, }, /* After packet filtering, change source */ { @@ -791,6 +794,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_NAT_SRC, + .hook_ops_type = NF_HOOK_OP_NAT, }, }; @@ -1031,6 +1035,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { .pf = NFPROTO_IPV6, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP6_PRI_NAT_DST, + .hook_ops_type = NF_HOOK_OP_NAT, }, /* After packet filtering, change source */ { @@ -1038,6 +1043,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_NAT_SRC, + .hook_ops_type = NF_HOOK_OP_NAT, }, /* Before packet filtering, change destination */ { @@ -1045,6 +1051,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_NAT_DST, + .hook_ops_type = NF_HOOK_OP_NAT, }, /* After packet filtering, change source */ { @@ -1052,6 +1059,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP6_PRI_NAT_SRC, + .hook_ops_type = NF_HOOK_OP_NAT, }, }; diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c index 5623c18fcd12..95005e9a6066 100644 --- a/net/netfilter/nfnetlink_hook.c +++ b/net/netfilter/nfnetlink_hook.c @@ -190,7 +190,7 @@ static int nfnl_hook_put_nft_ft_info(struct sk_buff *nlskb, static int nfnl_hook_dump_one(struct sk_buff *nlskb, const struct nfnl_dump_hook_data *ctx, - const struct nf_hook_ops *ops, + const struct nf_hook_ops *ops, int priority, int family, unsigned int seq) { u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET); @@ -244,7 +244,7 @@ static int nfnl_hook_dump_one(struct sk_buff *nlskb, if (ret) goto nla_put_failure; - ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority)); + ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(priority)); if (ret) goto nla_put_failure; @@ -337,6 +337,30 @@ nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *de return hook_head; } +static int nfnl_hook_dump_nat(struct sk_buff *nlskb, + const struct nfnl_dump_hook_data *ctx, + const struct nf_hook_ops *ops, + int family, unsigned int seq) +{ + struct nf_nat_lookup_hook_priv *priv = ops->priv; + struct nf_hook_entries *e = rcu_dereference(priv->entries); + struct nf_hook_ops **nat_ops; + int i, err; + + if (!e) + return 0; + + nat_ops = nf_hook_entries_get_hook_ops(e); + + for (i = 0; i < e->num_hook_entries; i++) { + err = nfnl_hook_dump_one(nlskb, ctx, nat_ops[i], + ops->priority, family, seq); + if (err) + return err; + } + return 0; +} + static int nfnl_hook_dump(struct sk_buff *nlskb, struct netlink_callback *cb) { @@ -365,8 +389,13 @@ static int nfnl_hook_dump(struct sk_buff *nlskb, ops = nf_hook_entries_get_hook_ops(e); for (; i < e->num_hook_entries; i++) { - err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family, - cb->nlh->nlmsg_seq); + if (ops[i]->hook_ops_type == NF_HOOK_OP_NAT) + err = nfnl_hook_dump_nat(nlskb, ctx, ops[i], family, + cb->nlh->nlmsg_seq); + else + err = nfnl_hook_dump_one(nlskb, ctx, ops[i], + ops[i]->priority, family, + cb->nlh->nlmsg_seq); if (err) break; } -- cgit From 32b00984e002708d55b3ad3830198d3ba9126e09 Mon Sep 17 00:00:00 2001 From: Carlos Grillet Date: Thu, 25 Jun 2026 19:25:46 +0200 Subject: netfilter: replace u_int8_t and u_int16t with u8 and u16 Use preferred kernel integer type u8 instead of the POSIX u_int8_t variant. No functional change. Signed-off-by: Carlos Grillet Signed-off-by: Florian Westphal --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_nfct.c | 2 +- net/netfilter/nf_conntrack_amanda.c | 2 +- net/netfilter/nf_conntrack_h323_main.c | 2 +- net/netfilter/xt_TCPOPTSTRIP.c | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 49297fec448a..ed2e9bc1bb4e 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -2123,7 +2123,7 @@ void ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, int outin); int ip_vs_confirm_conntrack(struct sk_buff *skb); void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct, - struct ip_vs_conn *cp, u_int8_t proto, + struct ip_vs_conn *cp, u8 proto, const __be16 port, int from_rs); void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp); diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c index 81974f69e5bb..347185fd0c8c 100644 --- a/net/netfilter/ipvs/ip_vs_nfct.c +++ b/net/netfilter/ipvs/ip_vs_nfct.c @@ -208,7 +208,7 @@ alter: * Use port 0 to expect connection from any port. */ void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct, - struct ip_vs_conn *cp, u_int8_t proto, + struct ip_vs_conn *cp, u8 proto, const __be16 port, int from_rs) { struct nf_conntrack_expect *exp; diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index ddafbdfc96dc..f10ac2c49f4b 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -89,7 +89,7 @@ static int amanda_help(struct sk_buff *skb, struct nf_conntrack_tuple *tuple; unsigned int dataoff, start, stop, off, i; char pbuf[sizeof("65535")], *tmp; - u_int16_t len; + u16 len; __be16 port; int ret = NF_ACCEPT; nf_nat_amanda_hook_fn *nf_nat_amanda; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 24931e379985..37b6314ca772 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -671,7 +671,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, static int callforward_do_filter(struct net *net, const union nf_inet_addr *src, const union nf_inet_addr *dst, - u_int8_t family) + u8 family) { int ret = 0; diff --git a/net/netfilter/xt_TCPOPTSTRIP.c b/net/netfilter/xt_TCPOPTSTRIP.c index 93f064306901..265d21697847 100644 --- a/net/netfilter/xt_TCPOPTSTRIP.c +++ b/net/netfilter/xt_TCPOPTSTRIP.c @@ -16,7 +16,7 @@ #include #include -static inline unsigned int optlen(const u_int8_t *opt, unsigned int offset) +static inline unsigned int optlen(const u8 *opt, unsigned int offset) { /* Beware zero-length options: make finite progress */ if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) @@ -33,8 +33,8 @@ tcpoptstrip_mangle_packet(struct sk_buff *skb, const struct xt_tcpoptstrip_target_info *info = par->targinfo; struct tcphdr *tcph, _th; unsigned int optl, i, j; - u_int16_t n, o; - u_int8_t *opt; + u16 n, o; + u8 *opt; int tcp_hdrlen; /* This is a fragment, no TCP header is available */ @@ -97,7 +97,7 @@ tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); int tcphoff; - u_int8_t nexthdr; + u8 nexthdr; __be16 frag_off; nexthdr = ipv6h->nexthdr; -- cgit From 5de6c8ad0bcccef1be55ad07d29833df69b601cf Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 29 Jun 2026 14:58:22 +0200 Subject: netfilter: conntrack: get rid of tuple in helper definitions Leftover from the days when the kernel did automatic assignment of helpers based on a pre-registered / well-known-port. This helper autoassign was removed from the kernel, so all we really need are the l3 and l4 protocol numbers. In the broadcast helper, the only remaining consumer of the port number is removed. AFAICS its not needed: The expectation is populated from the control connection reply tuple, so the src port is the original directions destination (snmp/161 for example). LLM complained about silent l3num (u16) -> nfproto (u8) truncation, so add a netlink policy validation to reject large NFPROTO values upfront. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal --- include/net/netfilter/nf_conntrack_helper.h | 9 ++++----- net/netfilter/nf_conntrack_broadcast.c | 2 -- net/netfilter/nf_conntrack_helper.c | 22 +++++++++------------- net/netfilter/nf_conntrack_ovs.c | 6 +++--- net/netfilter/nfnetlink_cthelper.c | 21 +++++++++++---------- net/sched/act_ct.c | 4 ++-- 6 files changed, 29 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index c761cd8158b2..f3f0c1392e88 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -43,11 +43,10 @@ struct nf_conntrack_helper { refcount_t ct_refcnt; - /* Tuple of things we will help (compared against server response) */ - struct nf_conntrack_tuple tuple; + u8 nfproto; /* NFPROTO_*, can be NFPROTO_UNSPEC */ + u8 l4proto; /* IPPROTO_UDP/TCP */ - /* Function to call when data passes; return verdict, or -1 to - invalidate. */ + /* Function to call when data passes; return verdict */ int __rcu (*help)(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info conntrackinfo); @@ -94,7 +93,7 @@ struct nf_conntrack_helper *nf_conntrack_helper_try_module_get(const char *name, void nf_conntrack_helper_put(struct nf_conntrack_helper *helper); void nf_ct_helper_init(struct nf_conntrack_helper *helper, - u16 l3num, u16 protonum, const char *name, + u8 l3num, u16 protonum, const char *name, u16 default_port, u16 spec_port, u32 id, const struct nf_conntrack_expect_policy *exp_pol, u32 expect_class_max, diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index bf78828c7549..6ff954f1bfb8 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -66,8 +66,6 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; helper = rcu_dereference(help->helper); - if (helper) - exp->tuple.src.u.udp.port = helper->tuple.src.u.udp.port; exp->mask.src.u3.ip = mask; exp->mask.src.u.udp.port = htons(0xFFFF); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 5ad5429352a7..b28986100db0 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -66,12 +66,9 @@ __nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum) hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) { if (strcmp(h->name, name)) continue; - - if (h->tuple.src.l3num != NFPROTO_UNSPEC && - h->tuple.src.l3num != l3num) + if (h->nfproto != NFPROTO_UNSPEC && h->nfproto != l3num) continue; - - if (h->tuple.dst.protonum == protonum) + if (h->l4proto == protonum) return h; } return NULL; @@ -388,13 +385,13 @@ int __nf_conntrack_helper_register(struct nf_conntrack_helper *me) return -EINVAL; } - h = helper_hash(me->name, me->tuple.dst.protonum); + h = helper_hash(me->name, me->l4proto); mutex_lock(&nf_ct_helper_mutex); hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) { if (!strcmp(cur->name, me->name) && - (cur->tuple.src.l3num == NFPROTO_UNSPEC || - cur->tuple.src.l3num == me->tuple.src.l3num) && - cur->tuple.dst.protonum == me->tuple.dst.protonum) { + (cur->nfproto == NFPROTO_UNSPEC || + cur->nfproto == me->nfproto) && + cur->l4proto == me->l4proto) { ret = -EBUSY; goto out; } @@ -474,7 +471,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); void nf_ct_helper_init(struct nf_conntrack_helper *helper, - u16 l3num, u16 protonum, const char *name, + u8 l3num, u16 protonum, const char *name, u16 default_port, u16 spec_port, u32 id, const struct nf_conntrack_expect_policy *exp_pol, u32 expect_class_max, @@ -487,9 +484,8 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, { memset(helper, 0, sizeof(*helper)); - helper->tuple.src.l3num = l3num; - helper->tuple.dst.protonum = protonum; - helper->tuple.src.u.all = htons(spec_port); + helper->nfproto = l3num; + helper->l4proto = protonum; rcu_assign_pointer(helper->help, help); helper->from_nlattr = from_nlattr; diff --git a/net/netfilter/nf_conntrack_ovs.c b/net/netfilter/nf_conntrack_ovs.c index 49d1511e9921..b4085af3ad1c 100644 --- a/net/netfilter/nf_conntrack_ovs.c +++ b/net/netfilter/nf_conntrack_ovs.c @@ -31,8 +31,8 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, if (!helper) return NF_ACCEPT; - if (helper->tuple.src.l3num != NFPROTO_UNSPEC && - helper->tuple.src.l3num != proto) + if (helper->nfproto != NFPROTO_UNSPEC && + helper->nfproto != proto) return NF_ACCEPT; switch (proto) { @@ -60,7 +60,7 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, return NF_DROP; } - if (helper->tuple.dst.protonum != proto) + if (helper->l4proto != proto) return NF_ACCEPT; helper_cb = rcu_dereference(helper->help); diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index f1460b683d7a..56655cb7fe2a 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -67,7 +67,7 @@ nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, } static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = { - [NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, }, + [NFCTH_TUPLE_L3PROTONUM] = NLA_POLICY_MAX(NLA_BE16, NFPROTO_IPV6), [NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, }, }; @@ -254,7 +254,8 @@ nfnl_cthelper_create(const struct nlattr * const tb[], helper->data_len = size; helper->flags |= NF_CT_HELPER_F_USERSPACE; - memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple)); + helper->nfproto = tuple->src.l3num; + helper->l4proto = tuple->dst.protonum; helper->me = THIS_MODULE; helper->help = nfnl_userspace_cthelper; @@ -449,8 +450,8 @@ static int nfnl_cthelper_new(struct sk_buff *skb, const struct nfnl_info *info, if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) continue; - if ((tuple.src.l3num != cur->tuple.src.l3num || - tuple.dst.protonum != cur->tuple.dst.protonum)) + if ((tuple.src.l3num != cur->nfproto || + tuple.dst.protonum != cur->l4proto)) continue; if (info->nlh->nlmsg_flags & NLM_F_EXCL) @@ -479,10 +480,10 @@ nfnl_cthelper_dump_tuple(struct sk_buff *skb, goto nla_put_failure; if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM, - htons(helper->tuple.src.l3num))) + htons(helper->nfproto))) goto nla_put_failure; - if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum)) + if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->l4proto)) goto nla_put_failure; nla_nest_end(skb, nest_parms); @@ -661,8 +662,8 @@ static int nfnl_cthelper_get(struct sk_buff *skb, const struct nfnl_info *info, continue; if (tuple_set && - (tuple.src.l3num != cur->tuple.src.l3num || - tuple.dst.protonum != cur->tuple.dst.protonum)) + (tuple.src.l3num != cur->nfproto || + tuple.dst.protonum != cur->l4proto)) continue; skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -721,8 +722,8 @@ static int nfnl_cthelper_del(struct sk_buff *skb, const struct nfnl_info *info, continue; if (tuple_set && - (tuple.src.l3num != cur->tuple.src.l3num || - tuple.dst.protonum != cur->tuple.dst.protonum)) + (tuple.src.l3num != cur->nfproto || + tuple.dst.protonum != cur->l4proto)) continue; found = true; diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index be535a261fa0..4ca7964e83c8 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -1527,8 +1527,8 @@ static int tcf_ct_dump_helper(struct sk_buff *skb, return 0; if (nla_put_string(skb, TCA_CT_HELPER_NAME, helper->name) || - nla_put_u8(skb, TCA_CT_HELPER_FAMILY, helper->tuple.src.l3num) || - nla_put_u8(skb, TCA_CT_HELPER_PROTO, helper->tuple.dst.protonum)) + nla_put_u8(skb, TCA_CT_HELPER_FAMILY, helper->nfproto) || + nla_put_u8(skb, TCA_CT_HELPER_PROTO, helper->l4proto)) return -1; return 0; -- cgit From 78217fb2ccf9d3963dd32d86712ba42e7fd619a8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 29 Jun 2026 14:58:23 +0200 Subject: netfilter: conntrack: remove obsolete module parameters helper autoassign was removed years ago, all the port numbers are no longer functional. Signed-off-by: Florian Westphal --- include/linux/netfilter/nf_conntrack_h323.h | 2 -- include/linux/netfilter/nf_conntrack_pptp.h | 2 -- include/linux/netfilter/nf_conntrack_sane.h | 2 -- include/linux/netfilter/nf_conntrack_tftp.h | 2 -- include/net/netfilter/nf_conntrack_helper.h | 1 - net/ipv4/netfilter/nf_nat_snmp_basic_main.c | 2 +- net/netfilter/nf_conntrack_amanda.c | 4 +-- net/netfilter/nf_conntrack_ftp.c | 32 ++++++-------------- net/netfilter/nf_conntrack_h323_main.c | 10 +++---- net/netfilter/nf_conntrack_helper.c | 6 +--- net/netfilter/nf_conntrack_irc.c | 27 +++++------------ net/netfilter/nf_conntrack_netbios_ns.c | 2 -- net/netfilter/nf_conntrack_pptp.c | 2 +- net/netfilter/nf_conntrack_sane.c | 34 ++++++---------------- net/netfilter/nf_conntrack_sip.c | 45 +++++++++-------------------- net/netfilter/nf_conntrack_snmp.c | 4 +-- net/netfilter/nf_conntrack_tftp.c | 33 ++++++--------------- 17 files changed, 59 insertions(+), 151 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_h323.h b/include/linux/netfilter/nf_conntrack_h323.h index 81286c499325..b15f37604cde 100644 --- a/include/linux/netfilter/nf_conntrack_h323.h +++ b/include/linux/netfilter/nf_conntrack_h323.h @@ -9,8 +9,6 @@ #include #include -#define RAS_PORT 1719 -#define Q931_PORT 1720 #define H323_RTP_CHANNEL_MAX 4 /* Audio, video, FAX and other */ /* This structure exists only once per master */ diff --git a/include/linux/netfilter/nf_conntrack_pptp.h b/include/linux/netfilter/nf_conntrack_pptp.h index c3bdb4370938..c0b305ce7c3c 100644 --- a/include/linux/netfilter/nf_conntrack_pptp.h +++ b/include/linux/netfilter/nf_conntrack_pptp.h @@ -50,8 +50,6 @@ struct nf_nat_pptp { __be16 pac_call_id; /* NAT'ed PAC call id */ }; -#define PPTP_CONTROL_PORT 1723 - #define PPTP_PACKET_CONTROL 1 #define PPTP_PACKET_MGMT 2 diff --git a/include/linux/netfilter/nf_conntrack_sane.h b/include/linux/netfilter/nf_conntrack_sane.h index 46c7acd1b4a7..8501035d7335 100644 --- a/include/linux/netfilter/nf_conntrack_sane.h +++ b/include/linux/netfilter/nf_conntrack_sane.h @@ -3,8 +3,6 @@ #define _NF_CONNTRACK_SANE_H /* SANE tracking. */ -#define SANE_PORT 6566 - enum sane_state { SANE_STATE_NORMAL, SANE_STATE_START_REQUESTED, diff --git a/include/linux/netfilter/nf_conntrack_tftp.h b/include/linux/netfilter/nf_conntrack_tftp.h index 90b334bbce3c..e3d1739c557d 100644 --- a/include/linux/netfilter/nf_conntrack_tftp.h +++ b/include/linux/netfilter/nf_conntrack_tftp.h @@ -2,8 +2,6 @@ #ifndef _NF_CONNTRACK_TFTP_H #define _NF_CONNTRACK_TFTP_H -#define TFTP_PORT 69 - #include #include #include diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index f3f0c1392e88..bc5427d239f4 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -94,7 +94,6 @@ void nf_conntrack_helper_put(struct nf_conntrack_helper *helper); void nf_ct_helper_init(struct nf_conntrack_helper *helper, u8 l3num, u16 protonum, const char *name, - u16 default_port, u16 spec_port, u32 id, const struct nf_conntrack_expect_policy *exp_pol, u32 expect_class_max, int (*help)(struct sk_buff *skb, unsigned int protoff, diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c index 0ede138dfd29..e540b86bd15b 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c @@ -213,7 +213,7 @@ static int __init nf_nat_snmp_basic_init(void) RCU_INIT_POINTER(nf_nat_snmp_hook, help); nf_ct_helper_init(&snmp_trap_helper, AF_INET, IPPROTO_UDP, - "snmp_trap", SNMP_TRAP_PORT, SNMP_TRAP_PORT, SNMP_TRAP_PORT, + "snmp_trap", &snmp_exp_policy, 0, help, NULL, THIS_MODULE); err = nf_conntrack_helper_register(&snmp_trap_helper, &snmp_trap_helper_ptr); diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index f10ac2c49f4b..06d6ec12c86d 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -199,10 +199,10 @@ static int __init nf_conntrack_amanda_init(void) } nf_ct_helper_init(&amanda_helper[0], AF_INET, IPPROTO_UDP, - HELPER_NAME, 10080, 10080, 10080, + HELPER_NAME, &amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE); nf_ct_helper_init(&amanda_helper[1], AF_INET6, IPPROTO_UDP, - HELPER_NAME, 10080, 10080, 10080, + HELPER_NAME, &amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE); ret = nf_conntrack_helpers_register(amanda_helper, diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 0847f845613d..f3944598c172 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -35,11 +35,6 @@ MODULE_ALIAS("ip_conntrack_ftp"); MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); static DEFINE_SPINLOCK(nf_ftp_lock); -#define MAX_PORTS 8 -static u_int16_t ports[MAX_PORTS]; -static unsigned int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); - static bool loose; module_param(loose, bool, 0600); @@ -560,8 +555,8 @@ static int nf_ct_ftp_from_nlattr(struct nlattr *attr, struct nf_conn *ct) return 0; } -static struct nf_conntrack_helper ftp[MAX_PORTS * 2] __read_mostly; -static struct nf_conntrack_helper *ftp_ptr[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper ftp __read_mostly; +static struct nf_conntrack_helper *ftp_ptr __read_mostly; static const struct nf_conntrack_expect_policy ftp_exp_policy = { .max_expected = 1, @@ -570,32 +565,23 @@ static const struct nf_conntrack_expect_policy ftp_exp_policy = { static void __exit nf_conntrack_ftp_fini(void) { - nf_conntrack_helpers_unregister(ftp_ptr, ports_c * 2); + nf_conntrack_helper_unregister(ftp_ptr); } static int __init nf_conntrack_ftp_init(void) { - int i, ret = 0; + int ret = 0; NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master)); - if (ports_c == 0) - ports[ports_c++] = FTP_PORT; - /* FIXME should be configurable whether IPv4 and IPv6 FTP connections are tracked or not - YK */ - for (i = 0; i < ports_c; i++) { - nf_ct_helper_init(&ftp[2 * i], AF_INET, IPPROTO_TCP, - HELPER_NAME, FTP_PORT, ports[i], ports[i], - &ftp_exp_policy, 0, help, - nf_ct_ftp_from_nlattr, THIS_MODULE); - nf_ct_helper_init(&ftp[2 * i + 1], AF_INET6, IPPROTO_TCP, - HELPER_NAME, FTP_PORT, ports[i], ports[i], - &ftp_exp_policy, 0, help, - nf_ct_ftp_from_nlattr, THIS_MODULE); - } + nf_ct_helper_init(&ftp, NFPROTO_UNSPEC, IPPROTO_TCP, + HELPER_NAME, + &ftp_exp_policy, 0, help, + nf_ct_ftp_from_nlattr, THIS_MODULE); - ret = nf_conntrack_helpers_register(ftp, ports_c * 2, ftp_ptr); + ret = nf_conntrack_helper_register(&ftp, &ftp_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 37b6314ca772..4cb1665bba02 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -1713,19 +1713,19 @@ static int __init h323_helper_init(void) int ret; nf_ct_helper_init(&nf_conntrack_helper_ras[0], AF_INET, IPPROTO_UDP, - "RAS", RAS_PORT, RAS_PORT, RAS_PORT, + "RAS", &ras_exp_policy, 0, ras_help, NULL, THIS_MODULE); nf_ct_helper_init(&nf_conntrack_helper_ras[1], AF_INET6, IPPROTO_UDP, - "RAS", RAS_PORT, RAS_PORT, RAS_PORT, + "RAS", &ras_exp_policy, 0, ras_help, NULL, THIS_MODULE); nf_ct_helper_init(&nf_conntrack_helper_h245, AF_UNSPEC, IPPROTO_UDP, - "H.245", 0, 0, 0, + "H.245", &h245_exp_policy, 0, h245_help, NULL, THIS_MODULE); nf_ct_helper_init(&nf_conntrack_helper_q931[0], AF_INET, IPPROTO_TCP, - "Q.931", Q931_PORT, Q931_PORT, Q931_PORT, + "Q.931", &q931_exp_policy, 0, q931_help, NULL, THIS_MODULE); nf_ct_helper_init(&nf_conntrack_helper_q931[1], AF_INET6, IPPROTO_TCP, - "Q.931", Q931_PORT, Q931_PORT, Q931_PORT, + "Q.931", &q931_exp_policy, 0, q931_help, NULL, THIS_MODULE); ret = nf_conntrack_helper_register(&nf_conntrack_helper_h245, diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index b28986100db0..506c58034761 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -472,7 +472,6 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); void nf_ct_helper_init(struct nf_conntrack_helper *helper, u8 l3num, u16 protonum, const char *name, - u16 default_port, u16 spec_port, u32 id, const struct nf_conntrack_expect_policy *exp_pol, u32 expect_class_max, int (*help)(struct sk_buff *skb, unsigned int protoff, @@ -493,10 +492,7 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name), NF_NAT_HELPER_PREFIX "%s", name); - if (spec_port == default_port) - snprintf(helper->name, sizeof(helper->name), "%s", name); - else - snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id); + snprintf(helper->name, sizeof(helper->name), "%s", name); if (WARN_ON_ONCE(expect_class_max >= NF_CT_MAX_EXPECT_CLASSES)) return; diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 193ab34db795..4e6bafe41437 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -21,9 +21,6 @@ #include #include -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static unsigned int ports_c; static unsigned int max_dcc_channels = 8; static unsigned int dcc_timeout __read_mostly = 300; /* This is slow, but it's simple. --RR */ @@ -42,8 +39,6 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("ip_conntrack_irc"); MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of IRC servers"); module_param(max_dcc_channels, uint, 0400); MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per " "IRC session"); @@ -254,13 +249,13 @@ static int help(struct sk_buff *skb, unsigned int protoff, return ret; } -static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly; -static struct nf_conntrack_helper *irc_ptr[MAX_PORTS] __read_mostly; +static struct nf_conntrack_helper irc __read_mostly; +static struct nf_conntrack_helper *irc_ptr __read_mostly; static struct nf_conntrack_expect_policy irc_exp_policy; static int __init nf_conntrack_irc_init(void) { - int i, ret; + int ret; nf_conntrack_helper_deprecated(HELPER_NAME); @@ -282,17 +277,11 @@ static int __init nf_conntrack_irc_init(void) if (!irc_buffer) return -ENOMEM; - /* If no port given, default to standard irc port */ - if (ports_c == 0) - ports[ports_c++] = IRC_PORT; + nf_ct_helper_init(&irc, AF_INET, IPPROTO_TCP, HELPER_NAME, + &irc_exp_policy, + 0, help, NULL, THIS_MODULE); - for (i = 0; i < ports_c; i++) { - nf_ct_helper_init(&irc[i], AF_INET, IPPROTO_TCP, HELPER_NAME, - IRC_PORT, ports[i], i, &irc_exp_policy, - 0, help, NULL, THIS_MODULE); - } - - ret = nf_conntrack_helpers_register(&irc[0], ports_c, irc_ptr); + ret = nf_conntrack_helper_register(&irc, &irc_ptr); if (ret) { pr_err("failed to register helpers\n"); kfree(irc_buffer); @@ -304,7 +293,7 @@ static int __init nf_conntrack_irc_init(void) static void __exit nf_conntrack_irc_fini(void) { - nf_conntrack_helpers_unregister(irc_ptr, ports_c); + nf_conntrack_helper_unregister(irc_ptr); kfree(irc_buffer); } diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c index 89d1cf7d6512..caa2b101fa9e 100644 --- a/net/netfilter/nf_conntrack_netbios_ns.c +++ b/net/netfilter/nf_conntrack_netbios_ns.c @@ -21,7 +21,6 @@ #include #define HELPER_NAME "netbios-ns" -#define NMBD_PORT 137 MODULE_AUTHOR("Patrick McHardy "); MODULE_DESCRIPTION("NetBIOS name service broadcast connection tracking helper"); @@ -54,7 +53,6 @@ static int __init nf_conntrack_netbios_ns_init(void) exp_policy.timeout = timeout; nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP, HELPER_NAME, - NMBD_PORT, NMBD_PORT, NMBD_PORT, &exp_policy, 0, netbios_ns_help, NULL, THIS_MODULE); return nf_conntrack_helper_register(&helper, &helper_ptr); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 80fc14c87ddc..cbf32a3cb1f6 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -540,7 +540,7 @@ static int __init nf_conntrack_pptp_init(void) NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_pptp_master)); nf_ct_helper_init(&pptp, AF_INET, IPPROTO_TCP, - "pptp", PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, + "pptp", &pptp_exp_policy, 0, conntrack_pptp_help, NULL, THIS_MODULE); pptp.destroy = gre_pptp_destroy_siblings; diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 39085acf7a71..a0658f69d78f 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -34,11 +34,6 @@ MODULE_AUTHOR("Michal Schmidt "); MODULE_DESCRIPTION("SANE connection tracking helper"); MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); -#define MAX_PORTS 8 -static u_int16_t ports[MAX_PORTS]; -static unsigned int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); - struct sane_request { __be32 RPC_code; #define SANE_NET_START 7 /* RPC code */ @@ -169,8 +164,8 @@ static int help(struct sk_buff *skb, return ret; } -static struct nf_conntrack_helper sane[MAX_PORTS * 2] __read_mostly; -static struct nf_conntrack_helper *sane_ptr[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper sane __read_mostly; +static struct nf_conntrack_helper *sane_ptr __read_mostly; static const struct nf_conntrack_expect_policy sane_exp_policy = { .max_expected = 1, @@ -179,32 +174,21 @@ static const struct nf_conntrack_expect_policy sane_exp_policy = { static void __exit nf_conntrack_sane_fini(void) { - nf_conntrack_helpers_unregister(sane_ptr, ports_c * 2); + nf_conntrack_helper_unregister(sane_ptr); } static int __init nf_conntrack_sane_init(void) { - int i, ret = 0; + int ret = 0; NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sane_master)); - if (ports_c == 0) - ports[ports_c++] = SANE_PORT; - - /* FIXME should be configurable whether IPv4 and IPv6 connections - are tracked or not - YK */ - for (i = 0; i < ports_c; i++) { - nf_ct_helper_init(&sane[2 * i], AF_INET, IPPROTO_TCP, - HELPER_NAME, SANE_PORT, ports[i], ports[i], - &sane_exp_policy, 0, help, NULL, - THIS_MODULE); - nf_ct_helper_init(&sane[2 * i + 1], AF_INET6, IPPROTO_TCP, - HELPER_NAME, SANE_PORT, ports[i], ports[i], - &sane_exp_policy, 0, help, NULL, - THIS_MODULE); - } + nf_ct_helper_init(&sane, NFPROTO_UNSPEC, IPPROTO_TCP, + HELPER_NAME, + &sane_exp_policy, 0, help, NULL, + THIS_MODULE); - ret = nf_conntrack_helpers_register(sane, ports_c * 2, sane_ptr); + ret = nf_conntrack_helper_register(&sane, &sane_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 5ec3a4a4bbd7..d0b85b8ad1e6 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -35,12 +35,6 @@ MODULE_DESCRIPTION("SIP connection tracking helper"); MODULE_ALIAS("ip_conntrack_sip"); MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static unsigned int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "port numbers of SIP servers"); - static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT; module_param(sip_timeout, uint, 0600); MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); @@ -1764,8 +1758,8 @@ static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, return process_sip_msg(skb, ct, protoff, dataoff, &dptr, &datalen); } -static struct nf_conntrack_helper sip[MAX_PORTS * 4] __read_mostly; -static struct nf_conntrack_helper *sip_ptr[MAX_PORTS * 4] __read_mostly; +static struct nf_conntrack_helper sip[2] __read_mostly; +static struct nf_conntrack_helper *sip_ptr[2] __read_mostly; static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = { [SIP_EXPECT_SIGNALLING] = { @@ -1792,38 +1786,25 @@ static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1 static void __exit nf_conntrack_sip_fini(void) { - nf_conntrack_helpers_unregister(sip_ptr, ports_c * 4); + nf_conntrack_helpers_unregister(sip_ptr, 2); } static int __init nf_conntrack_sip_init(void) { - int i, ret; + int ret; NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sip_master)); - if (ports_c == 0) - ports[ports_c++] = SIP_PORT; - - for (i = 0; i < ports_c; i++) { - nf_ct_helper_init(&sip[4 * i], AF_INET, IPPROTO_UDP, - HELPER_NAME, SIP_PORT, ports[i], i, - sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp, - NULL, THIS_MODULE); - nf_ct_helper_init(&sip[4 * i + 1], AF_INET, IPPROTO_TCP, - HELPER_NAME, SIP_PORT, ports[i], i, - sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp, - NULL, THIS_MODULE); - nf_ct_helper_init(&sip[4 * i + 2], AF_INET6, IPPROTO_UDP, - HELPER_NAME, SIP_PORT, ports[i], i, - sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp, - NULL, THIS_MODULE); - nf_ct_helper_init(&sip[4 * i + 3], AF_INET6, IPPROTO_TCP, - HELPER_NAME, SIP_PORT, ports[i], i, - sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp, - NULL, THIS_MODULE); - } + nf_ct_helper_init(&sip[0], NFPROTO_UNSPEC, IPPROTO_UDP, + HELPER_NAME, + sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp, + NULL, THIS_MODULE); + nf_ct_helper_init(&sip[1], NFPROTO_UNSPEC, IPPROTO_TCP, + HELPER_NAME, + sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp, + NULL, THIS_MODULE); - ret = nf_conntrack_helpers_register(sip, ports_c * 4, sip_ptr); + ret = nf_conntrack_helpers_register(sip, 2, sip_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c index b6fce5703fce..109986d5d55e 100644 --- a/net/netfilter/nf_conntrack_snmp.c +++ b/net/netfilter/nf_conntrack_snmp.c @@ -14,8 +14,6 @@ #include #include -#define SNMP_PORT 161 - MODULE_AUTHOR("Jiri Olsa "); MODULE_DESCRIPTION("SNMP service broadcast connection tracking helper"); MODULE_LICENSE("GPL"); @@ -55,7 +53,7 @@ static int __init nf_conntrack_snmp_init(void) exp_policy.timeout = timeout; nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP, - "snmp", SNMP_PORT, SNMP_PORT, SNMP_PORT, + "snmp", &exp_policy, 0, snmp_conntrack_help, NULL, THIS_MODULE); diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index 4393c435aa35..a69559edf9b3 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -26,12 +26,6 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("ip_conntrack_tftp"); MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); -#define MAX_PORTS 8 -static unsigned short ports[MAX_PORTS]; -static unsigned int ports_c; -module_param_array(ports, ushort, &ports_c, 0400); -MODULE_PARM_DESC(ports, "Port numbers of TFTP servers"); - nf_nat_tftp_hook_fn __rcu *nf_nat_tftp_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_tftp_hook); @@ -95,8 +89,8 @@ static int tftp_help(struct sk_buff *skb, return ret; } -static struct nf_conntrack_helper tftp[MAX_PORTS * 2] __read_mostly; -static struct nf_conntrack_helper *tftp_ptr[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper tftp __read_mostly; +static struct nf_conntrack_helper *tftp_ptr __read_mostly; static const struct nf_conntrack_expect_policy tftp_exp_policy = { .max_expected = 1, @@ -105,30 +99,21 @@ static const struct nf_conntrack_expect_policy tftp_exp_policy = { static void __exit nf_conntrack_tftp_fini(void) { - nf_conntrack_helpers_unregister(tftp_ptr, ports_c * 2); + nf_conntrack_helper_unregister(tftp_ptr); } static int __init nf_conntrack_tftp_init(void) { - int i, ret; + int ret; NF_CT_HELPER_BUILD_BUG_ON(0); - if (ports_c == 0) - ports[ports_c++] = TFTP_PORT; - - for (i = 0; i < ports_c; i++) { - nf_ct_helper_init(&tftp[2 * i], AF_INET, IPPROTO_UDP, - HELPER_NAME, TFTP_PORT, ports[i], i, - &tftp_exp_policy, 0, tftp_help, NULL, - THIS_MODULE); - nf_ct_helper_init(&tftp[2 * i + 1], AF_INET6, IPPROTO_UDP, - HELPER_NAME, TFTP_PORT, ports[i], i, - &tftp_exp_policy, 0, tftp_help, NULL, - THIS_MODULE); - } + nf_ct_helper_init(&tftp, NFPROTO_UNSPEC, IPPROTO_UDP, + HELPER_NAME, + &tftp_exp_policy, 0, tftp_help, NULL, + THIS_MODULE); - ret = nf_conntrack_helpers_register(tftp, ports_c * 2, tftp_ptr); + ret = nf_conntrack_helper_register(&tftp, &tftp_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; -- cgit From b5997f911eec53790d56ca438c8ad61e872d795b Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 30 Jun 2026 07:01:26 -0700 Subject: net: add sockopt_init_user() for getsockopt conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper that initializes a user-backed sockopt_t from the (optval, optlen) __user pair passed to a getsockopt() callback. It is used by transitional __user getsockopt wrappers while the proto-layer getsockopt callbacks are converted to take a sockopt_t, and is removed once the conversion is complete. The goal is to help to convert leafs. Example: sock_common_getsockopt(... char __user *optval, int __user *optlen) → udp_getsockopt(sk, level, optname, optval__user, optlen__user) → udp_lib_getsockopt(sk, level, optname, &opt) /* needs a sockopt_t */ Signed-off-by: Breno Leitao Acked-by: Stanislav Fomichev Acked-by: Willem de Bruijn Link: https://patch.msgid.link/20260630-getsockopt_phase2-v2-1-193335f3d4d1@debian.org Signed-off-by: Paolo Abeni --- include/linux/net.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'include') diff --git a/include/linux/net.h b/include/linux/net.h index f268f395ce47..277188a40c72 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -47,6 +47,29 @@ typedef struct sockopt { int optlen; } sockopt_t; +/* + * Initialize a user-backed sockopt_t from the (optval, optlen) __user pair of + * a getsockopt() callback. Used by transitional __user getsockopt wrappers + * while the proto-layer callbacks are converted to take a sockopt_t; the + * caller writes opt->optlen back to the user optlen after the callback. + */ +static inline int sockopt_init_user(sockopt_t *opt, char __user *optval, + int __user *optlen) +{ + int len; + + if (get_user(len, optlen)) + return -EFAULT; + if (len < 0) + return -EINVAL; + + iov_iter_ubuf(&opt->iter_out, ITER_DEST, optval, len); + iov_iter_ubuf(&opt->iter_in, ITER_SOURCE, optval, len); + opt->optlen = len; + + return 0; +} + struct poll_table_struct; struct pipe_inode_info; struct inode; -- cgit From f99d7065a4d45529ccfdc630c1f278da805cf59f Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 30 Jun 2026 07:01:27 -0700 Subject: udp: convert udp_lib_getsockopt to sockopt_t In preparation for converting the proto-layer getsockopt callbacks to the sockopt_t interface, switch udp_lib_getsockopt() to take a sockopt_t. The thin udp_getsockopt()/udpv6_getsockopt() wrappers keep their __user signature for now: they build a user-backed sockopt_t with sockopt_init_user(), call the helper, and write the returned length back to optlen. The helper uses copy_to_iter() instead of copy_to_user(). No functional change. Signed-off-by: Breno Leitao Acked-by: Stanislav Fomichev Acked-by: Willem de Bruijn Link: https://patch.msgid.link/20260630-getsockopt_phase2-v2-2-193335f3d4d1@debian.org Signed-off-by: Paolo Abeni --- include/net/udp.h | 2 +- net/ipv4/udp.c | 39 +++++++++++++++++++++++++++++---------- net/ipv6/udp.c | 19 ++++++++++++++++--- 3 files changed, 46 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/udp.h b/include/net/udp.h index 8262e2b215b4..1fee17274745 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -430,7 +430,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, netdev_features_t features, bool is_ipv6); int udp_lib_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); + sockopt_t *opt); int udp_lib_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen, int (*push_pending_frames)(struct sock *)); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 70f6cbd4ef73..59248a59358c 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -76,6 +76,7 @@ #include #include +#include #include #include #include @@ -2995,14 +2996,13 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t opt } int udp_lib_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct udp_sock *up = udp_sk(sk); int val, len; - if (get_user(len, optlen)) - return -EFAULT; - + len = opt->optlen; + /* keep the check so direct sockopt_t callers stay covered. */ if (len < 0) return -EINVAL; @@ -3037,9 +3037,8 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, return -ENOPROTOOPT; } - if (put_user(len, optlen)) - return -EFAULT; - if (copy_to_user(optval, &val, len)) + opt->optlen = len; + if (copy_to_iter(&val, len, &opt->iter_out) != len) return -EFAULT; return 0; } @@ -3047,9 +3046,29 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, static int udp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { - if (level == SOL_UDP) - return udp_lib_getsockopt(sk, level, optname, optval, optlen); - return ip_getsockopt(sk, level, optname, optval, optlen); + sockopt_t opt; + int err; + + /* + * keep the old __user pointers, until ip_getsockopt() moves + * to sockopt_t + */ + if (level != SOL_UDP) + return ip_getsockopt(sk, level, optname, optval, optlen); + + err = sockopt_init_user(&opt, optval, optlen); + if (err) + return err; + + err = udp_lib_getsockopt(sk, level, optname, &opt); + if (err) + return err; + + /* optval was written by copy_to_iter() in udp_lib_getsockopt() */ + if (put_user(opt.optlen, optlen)) + return -EFAULT; + + return 0; } /** diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 15e032194ecc..392e18b97045 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1826,9 +1826,22 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname, static int udpv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { - if (level == SOL_UDP) - return udp_lib_getsockopt(sk, level, optname, optval, optlen); - return ipv6_getsockopt(sk, level, optname, optval, optlen); + sockopt_t opt; + int err; + + if (level != SOL_UDP) + return ipv6_getsockopt(sk, level, optname, optval, optlen); + + err = sockopt_init_user(&opt, optval, optlen); + if (err) + return err; + + err = udp_lib_getsockopt(sk, level, optname, &opt); + if (err) + return err; + if (put_user(opt.optlen, optlen)) + return -EFAULT; + return 0; } -- cgit From ffd9dada630d9149fe0572efea00e1d99981bc50 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 2 Jul 2026 21:12:40 +0900 Subject: kprobes: Replace __ASSEMBLY__ with __ASSEMBLER__ in header file While the GCC and Clang compilers already define __ASSEMBLER__ automatically when compiling assembly code, __ASSEMBLY__ is a macro that only gets defined by the Makefiles in the kernel. This can be very confusing when switching between userspace and kernelspace coding, or when dealing with uapi headers that rather should use __ASSEMBLER__ instead. So let's standardize now on the __ASSEMBLER__ macro that is provided by the compilers. Link: https://lore.kernel.org/all/20260619161434.88270-1-thuth@redhat.com/ Signed-off-by: Thomas Huth Signed-off-by: Masami Hiramatsu (Google) --- include/asm-generic/kprobes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h index 5290a2b2e15a..16f16963d503 100644 --- a/include/asm-generic/kprobes.h +++ b/include/asm-generic/kprobes.h @@ -2,7 +2,7 @@ #ifndef _ASM_GENERIC_KPROBES_H #define _ASM_GENERIC_KPROBES_H -#if defined(__KERNEL__) && !defined(__ASSEMBLY__) +#if defined(__KERNEL__) && !defined(__ASSEMBLER__) #ifdef CONFIG_KPROBES /* * Blacklist ganerating macro. Specify functions which is not probed @@ -21,6 +21,6 @@ static unsigned long __used \ # define __kprobes # define nokprobe_inline inline #endif -#endif /* defined(__KERNEL__) && !defined(__ASSEMBLY__) */ +#endif /* defined(__KERNEL__) && !defined(__ASSEMBLER__) */ #endif /* _ASM_GENERIC_KPROBES_H */ -- cgit From 462afde2150fface1122120af233e0be88e06814 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 2 Jul 2026 21:15:45 +0900 Subject: bootconfig: add xbc_prepend_embedded_cmdline() helper Add a helper that prepends the build-time-rendered embedded bootconfig "kernel" subtree (embedded_kernel_cmdline[] from embedded-cmdline.S) to a cmdline buffer with a separating space. Architectures call this from setup_arch() before parse_early_param() so early_param() handlers (mem=, earlycon=, loglevel=, ...) see values supplied via the embedded bootconfig. The in-place prepend (shift the existing string right, then drop the embedded string in front) is factored into a small str_prepend() helper. On overflow the helper logs an error and leaves the cmdline untouched rather than panicking. Booting without the embedded values is better than refusing to boot, and the error tells the user why their embedded keys are missing. The helper records whether it actually prepended, exposed via xbc_embedded_cmdline_applied(). setup_boot_config() uses this to decide whether the runtime "kernel" render would duplicate keys already folded into boot_command_line. Also add bootconfig_cmdline_requested(), a small parse_args() wrapper that reports whether "bootconfig" was passed on the command line and, via an optional out-parameter, where the "--" init arguments begin. setup_arch() and setup_boot_config() share it so the early and late paths agree on the opt-in. It sits under CONFIG_BOOT_CONFIG rather than CONFIG_CMDLINE_FROM_BOOTCONFIG because the runtime parser needs it on every bootconfig build. When CONFIG_CMDLINE_FROM_BOOTCONFIG=n, the public declaration in resolves to a no-op stub so callers compile unchanged. Link: https://lore.kernel.org/all/20260626-bootconfig_using_tools-v7-5-24ab72139c29@debian.org/ Signed-off-by: Breno Leitao Signed-off-by: Masami Hiramatsu (Google) --- include/linux/bootconfig.h | 14 +++++ lib/bootconfig.c | 128 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h index 1c7f3b74ffcf..deda507500da 100644 --- a/include/linux/bootconfig.h +++ b/include/linux/bootconfig.h @@ -308,4 +308,18 @@ static inline const char *xbc_get_embedded_bootconfig(size_t *size) } #endif +/* Bootconfig opt-in detection, shared by setup_arch() and setup_boot_config() */ +#ifdef CONFIG_BOOT_CONFIG +bool __init bootconfig_cmdline_requested(const char *boot_cmdline, int *end_offset); +#endif + +/* Build-time-rendered bootconfig cmdline prepended in setup_arch() */ +#ifdef CONFIG_CMDLINE_FROM_BOOTCONFIG +void __init xbc_prepend_embedded_cmdline(char *dst, size_t size); +bool __init xbc_embedded_cmdline_applied(void); +#else +static inline void xbc_prepend_embedded_cmdline(char *dst, size_t size) { } +static inline bool xbc_embedded_cmdline_applied(void) { return false; } +#endif + #endif diff --git a/lib/bootconfig.c b/lib/bootconfig.c index 926094d97397..89c88e359179 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -19,9 +19,13 @@ #include #include #include +#include +#include +#include #include #include #include +#include /* COMMAND_LINE_SIZE */ #ifdef CONFIG_BOOT_CONFIG_EMBED /* embedded_bootconfig_data is defined in bootconfig-data.S */ @@ -34,7 +38,129 @@ const char * __init xbc_get_embedded_bootconfig(size_t *size) return (*size) ? embedded_bootconfig_data : NULL; } #endif -#endif + +#ifdef CONFIG_CMDLINE_FROM_BOOTCONFIG +/* embedded_kernel_cmdline is defined in embedded-cmdline.S */ +extern __visible const char embedded_kernel_cmdline[]; +extern __visible const char embedded_kernel_cmdline_end[]; + +/* Set once the embedded cmdline has actually been prepended. */ +static bool xbc_cmdline_applied __initdata; + +/* + * str_prepend() - Prepend @src in front of the string in @dst, in place + * @dst: NUL-terminated destination buffer, currently @dst_len bytes long + * @dst_len: length of the current @dst string (excluding its NUL) + * @src: bytes to prepend (not NUL-terminated) + * @src_len: number of bytes from @src to prepend + * + * The caller must guarantee @dst has room for src_len + dst_len + 1 bytes. + * Moving dst_len + 1 bytes carries @dst's NUL terminator too, so an empty + * @dst needs no special case. + */ +static void __init str_prepend(char *dst, size_t dst_len, + const char *src, size_t src_len) +{ + memmove(dst + src_len, dst, dst_len + 1); + memcpy(dst, src, src_len); +} + +/** + * xbc_prepend_embedded_cmdline() - Prepend embedded bootconfig cmdline + * @dst: cmdline buffer to prepend into (must already contain a NUL byte) + * @size: total capacity of @dst in bytes + * + * Prepend the build-time-rendered "kernel" subtree of the embedded + * bootconfig to @dst. The rendered string already ends with a single + * space (the xbc_snprint_cmdline() invariant), which serves as the + * separator between the embedded keys and any existing content of @dst. + * On overflow, log an error and leave @dst untouched rather than + * silently truncating: booting without the embedded values is better + * than refusing to boot, and the error message tells the user why + * their embedded keys are missing. + * + * Intended to be called from setup_arch() before parse_early_param() so + * that early_param() handlers see the embedded values. + */ +void __init xbc_prepend_embedded_cmdline(char *dst, size_t size) +{ + size_t embed_len = embedded_kernel_cmdline_end - embedded_kernel_cmdline; + size_t dst_len; + + if (!size || embed_len <= 1) /* trailing NUL only */ + return; + embed_len--; /* exclude trailing NUL byte */ + + dst_len = strnlen(dst, size); + if (embed_len + dst_len + 1 > size) { + pr_err("embedded bootconfig cmdline (%zu bytes) does not fit in COMMAND_LINE_SIZE with %zu bytes already used; ignoring embedded values\n", + embed_len, dst_len); + return; + } + + str_prepend(dst, dst_len, embedded_kernel_cmdline, embed_len); + xbc_cmdline_applied = true; +} + +/** + * xbc_embedded_cmdline_applied() - Did the embedded cmdline get prepended? + * + * Return true if xbc_prepend_embedded_cmdline() actually prepended the + * embedded "kernel" subtree. setup_boot_config() uses this to avoid + * rendering the same keys a second time. + */ +bool __init xbc_embedded_cmdline_applied(void) +{ + return xbc_cmdline_applied; +} +#endif /* CONFIG_CMDLINE_FROM_BOOTCONFIG */ + +/* parse_args() callback: flag when the "bootconfig" parameter is present. */ +static int __init bootconfig_optin(char *param, char *val, + const char *unused, void *arg) +{ + if (!strcmp(param, "bootconfig")) + *(bool *)arg = true; + return 0; +} + +/** + * bootconfig_cmdline_requested() - Was "bootconfig" passed on the cmdline? + * @boot_cmdline: kernel command line to inspect (not modified) + * @end_offset: if non-NULL, set to the offset of the init arguments that + * follow a "--" separator, or 0 when there is none + * + * Parse a private copy of @boot_cmdline (parse_args() is destructive) and + * report whether "bootconfig" is present before the "--" separator. + * setup_arch() uses this to gate prepending the build-time embedded cmdline; + * setup_boot_config() uses it for the runtime opt-in and to locate the init + * arguments via @end_offset. Sharing one parser keeps the early and late + * paths agreeing on what counts as opt-in. CONFIG_BOOT_CONFIG_FORCE is not + * folded in here; callers apply it where they need it. + */ +bool __init bootconfig_cmdline_requested(const char *boot_cmdline, int *end_offset) +{ + static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata; + bool found = false; + char *err; + + if (end_offset) + *end_offset = 0; + + strscpy(tmp_cmdline, boot_cmdline, COMMAND_LINE_SIZE); + err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, + &found, bootconfig_optin); + if (IS_ERR(err)) + return false; + + /* parse_args() stops at "--" and returns the address of the rest. */ + if (end_offset && err) + *end_offset = err - tmp_cmdline; + + return found; +} + +#endif /* __KERNEL__ */ /* * Extra Boot Config (XBC) is given as tree-structured ascii text of -- cgit From 348ff65577603c0565257ecfcaa015ebaaeb200e Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Tue, 23 Jun 2026 11:16:13 +0300 Subject: dt-bindings: iio: adc: mediatek,mt6359-auxadc: add mt6323 PMIC AUXADC The MediaTek mt6323 PMIC includes an AUXADC used for battery voltage, temperature, and other internal measurements. The IP block is not register-compatible with mt6359. Add the devicetree binding documentation and the associated header file defining the ADC channel constants. Also change the description to 'MT6350 series and similar' because the binding already includes more than mt635x series PMICs. Finally, add the MAINTAINERS entry for the header with ADC constants. Acked-by: Conor Dooley Signed-off-by: Roman Vivchar Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/mediatek,mt6359-auxadc.yaml | 3 ++- MAINTAINERS | 6 ++++++ .../dt-bindings/iio/adc/mediatek,mt6323-auxadc.h | 24 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml index 9936aa605c7b..c2f7387e4bfc 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/adc/mediatek,mt6359-auxadc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: MediaTek MT6350 series PMIC AUXADC +title: MediaTek MT6350 series and similar PMIC AUXADC maintainers: - AngeloGioacchino Del Regno @@ -20,6 +20,7 @@ properties: compatible: oneOf: - enum: + - mediatek,mt6323-auxadc - mediatek,mt6357-auxadc - mediatek,mt6358-auxadc - mediatek,mt6359-auxadc diff --git a/MAINTAINERS b/MAINTAINERS index bf8f3f565fda..c413c2cb23ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16564,6 +16564,12 @@ S: Maintained F: Documentation/devicetree/bindings/mmc/mtk-sd.yaml F: drivers/mmc/host/mtk-sd.c +MEDIATEK MT6323 PMIC AUXADC DRIVER +M: Roman Vivchar +L: linux-iio@vger.kernel.org +S: Maintained +F: include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h + MEDIATEK MT6735 CLOCK & RESET DRIVERS M: Yassine Oudjana L: linux-clk@vger.kernel.org diff --git a/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h b/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h new file mode 100644 index 000000000000..6ee9a9ecffc1 --- /dev/null +++ b/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_MEDIATEK_MT6323_AUXADC_H +#define _DT_BINDINGS_MEDIATEK_MT6323_AUXADC_H + +#define MT6323_AUXADC_BATON2 0 +#define MT6323_AUXADC_CH6 1 +#define MT6323_AUXADC_BAT_TEMP 2 +#define MT6323_AUXADC_CHIP_TEMP 3 +#define MT6323_AUXADC_VCDT 4 +#define MT6323_AUXADC_BATON1 5 +#define MT6323_AUXADC_ISENSE 6 +#define MT6323_AUXADC_BATSNS 7 +#define MT6323_AUXADC_ACCDET 8 +#define MT6323_AUXADC_AUDIO0 9 +#define MT6323_AUXADC_AUDIO1 10 +#define MT6323_AUXADC_AUDIO2 11 +#define MT6323_AUXADC_AUDIO3 12 +#define MT6323_AUXADC_AUDIO4 13 +#define MT6323_AUXADC_AUDIO5 14 +#define MT6323_AUXADC_AUDIO6 15 +#define MT6323_AUXADC_AUDIO7 16 + +#endif -- cgit From 7cb8198761e627ff3a3b4770c8f147e75c4e649d Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Tue, 30 Jun 2026 20:02:05 +0900 Subject: net: ipv4: report multicast group user count RTM_GETMULTICAST has been part of the rtnetlink ABI for a long time and already reports IPv4 multicast group membership through IFA_MULTICAST and IFA_CACHEINFO. It does not report how many consumers hold each membership, so userspace still has to parse /proc/net/igmp to get the Users column. Add IFA_MC_USERS as a u32 attribute carrying ip_mc_list::users in RTM_GETMULTICAST replies and entry-lifecycle notifications. This gives iproute2 enough information to migrate the IPv4 part of "ip maddr show" from procfs parsing to rtnetlink. Signed-off-by: Yuyang Huang Reviewed-by: Vadim Fedorenko Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260630110207.37841-2-sigefriedhyy@gmail.com Signed-off-by: Paolo Abeni --- Documentation/netlink/specs/rt-addr.yaml | 4 ++++ include/uapi/linux/if_addr.h | 1 + net/ipv4/igmp.c | 2 ++ 3 files changed, 7 insertions(+) (limited to 'include') diff --git a/Documentation/netlink/specs/rt-addr.yaml b/Documentation/netlink/specs/rt-addr.yaml index 163a106c41bb..0ecbd24c890c 100644 --- a/Documentation/netlink/specs/rt-addr.yaml +++ b/Documentation/netlink/specs/rt-addr.yaml @@ -123,6 +123,9 @@ attribute-sets: - name: proto type: u8 + - + name: mc-users + type: u32 operations: @@ -176,6 +179,7 @@ operations: value: 58 attributes: &mcaddr-attrs - multicast + - mc-users - cacheinfo dump: request: diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h index aa7958b4e41d..7fb630b7fe31 100644 --- a/include/uapi/linux/if_addr.h +++ b/include/uapi/linux/if_addr.h @@ -36,6 +36,7 @@ enum { IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ IFA_TARGET_NETNSID, IFA_PROTO, /* u8, address protocol */ + IFA_MC_USERS, /* u32, multicast group users */ __IFA_MAX, }; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index b6337a47c141..116ce7cec80e 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1473,6 +1473,7 @@ int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev, ci.ifa_valid = INFINITY_LIFE_TIME; if (nla_put_in_addr(skb, IFA_MULTICAST, im->multiaddr) < 0 || + nla_put_u32(skb, IFA_MC_USERS, READ_ONCE(im->users)) < 0 || nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci) < 0) { nlmsg_cancel(skb, nlh); return -EMSGSIZE; @@ -1494,6 +1495,7 @@ static void inet_ifmcaddr_notify(struct net_device *dev, skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + nla_total_size(sizeof(__be32)) + + nla_total_size(sizeof(u32)) + nla_total_size(sizeof(struct ifa_cacheinfo)), GFP_KERNEL); if (!skb) -- cgit From 3386c50d66759e4e6ddedabc178db3db33836aa6 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 3 Jul 2026 10:47:14 +0200 Subject: goldfish: remove unused gf_write_dma_addr() The last user was removed in 2020 by commit c869eaa617e4 ("drivers: staging: retire drivers/staging/goldfish"). Drop it. Signed-off-by: Jiri Slaby (SUSE) Link: https://patch.msgid.link/20260703084717.176442-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/goldfish.h | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'include') diff --git a/include/linux/goldfish.h b/include/linux/goldfish.h index bcc17f95b906..40a059e03d78 100644 --- a/include/linux/goldfish.h +++ b/include/linux/goldfish.h @@ -26,15 +26,4 @@ static inline void gf_write_ptr(const void *ptr, void __iomem *portl, #endif } -static inline void gf_write_dma_addr(const dma_addr_t addr, - void __iomem *portl, - void __iomem *porth) -{ - gf_iowrite32(lower_32_bits(addr), portl); -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - gf_iowrite32(upper_32_bits(addr), porth); -#endif -} - - #endif /* __LINUX_GOLDFISH_H */ -- cgit From e31bd02f19ddb01c1e1fb6d79b72ace8f014cb27 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 3 Jul 2026 10:47:16 +0200 Subject: tty: goldfish: move gf_write_ptr() to tty/goldfish.c tty/goldfish.c is the only user of gf_write_ptr(). Move it there, drop the unneeded casts, and name it appropriately. FTR, the last non-tty user was removed in 2018 by 4ae0fe70a097 ("Delete the goldfish_nand driver."). Signed-off-by: Jiri Slaby (SUSE) Link: https://patch.msgid.link/20260703084717.176442-3-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/goldfish.c | 13 +++++++++++-- include/linux/goldfish.h | 13 ------------- 2 files changed, 11 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index ba060ce4e9b5..fb135bf5996c 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -18,6 +18,7 @@ #include #include #include +#include /* Goldfish tty register's offsets */ #define GOLDFISH_TTY_REG_BYTES_READY 0x04 @@ -49,6 +50,14 @@ static u32 goldfish_tty_line_count = 8; static u32 goldfish_tty_current_line_count; static struct goldfish_tty *goldfish_ttys; +static inline void gf_write_addr(unsigned long addr, void __iomem *portl, void __iomem *porth) +{ + gf_iowrite32(lower_32_bits(addr), portl); +#ifdef CONFIG_64BIT + gf_iowrite32(upper_32_bits(addr), porth); +#endif +} + static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, size_t count, bool is_write) { @@ -56,8 +65,8 @@ static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, void __iomem *base = qtty->base; spin_lock_irqsave(&qtty->lock, irq_flags); - gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR, - base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); + gf_write_addr(address, base + GOLDFISH_TTY_REG_DATA_PTR, + base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); gf_iowrite32(count, base + GOLDFISH_TTY_REG_DATA_LEN); if (is_write) diff --git a/include/linux/goldfish.h b/include/linux/goldfish.h index 40a059e03d78..98a4719c6776 100644 --- a/include/linux/goldfish.h +++ b/include/linux/goldfish.h @@ -2,8 +2,6 @@ #ifndef __LINUX_GOLDFISH_H #define __LINUX_GOLDFISH_H -#include -#include #include /* Helpers for Goldfish virtual platform */ @@ -15,15 +13,4 @@ #define gf_iowrite32 iowrite32 #endif -static inline void gf_write_ptr(const void *ptr, void __iomem *portl, - void __iomem *porth) -{ - const unsigned long addr = (unsigned long)ptr; - - gf_iowrite32(lower_32_bits(addr), portl); -#ifdef CONFIG_64BIT - gf_iowrite32(upper_32_bits(addr), porth); -#endif -} - #endif /* __LINUX_GOLDFISH_H */ -- cgit From 38af0dd6a266057002eacb170c08298ea912fb0a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 3 Jul 2026 13:49:06 +0200 Subject: uprobes/x86: Remove struct uprobe_trampoline object Removing struct uprobe_trampoline object and it's tracking code, because it's not needed. We can do same thing directly on top of struct vm_area_struct objects. This makes the code simpler and allows easy propagation of the trampoline vma object into child process in following change. Note the original code called destroy_uprobe_trampoline if the optimiation failed, but it only freed the struct uprobe_trampoline object, not the vma. The new vma leak is fixed in following change. Signed-off-by: Jiri Olsa Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Oleg Nesterov Acked-by: Andrii Nakryiko Link: https://patch.msgid.link/20260703114917.238144-3-jolsa@kernel.org --- arch/x86/kernel/uprobes.c | 106 ++++++++++------------------------------------ include/linux/uprobes.h | 5 --- kernel/events/uprobes.c | 10 ----- kernel/fork.c | 1 - 4 files changed, 22 insertions(+), 100 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 3af979fb41d3..a76203a33a7b 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -631,11 +631,6 @@ static struct vm_special_mapping tramp_mapping = { .pages = tramp_mapping_pages, }; -struct uprobe_trampoline { - struct hlist_node node; - unsigned long vaddr; -}; - static bool is_reachable_by_call(unsigned long vtramp, unsigned long vaddr) { long delta = (long)(vaddr + 5 - vtramp); @@ -682,83 +677,28 @@ static unsigned long find_nearest_trampoline(unsigned long vaddr) return high_tramp; } -static struct uprobe_trampoline *create_uprobe_trampoline(unsigned long vaddr) +static struct vm_area_struct *get_uprobe_trampoline(struct mm_struct *mm, unsigned long vaddr) { - struct pt_regs *regs = task_pt_regs(current); - struct mm_struct *mm = current->mm; - struct uprobe_trampoline *tramp; + VMA_ITERATOR(vmi, mm, 0); struct vm_area_struct *vma; - if (!user_64bit_mode(regs)) - return NULL; + if (vaddr > TASK_SIZE || vaddr < PAGE_SIZE) + return ERR_PTR(-EINVAL); + + for_each_vma(vmi, vma) { + if (!vma_is_special_mapping(vma, &tramp_mapping)) + continue; + if (is_reachable_by_call(vma->vm_start, vaddr)) + return vma; + } vaddr = find_nearest_trampoline(vaddr); if (IS_ERR_VALUE(vaddr)) - return NULL; + return ERR_PTR(vaddr); - tramp = kzalloc_obj(*tramp); - if (unlikely(!tramp)) - return NULL; - - tramp->vaddr = vaddr; - vma = _install_special_mapping(mm, tramp->vaddr, PAGE_SIZE, + return _install_special_mapping(mm, vaddr, PAGE_SIZE, VM_READ|VM_EXEC|VM_MAYEXEC|VM_MAYREAD|VM_DONTCOPY|VM_IO, &tramp_mapping); - if (IS_ERR(vma)) { - kfree(tramp); - return NULL; - } - return tramp; -} - -static struct uprobe_trampoline *get_uprobe_trampoline(unsigned long vaddr, bool *new) -{ - struct uprobes_state *state = ¤t->mm->uprobes_state; - struct uprobe_trampoline *tramp = NULL; - - if (vaddr > TASK_SIZE || vaddr < PAGE_SIZE) - return NULL; - - hlist_for_each_entry(tramp, &state->head_tramps, node) { - if (is_reachable_by_call(tramp->vaddr, vaddr)) { - *new = false; - return tramp; - } - } - - tramp = create_uprobe_trampoline(vaddr); - if (!tramp) - return NULL; - - *new = true; - hlist_add_head(&tramp->node, &state->head_tramps); - return tramp; -} - -static void destroy_uprobe_trampoline(struct uprobe_trampoline *tramp) -{ - /* - * We do not unmap and release uprobe trampoline page itself, - * because there's no easy way to make sure none of the threads - * is still inside the trampoline. - */ - hlist_del(&tramp->node); - kfree(tramp); -} - -void arch_uprobe_init_state(struct mm_struct *mm) -{ - INIT_HLIST_HEAD(&mm->uprobes_state.head_tramps); -} - -void arch_uprobe_clear_state(struct mm_struct *mm) -{ - struct uprobes_state *state = &mm->uprobes_state; - struct uprobe_trampoline *tramp; - struct hlist_node *n; - - hlist_for_each_entry_safe(tramp, n, &state->head_tramps, node) - destroy_uprobe_trampoline(tramp); } static bool __in_uprobe_trampoline(struct mm_struct *mm, unsigned long ip) @@ -1111,21 +1051,19 @@ int set_orig_insn(struct arch_uprobe *auprobe, struct vm_area_struct *vma, static int __arch_uprobe_optimize(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { - struct uprobe_trampoline *tramp; - struct vm_area_struct *vma; - bool new = false; - int err = 0; + struct pt_regs *regs = task_pt_regs(current); + struct vm_area_struct *vma, *tramp; + int ret; + if (!user_64bit_mode(regs)) + return -EINVAL; vma = find_vma(mm, vaddr); if (!vma) return -EINVAL; - tramp = get_uprobe_trampoline(vaddr, &new); - if (!tramp) - return -EINVAL; - err = swbp_optimize(auprobe, vma, vaddr, tramp->vaddr); - if (WARN_ON_ONCE(err) && new) - destroy_uprobe_trampoline(tramp); - return err; + tramp = get_uprobe_trampoline(mm, vaddr); + if (IS_ERR(tramp)) + return PTR_ERR(tramp); + return WARN_ON_ONCE(swbp_optimize(auprobe, vma, vaddr, tramp->vm_start)); } void arch_uprobe_optimize(struct arch_uprobe *auprobe, unsigned long vaddr) diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index f548fea2adec..18be159bbc34 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -186,9 +186,6 @@ struct xol_area; struct uprobes_state { struct xol_area *xol_area; -#ifdef CONFIG_X86_64 - struct hlist_head head_tramps; -#endif }; typedef int (*uprobe_write_verify_t)(struct page *page, unsigned long vaddr, @@ -238,8 +235,6 @@ extern void uprobe_handle_trampoline(struct pt_regs *regs); extern void *arch_uretprobe_trampoline(unsigned long *psize); extern unsigned long uprobe_get_trampoline_vaddr(void); extern void uprobe_copy_from_page(struct page *page, unsigned long vaddr, void *dst, int len); -extern void arch_uprobe_clear_state(struct mm_struct *mm); -extern void arch_uprobe_init_state(struct mm_struct *mm); extern void handle_syscall_uprobe(struct pt_regs *regs, unsigned long bp_vaddr); extern void arch_uprobe_optimize(struct arch_uprobe *auprobe, unsigned long vaddr); extern unsigned long arch_uprobe_get_xol_area(void); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 4084e926e284..b5c516168f84 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1806,14 +1806,6 @@ static struct xol_area *get_xol_area(void) return area; } -void __weak arch_uprobe_clear_state(struct mm_struct *mm) -{ -} - -void __weak arch_uprobe_init_state(struct mm_struct *mm) -{ -} - /* * uprobe_clear_state - Free the area allocated for slots. */ @@ -1825,8 +1817,6 @@ void uprobe_clear_state(struct mm_struct *mm) delayed_uprobe_remove(NULL, mm); mutex_unlock(&delayed_uprobe_lock); - arch_uprobe_clear_state(mm); - if (!area) return; diff --git a/kernel/fork.c b/kernel/fork.c index 13e38e89a1f3..00b52c7314d1 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1064,7 +1064,6 @@ static void mm_init_uprobes_state(struct mm_struct *mm) { #ifdef CONFIG_UPROBES mm->uprobes_state.xol_area = NULL; - arch_uprobe_init_state(mm); #endif } -- cgit From 9bb4c0b37d54fc7d61f2a21cfa635fa2e3a29ac5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 2 Jul 2026 14:42:54 +0200 Subject: regmap-irq: Provide IRQ resource request and release callbacks The users which rely on regmap IRQ to create the IRQ chip may also want to have an additional tracking of the IRQ requests and releases. Provide a callback for them. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260702130903.1790633-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 22 ++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 07234d415b51..99b55b1053ee 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -296,6 +296,26 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } +static int regmap_irq_reqres(struct irq_data *data) +{ + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); + + if (d->chip->irq_reqres) + return d->chip->irq_reqres(d->chip->irq_drv_data, hwirq); + + return 0; +} + +static void regmap_irq_relres(struct irq_data *data) +{ + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); + + if (d->chip->irq_relres) + d->chip->irq_relres(d->chip->irq_drv_data, hwirq); +} + static const struct irq_chip regmap_irq_chip = { .irq_bus_lock = regmap_irq_lock, .irq_bus_sync_unlock = regmap_irq_sync_unlock, @@ -303,6 +323,8 @@ static const struct irq_chip regmap_irq_chip = { .irq_enable = regmap_irq_enable, .irq_set_type = regmap_irq_set_type, .irq_set_wake = regmap_irq_set_wake, + .irq_request_resources = regmap_irq_reqres, + .irq_release_resources = regmap_irq_relres, }; static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index df44cb30f53b..370baa19db87 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1770,6 +1770,8 @@ struct regmap_irq_chip { void *irq_drv_data); unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data, unsigned int base, int index); + int (*irq_reqres)(void *irq_drv_data, irq_hw_number_t hwirq); + void (*irq_relres)(void *irq_drv_data, irq_hw_number_t hwirq); void *irq_drv_data; }; -- cgit From 08c5e98b7b5ff5aa0c2774bf58a5a71e2741f603 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:15 +0530 Subject: ASoC: SOF: amd: add ACP I2S format field and topology token Add format field to sof_ipc_dai_acp_params for ACP I2S format selection. Add SOF_TKN_AMD_ACPI2S_FORMAT (1703) to the existing SOF_ACPI2S_TOKENS tuple and wire it into acpi2s_tokens[] so integrators continue using the same ACPI2S token group as earlier ACP I2S topologies, not a separate ACPTDM-specific token set. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-15-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/sound/sof/dai-amd.h | 1 + include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/ipc3-topology.c | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h index 59cd014392c1..e2107da6558f 100644 --- a/include/sound/sof/dai-amd.h +++ b/include/sound/sof/dai-amd.h @@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { uint32_t fsync_rate; /* FSYNC frequency in Hz */ uint32_t tdm_slots; uint32_t tdm_mode; + uint32_t format; } __packed; /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index f4a7baadb44d..cc694a397987 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -223,6 +223,7 @@ #define SOF_TKN_AMD_ACPI2S_RATE 1700 #define SOF_TKN_AMD_ACPI2S_CH 1701 #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 +#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 /* MICFIL PDM */ #define SOF_TKN_IMX_MICFIL_RATE 2000 diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 4e066bbded91..9eb8335a3c07 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, }; -/* ACPI2S */ +/* + * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM + * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. + */ static const struct sof_topology_token acpi2s_tokens[] = { {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, @@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, + {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, format)}, }; /* MICFIL PDM */ -- cgit From 3805b8e6f932fbf9bfe5803b6a85328cc468b75d Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:16 +0530 Subject: ASoC: SOF: amd: add ACP7x I2S DAI type and topology support Add SOF_DAI_AMD_I2S DAI type for ACP7.B/7.F I2S/TDM interfaces. Register the ACPTDM topology DAI name and map it to SOF_DAI_AMD_I2S; IPC3 continues to parse ACP I2S link parameters through SOF_ACPI2S_TOKENS (including the format token from the prior commit), not a new token group named after ACPTDM. Add sof_link_acp_i2s_load() and the SOF_DAI_AMD_I2S PCM dai link fixup path. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-16-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 2 ++ sound/soc/sof/ipc3-pcm.c | 6 ++++++ sound/soc/sof/ipc3-topology.c | 32 ++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 3 ++- 4 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 0b6a6ba6489a..e3fe492e78f5 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -91,6 +91,7 @@ enum sof_ipc_dai_type { SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ + SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ }; /* general purpose DAI configuration */ @@ -122,6 +123,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_mtk_afe_params afe; struct sof_ipc_dai_micfil_params micfil; struct sof_ipc_dai_acp_sdw_params acp_sdw; + struct sof_ipc_dai_acp_params acp_i2s; }; } __packed; diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 90ef5d99f626..143bf0fe8dd9 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_I2S: + rate->min = private->dai_config->acp_i2s.fsync_rate; + rate->max = private->dai_config->acp_i2s.fsync_rate; + channels->min = private->dai_config->acp_i2s.tdm_slots; + channels->max = private->dai_config->acp_i2s.tdm_slots; + break; default: dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 9eb8335a3c07..26d85ca9be26 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1368,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof return 0; } +static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); + config->hdr.size = size; + + ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { @@ -1697,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) case SOF_DAI_AMD_SDW: ret = sof_link_acp_sdw_load(scomp, slink, config, dai); break; + case SOF_DAI_AMD_I2S: + ret = sof_link_acp_i2s_load(scomp, slink, config, dai); + break; default: break; } diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 6de8a6c1c127..6fd69ba11c41 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, {"MICFIL", SOF_DAI_IMX_MICFIL}, {"ACP_SDW", SOF_DAI_AMD_SDW}, - + {"ACPTDM", SOF_DAI_AMD_I2S}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -1994,6 +1994,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ case SOF_DAI_AMD_HS: case SOF_DAI_AMD_SP_VIRTUAL: case SOF_DAI_AMD_HS_VIRTUAL: + case SOF_DAI_AMD_I2S: token_id = SOF_ACPI2S_TOKENS; num_tuples += token_list[SOF_ACPI2S_TOKENS].count; break; -- cgit From 3a8bfa1f2af71ca21818253753fccc53337b7b9b Mon Sep 17 00:00:00 2001 From: Rafael Passos Date: Tue, 30 Jun 2026 22:20:58 -0300 Subject: drm/xe: Documentation: fix chars used for subsection Fixes "ERROR: A level 2 section cannot be used here". Equal signs are reserved for document titles. This file docs gets imported by driver-uapi.rst, and the page title is defined there. Signed-off-by: Rafael Passos Reviewed-by: Randy Dunlap Tested-by: Randy Dunlap Link: https://patch.msgid.link/20260701012141.167868-1-rafael@rcpassos.me Signed-off-by: Rodrigo Vivi [Rodrigo modified the subject while pushing it] --- include/uapi/drm/xe_drm.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 50c80af4ad4e..509202a7b13e 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -2537,21 +2537,21 @@ struct drm_xe_exec_queue_set_property { * Refer to Documentation/netlink/specs/drm_ras.yaml for complete interface specification. * * Node Registration - * ================= + * ----------------- * * The driver registers DRM RAS nodes for each error severity level. * enum drm_xe_ras_error_severity defines the node-id, while DRM_XE_RAS_ERROR_SEVERITY_NAMES maps * node-id to node-name. * * Error Classification - * ==================== + * -------------------- * * Each node contains a list of error counters. Each error is identified by a error-id and * an error-name. enum drm_xe_ras_error_component defines the error-id, while * DRM_XE_RAS_ERROR_COMPONENT_NAMES maps error-id to error-name. * * User Interface - * ============== + * -------------- * * To retrieve error values of a error counter, userspace applications should * follow the below steps: -- cgit From c4c4434e41e5875b1c922e4c5783466687e75c76 Mon Sep 17 00:00:00 2001 From: Adrián Larumbe Date: Mon, 8 Jun 2026 23:33:52 +0100 Subject: drm/gpuvm: Remove dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_gpuva_find_{prev|next}() have no consumers. Signed-off-by: Adrián Larumbe Link: https://patch.msgid.link/20260608-gpuvm-minor-fixes-v2-1-af07ef9ca969@collabora.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/drm_gpuvm.c | 44 -------------------------------------------- include/drm/drm_gpuvm.h | 2 -- 2 files changed, 46 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index c422c5af1f4b..d1c80ad3dead 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -2229,50 +2229,6 @@ out: } EXPORT_SYMBOL_GPL(drm_gpuva_find); -/** - * drm_gpuva_find_prev() - find the &drm_gpuva before the given address - * @gpuvm: the &drm_gpuvm to search in - * @start: the given GPU VA's start address - * - * Find the adjacent &drm_gpuva before the GPU VA with given &start address. - * - * Note that if there is any free space between the GPU VA mappings no mapping - * is returned. - * - * Returns: a pointer to the found &drm_gpuva or NULL if none was found - */ -struct drm_gpuva * -drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start) -{ - if (!drm_gpuvm_range_valid(gpuvm, start - 1, 1)) - return NULL; - - return drm_gpuva_it_iter_first(&gpuvm->rb.tree, start - 1, start); -} -EXPORT_SYMBOL_GPL(drm_gpuva_find_prev); - -/** - * drm_gpuva_find_next() - find the &drm_gpuva after the given address - * @gpuvm: the &drm_gpuvm to search in - * @end: the given GPU VA's end address - * - * Find the adjacent &drm_gpuva after the GPU VA with given &end address. - * - * Note that if there is any free space between the GPU VA mappings no mapping - * is returned. - * - * Returns: a pointer to the found &drm_gpuva or NULL if none was found - */ -struct drm_gpuva * -drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end) -{ - if (!drm_gpuvm_range_valid(gpuvm, end, 1)) - return NULL; - - return drm_gpuva_it_iter_first(&gpuvm->rb.tree, end, end + 1); -} -EXPORT_SYMBOL_GPL(drm_gpuva_find_next); - /** * drm_gpuvm_interval_empty() - indicate whether a given interval of the VA space * is empty diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h index 655bd9104ffb..f8fc0296c4b7 100644 --- a/include/drm/drm_gpuvm.h +++ b/include/drm/drm_gpuvm.h @@ -159,8 +159,6 @@ struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm, u64 addr, u64 range); struct drm_gpuva *drm_gpuva_find_first(struct drm_gpuvm *gpuvm, u64 addr, u64 range); -struct drm_gpuva *drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start); -struct drm_gpuva *drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end); /** * drm_gpuva_invalidate() - sets whether the backing GEM of this &drm_gpuva is -- cgit From 49d0307c55c4f713bf9515e6ef52ed0942cea27d Mon Sep 17 00:00:00 2001 From: Adrián Larumbe Date: Mon, 8 Jun 2026 23:33:53 +0100 Subject: drm/gpuvm: Fix comment to reflect remap operation operand status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a new mapping intersects with an existing GPU VA, but either end lies before or beyond the existing VA's edges, then the prev and next mapping operations part of a remap will reflect this condition by being set to NULL. Signed-off-by: Adrián Larumbe Link: https://patch.msgid.link/20260608-gpuvm-minor-fixes-v2-2-af07ef9ca969@collabora.com Signed-off-by: Danilo Krummrich --- include/drm/drm_gpuvm.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h index f8fc0296c4b7..38221d83285b 100644 --- a/include/drm/drm_gpuvm.h +++ b/include/drm/drm_gpuvm.h @@ -928,6 +928,8 @@ struct drm_gpuva_op_unmap { * If either a new mapping's start address is aligned with the start address * of the old mapping or the new mapping's end address is aligned with the * end address of the old mapping, either @prev or @next is NULL. + * This will also be the case when the requested mapping begins before the + * old mapping's start address or stretches beyond its end address. * * Note, the reason for a dedicated remap operation, rather than arbitrary * unmap and map operations, is to give drivers the chance of extracting driver -- cgit From ba16486d79d44e3d07c713ff566be156292ed744 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 4 Jun 2026 10:21:17 +0800 Subject: rhashtable: Add workqueue/irq_work header inclusions Add inclusions for irq_work.h and workqueue.h to rhashtable.c rather than relying on indirect inclusions from elsewhere. Remove workqueue.h from rhashtable.h now that it uses IRQ work only. Signed-off-by: Herbert Xu --- include/linux/rhashtable.h | 1 - lib/rhashtable.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 79f83b6eec27..57a2a29bef0e 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 40cfb38ac919..ef975510ec38 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #define HASH_DEFAULT_SIZE 64UL #define HASH_MIN_SIZE 4U -- cgit From 2fdf279ccf1bdea919b7dfa56081047c7a8d5015 Mon Sep 17 00:00:00 2001 From: "Pratik R. Sampat" Date: Mon, 15 Jun 2026 15:23:15 +0000 Subject: crypto: ccp - Introduce SNP_VERIFY_MITIGATION command The SEV-SNP firmware provides the SNP_VERIFY_MITIGATION command, which can be used to query the status of currently supported vulnerability mitigations and to initiate mitigations within the firmware. This command is an explicit mechanism to ascertain if a firmware mitigation is applied without needing a full RMP re-build, which is most useful in a live firmware update scenario. The firmware supports two subcommands: STATUS and VERIFY. The STATUS subcommand is used to query the supported and verified mitigation bits. The VERIFY subcommand initiates the mitigation process within the FW for the specified vulnerability. Expose a userspace interface under: /sys/firmware/sev/vulnerabilities/ - supported_mitigations (read-only): supported mitigation vector mask - verified_mitigations (read/write): current verified mask; write a vector to request VERIFY for that bit The behavior of SNP_VERIFY_MITIGATION and the pre-requisites for using it are bug-specific. Information about supported mitigations and its corresponding vector is to be published as part of the AMD Security Bulletin. See SEV-SNP Firmware ABI specifications 1.58, SNP_VERIFY_MITIGATION for more details. Reviewed-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Pratik R. Sampat Signed-off-by: Herbert Xu --- .../ABI/testing/sysfs-firmware-sev-vulnerabilities | 19 +++ drivers/crypto/ccp/sev-dev.c | 177 +++++++++++++++++++++ drivers/crypto/ccp/sev-dev.h | 3 + include/linux/psp-sev.h | 51 ++++++ 4 files changed, 250 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-firmware-sev-vulnerabilities (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-firmware-sev-vulnerabilities b/Documentation/ABI/testing/sysfs-firmware-sev-vulnerabilities new file mode 100644 index 000000000000..964362558bb2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-sev-vulnerabilities @@ -0,0 +1,19 @@ +What: /sys/firmware/sev/vulnerabilities/supported_mitigations +Date: June 2026 +Contact: linux-crypto@vger.kernel.org +Description: + Read-only interface that reports the vector of SEV-SNP + firmware vulnerability mitigations supported by the firmware. + +What: /sys/firmware/sev/vulnerabilities/verified_mitigations +Date: June 2026 +Contact: linux-crypto@vger.kernel.org +Description: + Read/write interface that reports the vector of SEV-SNP + firmware vulnerability mitigations already verified by the + firmware. Writing a vector value requests the firmware to + VERIFY the corresponding mitigation bit(s). + + The list of supported mitigations and the meaning of each + vector bit are both platform- and bug-specific and are + published as part of the AMD Security Bulletin. diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index ca473ca198b8..8be4dab05cbb 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -245,6 +245,7 @@ static int sev_cmd_buffer_len(int cmd) case SEV_CMD_SNP_LAUNCH_FINISH: return sizeof(struct sev_data_snp_launch_finish); case SEV_CMD_SNP_DBG_DECRYPT: return sizeof(struct sev_data_snp_dbg); case SEV_CMD_SNP_DBG_ENCRYPT: return sizeof(struct sev_data_snp_dbg); + case SEV_CMD_SNP_VERIFY_MITIGATION: return sizeof(struct sev_data_snp_verify_mitigation); case SEV_CMD_SNP_PAGE_UNSMASH: return sizeof(struct sev_data_snp_page_unsmash); case SEV_CMD_SNP_PLATFORM_STATUS: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_GUEST_REQUEST: return sizeof(struct sev_data_snp_guest_request); @@ -1352,6 +1353,162 @@ static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg) return 0; } +#ifdef CONFIG_SYSFS +static int snp_verify_mitigation(u16 command, u64 vector, + struct sev_data_snp_verify_mitigation_dst *dst) +{ + struct sev_data_snp_verify_mitigation_dst *mit_dst = NULL; + struct sev_data_snp_verify_mitigation data = {0}; + struct sev_device *sev = psp_master->sev_data; + int ret, error = 0; + + mit_dst = snp_alloc_firmware_page(GFP_KERNEL | __GFP_ZERO); + if (!mit_dst) + return -ENOMEM; + + data.length = sizeof(data); + data.subcommand = command; + data.vector = vector; + data.dst_paddr = __psp_pa(mit_dst); + data.dst_paddr_en = true; + + ret = sev_do_cmd(SEV_CMD_SNP_VERIFY_MITIGATION, &data, &error); + if (!ret) + memcpy(dst, mit_dst, sizeof(*mit_dst)); + else + dev_err(sev->dev, "SNP_VERIFY_MITIGATION command failed, ret = %d, error = %#x\n", + ret, error); + + snp_free_firmware_page(mit_dst); + + return ret; +} + +static ssize_t supported_mitigations_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct sev_data_snp_verify_mitigation_dst dst; + int ret; + + ret = snp_verify_mitigation(SNP_MIT_SUBCMD_REQ_STATUS, 0, &dst); + if (ret) + return ret; + + return sysfs_emit(buf, "0x%llx\n", dst.mit_supported_vector); +} + +static struct kobj_attribute supported_attr = + __ATTR_RO_MODE(supported_mitigations, 0400); + +static ssize_t verified_mitigations_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct sev_data_snp_verify_mitigation_dst dst; + int ret; + + ret = snp_verify_mitigation(SNP_MIT_SUBCMD_REQ_STATUS, 0, &dst); + if (ret) + return ret; + + return sysfs_emit(buf, "0x%llx\n", dst.mit_verified_vector); +} + +static ssize_t verified_mitigations_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct sev_data_snp_verify_mitigation_dst dst; + struct sev_device *sev = psp_master->sev_data; + u64 vector; + int ret; + + ret = kstrtoull(buf, 0, &vector); + if (ret) + return ret; + + /* + * The firmware verifies a single mitigation per call. Reject vectors + * with more than one bit set early to avoid a guaranteed-to-fail call + */ + if (hweight64(vector) != 1) + return -EINVAL; + + ret = snp_verify_mitigation(SNP_MIT_SUBCMD_REQ_VERIFY, vector, &dst); + if (ret) + return ret; + + if (dst.mit_failure_status) { + dev_err(sev->dev, "Verify Mitigation - failure status: 0x%x\n", + dst.mit_failure_status); + return -EINVAL; + } + + return count; +} + +static struct kobj_attribute verified_attr = + __ATTR_RW_MODE(verified_mitigations, 0600); + +static struct attribute *mitigation_attrs[] = { + &supported_attr.attr, + &verified_attr.attr, + NULL +}; + +static const struct attribute_group mit_attr_group = { + .attrs = mitigation_attrs, +}; + +static void sev_snp_register_verify_mitigation(struct sev_device *sev) +{ + int rc; + + if (!(sev->snp_feat_info_0.ecx & SNP_VERIFY_MITIGATION_SUPPORTED) || + sev->verify_mit) + return; + + if (!sev->sev_kobj) { + sev->sev_kobj = kobject_create_and_add("sev", firmware_kobj); + if (!sev->sev_kobj) + return; + } + + sev->verify_mit = kobject_create_and_add("vulnerabilities", sev->sev_kobj); + if (!sev->verify_mit) + goto err_sev_kobj; + + rc = sysfs_create_group(sev->verify_mit, &mit_attr_group); + if (rc) + goto err_verify_mit; + + return; + +err_verify_mit: + kobject_put(sev->verify_mit); + sev->verify_mit = NULL; +err_sev_kobj: + kobject_put(sev->sev_kobj); + sev->sev_kobj = NULL; +} + +static void sev_snp_unregister_verify_mitigation(struct sev_device *sev) +{ + if (sev->verify_mit) { + sysfs_remove_group(sev->verify_mit, &mit_attr_group); + kobject_put(sev->verify_mit); + sev->verify_mit = NULL; + } + + if (sev->sev_kobj) { + kobject_put(sev->sev_kobj); + sev->sev_kobj = NULL; + } +} +#else // CONFIG_SYSFS +static void sev_snp_register_verify_mitigation(struct sev_device *sev) { } +static void sev_snp_unregister_verify_mitigation(struct sev_device *sev) { } +#endif // CONFIG_SYSFS + static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) { struct sev_data_range_list *snp_range_list __free(kfree) = NULL; @@ -1675,6 +1832,17 @@ int sev_platform_init(struct sev_platform_init_args *args) rc = _sev_platform_init_locked(args); mutex_unlock(&sev_cmd_mutex); + /* + * Register the sysfs interface outside the sev_cmd_mutex. The + * _show()/_store() handlers issue SEV commands that acquire the + * sev_cmd_mutex, so creating (and on the shutdown path, removing) the + * sysfs group must stay outside that lock. sysfs provides its own + * synchronization between group creation/removal and concurrent + * attribute access. + */ + if (!rc) + sev_snp_register_verify_mitigation(psp_master->sev_data); + return rc; } EXPORT_SYMBOL_GPL(sev_platform_init); @@ -2769,6 +2937,15 @@ static void sev_firmware_shutdown(struct sev_device *sev) if (sev->tio_status) sev_tsm_uninit(sev); + /* + * Remove the sysfs interface before taking the sev_cmd_mutex. + * sysfs_remove_group() waits for in-flight _show()/_store() handlers + * to drain, and those handlers issue SNP_VERIFY_MITIGATION via + * sev_do_cmd() which acquires the sev_cmd_mutex. Removing the group + * while holding the mutex could therefore deadlock. + */ + sev_snp_unregister_verify_mitigation(sev); + mutex_lock(&sev_cmd_mutex); __sev_firmware_shutdown(sev, false); diff --git a/drivers/crypto/ccp/sev-dev.h b/drivers/crypto/ccp/sev-dev.h index b1cd556bbbf6..d5e596606def 100644 --- a/drivers/crypto/ccp/sev-dev.h +++ b/drivers/crypto/ccp/sev-dev.h @@ -59,6 +59,9 @@ struct sev_device { bool snp_initialized; + struct kobject *sev_kobj; + struct kobject *verify_mit; + struct sev_user_data_status sev_plat_status; struct sev_user_data_snp_status snp_plat_status; diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h index ce16bbc0b308..03a79786df1d 100644 --- a/include/linux/psp-sev.h +++ b/include/linux/psp-sev.h @@ -129,6 +129,7 @@ enum sev_cmd { SEV_CMD_SNP_LAUNCH_FINISH = 0x0A2, SEV_CMD_SNP_DBG_DECRYPT = 0x0B0, SEV_CMD_SNP_DBG_ENCRYPT = 0x0B1, + SEV_CMD_SNP_VERIFY_MITIGATION = 0x0B2, SEV_CMD_SNP_PAGE_SWAP_OUT = 0x0C0, SEV_CMD_SNP_PAGE_SWAP_IN = 0x0C1, SEV_CMD_SNP_PAGE_MOVE = 0x0C2, @@ -898,10 +899,60 @@ struct snp_feature_info { #define SNP_CIPHER_TEXT_HIDING_SUPPORTED BIT(3) #define SNP_AES_256_XTS_POLICY_SUPPORTED BIT(4) #define SNP_CXL_ALLOW_POLICY_SUPPORTED BIT(5) +#define SNP_VERIFY_MITIGATION_SUPPORTED BIT(13) /* Feature bits in EBX */ #define SNP_SEV_TIO_SUPPORTED BIT(1) +#define SNP_MIT_SUBCMD_REQ_STATUS 0x0 +#define SNP_MIT_SUBCMD_REQ_VERIFY 0x1 + +/** + * struct sev_data_snp_verify_mitigation - SNP_VERIFY_MITIGATION command params + * + * @length: Length of the command buffer read by the PSP + * @subcommand: Mitigation sub-command for the firmware to execute. + * REQ_STATUS: 0x0 - Request status about currently supported and + * verified mitigations + * REQ_VERIFY: 0x1 - Request to initiate verification mitigation + * operation on a specific mitigation + * @rsvd: Reserved + * @vector: Bit specifying the vulnerability mitigation to process + * @dst_paddr_en: Destination paddr enabled + * @src_paddr_en: Source paddr enabled + * @rsvd1: Reserved + * @rsvd2: Reserved + * @src_paddr: Source address for optional input data + * @dst_paddr: Destination address to write the result + * @rsvd3: Reserved + */ +struct sev_data_snp_verify_mitigation { + u32 length; + u16 subcommand; + u16 rsvd; + u64 vector; + u32 dst_paddr_en : 1, + src_paddr_en : 1, + rsvd1 : 30; + u8 rsvd2[4]; + u64 src_paddr; + u64 dst_paddr; + u8 rsvd3[24]; +} __packed; + +/** + * struct sev_data_snp_verify_mitigation_dst - mitigation result vectors + * + * @mit_verified_vector: Bit vector of vulnerability mitigations verified + * @mit_supported_vector: Bit vector of vulnerability mitigations supported + * @mit_failure_status: Status of the verification operation + */ +struct sev_data_snp_verify_mitigation_dst { + u64 mit_verified_vector; /* OUT */ + u64 mit_supported_vector; /* OUT */ + u32 mit_failure_status; /* OUT */ +} __packed; + /** * struct sev_snp_tcb_version_genoa_milan * -- cgit From 2f204fe718f5bf519013cc2536ad7bb2cbb51661 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 22 Jun 2026 16:48:03 -0700 Subject: crypto: af_alg - Add af_alg_restrict sysctl, defaulting to 1 AF_ALG is a frequent source of vulnerabilities and a maintenance nightmare. It exposes far more functionality to userspace than ever should have been exposed, especially to unprivileged processes. Recent exploits have targeted kernel internal implementation details like "authencesn" that have zero use case for userspace access. Fortunately, AF_ALG is rarely used in practice, as userspace crypto libraries exist. And when it is used, only some functionality is known to be used, and many users are known to hold capabilities already. iwd for example requires CAP_NET_ADMIN and has a known algorithm list (https://lore.kernel.org/linux-crypto/bcbbef00-5881-421b-8892-7be6c04b832d@gmail.com/). Thus, let's restrict the set of allowed algorithms by default, depending on the capabilities held. Add a sysctl /proc/sys/crypto/af_alg_restrict with meaning: 0: unrestricted 1: limited functionality 2: completely disabled Set the default value to 1, which enables an algorithm allowlist for unprivileged processes and a slightly longer allowlist for privileged processes. Note that the list may be tweaked in the future. However, the common use cases such as iwd and bluez are taken into account already. I've tested that iwd still works with the default value of 1. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu --- Documentation/admin-guide/sysctl/crypto.rst | 36 +++++++++++++++ Documentation/crypto/userspace-if.rst | 13 ++++-- crypto/af_alg.c | 72 ++++++++++++++++++++++++++--- crypto/algif_aead.c | 11 +++++ crypto/algif_hash.c | 24 ++++++++++ crypto/algif_rng.c | 9 ++++ crypto/algif_skcipher.c | 20 ++++++++ include/crypto/if_alg.h | 8 ++++ 8 files changed, 184 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/admin-guide/sysctl/crypto.rst b/Documentation/admin-guide/sysctl/crypto.rst index b707bd314a64..9a1bd53287f4 100644 --- a/Documentation/admin-guide/sysctl/crypto.rst +++ b/Documentation/admin-guide/sysctl/crypto.rst @@ -7,6 +7,42 @@ kernel configuration: .. contents:: :local: +.. _af_alg_restrict: + +af_alg_restrict +=============== + +Controls the level of restriction of AF_ALG. + +AF_ALG is a deprecated and rarely-used userspace interface that is a +frequent source of vulnerabilities. It also unnecessarily exposes a +large number of kernel implementation details. For more information +about AF_ALG, see :ref:`Documentation/crypto/userspace-if.rst +`. + +Starting in Linux v7.3, AF_ALG supports only a limited set of +algorithms by default. This sysctl allows the system administrator to +remove this restriction when needed for compatibility reasons, or to +go further and disable AF_ALG entirely. The default value is 1. + +=== ================================================================== +0 AF_ALG is unrestricted. + +1 AF_ALG is supported with a limited list of algorithms. The list + is designed for compatibility with known users such as iwd and + bluez that haven't yet been fixed to use userspace crypto code. + + Specifically, there is an allowlist for unprivileged processes + and a somewhat longer allowlist for processes that hold + CAP_SYS_ADMIN or CAP_NET_ADMIN in the initial user namespace. + + Attempts to bind() an AF_ALG socket with a disallowed algorithm + fail with ENOENT. + +2 AF_ALG is completely disabled. Attempts to create an AF_ALG + socket fail with EAFNOSUPPORT. +=== ================================================================== + fips_enabled ============ diff --git a/Documentation/crypto/userspace-if.rst b/Documentation/crypto/userspace-if.rst index ab93300c8e04..d6194346e366 100644 --- a/Documentation/crypto/userspace-if.rst +++ b/Documentation/crypto/userspace-if.rst @@ -1,3 +1,5 @@ +.. _crypto_userspace_interface: + User Space Interface ==================== @@ -12,9 +14,14 @@ AF_ALG is insecure and is deprecated. Originally added to the kernel in 2010, most kernel developers now consider it to be a mistake. Support for hardware accelerators, which was the original purpose of AF_ALG, has been removed. -AF_ALG continues to be supported only for backwards compatibility. On systems -where no programs using AF_ALG remain, the support for it should be disabled by -disabling ``CONFIG_CRYPTO_USER_API_*``. +AF_ALG continues to be supported only for backwards compatibility. + +Starting in Linux v7.3, the set of algorithms supported by AF_ALG is limited by +default. See :ref:`/proc/sys/crypto/af_alg_restrict `. + +On systems where no programs using AF_ALG remain, the support for it should be +disabled entirely by setting ``/proc/sys/crypto/af_alg_restrict`` to 2 or by +disabling ``CONFIG_CRYPTO_USER_API_*`` in the kernel configuration. Deprecation ----------- diff --git a/crypto/af_alg.c b/crypto/af_alg.c index cce000e8590e..34b801568fba 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -22,10 +23,28 @@ #include #include #include +#include +#include #include #include #include +static int af_alg_restrict = 1; + +static const struct ctl_table af_alg_table[] = { + { + .procname = "af_alg_restrict", + .data = &af_alg_restrict, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_TWO, + }, +}; + +static struct ctl_table_header *af_alg_header; + struct alg_type_list { const struct af_alg_type *type; struct list_head list; @@ -110,6 +129,39 @@ int af_alg_unregister_type(const struct af_alg_type *type) } EXPORT_SYMBOL_GPL(af_alg_unregister_type); +static bool af_alg_capable(void) +{ + return ns_capable_noaudit(&init_user_ns, CAP_NET_ADMIN) || + capable(CAP_SYS_ADMIN); +} + +int af_alg_check_restriction(const char *name, + const struct af_alg_allowlist_entry allowlist[]) +{ + int level = READ_ONCE(af_alg_restrict); + + if (level == 0) + return 0; + if (level == 1) { + for (const struct af_alg_allowlist_entry *ent = allowlist; + ent->name; ent++) { + if (strcmp(name, ent->name) == 0 && + (!ent->privileged || af_alg_capable())) + return 0; + } + } + /* + * Use -ENOENT (the error code for "algorithm not found") instead of + * -EACCES or -EPERM, for the highest chance of correctly triggering + * fallback code paths in userspace programs. + * + * Don't log a warning, since it would be noisy. iwd tries to bind a + * bunch of algorithms that it never uses. + */ + return -ENOENT; +} +EXPORT_SYMBOL_GPL(af_alg_check_restriction); + static void alg_do_release(const struct af_alg_type *type, void *private) { if (!type) @@ -506,6 +558,9 @@ static int alg_create(struct net *net, struct socket *sock, int protocol, struct sock *sk; int err; + if (READ_ONCE(af_alg_restrict) == 2) + return -EAFNOSUPPORT; + if (sock->type != SOCK_SEQPACKET) return -ESOCKTNOSUPPORT; if (protocol != 0) @@ -1222,27 +1277,32 @@ EXPORT_SYMBOL_GPL(af_alg_get_rsgl); static int __init af_alg_init(void) { - int err = proto_register(&alg_proto, 0); + int err; + + af_alg_header = register_sysctl("crypto", af_alg_table); + err = proto_register(&alg_proto, 0); if (err) - goto out; + goto out_unregister_sysctl; err = sock_register(&alg_family); - if (err != 0) + if (err) goto out_unregister_proto; -out: - return err; + return 0; out_unregister_proto: proto_unregister(&alg_proto); - goto out; +out_unregister_sysctl: + unregister_sysctl_table(af_alg_header); + return err; } static void __exit af_alg_exit(void) { sock_unregister(PF_ALG); proto_unregister(&alg_proto); + unregister_sysctl_table(af_alg_header); } module_init(af_alg_init); diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 787aac8aeb24..b9217f9086aa 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -34,6 +34,11 @@ #include #include +static const struct af_alg_allowlist_entry aead_allowlist[] = { + { "ccm(aes)", true }, /* bluez */ + {}, +}; + static inline bool aead_sufficient_data(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); @@ -344,6 +349,12 @@ static struct proto_ops algif_aead_ops_nokey = { static void *aead_bind(const char *name) { + int err; + + err = af_alg_check_restriction(name, aead_allowlist); + if (err) + return ERR_PTR(err); + return crypto_alloc_aead(name, 0, AF_ALG_CRYPTOAPI_MASK); } diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 5452ad6c1506..a8d958d51ece 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -16,6 +16,24 @@ #include #include +static const struct af_alg_allowlist_entry hash_allowlist[] = { + { "cmac(aes)", true }, /* iwd, bluez */ + { "hmac(md5)", true }, /* iwd */ + { "hmac(sha1)", true }, /* iwd */ + { "hmac(sha224)", true }, /* iwd */ + { "hmac(sha256)", true }, /* iwd */ + { "hmac(sha384)", true }, /* iwd */ + { "hmac(sha512)", true }, /* iwd, sha512hmac */ + { "md4", true }, /* iwd */ + { "md5", true }, /* iwd */ + { "sha1", false }, /* iwd, iproute2 < 7.0 */ + { "sha224", true }, /* iwd */ + { "sha256", true }, /* iwd */ + { "sha384", true }, /* iwd */ + { "sha512", true }, /* iwd */ + {}, +}; + struct hash_ctx { struct af_alg_sgl sgl; @@ -382,6 +400,12 @@ static struct proto_ops algif_hash_ops_nokey = { static void *hash_bind(const char *name) { + int err; + + err = af_alg_check_restriction(name, hash_allowlist); + if (err) + return ERR_PTR(err); + return crypto_alloc_ahash(name, 0, AF_ALG_CRYPTOAPI_MASK); } diff --git a/crypto/algif_rng.c b/crypto/algif_rng.c index 4dfe7899f8fa..bd522915d56d 100644 --- a/crypto/algif_rng.c +++ b/crypto/algif_rng.c @@ -50,6 +50,10 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Stephan Mueller "); MODULE_DESCRIPTION("User-space interface for random number generators"); +static const struct af_alg_allowlist_entry rng_allowlist[] = { + {}, +}; + struct rng_ctx { #define MAXSIZE 128 unsigned int len; @@ -201,6 +205,11 @@ static void *rng_bind(const char *name) { struct rng_parent_ctx *pctx; struct crypto_rng *rng; + int err; + + err = af_alg_check_restriction(name, rng_allowlist); + if (err) + return ERR_PTR(err); pctx = kzalloc_obj(*pctx); if (!pctx) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index df20bdfe1f1f..2b8069667974 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -34,6 +34,20 @@ #include #include +static const struct af_alg_allowlist_entry skcipher_allowlist[] = { + { "adiantum(xchacha12,aes)", false }, /* cryptsetup */ + { "adiantum(xchacha20,aes)", false }, /* cryptsetup */ + { "cbc(aes)", true }, /* iwd */ + { "cbc(des)", true }, /* iwd */ + { "cbc(des3_ede)", true }, /* iwd */ + { "ctr(aes)", true }, /* iwd */ + { "ecb(aes)", true }, /* iwd, bluez */ + { "ecb(des)", true }, /* iwd */ + { "hctr2(aes)", false }, /* cryptsetup */ + { "xts(aes)", false }, /* cryptsetup benchmark */ + {}, +}; + static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { @@ -309,6 +323,12 @@ static struct proto_ops algif_skcipher_ops_nokey = { static void *skcipher_bind(const char *name) { + int err; + + err = af_alg_check_restriction(name, skcipher_allowlist); + if (err) + return ERR_PTR(err); + return crypto_alloc_skcipher(name, 0, AF_ALG_CRYPTOAPI_MASK); } diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 7643ba954125..4e9ed8e73403 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -161,9 +161,17 @@ struct af_alg_ctx { unsigned int inflight; }; +struct af_alg_allowlist_entry { + const char *name; + bool privileged; +}; + int af_alg_register_type(const struct af_alg_type *type); int af_alg_unregister_type(const struct af_alg_type *type); +int af_alg_check_restriction(const char *name, + const struct af_alg_allowlist_entry allowlist[]); + int af_alg_release(struct socket *sock); void af_alg_release_parent(struct sock *sk); int af_alg_accept(struct sock *sk, struct socket *newsock, -- cgit From d529495c991a2b7d07c01622e8b3cb5ec066acee Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sat, 27 Jun 2026 19:50:15 +0200 Subject: dt-bindings: arm: qcom,ids: Add SoC ID for Snapdragon SDM 850 Add SoC ID for Qualcomm Snapdragon SDM850. Signed-off-by: David Heidelberg Reviewed-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260627-sda850-v2-1-44bf46ade42e@ixit.cz Signed-off-by: Bjorn Andersson --- include/dt-bindings/arm/qcom,ids.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index 1af73c0ad41c..2cf433f61d5e 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -184,6 +184,7 @@ #define QCOM_ID_IPQ8078 344 #define QCOM_ID_SDM636 345 #define QCOM_ID_SDA636 346 +#define QCOM_ID_SDM850 348 #define QCOM_ID_SDM632 349 #define QCOM_ID_SDA632 350 #define QCOM_ID_SDA450 351 -- cgit From e21d3b2e5c4387b243424daa0ff2d45c6fbf73e2 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:29 +0300 Subject: soc: qcom: ubwc: set min_acc length to 64 for all UBWC 1.0 targets According to the documentation, the MAL should be set for all UBWC 1.0 targets, no matter what is the version of the UBWC decoders are present on the device. The helper comes from DPU / GPU world, where there was no separate bit to control MAL before UBWC 2.0. As the helper is now being used by other drivers too, correct the helper to return the correct MAL value (Iris doesn't support UBWC 1.0 devices for now, so there is no changes of the behaviour). Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-22-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/ubwc.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 83d2c2a7116c..0b5aa9d0343b 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -75,14 +75,9 @@ static inline bool qcom_ubwc_get_ubwc_mode(const struct qcom_ubwc_cfg_data *cfg) return ret; } -/* - * This is the best guess, based on the MDSS driver, which worked so far. - */ static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data *cfg) { - return cfg->ubwc_enc_version == UBWC_1_0 && - (cfg->ubwc_dec_version == UBWC_2_0 || - cfg->ubwc_dec_version == UBWC_3_0); + return cfg->ubwc_enc_version == UBWC_1_0; } static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg) -- cgit From e7012ac2fc000fe7ae3e58a7449b80fa955b124a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:30 +0300 Subject: soc: qcom: ubwc: drop ubwc_dec_version The ubwc_dec_version field has been inherited from the MDSS driver and it is equal to the version of the UBWC decoder in the display block only. Other IP Cores can have different UBWC decoders and so the version would vary between blocks. As the value is no longer used as is not relevant to other UBWC database consumers, drop it from the UBWC database. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-23-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 23 ----------------------- include/linux/soc/qcom/ubwc.h | 2 -- 2 files changed, 25 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 3fe47d8f0f63..1344cda0fb75 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -18,7 +18,6 @@ static const struct qcom_ubwc_cfg_data no_ubwc_data = { static const struct qcom_ubwc_cfg_data eliza_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_dec_version = UBWC_5_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -29,7 +28,6 @@ static const struct qcom_ubwc_cfg_data eliza_data = { static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_enc_version = UBWC_6_0, - .ubwc_dec_version = UBWC_6_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -39,7 +37,6 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { static const struct qcom_ubwc_cfg_data msm8937_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_1_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -48,7 +45,6 @@ static const struct qcom_ubwc_cfg_data msm8937_data = { static const struct qcom_ubwc_cfg_data msm8998_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_1_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -62,7 +58,6 @@ static const struct qcom_ubwc_cfg_data qcm2290_data = { static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, .highest_bank_bit = 13, @@ -71,7 +66,6 @@ static const struct qcom_ubwc_cfg_data sa8775p_data = { static const struct qcom_ubwc_cfg_data sar2130p_data = { .ubwc_enc_version = UBWC_3_0, /* 4.0.2 in hw */ - .ubwc_dec_version = UBWC_4_3, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -81,7 +75,6 @@ static const struct qcom_ubwc_cfg_data sar2130p_data = { static const struct qcom_ubwc_cfg_data sc7180_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -90,7 +83,6 @@ static const struct qcom_ubwc_cfg_data sc7180_data = { static const struct qcom_ubwc_cfg_data sc7280_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -100,7 +92,6 @@ static const struct qcom_ubwc_cfg_data sc7280_data = { static const struct qcom_ubwc_cfg_data sc8180x_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_dec_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, @@ -109,7 +100,6 @@ static const struct qcom_ubwc_cfg_data sc8180x_data = { static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -119,7 +109,6 @@ static const struct qcom_ubwc_cfg_data sc8280xp_data = { static const struct qcom_ubwc_cfg_data sdm670_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, @@ -127,7 +116,6 @@ static const struct qcom_ubwc_cfg_data sdm670_data = { static const struct qcom_ubwc_cfg_data sdm845_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, @@ -135,7 +123,6 @@ static const struct qcom_ubwc_cfg_data sdm845_data = { static const struct qcom_ubwc_cfg_data sm6115_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -145,7 +132,6 @@ static const struct qcom_ubwc_cfg_data sm6115_data = { static const struct qcom_ubwc_cfg_data sm6125_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -154,7 +140,6 @@ static const struct qcom_ubwc_cfg_data sm6125_data = { static const struct qcom_ubwc_cfg_data sm6150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, @@ -162,7 +147,6 @@ static const struct qcom_ubwc_cfg_data sm6150_data = { static const struct qcom_ubwc_cfg_data sm6350_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -171,7 +155,6 @@ static const struct qcom_ubwc_cfg_data sm6350_data = { static const struct qcom_ubwc_cfg_data sm7150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, @@ -179,7 +162,6 @@ static const struct qcom_ubwc_cfg_data sm7150_data = { static const struct qcom_ubwc_cfg_data sm8150_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_dec_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, @@ -187,7 +169,6 @@ static const struct qcom_ubwc_cfg_data sm8150_data = { static const struct qcom_ubwc_cfg_data sm8250_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -198,7 +179,6 @@ static const struct qcom_ubwc_cfg_data sm8250_data = { static const struct qcom_ubwc_cfg_data sm8350_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -209,7 +189,6 @@ static const struct qcom_ubwc_cfg_data sm8350_data = { static const struct qcom_ubwc_cfg_data sm8550_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_3, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -220,7 +199,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_dec_version = UBWC_5_0, .ubwc_swizzle = 6, .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ @@ -230,7 +208,6 @@ static const struct qcom_ubwc_cfg_data sm8750_data = { static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_dec_version = UBWC_5_0, .ubwc_swizzle = 0, .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 0b5aa9d0343b..c3f9efae5db8 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -13,8 +13,6 @@ struct qcom_ubwc_cfg_data { u32 ubwc_enc_version; - /* Can be read from MDSS_BASE + 0x58 */ - u32 ubwc_dec_version; /** * @ubwc_swizzle: Whether to enable level 1, 2 & 3 bank swizzling. -- cgit From 5a9f4c535ae05facd4f6ef116b18062f9f2ad768 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:31 +0300 Subject: soc: qcom: ubwc: drop ubwc_bank_spread According to the documentation, UBWC bank spreading should be enabled for all targets. It's just not all targets have separate bit to control it. Drop the bit from the database and make the helper always return true. If we need to change it later, the helper can be adjusted according to the programming guides. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-24-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 14 -------------- include/linux/soc/qcom/ubwc.h | 3 +-- 2 files changed, 1 insertion(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 1344cda0fb75..35cde4e9a238 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -20,7 +20,6 @@ static const struct qcom_ubwc_cfg_data eliza_data = { .ubwc_enc_version = UBWC_5_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 14 for LP_DDR4 */ .highest_bank_bit = 15, .macrotile_mode = true, @@ -30,7 +29,6 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_enc_version = UBWC_6_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 16, .macrotile_mode = true, }; @@ -59,7 +57,6 @@ static const struct qcom_ubwc_cfg_data qcm2290_data = { static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 13, .macrotile_mode = true, }; @@ -68,7 +65,6 @@ static const struct qcom_ubwc_cfg_data sar2130p_data = { .ubwc_enc_version = UBWC_3_0, /* 4.0.2 in hw */ .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 13, .macrotile_mode = true, }; @@ -77,7 +73,6 @@ static const struct qcom_ubwc_cfg_data sc7180_data = { .ubwc_enc_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, }; @@ -85,7 +80,6 @@ static const struct qcom_ubwc_cfg_data sc7280_data = { .ubwc_enc_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, .macrotile_mode = true, }; @@ -102,7 +96,6 @@ static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 16, .macrotile_mode = true, }; @@ -126,7 +119,6 @@ static const struct qcom_ubwc_cfg_data sm6115_data = { .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, }; @@ -149,7 +141,6 @@ static const struct qcom_ubwc_cfg_data sm6350_data = { .ubwc_enc_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, }; @@ -171,7 +162,6 @@ static const struct qcom_ubwc_cfg_data sm8250_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -181,7 +171,6 @@ static const struct qcom_ubwc_cfg_data sm8350_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -191,7 +180,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -200,7 +188,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_enc_version = UBWC_5_0, .ubwc_swizzle = 6, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -209,7 +196,6 @@ static const struct qcom_ubwc_cfg_data sm8750_data = { static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, .ubwc_swizzle = 0, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index c3f9efae5db8..254721f5ea3c 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -33,7 +33,6 @@ struct qcom_ubwc_cfg_data { * DDR bank. This should ideally use DRAM type detection. */ int highest_bank_bit; - bool ubwc_bank_spread; /** * @macrotile_mode: Macrotile Mode @@ -85,7 +84,7 @@ static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg) { - return cfg->ubwc_bank_spread; + return true; } static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) -- cgit From fc54caf87a5a7289502ecbcdc80bb71f7bb5de9c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:32 +0300 Subject: soc: qcom: ubwc: drop macrotile_mode from the database All the users have been migrated to using qcom_ubwc_macrotile_mode() instead of reading the raw value from the config structure. Drop the field from struct qcom_ubwc_cfg_data and replace it with the calculated value. Split single UBWC_3_0 into UBWC_3_0 (no macrotile mode) and UBWC_3_1 (with macrotile mode). Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-25-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 18 +++--------------- include/linux/soc/qcom/ubwc.h | 18 ++++++++---------- 2 files changed, 11 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 35cde4e9a238..8dd91d0b3974 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -22,7 +22,6 @@ static const struct qcom_ubwc_cfg_data eliza_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 14 for LP_DDR4 */ .highest_bank_bit = 15, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data kaanapali_data = { @@ -30,7 +29,6 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data msm8937_data = { @@ -58,15 +56,13 @@ static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 13, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sar2130p_data = { - .ubwc_enc_version = UBWC_3_0, /* 4.0.2 in hw */ + .ubwc_enc_version = UBWC_3_1, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 13, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sc7180_data = { @@ -77,19 +73,17 @@ static const struct qcom_ubwc_cfg_data sc7180_data = { }; static const struct qcom_ubwc_cfg_data sc7280_data = { - .ubwc_enc_version = UBWC_3_0, + .ubwc_enc_version = UBWC_3_1, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sc8180x_data = { - .ubwc_enc_version = UBWC_3_0, + .ubwc_enc_version = UBWC_3_1, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sc8280xp_data = { @@ -97,7 +91,6 @@ static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sdm670_data = { @@ -164,7 +157,6 @@ static const struct qcom_ubwc_cfg_data sm8250_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sm8350_data = { @@ -173,7 +165,6 @@ static const struct qcom_ubwc_cfg_data sm8350_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sm8550_data = { @@ -182,7 +173,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sm8750_data = { @@ -190,7 +180,6 @@ static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_swizzle = 6, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data glymur_data = { @@ -198,7 +187,6 @@ static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_swizzle = 0, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 254721f5ea3c..fee778360ac2 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -33,15 +33,6 @@ struct qcom_ubwc_cfg_data { * DDR bank. This should ideally use DRAM type detection. */ int highest_bank_bit; - - /** - * @macrotile_mode: Macrotile Mode - * - * Whether to use 4-channel macrotiling mode or the newer - * 8-channel macrotiling mode introduced in UBWC 3.1. 0 is - * 4-channel and 1 is 8-channel. - */ - bool macrotile_mode; }; #define UBWC_1_0 0x10000000 @@ -77,9 +68,16 @@ static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data return cfg->ubwc_enc_version == UBWC_1_0; } +/* + * @qcom_ubwc_macrotile_mode: whether to use 4-channel or 8-channel macrotiling + * + * The 8-channel macrotiling mode was introduced in UBWC 3.1. + * + * Returns: false for the 4-channel and true for 8-channel. + */ static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg) { - return cfg->macrotile_mode; + return cfg->ubwc_enc_version >= UBWC_3_1; } static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg) -- cgit From 17120525ff070da978effc009877bace3d788698 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:33 +0300 Subject: soc: qcom: ubwc: use fixed values for UBWC swizzle for UBWC < 4.0 UBWC devices before 4.0 use standard UBWC swizzle levels. As all the drivers now use the qcom_ubwc_swizzle() helper, move those values to the helper, leaving UBWC 4.0+ intact for now. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-26-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 34 ---------------------------------- include/linux/soc/qcom/ubwc.h | 33 ++++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 8dd91d0b3974..7e321389a399 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -33,17 +33,11 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { static const struct qcom_ubwc_cfg_data msm8937_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data msm8998_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, }; @@ -60,94 +54,66 @@ static const struct qcom_ubwc_cfg_data sa8775p_data = { static const struct qcom_ubwc_cfg_data sar2130p_data = { .ubwc_enc_version = UBWC_3_1, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 13, }; static const struct qcom_ubwc_cfg_data sc7180_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sc7280_data = { .ubwc_enc_version = UBWC_3_1, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sc8180x_data = { .ubwc_enc_version = UBWC_3_1, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sdm670_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sdm845_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, }; static const struct qcom_ubwc_cfg_data sm6115_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm6125_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm6150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm6350_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm7150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm8150_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, }; diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index fee778360ac2..7c9506741001 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -22,9 +22,6 @@ struct qcom_ubwc_cfg_data { * UBWC 4.0 adds the optional ability to disable levels 2 & 3. */ u32 ubwc_swizzle; -#define UBWC_SWIZZLE_ENABLE_LVL1 BIT(0) -#define UBWC_SWIZZLE_ENABLE_LVL2 BIT(1) -#define UBWC_SWIZZLE_ENABLE_LVL3 BIT(2) /** * @highest_bank_bit: Highest Bank Bit @@ -55,12 +52,7 @@ static inline const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void) static inline bool qcom_ubwc_get_ubwc_mode(const struct qcom_ubwc_cfg_data *cfg) { - bool ret = cfg->ubwc_enc_version == UBWC_1_0; - - if (ret && !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL1)) - pr_err("UBWC config discrepancy - level 1 swizzling disabled on UBWC 1.0\n"); - - return ret; + return cfg->ubwc_enc_version == UBWC_1_0; } static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data *cfg) @@ -85,8 +77,31 @@ static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg) return true; } +#define UBWC_SWIZZLE_ENABLE_LVL1 BIT(0) +#define UBWC_SWIZZLE_ENABLE_LVL2 BIT(1) +#define UBWC_SWIZZLE_ENABLE_LVL3 BIT(2) + +/** + * @qcom_ubwc_swizzle: Whether to enable level 1, 2 & 3 bank swizzling. + * + * UBWC 1.0 always enables all three levels. + * UBWC 2.0 removes level 1 bank swizzling, leaving levels 2 & 3. + * UBWC 4.0 adds the optional ability to disable levels 2 & 3. + */ static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) { + if (cfg->ubwc_enc_version == 0) + return 0; + + if (cfg->ubwc_enc_version == UBWC_1_0) + return UBWC_SWIZZLE_ENABLE_LVL1 | + UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3; + + if (cfg->ubwc_enc_version < UBWC_4_0) + return UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3; + return cfg->ubwc_swizzle; } -- cgit From 31e1c3248586dcc9aae42539152ab271dcfd37b6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:34 +0300 Subject: soc: qcom: ubwc: sort out the rest of the UBWC swizzle settings Sort out the remaining UBWC swizzle values, using flags to control whether level 2 and level 3 swizzling are enabled or not. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-27-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 16 +++------------- include/linux/soc/qcom/ubwc.h | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 7e321389a399..f27440d5c06f 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -18,16 +18,12 @@ static const struct qcom_ubwc_cfg_data no_ubwc_data = { static const struct qcom_ubwc_cfg_data eliza_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 14 for LP_DDR4 */ .highest_bank_bit = 15, }; static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_enc_version = UBWC_6_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, }; @@ -48,7 +44,7 @@ static const struct qcom_ubwc_cfg_data qcm2290_data = { static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, + .flags = UBWC_FLAG_DISABLE_SWIZZLE_LVL2, .highest_bank_bit = 13, }; @@ -119,38 +115,32 @@ static const struct qcom_ubwc_cfg_data sm8150_data = { static const struct qcom_ubwc_cfg_data sm8250_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sm8350_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sm8550_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_swizzle = 6, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_swizzle = 0, + .flags = UBWC_FLAG_DISABLE_SWIZZLE_LVL2 | + UBWC_FLAG_DISABLE_SWIZZLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 7c9506741001..a7372d9c25fb 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -14,15 +14,6 @@ struct qcom_ubwc_cfg_data { u32 ubwc_enc_version; - /** - * @ubwc_swizzle: Whether to enable level 1, 2 & 3 bank swizzling. - * - * UBWC 1.0 always enables all three levels. - * UBWC 2.0 removes level 1 bank swizzling, leaving levels 2 & 3. - * UBWC 4.0 adds the optional ability to disable levels 2 & 3. - */ - u32 ubwc_swizzle; - /** * @highest_bank_bit: Highest Bank Bit * @@ -30,6 +21,10 @@ struct qcom_ubwc_cfg_data { * DDR bank. This should ideally use DRAM type detection. */ int highest_bank_bit; + + unsigned int flags; +#define UBWC_FLAG_DISABLE_SWIZZLE_LVL2 BIT(0) +#define UBWC_FLAG_DISABLE_SWIZZLE_LVL3 BIT(1) }; #define UBWC_1_0 0x10000000 @@ -98,11 +93,16 @@ static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3; - if (cfg->ubwc_enc_version < UBWC_4_0) - return UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3; + u32 ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3; + + if (cfg->flags & UBWC_FLAG_DISABLE_SWIZZLE_LVL2) + ubwc_swizzle &= ~UBWC_SWIZZLE_ENABLE_LVL2; + + if (cfg->flags & UBWC_FLAG_DISABLE_SWIZZLE_LVL3) + ubwc_swizzle &= ~UBWC_SWIZZLE_ENABLE_LVL3; - return cfg->ubwc_swizzle; + return ubwc_swizzle; } static inline u32 qcom_ubwc_version_tag(const struct qcom_ubwc_cfg_data *cfg) -- cgit From e64e6b5dc86758c14ed28a6e85bf5d1b78146ed5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 2 Jul 2026 12:59:59 +0200 Subject: ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND DSM TRIM currently always emits a single 512-byte page of LBA Range Entries (ATA_MAX_TRIM_RNUM == 64 ranges), regardless of how many pages the device can accept in one DATA SET MANAGEMENT command. The maximum is reported by MAX PAGES PER DSM COMMAND (IDENTIFY DEVICE word 105). Honour it: size the TRIM descriptor as a whole number of 512-byte pages, bounded by that limit and by the logical sector size (the WRITE SAME data-out buffer is a single logical block). Build and transfer only as many pages as the request needs, and set the DSM COUNT field, qc->nbytes and the maximum WRITE SAME length in the Block Limits VPD page accordingly. Build the descriptor straight into the WRITE SAME data-out buffer using an atomic sg_miter mapping, instead of staging it in the shared ata_scsi_rbuf and copying it out. This removes the global ata_scsi_rbuf_lock and a memcpy from the TRIM path. While commit 9379e6b8e0f9 ("libata: Safely overwrite attached page in WRITE SAME xlat") replaced direct access to the data-out buffer with an intermediate step that writes the entries in the ata_scsi_rbuf buffer, this solution writes to the data-out buffer using sg_miter, which maps each segment with kmap_atomic (SG_MITER_ATOMIC), so it's highmem- and multi-segment-safe, and it's usable from the non-sleeping command-submission path (unlike the page_address() access that ata_scsi_rbuf originally replaced). A 512-byte-sector device still uses a single page, so its behaviour is unchanged. Add ata_id_dsm_max_pages() to read IDENTIFY DEVICE word 105. Reviewed-by: Hannes Reinecke Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-scsi.c | 135 ++++++++++++++++++++++++++++++++-------------- include/linux/ata.h | 13 +++++ 2 files changed, 107 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 97c959ef5114..5cddb63a6bc6 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2201,6 +2201,39 @@ static unsigned int ata_scsiop_inq_89(struct ata_device *dev, return get_unaligned_be16(&rbuf[2]) + 4; } +/** + * ata_dsm_trim_pages - maximum DSM TRIM payload for a device, in 512-byte pages + * @dev: ATA device the DATA SET MANAGEMENT TRIM command will be sent to + * + * A DATA SET MANAGEMENT TRIM payload is a list of 512-byte pages, each holding + * up to ATA_MAX_TRIM_RNUM (64) LBA Range Entries; the format is page-based and + * unrelated to the logical sector size. + * + * The logical sector size still bounds it, though: the descriptor is written + * directly into the WRITE SAME data-out buffer, which sd sizes to a single + * logical block, so it can hold at most sector_size / 512 pages. + * + * Return: the maximum number of 512-byte pages a single translated WRITE SAME + * command may send to @dev (never less than one), that is the smaller of: + * - MAX PAGES PER DSM COMMAND (IDENTIFY DEVICE word 105), when the device + * reports a non-zero limit; and + * - the logical sector size expressed in 512-byte pages (see above). + */ +static unsigned int ata_dsm_trim_pages(struct ata_device *dev) +{ + unsigned int sector_size = ata_id_logical_sector_size(dev->id); + unsigned int max_pages = ata_id_dsm_max_pages(dev->id); + unsigned int pages = sector_size / ATA_SECT_SIZE; + + /* If the device does not specify a limit, assume only a single page. */ + if (!max_pages) + max_pages = 1; + + pages = min_not_zero(pages, max_pages); + + return pages; +} + /** * ata_scsiop_inq_b0 - Simulate INQUIRY VPD page B0, Block Limits * @dev: Target device. @@ -2240,7 +2273,8 @@ static unsigned int ata_scsiop_inq_b0(struct ata_device *dev, * with the unmap bit set. */ if (ata_id_has_trim(dev->id)) { - u64 max_blocks = 65535 * ATA_MAX_TRIM_RNUM; + unsigned int max_pages = ata_dsm_trim_pages(dev); + u64 max_blocks = max_pages * ATA_MAX_TRIM_RNUM * (u64)U16_MAX; if (dev->quirks & ATA_QUIRK_MAX_TRIM_128M) max_blocks = 128 << (20 - SECTOR_SHIFT); @@ -3429,14 +3463,14 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) /** * ata_format_dsm_trim_descr() - SATL Write Same to DSM Trim * @cmd: SCSI command being translated - * @trmax: Maximum number of entries that will fit in sector_size bytes. + * @size: DSM TRIM payload size in bytes (a multiple of 512) * @sector: Starting sector * @count: Total Range of request in logical sectors * * Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted * descriptor. * - * Upto 64 entries of the format: + * The payload is a list of @size / 8 entries of the format: * 63:48 Range Length * 47:0 LBA * @@ -3445,35 +3479,45 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) * * NOTE: this is the same format as ADD LBA(S) TO NV CACHE PINNED SET * - * Return: Number of bytes copied into sglist. + * The descriptor is written straight into the WRITE SAME data-out buffer; + * ata_dsm_trim_pages() guarantees @size does not exceed that buffer (one + * logical block). An atomic sg_miter mapping is used so this works from the + * command submission path and, unlike page_address(), copes with a high + * memory payload. + * + * Return: Number of bytes written into the data-out buffer. */ -static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax, +static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, size_t size, u64 sector, u32 count) { - size_t len = ATA_SECT_SIZE; - size_t r; - __le64 *buf; - u32 i = 0; - unsigned long flags; + struct sg_mapping_iter miter; + size_t offset = 0; - BUILD_BUG_ON(ATA_SECT_SIZE > ATA_SCSI_RBUF_SIZE); + sg_miter_start(&miter, scsi_sglist(cmd), scsi_sg_count(cmd), + SG_MITER_TO_SG | SG_MITER_ATOMIC); + while (offset < size && sg_miter_next(&miter)) { + __le64 *buf = miter.addr; + size_t chunk = min_t(size_t, miter.length, size - offset); + unsigned int n = chunk / sizeof(__le64); + unsigned int i; - spin_lock_irqsave(&ata_scsi_rbuf_lock, flags); - buf = ((void *)ata_scsi_rbuf); - memset(buf, 0, len); - while (i < trmax) { - u64 entry = sector | - ((u64)(count > 0xffff ? 0xffff : count) << 48); - buf[i++] = __cpu_to_le64(entry); - if (count <= 0xffff) - break; - count -= 0xffff; - sector += 0xffff; + for (i = 0; i < n; i++) { + u64 entry = 0; + + if (count) { + u32 rlen = min_t(u32, count, 0xffff); + + entry = sector | ((u64)rlen << 48); + sector += rlen; + count -= rlen; + } + buf[i] = cpu_to_le64(entry); + } + offset += n * sizeof(__le64); } - r = sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buf, len); - spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags); + sg_miter_stop(&miter); - return r; + return offset; } /** @@ -3493,10 +3537,11 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) struct scsi_cmnd *scmd = qc->scsicmd; struct ata_device *dev = qc->dev; const u8 *cdb = scmd->cmnd; + unsigned int max_pages = ata_dsm_trim_pages(dev); + unsigned int n_pages; + size_t size; u64 block; u32 n_block; - const u32 trmax = ATA_MAX_TRIM_RNUM; - u32 size; u16 fp; u8 bp = 0xff; u8 unmap = cdb[1] & 0x8; @@ -3526,7 +3571,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) goto invalid_fld; } /* If the request is too large the cmd is invalid */ - if (n_block > 0xffff * trmax) { + if (n_block > max_pages * ATA_MAX_TRIM_RNUM * (u64)U16_MAX) { fp = 2; goto invalid_fld; } @@ -3539,31 +3584,39 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) goto invalid_param_len; /* - * The TRIM descriptor is a single 512-byte page, which is the maximum - * WRITE SAME length advertised in the Block Limits VPD page. For DATA - * SET MANAGEMENT TRIM the COUNT field (aka nsect) is the number of - * 512-byte blocks to be transferred. + * The DATA SET MANAGEMENT TRIM payload is a whole number of 512-byte + * pages (each holding up to ATA_MAX_TRIM_RNUM LBA Range Entries), + * independent of the logical sector size. Only use as many pages as + * are needed to describe the request, capped at max_pages. */ - size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); - if (size != ATA_SECT_SIZE) + n_pages = DIV_ROUND_UP(DIV_ROUND_UP(n_block, U16_MAX), + ATA_MAX_TRIM_RNUM); + n_pages = clamp(n_pages, 1U, max_pages); + size = (size_t)n_pages * ATA_SECT_SIZE; + + if (ata_format_dsm_trim_descr(scmd, size, block, n_block) != size) goto invalid_param_len; + /* + * For DATA SET MANAGEMENT TRIM the COUNT field (aka nsect) is the + * number of 512-byte pages to be transferred. + */ if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) { /* Newer devices support queued TRIM commands */ tf->protocol = ATA_PROT_NCQ; tf->command = ATA_CMD_FPDMA_SEND; tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f; tf->nsect = qc->hw_tag << 3; - tf->hob_feature = (size / 512) >> 8; - tf->feature = size / 512; + tf->hob_feature = n_pages >> 8; + tf->feature = n_pages; tf->auxiliary = 1; } else { tf->protocol = ATA_PROT_DMA; tf->hob_feature = 0; tf->feature = ATA_DSM_TRIM; - tf->hob_nsect = (size / 512) >> 8; - tf->nsect = size / 512; + tf->hob_nsect = n_pages >> 8; + tf->nsect = n_pages; tf->command = ATA_CMD_DSM; } @@ -3572,9 +3625,9 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) ata_qc_set_pc_nbytes(qc); /* - * The DSM TRIM payload is a single 512-byte page, which may be smaller - * than the WRITE SAME data-out buffer (one logical block); only - * transfer that page so the length matches the COUNT field. + * The DSM TRIM payload (size) may be smaller than the WRITE SAME + * data-out buffer (one logical block); only transfer the pages that + * were actually built so the transfer length matches the COUNT field. */ qc->nbytes = size; diff --git a/include/linux/ata.h b/include/linux/ata.h index 8fd48bcb2a46..ac5616a9668b 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -75,6 +75,7 @@ enum { ATA_ID_HW_CONFIG = 93, ATA_ID_SPG = 98, ATA_ID_LBA_CAPACITY_2 = 100, + ATA_ID_MAX_PAGES_PER_DSM = 105, ATA_ID_SECTOR_SIZE = 106, ATA_ID_WWN = 108, ATA_ID_LOGICAL_SECTOR_SIZE = 117, /* and 118 */ @@ -928,6 +929,18 @@ static inline bool ata_id_has_trim(const u16 *id) return false; } +static inline u16 ata_id_dsm_max_pages(const u16 *id) +{ + /* + * IDENTIFY DEVICE word 105: MAX PAGES PER DSM COMMAND. Maximum number + * of 512-byte pages of LBA Range Entries the device accepts in a + * single DATA SET MANAGEMENT command. Zero means the device does not + * specify a limit. The field is reserved unless TRIM is supported, so + * callers must gate on ata_id_has_trim(). + */ + return id[ATA_ID_MAX_PAGES_PER_DSM]; +} + static inline bool ata_id_has_zero_after_trim(const u16 *id) { /* DSM supported, deterministic read, and read zero after trim set */ -- cgit From 7bc99dff4d975e850ecb3393811c4264aee24335 Mon Sep 17 00:00:00 2001 From: Ahmed Yaseen Date: Tue, 19 May 2026 18:12:13 +0000 Subject: platform/x86: asus-armoury: gate PPT writes behind active fan curve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On models flagged with requires_fan_curve in the DMI power_data table (30 entries), the BIOS ACPI method SPLX only writes PPT values to the EC when the fan mode is set to Manual (FANM=4). FANM is set to 4 by the DEFC method when a custom fan curve is written. Without an active custom fan curve, the WMI DEVS call returns success but the firmware silently ignores the PPT value, so userspace observes no effect from its write. Gate writes to ASUS_WMI_DEVID_PPT_{PL1_SPL,PL2_SPPT,PL3_FPPT,APU_SPPT, PLAT_SPPT} on a check of asus_wmi_custom_fan_curve_is_enabled(), and return -EBUSY with a pr_warn_once() when no fan curve is active on an affected model. Export the helper from asus-wmi so asus-armoury can call it across module boundaries. Signed-off-by: Ahmed Yaseen Reviewed-by: Denis Benato Link: https://patch.msgid.link/20260519181155.46044-2-yaseen@ghoul.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.c | 20 ++++++++++++++++++++ drivers/platform/x86/asus-wmi.c | 24 ++++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 5 +++++ 3 files changed, 49 insertions(+) (limited to 'include') diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c index 495dc1e31d40..f2a880eb0cdf 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -93,6 +93,8 @@ struct asus_armoury_priv { u32 mini_led_dev_id; u32 gpu_mux_dev_id; + + bool requires_fan_curve; }; static struct asus_armoury_priv asus_armoury = { @@ -216,6 +218,22 @@ static int armoury_set_devstate(struct kobj_attribute *attr, u32 result; int err; + /* On some models, PPT changes require an active fan curve */ + if (asus_armoury.requires_fan_curve) { + switch (dev_id) { + case ASUS_WMI_DEVID_PPT_PL1_SPL: + case ASUS_WMI_DEVID_PPT_PL2_SPPT: + case ASUS_WMI_DEVID_PPT_PL3_FPPT: + case ASUS_WMI_DEVID_PPT_APU_SPPT: + case ASUS_WMI_DEVID_PPT_PLAT_SPPT: + if (!asus_wmi_custom_fan_curve_is_enabled()) { + pr_warn_once("PPT change requires an active fan curve on this model. Enable a custom fan curve first.\n"); + return -EBUSY; + } + break; + } + } + /* * Prevent developers from bricking devices or issuing dangerous * commands that can be difficult or impossible to recover from. @@ -1010,6 +1028,8 @@ static void init_rog_tunables(void) return; } + asus_armoury.requires_fan_curve = power_data->requires_fan_curve; + /* Initialize AC power tunables */ ac_limits = power_data->ac_data; if (ac_limits) { diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 3c9ef826551d..c7c8fcfc1d72 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -4053,6 +4053,30 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) return 0; } +/* + * Returns true if at least one custom fan curve is active + * + * Used by asus-armoury to check if PPT writes will be accepted by the BIOS + * on models that require an active fan curve for TDP changes. + */ +bool asus_wmi_custom_fan_curve_is_enabled(void) +{ + struct fan_curve_data *curves; + struct asus_wmi *asus; + + guard(spinlock_irqsave)(&asus_ref.lock); + asus = asus_ref.asus; + if (!asus) + return false; + + curves = asus->custom_fan_curves; + + return (asus->cpu_fan_curve_available && curves[FAN_CURVE_DEV_CPU].enabled) || + (asus->gpu_fan_curve_available && curves[FAN_CURVE_DEV_GPU].enabled) || + (asus->mid_fan_curve_available && curves[FAN_CURVE_DEV_MID].enabled); +} +EXPORT_SYMBOL_NS_GPL(asus_wmi_custom_fan_curve_is_enabled, "ASUS_WMI"); + /* Throttle thermal policy ****************************************************/ static int throttle_thermal_policy_write(struct asus_wmi *asus) { diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index c29962d5baac..b5ed8c83ace1 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -203,6 +203,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); int asus_hid_register_listener(struct asus_hid_listener *cdev); void asus_hid_unregister_listener(struct asus_hid_listener *cdev); int asus_hid_event(enum asus_hid_event event); +bool asus_wmi_custom_fan_curve_is_enabled(void); #else static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status) { @@ -234,6 +235,10 @@ static inline int asus_hid_event(enum asus_hid_event event) { return -ENODEV; } +static inline bool asus_wmi_custom_fan_curve_is_enabled(void) +{ + return false; +} #endif #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ -- cgit From ecc025ec2861ea4f48624e9a86466dd4dc9b5c6c Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Mon, 29 Jun 2026 10:36:32 +0200 Subject: drm/sched: Remove relic from entity docu commit 4827d6d83f07 ("drm/sched: Remove racy hack from drm_sched_fini()") removed the necessity to mark an entity as stopped in drm_sched_fini(). The documentation, however, still details that. Update sched_entity's documentation. Acked-by: Danilo Krummrich Signed-off-by: Philipp Stanner Link: https://patch.msgid.link/20260629083631.2547199-2-phasta@kernel.org --- include/drm/gpu_scheduler.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index d61c19e78182..363d13fc929f 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -217,8 +217,7 @@ struct drm_sched_entity { * @stopped: * * Marks the enity as removed from rq and destined for - * termination. This is set by calling drm_sched_entity_flush() and by - * drm_sched_fini(). + * termination. This is set by calling drm_sched_entity_flush(). */ bool stopped; -- cgit From 25bc29ba671d0496b599149a0823b5dbb5409f94 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 12 Apr 2026 15:26:09 +0300 Subject: wifi: radiotap: add definitions for the new UHR TLVs Add the necessary definitions to create radiotap UHR TLVs for UHR sniffers. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260412152605.73e682d0c8c3.I5a0c858467c852b7a2a00f580bd073af29c37705@changeid Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 190 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) (limited to 'include') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index c60867e7e43c..8bbaf77da7cf 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -95,6 +95,8 @@ enum ieee80211_radiotap_presence { IEEE80211_RADIOTAP_EXT = 31, IEEE80211_RADIOTAP_EHT_USIG = 33, IEEE80211_RADIOTAP_EHT = 34, + IEEE80211_RADIOTAP_UHR_ELR = 37, + IEEE80211_RADIOTAP_UHR = 38, }; /* for IEEE80211_RADIOTAP_FLAGS */ @@ -602,6 +604,194 @@ enum ieee80211_radiotap_eht_usig_tb { IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL = 0xfc000000, }; +/* + * ieee80211_radiotap_uhr_elr - content of UHR-ELR TLV (type 37) + * see https://www.radiotap.org/fields/UHR-ELR for details + */ +struct ieee80211_radiotap_uhr_elr { + __le32 known; + __le32 sig1, sig2, mark; +} __packed; + +enum ieee80211_radiotap_uhr_elr_known { + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_VERSION_ID = 0x00000001, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_UL_DL = 0x00000002, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_MCS = 0x00000004, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_CODING = 0x00000008, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_LENGTH = 0x00000010, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_LDPC_EXTRA_OFDM_SYM = 0x00000020, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_CRC = 0x00000040, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_TAIL = 0x00000080, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_STA_ID = 0x00000100, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_DISREGARD = 0x00000200, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_CRC = 0x00000400, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_TAIL = 0x00000800, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_CRC_CHECKED = 0x00001000, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_CRC_CHECKED = 0x00002000, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_MARK_BSS_COLOR = 0x00010000, +}; + +enum ieee80211_radiotap_uhr_elr_sig1 { + IEEE80211_RADIOTAP_UHR_ELR_SIG1_VERSION_ID = 0x00000001, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_UL_DL = 0x00000002, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_MCS = 0x00000004, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_CODING = 0x00000008, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_LENGTH = 0x00001FF0, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_LDPC_EXTRA_OFDM_SYM = 0x00002000, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_CRC = 0x0003C000, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_TAIL = 0x00FC0000, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_CRC_VALID = 0x80000000, +}; + +enum ieee80211_radiotap_uhr_elr_sig2 { + IEEE80211_RADIOTAP_UHR_ELR_SIG2_STA_ID = 0x000007FF, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_DISREGARD = 0x00003800, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_CRC = 0x0003C000, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_TAIL = 0x00FC0000, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_CRC_VALID = 0x80000000, +}; + +enum ieee80211_radiotap_uhr_elr_mark { + IEEE80211_RADIOTAP_UHR_ELR_MARK_BSS_COLOR = 0x0000003F, +}; + +/* + * ieee80211_radiotap_uhr - content of UHR TLV (type 38) + * see https://www.radiotap.org/fields/UHR for details + */ +struct ieee80211_radiotap_uhr { + __le32 known; + __le32 data[9]; + struct { + __le32 known, info; + } user[]; +} __packed; + +enum ieee80211_radiotap_uhr_known { + IEEE80211_RADIOTAP_UHR_KNOWN_SPATIAL_REUSE = 0x00000001, + IEEE80211_RADIOTAP_UHR_KNOWN_GI_LTF_SIZE = 0x00000002, + IEEE80211_RADIOTAP_UHR_KNOWN_NUMBER_OF_UHR_LTF_SYMBOLS = 0x00000004, + IEEE80211_RADIOTAP_UHR_KNOWN_LDPC_EXTRA_SYMBOL_SEGMENT = 0x00000008, + IEEE80211_RADIOTAP_UHR_KNOWN_PRE_FEC_PADDING_FACTOR = 0x00000010, + IEEE80211_RADIOTAP_UHR_KNOWN_PE_DISAMBIGUITY = 0x00000020, + IEEE80211_RADIOTAP_UHR_KNOWN_DISREGARD_OFDMA = 0x00000040, + IEEE80211_RADIOTAP_UHR_KNOWN_CRC1 = 0x00000080, + IEEE80211_RADIOTAP_UHR_KNOWN_TAIL1 = 0x00000100, + IEEE80211_RADIOTAP_UHR_KNOWN_CRC2 = 0x00000200, + IEEE80211_RADIOTAP_UHR_KNOWN_TAIL2 = 0x00000400, + IEEE80211_RADIOTAP_UHR_KNOWN_INTERFERENCE_MITIGATION = 0x00000800, + IEEE80211_RADIOTAP_UHR_KNOWN_DISREGARD_NON_OFDMA = 0x00001000, + IEEE80211_RADIOTAP_UHR_KNOWN_NUMBER_OF_NON_OFDMA_USERS = 0x00002000, + IEEE80211_RADIOTAP_UHR_KNOWN_COMMON_ENCODING_BLOCK_CRC = 0x00004000, + IEEE80211_RADIOTAP_UHR_KNOWN_COMMON_ENCODING_BLOCK_TAIL = 0x00008000, + IEEE80211_RADIOTAP_UHR_KNOWN_RU_MRU_DRU_SIZE = 0x00010000, + IEEE80211_RADIOTAP_UHR_KNOWN_RU_MRU_INDEX = 0x00020000, + IEEE80211_RADIOTAP_UHR_KNOWN_DRU_RRU_ALLOC_TB_FMT = 0x00040000, + IEEE80211_RADIOTAP_UHR_KNOWN_PRI80_CHAN_POS = 0x00080000, +}; + +enum ieee80211_radiotap_uhr_data { + /* data[0] */ + IEEE80211_RADIOTAP_UHR_DATA0_SPATIAL_REUSE = 0x0000000F, + IEEE80211_RADIOTAP_UHR_DATA0_GI_LTF_SIZE = 0x00000030, + IEEE80211_RADIOTAP_UHR_DATA0_NUMBER_OF_LTF_SYMBOLS = 0x00000700, + IEEE80211_RADIOTAP_UHR_DATA0_LDPC_EXTRA_SYMBOL_SEGMENT = 0x00000800, + IEEE80211_RADIOTAP_UHR_DATA0_PRE_FEC_PADDING_FACTOR = 0x00003000, + IEEE80211_RADIOTAP_UHR_DATA0_PE_DISAMBIGUITY = 0x00004000, + IEEE80211_RADIOTAP_UHR_DATA0_DISREGARD_OFDMA = 0x00078000, + IEEE80211_RADIOTAP_UHR_DATA0_CRC1 = 0x00780000, + IEEE80211_RADIOTAP_UHR_DATA0_TAIL1 = 0x1f800000, + /* data[1] */ + IEEE80211_RADIOTAP_UHR_DATA1_RU_MRU_DRU_SIZE = 0x0000001f, + IEEE80211_RADIOTAP_UHR_DATA1_RU_MRU_INDEX = 0x00001fe0, + IEEE80211_RADIOTAP_UHR_DATA1_RU_ALLOC_CC_1_1_1 = 0x003fe000, + IEEE80211_RADIOTAP_UHR_DATA1_RU_ALLOC_CC_1_1_1_KNOWN = 0x00400000, + IEEE80211_RADIOTAP_UHR_DATA1_PRI80_CHAN_POS = 0xc0000000, + /* data[2] */ + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_1 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_1_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_1_1_2 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_1_1_2_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_2 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_2_KNOWN = 0x20000000, + /* data[3] */ + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_1 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_1_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_2_2_1 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_2_2_1_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_2 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_2_KNOWN = 0x20000000, + /* data[4] */ + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_2 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_2_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_1_2_3 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_1_2_3_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_3 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_3_KNOWN = 0x20000000, + /* data[5] */ + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_4 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_4_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_2_2_4 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_2_2_4_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_5 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_5_KNOWN = 0x20000000, + /* data[6] */ + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_5 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_5_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_1_2_6 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_1_2_6_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_6 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_6_KNOWN = 0x20000000, + /* data[7] */ + IEEE80211_RADIOTAP_UHR_DATA7_CRC2 = 0x0000000f, + IEEE80211_RADIOTAP_UHR_DATA7_TAIL2 = 0x000003f0, + IEEE80211_RADIOTAP_UHR_DATA7_INTERFERENCE_MITIGATION = 0x00000400, + IEEE80211_RADIOTAP_UHR_DATA7_DISREGARD_NON_OFDMA = 0x00001800, + IEEE80211_RADIOTAP_UHR_DATA7_NUMBER_OF_NON_OFDMA_USERS = 0x0000e000, + IEEE80211_RADIOTAP_UHR_DATA7_COMMON_ENCODING_BLOCK_CRC = 0x000f0000, + IEEE80211_RADIOTAP_UHR_DATA7_COMMON_ENCODING_BLOCK_TAIL = 0x03f00000, + /* data[8] */ + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_PS_160= 0x00000001, + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_B0 = 0x00000002, + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_B7_B1 = 0x000001fc, + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_INDICATION = 0x00000200, +}; + +enum ieee80211_radiotap_uhr_user_known { + IEEE80211_RADIOTAP_UHR_USER_KNOWN_STA_ID = 0x00000001, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_MCS = 0x00000002, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_NSS = 0x00000004, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_UEQM = 0x00000008, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_BF = 0x00000010, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_CODING = 0x00000020, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_UEQM_PATTERN = 0x00000040, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_2X_LDPC = 0x00000080, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_SPATIAL_CONFIG = 0x00000100, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_DISREGARD = 0x00000200, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_BSS_COLOR_INDICATION = 0x00000400, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_USR_ENC_BLK_CRC = 0x00000800, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_USR_ENC_BLK_TAIL = 0x00001000, + /* really 'known' but actual data */ + IEEE80211_RADIOTAP_UHR_USER_KNOWN_DATA_USR_ENC_BLK_CRC = 0x000f0000, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_DATA_USR_ENC_BLK_TAIL = 0x03f00000, + /* indicates this user was captured */ + IEEE80211_RADIOTAP_UHR_USER_KNOWN_USER_CAPTURED = 0x80000000, +}; + +enum ieee80211_radiotap_uhr_user_info { + IEEE80211_RADIOTAP_UHR_USER_INFO_STA_ID = 0x000007ff, + IEEE80211_RADIOTAP_UHR_USER_INFO_MCS = 0x0000f800, + IEEE80211_RADIOTAP_UHR_USER_INFO_NSS = 0x00070000, + IEEE80211_RADIOTAP_UHR_USER_INFO_SPATIAL_CONFIG = 0x000f0000, + IEEE80211_RADIOTAP_UHR_USER_INFO_UEQM = 0x00100000, + IEEE80211_RADIOTAP_UHR_USER_INFO_DISREGARD = 0x00100000, + IEEE80211_RADIOTAP_UHR_USER_INFO_BF = 0x00200000, + IEEE80211_RADIOTAP_UHR_USER_INFO_BSS_COLOR_INDICATION = 0x00200000, + IEEE80211_RADIOTAP_UHR_USER_INFO_UEQM_PATTERN = 0x00c00000, + IEEE80211_RADIOTAP_UHR_USER_INFO_CODING = 0x01000000, + IEEE80211_RADIOTAP_UHR_USER_INFO_2X_LDPC = 0x02000000, +}; + /** * ieee80211_get_radiotap_len - get radiotap header length * @data: pointer to the header -- cgit From 4ab9b637b94a3821acfcd6d6037dffe33669245a Mon Sep 17 00:00:00 2001 From: Priyansha Tiwari Date: Thu, 11 Jun 2026 11:52:22 +0530 Subject: wifi: nl80211/cfg80211: rename probe_client to probe_peer Rename NL80211_CMD_PROBE_CLIENT to NL80211_CMD_PROBE_PEER in the UAPI enum and retain NL80211_CMD_PROBE_CLIENT as a compatibility alias. Rename the .probe_client cfg80211_ops callback to .probe_peer and update all in-tree users (wil6210, mwifiex) and mac80211 so the tree continues to build after this change. Signed-off-by: Priyansha Tiwari Link: https://patch.msgid.link/20260611062225.2144241-2-pritiwa@qti.qualcomm.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 8 ++++---- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 8 ++++---- include/net/cfg80211.h | 6 +++--- include/uapi/linux/nl80211.h | 5 +++-- net/mac80211/cfg.c | 6 +++--- net/wireless/nl80211.c | 17 ++++++++--------- net/wireless/rdev-ops.h | 10 +++++----- net/wireless/trace.h | 2 +- 8 files changed, 31 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index d6ef92cfcbaf..a85ff2a4316b 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2379,9 +2379,9 @@ void wil_probe_client_flush(struct wil6210_vif *vif) mutex_unlock(&vif->probe_client_mutex); } -static int wil_cfg80211_probe_client(struct wiphy *wiphy, - struct net_device *dev, - const u8 *peer, u64 *cookie) +static int wil_cfg80211_probe_peer(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, u64 *cookie) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct wil6210_vif *vif = ndev_to_vif(dev); @@ -2660,7 +2660,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = { .add_station = wil_cfg80211_add_station, .del_station = wil_cfg80211_del_station, .change_station = wil_cfg80211_change_station, - .probe_client = wil_cfg80211_probe_client, + .probe_peer = wil_cfg80211_probe_peer, .change_bss = wil_cfg80211_change_bss, /* P2P device */ .start_p2p_device = wil_cfg80211_start_p2p_device, diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index c9daf893472f..99d96088e364 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4558,9 +4558,9 @@ mwifiex_cfg80211_disassociate(struct wiphy *wiphy, } static int -mwifiex_cfg80211_probe_client(struct wiphy *wiphy, - struct net_device *dev, const u8 *peer, - u64 *cookie) +mwifiex_cfg80211_probe_peer(struct wiphy *wiphy, + struct net_device *dev, const u8 *peer, + u64 *cookie) { /* hostapd looks for NL80211_CMD_PROBE_CLIENT support; otherwise, * it requires monitor-mode support (which mwifiex doesn't support). @@ -4726,7 +4726,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) ops->disassoc = mwifiex_cfg80211_disassociate; ops->disconnect = NULL; ops->connect = NULL; - ops->probe_client = mwifiex_cfg80211_probe_client; + ops->probe_peer = mwifiex_cfg80211_probe_peer; } wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8188ad200de5..549b2214e833 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5086,7 +5086,7 @@ struct mgmt_frame_regs { * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * - * @probe_client: probe an associated client, must return a cookie that it + * @probe_peer: probe an associated client, must return a cookie that it * later passes to cfg80211_probe_status(). * * @set_noack_map: Set the NoAck Map for the TIDs. @@ -5488,8 +5488,8 @@ struct cfg80211_ops { int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); - int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u64 *cookie); + int (*probe_peer)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie); int (*set_noack_map)(struct wiphy *wiphy, struct net_device *dev, diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9998f6c0a665..d1907dd12a80 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -922,7 +922,7 @@ * and wasn't already in a 4-addr VLAN. The event will be sent similarly * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. * - * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * @NL80211_CMD_PROBE_PEER: Probe an associated station on an AP interface * by sending a null data frame to it and reporting when the frame is * acknowledged. This is used to allow timing out inactive clients. Uses * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a @@ -1558,7 +1558,7 @@ enum nl80211_commands { NL80211_CMD_UNEXPECTED_FRAME, - NL80211_CMD_PROBE_CLIENT, + NL80211_CMD_PROBE_PEER, NL80211_CMD_REGISTER_BEACONS, @@ -1729,6 +1729,7 @@ enum nl80211_commands { #define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG #define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG #define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE +#define NL80211_CMD_PROBE_CLIENT NL80211_CMD_PROBE_PEER /** * enum nl80211_attrs - nl80211 netlink attributes diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3b58af59f7e4..9c311c8290f7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4949,8 +4949,8 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy, return 0; } -static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u64 *cookie) +static int ieee80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -6060,7 +6060,7 @@ const struct cfg80211_ops mac80211_config_ops = { .tdls_mgmt = ieee80211_tdls_mgmt, .tdls_channel_switch = ieee80211_tdls_channel_switch, .tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch, - .probe_client = ieee80211_probe_client, + .probe_peer = ieee80211_probe_peer, .set_noack_map = ieee80211_set_noack_map, #ifdef CONFIG_PM .set_wakeup = ieee80211_set_wakeup, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 53b4b3f76697..0d651a46b9d6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2444,7 +2444,7 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, } if (rdev->wiphy.max_sched_scan_reqs) CMD(sched_scan_start, START_SCHED_SCAN); - CMD(probe_client, PROBE_CLIENT); + CMD(probe_peer, PROBE_PEER); CMD(set_noack_map, SET_NOACK_MAP); if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { i++; @@ -16150,8 +16150,7 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb, return 0; } -static int nl80211_probe_client(struct sk_buff *skb, - struct genl_info *info) +static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; @@ -16169,7 +16168,7 @@ static int nl80211_probe_client(struct sk_buff *skb, if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; - if (!rdev->ops->probe_client) + if (!rdev->ops->probe_peer) return -EOPNOTSUPP; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -16177,7 +16176,7 @@ static int nl80211_probe_client(struct sk_buff *skb, return -ENOMEM; hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, - NL80211_CMD_PROBE_CLIENT); + NL80211_CMD_PROBE_PEER); if (!hdr) { err = -ENOBUFS; goto free_msg; @@ -16185,7 +16184,7 @@ static int nl80211_probe_client(struct sk_buff *skb, addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - err = rdev_probe_client(rdev, dev, addr, &cookie); + err = rdev_probe_peer(rdev, dev, addr, &cookie); if (err) goto free_msg; @@ -20042,9 +20041,9 @@ static const struct genl_small_ops nl80211_small_ops[] = { .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), }, { - .cmd = NL80211_CMD_PROBE_CLIENT, + .cmd = NL80211_CMD_PROBE_PEER, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = nl80211_probe_client, + .doit = nl80211_probe_peer, .flags = GENL_UNS_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), }, @@ -22614,7 +22613,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, if (!msg) return; - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_PEER); if (!hdr) { nlmsg_free(msg); return; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 63c26e8b1139..6c3bad8b2d6f 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -948,13 +948,13 @@ static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev, return ret; } -static inline int rdev_probe_client(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *peer, - u64 *cookie) +static inline int rdev_probe_peer(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *peer, + u64 *cookie) { int ret; - trace_rdev_probe_client(&rdev->wiphy, dev, peer); - ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie); + trace_rdev_probe_peer(&rdev->wiphy, dev, peer); + ret = rdev->ops->probe_peer(&rdev->wiphy, dev, peer, cookie); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 94944f2a39a4..8c2a91b85c39 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2132,7 +2132,7 @@ DECLARE_EVENT_CLASS(rdev_pmksa, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->bssid) ); -TRACE_EVENT(rdev_probe_client, +TRACE_EVENT(rdev_probe_peer, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *peer), TP_ARGS(wiphy, netdev, peer), -- cgit From 010e955c203e435d5bdba228fdc1556297d3d7ff Mon Sep 17 00:00:00 2001 From: Priyansha Tiwari Date: Thu, 11 Jun 2026 11:52:23 +0530 Subject: wifi: cfg80211/nl80211: add STA-mode peer probing Add NL80211_EXT_FEATURE_PROBE_AP to allow drivers to advertise support for probing the associated AP from STA/P2P-client mode. Extend nl80211_probe_peer() to accept STA/P2P-client interfaces when the driver advertises NL80211_EXT_FEATURE_PROBE_AP; in that case the MAC attribute must be omitted (the peer is implied by the association). Update cfg80211_probe_status() to accept an optional peer address and a link_id parameter (-1 for non-MLO), and include NL80211_ATTR_MLO_LINK_ID in the event when link_id >= 0. Update all callers. Signed-off-by: Priyansha Tiwari Link: https://patch.msgid.link/20260611062225.2144241-3-pritiwa@qti.qualcomm.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 2 +- include/net/cfg80211.h | 14 ++++---- include/uapi/linux/nl80211.h | 20 +++++++---- net/mac80211/status.c | 2 +- net/wireless/nl80211.c | 52 ++++++++++++++++++++--------- 5 files changed, 59 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index a85ff2a4316b..5f2bd9a31faf 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2326,7 +2326,7 @@ static void wil_probe_client_handle(struct wil6210_priv *wil, */ bool alive = (sta->status == wil_sta_connected); - cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, + cfg80211_probe_status(ndev, sta->addr, req->cookie, -1, alive, 0, false, GFP_KERNEL); } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 549b2214e833..ddefe5acc5ae 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5086,8 +5086,8 @@ struct mgmt_frame_regs { * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * - * @probe_peer: probe an associated client, must return a cookie that it - * later passes to cfg80211_probe_status(). + * @probe_peer: probe a connected peer (AP: STA MAC required; STA: no MAC), + * must return a cookie that is later passed to cfg80211_probe_status(). * * @set_noack_map: Set the NoAck Map for the TIDs. * @@ -9846,15 +9846,17 @@ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, /** * cfg80211_probe_status - notify userspace about probe status * @dev: the device the probe was sent on - * @addr: the address of the peer - * @cookie: the cookie filled in @probe_client previously + * @peer: The peer MAC address (or MLD address for MLO) or %NULL if not + * applicable (e.g. for STA/P2P-client) + * @cookie: the cookie filled in @probe_peer previously + * @link_id: The link ID on which the probe was sent (or -1 for non-MLO) * @acked: indicates whether probe was acked or not * @ack_signal: signal strength (in dBm) of the ACK frame. * @is_valid_ack_signal: indicates the ack_signal is valid or not. * @gfp: allocation flags */ -void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, s32 ack_signal, +void cfg80211_probe_status(struct net_device *dev, const u8 *peer, u64 cookie, + int link_id, bool acked, s32 ack_signal, bool is_valid_ack_signal, gfp_t gfp); /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d1907dd12a80..6b8071606e6f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -922,13 +922,15 @@ * and wasn't already in a 4-addr VLAN. The event will be sent similarly * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. * - * @NL80211_CMD_PROBE_PEER: Probe an associated station on an AP interface - * by sending a null data frame to it and reporting when the frame is - * acknowledged. This is used to allow timing out inactive clients. Uses - * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a - * direct reply with an %NL80211_ATTR_COOKIE that is later used to match - * up the event with the request. The event includes the same data and - * has %NL80211_ATTR_ACK set if the frame was ACKed. + * @NL80211_CMD_PROBE_PEER: Probe a connected peer by sending a null data + * frame and reporting when the frame is acknowledged. + * In AP/GO mode, %NL80211_ATTR_MAC is required to identify the client. + * In STA/P2P-client mode, %NL80211_ATTR_MAC must be omitted (the AP is + * implied); the driver must advertise %NL80211_EXT_FEATURE_PROBE_AP. + * The command returns a direct reply with an %NL80211_ATTR_COOKIE that + * is later used to match up the event with the request. The event + * includes the same data and has %NL80211_ATTR_ACK set if the frame + * was ACKed. * * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from * other BSSes when any interfaces are in AP mode. This helps implement @@ -7086,6 +7088,9 @@ enum nl80211_feature_flags { * LTF key seed via %NL80211_KEY_LTF_SEED. The seed is used to generate * secure LTF keys for secure LTF measurement sessions. * + * @NL80211_EXT_FEATURE_PROBE_AP: Driver supports probing the associated AP + * in STA mode using @NL80211_CMD_PROBE_PEER. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -7167,6 +7172,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_IEEE8021X_AUTH, NL80211_EXT_FEATURE_ROC_ADDR_FILTER, NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, + NL80211_EXT_FEATURE_PROBE_AP, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/status.c b/net/mac80211/status.c index dd1dbba06838..c3d29aed93fe 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -655,7 +655,7 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, GFP_ATOMIC); else if (ieee80211_is_any_nullfunc(hdr->frame_control)) cfg80211_probe_status(sdata->dev, hdr->addr1, - cookie, acked, + cookie, -1, acked, info->status.ack_signal, is_valid_ack_signal, GFP_ATOMIC); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0d651a46b9d6..a62e319f6aec 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -16157,16 +16157,32 @@ static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) struct wireless_dev *wdev = dev->ieee80211_ptr; struct sk_buff *msg; void *hdr; - const u8 *addr; + const u8 *addr = NULL; u64 cookie; int err; - if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) + /* Allow in AP, STA, and their P2P counterparts */ + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_PROBE_AP)) + return -EOPNOTSUPP; + if (!wdev->connected) + return -ENOLINK; + /* STA/P2P-client probes the currently associated AP/GO. */ + if (info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + break; + default: return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; + } if (!rdev->ops->probe_peer) return -EOPNOTSUPP; @@ -16182,8 +16198,6 @@ static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) goto free_msg; } - addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - err = rdev_probe_peer(rdev, dev, addr, &cookie); if (err) goto free_msg; @@ -22597,8 +22611,8 @@ nla_put_failure: } EXPORT_SYMBOL(cfg80211_sta_opmode_change_notify); -void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, s32 ack_signal, +void cfg80211_probe_status(struct net_device *dev, const u8 *peer, u64 cookie, + int link_id, bool acked, s32 ack_signal, bool is_valid_ack_signal, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -22606,7 +22620,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, struct sk_buff *msg; void *hdr; - trace_cfg80211_probe_status(dev, addr, cookie, acked); + trace_cfg80211_probe_status(dev, peer, cookie, acked); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); @@ -22621,12 +22635,18 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + (peer && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) || nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie, - NL80211_ATTR_PAD) || - (acked && nla_put_flag(msg, NL80211_ATTR_ACK)) || - (is_valid_ack_signal && nla_put_s32(msg, NL80211_ATTR_ACK_SIGNAL, - ack_signal))) + NL80211_ATTR_PAD)) + goto nla_put_failure; + + if (link_id >= 0 && + nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) + goto nla_put_failure; + + if ((acked && nla_put_flag(msg, NL80211_ATTR_ACK)) || + (is_valid_ack_signal && + nla_put_s32(msg, NL80211_ATTR_ACK_SIGNAL, ack_signal))) goto nla_put_failure; genlmsg_end(msg, hdr); -- cgit From f83378e6ce497eda7e9a7490de4e1a46459febb2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 15 Jun 2026 09:39:48 +0200 Subject: wifi: nl80211: clarify NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA content This is currently __le16, but really the whole content of the corresponding 802.11 element, which is even extensible and could, in theory, be increased in size. Clarify the docs. Link: https://patch.msgid.link/20260615093948.0f730833a6d5.I1c8c5c09dfe16b0b1dcb10d54fc030f6b1d4fc8c@changeid Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6b8071606e6f..d9a8c693457f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4477,8 +4477,8 @@ enum nl80211_mpath_info { * capabilities IE * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as * defined in HE capabilities IE - * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16), - * given for all 6 GHz band channels + * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities, + * given for all 6 GHz band channels (binary, element content) * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are * advertised on this band/for this iftype (binary) * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT -- cgit From 84442442e04be58a8977fb10debcbbfc1649d962 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 19 Jun 2026 14:21:07 +0200 Subject: wifi: cfg80211: remove WIPHY_FLAG_DISABLE_WEXT There are only two drivers left setting it, but they're both also setting WIPHY_FLAG_SUPPORTS_MLO for the relevant devices, so we can now remove WIPHY_FLAG_DISABLE_WEXT. Link: https://patch.msgid.link/20260619142107.150f1bbe3b83.I9ff3d419bad54313c76fa4c3485148c122e67fb3@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath12k/mac.c | 6 ------ drivers/net/wireless/realtek/rtw89/core.c | 3 --- include/net/cfg80211.h | 3 +-- net/wireless/wext-core.c | 6 ++---- 4 files changed, 3 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index af354bef5c0d..9775a87b3db3 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -14871,12 +14871,6 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION; - /* MLO is not yet supported so disable Wireless Extensions for now - * to make sure ath12k users don't use it. This flag can be removed - * once WIPHY_FLAG_SUPPORTS_MLO is enabled. - */ - wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; - /* Copy over MLO related capabilities received from * WMI_SERVICE_READY_EXT2_EVENT if single_chip_mlo_supp is set. */ diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 68dad6090f87..0f0e46cb4260 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7432,9 +7432,6 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) if (!chip->support_rnr) hw->wiphy->flags |= WIPHY_FLAG_SPLIT_SCAN_6GHZ; - if (chip->chip_gen == RTW89_CHIP_BE) - hw->wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; - if (rtwdev->support_mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; hw->wiphy->iftype_ext_capab = rtw89_iftypes_ext_capa; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ddefe5acc5ae..d91533a66712 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5690,7 +5690,6 @@ struct cfg80211_ops { * set this flag to update channels on beacon hints. * @WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY: support connection to non-primary link * of an NSTR mobile AP MLD. - * @WIPHY_FLAG_DISABLE_WEXT: disable wireless extensions for this device */ enum wiphy_flags { WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK = BIT(0), @@ -5702,7 +5701,7 @@ enum wiphy_flags { WIPHY_FLAG_4ADDR_STATION = BIT(6), WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), - WIPHY_FLAG_DISABLE_WEXT = BIT(9), + /* reuse bit 9 */ WIPHY_FLAG_MESH_AUTH = BIT(10), WIPHY_FLAG_SUPPORTS_EXT_KCK_32 = BIT(11), WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY = BIT(12), diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index c19dece2bc6e..db77912b3994 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -660,8 +660,7 @@ struct iw_statistics *get_wireless_stats(struct net_device *dev) dev->ieee80211_ptr->wiphy->wext && dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | - WIPHY_FLAG_DISABLE_WEXT)) + if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) return NULL; return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); } @@ -703,8 +702,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd) #ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | - WIPHY_FLAG_DISABLE_WEXT)) + if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) return NULL; handlers = dev->ieee80211_ptr->wiphy->wext; } -- cgit From 1c29c67bc7a9962e2ef91e8c65b7fc4fa227eded Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Fri, 26 Jun 2026 16:28:57 +1000 Subject: wifi: cfg80211: introduce helper to get S1G primary width This is needed for drivers and will be needed for mac80211/cfg80211 in the future so introduce a generic accessor to retrieve the chandefs S1G primary channel width. Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20260626063014.1275235-2-lachlan.hodges@morsemicro.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d91533a66712..b8e9fbb89e69 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1236,6 +1236,26 @@ ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef) return chandef->chan->max_power; } +/** + * cfg80211_chandef_s1g_pri_width - return S1G primary width in MHz + * + * An S1G interface may have a primary channel width of either 1 + * or 2MHz depending on whether chandef::s1g_primary_2mhz is set. + * + * Note: There is _always_ a 1MHz primary subchannel, regardless + * of the primary width. So chandef::chan always points to this + * 1MHz primary channel. + * + * @chandef: the chandef to use + * + * Returns: width in MHz of the S1G primary channel in use + */ +static inline int +cfg80211_chandef_s1g_pri_width(struct cfg80211_chan_def *chandef) +{ + return chandef->s1g_primary_2mhz ? 2 : 1; +} + /** * cfg80211_any_usable_channels - check for usable channels * @wiphy: the wiphy to check for -- cgit From cc61825060b01077da2b9796dc1047ec383e53c0 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Fri, 26 Jun 2026 16:28:58 +1000 Subject: wifi: ieee80211: introduce generic KHZ_TO_HZ helper Useful for S1G drivers due to the increased required granularity, but may be useful for others so include it as a generic helper. Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20260626063014.1275235-3-lachlan.hodges@morsemicro.com Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d40484451e9a..084ad45aa2d8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2616,6 +2616,7 @@ static inline int ieee80211_get_tdls_action(struct sk_buff *skb) /* convert frequencies */ #define MHZ_TO_KHZ(freq) ((freq) * 1000) #define KHZ_TO_MHZ(freq) ((freq) / 1000) +#define KHZ_TO_HZ(x) ((x) * 1000) #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000 #define KHZ_F "%d.%03d" -- cgit From 541b293ab186195855a3a8c5e015b6b3cf1a68b8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 1 Jul 2026 21:16:39 +0200 Subject: ACPI: bus: Eliminate struct acpi_driver Now that struct acpi_driver has no more users, eliminate it along with all of the code related to it. Also remove the file added by commit b8c8a8ea18ad ("ACPI: Documentation: driver-api: Disapprove of using ACPI drivers") because it will not be necessary any more after eliminating struct acpi_driver from the code. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/5132944.31r3eYUQgx@rafael.j.wysocki --- Documentation/driver-api/acpi/acpi-drivers.rst | 80 -------------- Documentation/driver-api/acpi/index.rst | 1 - drivers/acpi/bus.c | 138 +------------------------ drivers/acpi/power.c | 1 - drivers/acpi/scan.c | 16 +-- include/acpi/acpi_bus.h | 50 +-------- 6 files changed, 5 insertions(+), 281 deletions(-) delete mode 100644 Documentation/driver-api/acpi/acpi-drivers.rst (limited to 'include') diff --git a/Documentation/driver-api/acpi/acpi-drivers.rst b/Documentation/driver-api/acpi/acpi-drivers.rst deleted file mode 100644 index 376b6d8a678c..000000000000 --- a/Documentation/driver-api/acpi/acpi-drivers.rst +++ /dev/null @@ -1,80 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 -.. include:: - -========================================= -Why using ACPI drivers is not a good idea -========================================= - -:Copyright: |copy| 2026, Intel Corporation - -:Author: Rafael J. Wysocki - -Even though binding drivers directly to struct acpi_device objects, also -referred to as "ACPI device nodes", allows basic functionality to be provided -at least in some cases, there are problems with it, related to general -consistency, sysfs layout, power management operation ordering, and code -cleanliness. - -First of all, ACPI device nodes represent firmware entities rather than -hardware and in many cases they provide auxiliary information on devices -enumerated independently (like PCI devices or CPUs). It is therefore generally -questionable to assign resources to them because the entities represented by -them do not decode addresses in the memory or I/O address spaces and do not -generate interrupts or similar (all of that is done by hardware). - -Second, as a general rule, a struct acpi_device can only be a parent of another -struct acpi_device. If that is not the case, the location of the child device -in the device hierarchy is at least confusing and it may not be straightforward -to identify the piece of hardware providing functionality represented by it. -However, binding a driver directly to an ACPI device node may cause that to -happen if the given driver registers input devices or wakeup sources under it, -for example. - -Next, using system suspend and resume callbacks directly on ACPI device nodes -is also questionable because it may cause ordering problems to appear. Namely, -ACPI device nodes are registered before enumerating hardware corresponding to -them and they land on the PM list in front of the majority of other device -objects. Consequently, the execution ordering of their PM callbacks may be -different from what is generally expected. Also, in general, dependencies -returned by _DEP objects do not affect ACPI device nodes themselves, but the -"physical" devices associated with them, which potentially is one more source -of inconsistency related to treating ACPI device nodes as "real" device -representation. - -All of the above means that binding drivers to ACPI device nodes should -generally be avoided and so struct acpi_driver objects should not be used. - -Moreover, a device ID is necessary to bind a driver directly to an ACPI device -node, but device IDs are not generally associated with all of them. Some of -them contain alternative information allowing the corresponding pieces of -hardware to be identified, for example represented by an _ADR object return -value, and device IDs are not used in those cases. In consequence, confusingly -enough, binding an ACPI driver to an ACPI device node may even be impossible. - -When that happens, the piece of hardware corresponding to the given ACPI device -node is represented by another device object, like a struct pci_dev, and the -ACPI device node is the "ACPI companion" of that device, accessible through its -fwnode pointer used by the ACPI_COMPANION() macro. The ACPI companion holds -additional information on the device configuration and possibly some "recipes" -on device manipulation in the form of AML (ACPI Machine Language) bytecode -provided by the platform firmware. Thus the role of the ACPI device node is -similar to the role of a struct device_node on a system where Device Tree is -used for platform description. - -For consistency, this approach has been extended to the cases in which ACPI -device IDs are used. Namely, in those cases, an additional device object is -created to represent the piece of hardware corresponding to a given ACPI device -node. By default, it is a platform device, but it may also be a PNP device, a -CPU device, or another type of device, depending on what the given piece of -hardware actually is. There are even cases in which multiple devices are -"backed" or "accompanied" by one ACPI device node (e.g. ACPI device nodes -corresponding to GPUs that may provide firmware interfaces for backlight -brightness control in addition to GPU configuration information). - -This means that it really should never be necessary to bind a driver directly to -an ACPI device node because there is a "proper" device object representing the -corresponding piece of hardware that can be bound to by a "proper" driver using -the given ACPI device node as the device's ACPI companion. Thus, in principle, -there is no reason to use ACPI drivers and if they all were replaced with other -driver types (for example, platform drivers), some code could be dropped and -some complexity would go away. diff --git a/Documentation/driver-api/acpi/index.rst b/Documentation/driver-api/acpi/index.rst index 2b10d83f9994..ace0008e54c2 100644 --- a/Documentation/driver-api/acpi/index.rst +++ b/Documentation/driver-api/acpi/index.rst @@ -7,4 +7,3 @@ ACPI Support linuxized-acpica scan_handlers - acpi-drivers diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a30a904f6535..fae79cdd3610 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -620,41 +620,6 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) acpi_evaluate_ost(handle, type, ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); } -static void acpi_notify_device(acpi_handle handle, u32 event, void *data) -{ - struct acpi_device *device = data; - struct acpi_driver *acpi_drv = to_acpi_driver(device->dev.driver); - - acpi_drv->ops.notify(device, event); -} - -static int acpi_device_install_notify_handler(struct acpi_device *device, - struct acpi_driver *acpi_drv) -{ - u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ? - ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY; - acpi_status status; - - status = acpi_install_notify_handler(device->handle, type, - acpi_notify_device, device); - if (ACPI_FAILURE(status)) - return -EINVAL; - - return 0; -} - -static void acpi_device_remove_notify_handler(struct acpi_device *device, - struct acpi_driver *acpi_drv) -{ - u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ? - ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY; - - acpi_remove_notify_handler(device->handle, type, - acpi_notify_device); - - acpi_os_wait_events_complete(); -} - int acpi_dev_install_notify_handler(struct acpi_device *adev, u32 handler_type, acpi_notify_handler handler, void *context) @@ -1121,57 +1086,13 @@ bool acpi_driver_match_device(struct device *dev, } EXPORT_SYMBOL_GPL(acpi_driver_match_device); -/* -------------------------------------------------------------------------- - ACPI Driver Management - -------------------------------------------------------------------------- */ - -/** - * __acpi_bus_register_driver - register a driver with the ACPI bus - * @driver: driver being registered - * @owner: owning module/driver - * - * Registers a driver with the ACPI bus. Searches the namespace for all - * devices that match the driver's criteria and binds. Returns zero for - * success or a negative error status for failure. - */ -int __acpi_bus_register_driver(struct acpi_driver *driver, struct module *owner) -{ - if (acpi_disabled) - return -ENODEV; - driver->drv.name = driver->name; - driver->drv.bus = &acpi_bus_type; - driver->drv.owner = owner; - - return driver_register(&driver->drv); -} - -EXPORT_SYMBOL(__acpi_bus_register_driver); - -/** - * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus - * @driver: driver to unregister - * - * Unregisters a driver with the ACPI bus. Searches the namespace for all - * devices that match the driver's criteria and unbinds. - */ -void acpi_bus_unregister_driver(struct acpi_driver *driver) -{ - driver_unregister(&driver->drv); -} - -EXPORT_SYMBOL(acpi_bus_unregister_driver); - /* -------------------------------------------------------------------------- ACPI Bus operations -------------------------------------------------------------------------- */ static int acpi_bus_match(struct device *dev, const struct device_driver *drv) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - const struct acpi_driver *acpi_drv = to_acpi_driver(drv); - - return acpi_dev->flags.match_driver - && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); + return 0; } static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env *env) @@ -1179,66 +1100,9 @@ static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env * return __acpi_device_uevent_modalias(to_acpi_device(dev), env); } -static int acpi_device_probe(struct device *dev) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); - int ret; - - if (acpi_dev->handler && !acpi_is_pnp_device(acpi_dev)) - return -EINVAL; - - if (!acpi_drv->ops.add) - return -ENOSYS; - - ret = acpi_drv->ops.add(acpi_dev); - if (ret) { - acpi_dev->driver_data = NULL; - return ret; - } - - pr_debug("Driver [%s] successfully bound to device [%s]\n", - acpi_drv->name, acpi_dev->pnp.bus_id); - - if (acpi_drv->ops.notify) { - ret = acpi_device_install_notify_handler(acpi_dev, acpi_drv); - if (ret) { - if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev); - - acpi_dev->driver_data = NULL; - return ret; - } - } - - pr_debug("Found driver [%s] for device [%s]\n", acpi_drv->name, - acpi_dev->pnp.bus_id); - - get_device(dev); - return 0; -} - -static void acpi_device_remove(struct device *dev) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); - - if (acpi_drv->ops.notify) - acpi_device_remove_notify_handler(acpi_dev, acpi_drv); - - if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev); - - acpi_dev->driver_data = NULL; - - put_device(dev); -} - const struct bus_type acpi_bus_type = { .name = "acpi", .match = acpi_bus_match, - .probe = acpi_device_probe, - .remove = acpi_device_remove, .uevent = acpi_device_uevent, }; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index d4131c184be8..23a4e207a01e 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -954,7 +954,6 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) INIT_LIST_HEAD(&resource->list_node); INIT_LIST_HEAD(&resource->dependents); device->power.state = ACPI_STATE_UNKNOWN; - device->flags.match_driver = true; /* Evaluate the object to get the system level and resource order. */ status = acpi_evaluate_object(handle, NULL, NULL, &buffer); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9a7ac2eb9ce0..ee24c65d43ed 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -273,13 +273,9 @@ static int acpi_scan_check_and_detach(struct acpi_device *adev, void *p) } } - adev->flags.match_driver = false; - if (handler) { - if (handler->detach) - handler->detach(adev); - } else { - device_release_driver(&adev->dev); - } + if (handler && handler->detach) + handler->detach(adev); + /* * Most likely, the device is going away, so put it into D3cold before * that. @@ -1821,7 +1817,6 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_set_pnp_ids(handle, &device->pnp, type); acpi_init_properties(device); acpi_bus_get_flags(device); - device->flags.match_driver = false; device->flags.initialized = true; device->flags.enumeration_by_parent = acpi_device_enumeration_by_parent(device); @@ -2375,16 +2370,11 @@ static int acpi_bus_attach(struct acpi_device *device, void *first_pass) if (ret < 0) return 0; - device->flags.match_driver = true; if (ret > 0 && !device->flags.enumeration_by_parent) { acpi_device_set_enumerated(device); goto ok; } - ret = device_attach(&device->dev); - if (ret < 0) - return 0; - if (device->pnp.type.platform_id || device->pnp.type.backlight || device->flags.enumeration_by_parent) acpi_default_enumeration(device); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 714d111d8053..7b8051baed75 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -108,7 +108,6 @@ enum acpi_bus_device_type { ACPI_BUS_DEVICE_TYPE_COUNT }; -struct acpi_driver; struct acpi_device; /* @@ -158,32 +157,6 @@ struct acpi_hotplug_context { acpi_hp_fixup fixup; }; -/* - * ACPI Driver - * ----------- - */ - -typedef int (*acpi_op_add) (struct acpi_device * device); -typedef void (*acpi_op_remove) (struct acpi_device *device); -typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); - -struct acpi_device_ops { - acpi_op_add add; - acpi_op_remove remove; - acpi_op_notify notify; -}; - -#define ACPI_DRIVER_ALL_NOTIFY_EVENTS 0x1 /* system AND device events */ - -struct acpi_driver { - char name[80]; - char class[80]; - const struct acpi_device_id *ids; /* Supported Hardware IDs */ - unsigned int flags; - struct acpi_device_ops ops; - struct device_driver drv; -}; - /* * ACPI Device * ----------- @@ -211,7 +184,6 @@ struct acpi_device_flags { u32 removable:1; u32 ejectable:1; u32 power_manageable:1; - u32 match_driver:1; u32 initialized:1; u32 visited:1; u32 hotplug_notify:1; @@ -221,7 +193,7 @@ struct acpi_device_flags { u32 cca_seen:1; u32 enumeration_by_parent:1; u32 honor_deps:1; - u32 reserved:18; + u32 reserved:19; }; /* File System */ @@ -570,7 +542,6 @@ static inline void *acpi_driver_data(struct acpi_device *d) } #define to_acpi_device(d) container_of(d, struct acpi_device, dev) -#define to_acpi_driver(d) container_of_const(d, struct acpi_driver, drv) static inline struct acpi_device *acpi_dev_parent(struct acpi_device *adev) { @@ -676,13 +647,6 @@ void acpi_scan_lock_release(void); void acpi_lock_hp_context(void); void acpi_unlock_hp_context(void); int acpi_scan_add_handler(struct acpi_scan_handler *handler); -/* - * use a macro to avoid include chaining to get THIS_MODULE - */ -#define acpi_bus_register_driver(drv) \ - __acpi_bus_register_driver(drv, THIS_MODULE) -int __acpi_bus_register_driver(struct acpi_driver *driver, struct module *owner); -void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_scan(acpi_handle handle); void acpi_bus_trim(struct acpi_device *start); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); @@ -696,18 +660,6 @@ static inline bool acpi_device_enumerated(struct acpi_device *adev) return adev && adev->flags.initialized && adev->flags.visited; } -/** - * module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver - * @__acpi_driver: acpi_driver struct - * - * Helper macro for ACPI drivers which do not do anything special in module - * init/exit. This eliminates a lot of boilerplate. Each module may only - * use this macro once, and calling it replaces module_init() and module_exit() - */ -#define module_acpi_driver(__acpi_driver) \ - module_driver(__acpi_driver, acpi_bus_register_driver, \ - acpi_bus_unregister_driver) - /* * Bind physical devices with ACPI devices */ -- cgit From b1d0c412088e3908821ef2ec52e2c0e5e7f5a535 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Tue, 30 Jun 2026 14:55:32 +0200 Subject: dpll: add STATE_CONNECTED_OVERRIDE pin capability Add DPLL_PIN_CAPABILITIES_STATE_CONNECTED_OVERRIDE capability flag that indicates a pin can be set to connected regardless of the current DPLL device mode, overriding the active input selection. This is useful for automatic-only DPLL devices where mode cannot be switched to manual, allowing userspace to directly connect such pin from automatic mode. The capability requires STATE_CAN_CHANGE to be set as well; dpll_pin_register() warns if a driver violates this. Document the new capability in the Pin selection section of Documentation/driver-api/dpll.rst. Signed-off-by: Ivan Vecera Link: https://patch.msgid.link/20260630125536.720717-2-ivecera@redhat.com Signed-off-by: Paolo Abeni --- Documentation/driver-api/dpll.rst | 7 +++++++ Documentation/netlink/specs/dpll.yaml | 6 ++++++ drivers/dpll/dpll_core.c | 6 +++++- include/uapi/linux/dpll.h | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst index bae14766d4f7..f83150917814 100644 --- a/Documentation/driver-api/dpll.rst +++ b/Documentation/driver-api/dpll.rst @@ -91,6 +91,13 @@ following pin states: - ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as a valid input for automatic selection algorithm +Pins that have the ``DPLL_PIN_CAPABILITIES_STATE_CONNECTED_OVERRIDE`` +capability can additionally be set to ``DPLL_PIN_STATE_CONNECTED`` in +automatic mode, overriding the active input selection. This is useful +for automatic-only DPLL devices where mode cannot be switched to manual. +When such a pin is disconnected, the device returns to automatic input +selection. + The actual hardware status of a pin is reported via the operational state (``DPLL_A_PIN_OPERSTATE``) attribute nested under the parent device: diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml index 2bf83f6732ab..526a5b2df2bd 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -252,6 +252,12 @@ definitions: - name: state-can-change doc: pin state can be changed + - + name: state-connected-override + doc: | + pin state can be set to connected regardless of current + DPLL device mode, overriding the active input selection. + Requires state-can-change to be set as well. - type: const name: phase-offset-divider diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 2e8690cb3c16..bb1e8650c9d5 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -884,7 +884,11 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, WARN_ON(ops->measured_freq_get && (!dpll_device_ops(dpll)->freq_monitor_get || !dpll_device_ops(dpll)->freq_monitor_set)) || - WARN_ON(ops->supported_ffo && !ops->ffo_get)) + WARN_ON(ops->supported_ffo && !ops->ffo_get) || + WARN_ON((pin->prop.capabilities & + DPLL_PIN_CAPABILITIES_STATE_CONNECTED_OVERRIDE) && + !(pin->prop.capabilities & + DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE))) return -EINVAL; mutex_lock(&dpll_lock); diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h index 55eaa82f5f98..5d7ca6a413cd 100644 --- a/include/uapi/linux/dpll.h +++ b/include/uapi/linux/dpll.h @@ -208,11 +208,15 @@ enum dpll_pin_operstate { * @DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE: pin direction can be changed * @DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE: pin priority can be changed * @DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE: pin state can be changed + * @DPLL_PIN_CAPABILITIES_STATE_CONNECTED_OVERRIDE: pin state can be set to + * connected regardless of current DPLL device mode, overriding the active + * input selection. Requires state-can-change to be set as well. */ enum dpll_pin_capabilities { DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE = 1, DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE = 2, DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4, + DPLL_PIN_CAPABILITIES_STATE_CONNECTED_OVERRIDE = 8, }; #define DPLL_PHASE_OFFSET_DIVIDER 1000 -- cgit From 0cc8348a9786727e3622f833f442e8b45e2d363b Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Tue, 30 Jun 2026 14:55:33 +0200 Subject: dpll: add DPLL_PIN_TYPE_INT_NCO pin type Add DPLL_PIN_TYPE_INT_NCO pin type for virtual pins representing the NCO mode of a DPLL. When connected as a DPLL input, the DPLL enters NCO mode where the output frequency is adjusted by the host via the PTP clock interface. Update the fractional-frequency-offset and fractional-frequency- offset-ppt attribute documentation to note that for INT_NCO pins these attributes represent the DPLL's current output frequency offset from its nominal frequency. Reviewed-by: Jiri Pirko Signed-off-by: Ivan Vecera Link: https://patch.msgid.link/20260630125536.720717-3-ivecera@redhat.com Signed-off-by: Paolo Abeni --- Documentation/netlink/specs/dpll.yaml | 13 +++++++++++++ drivers/dpll/dpll_nl.c | 2 +- include/uapi/linux/dpll.h | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml index 526a5b2df2bd..cdc8c7b456df 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -165,6 +165,13 @@ definitions: - name: gnss doc: GNSS recovered clock + - + name: int-nco + doc: | + Device internal numerically controlled oscillator. + When connected as a DPLL input, the DPLL enters NCO mode + where the output frequency is adjusted by the host via + the PTP clock interface. render-max: true - type: enum @@ -462,6 +469,9 @@ attribute-sets: offset on the media associated with the pin. Inside the pin-parent-device nest it represents the frequency offset between the pin and its parent DPLL device. + For pins of type PIN_TYPE_INT_NCO this represents + the DPLL's current output frequency offset from its + nominal frequency. Value is in PPM (parts per million). This is a lower-precision version of fractional-frequency-offset-ppt. @@ -508,6 +518,9 @@ attribute-sets: offset on the media associated with the pin. Inside the pin-parent-device nest it represents the frequency offset between the pin and its parent DPLL device. + For pins of type PIN_TYPE_INT_NCO this represents + the DPLL's current output frequency offset from its + nominal frequency. Value is in PPT (parts per trillion, 10^-12). This is a higher-precision version of fractional-frequency-offset. diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c index ed3bbe9841ea..b1ba490e72b0 100644 --- a/drivers/dpll/dpll_nl.c +++ b/drivers/dpll/dpll_nl.c @@ -61,7 +61,7 @@ static const struct nla_policy dpll_pin_id_get_nl_policy[DPLL_A_PIN_TYPE + 1] = [DPLL_A_PIN_BOARD_LABEL] = { .type = NLA_NUL_STRING, }, [DPLL_A_PIN_PANEL_LABEL] = { .type = NLA_NUL_STRING, }, [DPLL_A_PIN_PACKAGE_LABEL] = { .type = NLA_NUL_STRING, }, - [DPLL_A_PIN_TYPE] = NLA_POLICY_RANGE(NLA_U32, 1, 5), + [DPLL_A_PIN_TYPE] = NLA_POLICY_RANGE(NLA_U32, 1, 6), }; /* DPLL_CMD_PIN_GET - do */ diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h index 5d7ca6a413cd..85b898b1db5e 100644 --- a/include/uapi/linux/dpll.h +++ b/include/uapi/linux/dpll.h @@ -129,6 +129,9 @@ enum dpll_type { * @DPLL_PIN_TYPE_SYNCE_ETH_PORT: ethernet port PHY's recovered clock * @DPLL_PIN_TYPE_INT_OSCILLATOR: device internal oscillator * @DPLL_PIN_TYPE_GNSS: GNSS recovered clock + * @DPLL_PIN_TYPE_INT_NCO: Device internal numerically controlled oscillator. + * When connected as a DPLL input, the DPLL enters NCO mode where the output + * frequency is adjusted by the host via the PTP clock interface. */ enum dpll_pin_type { DPLL_PIN_TYPE_MUX = 1, @@ -136,6 +139,7 @@ enum dpll_pin_type { DPLL_PIN_TYPE_SYNCE_ETH_PORT, DPLL_PIN_TYPE_INT_OSCILLATOR, DPLL_PIN_TYPE_GNSS, + DPLL_PIN_TYPE_INT_NCO, /* private: */ __DPLL_PIN_TYPE_MAX, -- cgit From ab9c44bbf6d7df22518ea66329595023c00be1bc Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 8 Jun 2026 19:54:51 -0700 Subject: ARM: PXA: remove remnants of PXA93x support Support for PXA93x chips was removed in commit d711b8a2987a ("ARM: pxa: remove pxa93x support"), but some code to handle them remains. Remove it. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 3 +-- include/linux/soc/pxa/cpu.h | 56 --------------------------------------------- 2 files changed, 1 insertion(+), 58 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index b5ea058ed467..f8427f071c00 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -43,8 +43,7 @@ #define NR_SG 1 #define CLKRT_OFF (~0) -#define mmc_has_26MHz() (cpu_is_pxa300() || cpu_is_pxa310() \ - || cpu_is_pxa935()) +#define mmc_has_26MHz() (cpu_is_pxa300() || cpu_is_pxa310()) struct pxamci_host { struct mmc_host *mmc; diff --git a/include/linux/soc/pxa/cpu.h b/include/linux/soc/pxa/cpu.h index 5782450ee45c..38bacdae684f 100644 --- a/include/linux/soc/pxa/cpu.h +++ b/include/linux/soc/pxa/cpu.h @@ -46,14 +46,6 @@ * PXA31x A2 0x69056892 0x2E649013 * PXA32x B1 0x69056825 0x5E642013 * PXA32x B2 0x69056826 0x6E642013 - * - * PXA930 B0 0x69056835 0x5E643013 - * PXA930 B1 0x69056837 0x7E643013 - * PXA930 B2 0x69056838 0x8E643013 - * - * PXA935 A0 0x56056931 0x1E653013 - * PXA935 B0 0x56056936 0x6E653013 - * PXA935 B1 0x56056938 0x8E653013 */ #ifdef CONFIG_PXA25x #define __cpu_is_pxa210(id) \ @@ -126,26 +118,6 @@ #define __cpu_is_pxa320(id) (0) #endif -#ifdef CONFIG_CPU_PXA930 -#define __cpu_is_pxa930(id) \ - ({ \ - unsigned int _id = (id) >> 4 & 0xfff; \ - _id == 0x683; \ - }) -#else -#define __cpu_is_pxa930(id) (0) -#endif - -#ifdef CONFIG_CPU_PXA935 -#define __cpu_is_pxa935(id) \ - ({ \ - unsigned int _id = (id) >> 4 & 0xfff; \ - _id == 0x693; \ - }) -#else -#define __cpu_is_pxa935(id) (0) -#endif - #define cpu_is_pxa210() \ ({ \ __cpu_is_pxa210(read_cpuid_id()); \ @@ -186,18 +158,6 @@ __cpu_is_pxa320(read_cpuid_id()); \ }) -#define cpu_is_pxa930() \ - ({ \ - __cpu_is_pxa930(read_cpuid_id()); \ - }) - -#define cpu_is_pxa935() \ - ({ \ - __cpu_is_pxa935(read_cpuid_id()); \ - }) - - - /* * CPUID Core Generation Bit * <= 0x2 for pxa21x/pxa25x/pxa26x/pxa27x @@ -218,22 +178,11 @@ __cpu_is_pxa300(id) \ || __cpu_is_pxa310(id) \ || __cpu_is_pxa320(id) \ - || __cpu_is_pxa93x(id); \ }) #else #define __cpu_is_pxa3xx(id) (0) #endif -#if defined(CONFIG_CPU_PXA930) || defined(CONFIG_CPU_PXA935) -#define __cpu_is_pxa93x(id) \ - ({ \ - __cpu_is_pxa930(id) \ - || __cpu_is_pxa935(id); \ - }) -#else -#define __cpu_is_pxa93x(id) (0) -#endif - #define cpu_is_pxa2xx() \ ({ \ __cpu_is_pxa2xx(read_cpuid_id()); \ @@ -244,9 +193,4 @@ __cpu_is_pxa3xx(read_cpuid_id()); \ }) -#define cpu_is_pxa93x() \ - ({ \ - __cpu_is_pxa93x(read_cpuid_id()); \ - }) - #endif -- cgit From 38d6b194a21c8626647b0a773bb8db5f2b84b0ad Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 6 Jul 2026 11:32:10 +0200 Subject: dt-bindings: rtc: sun6i: add sun60i-a733 support Add a new rtc compatible for the sun60i-a733 SoC and new IDs for the peripheral oscillator clock gates of this SoC. Acked-by: Alexandre Belloni Acked-by: Conor Dooley Signed-off-by: Jerome Brunet Link: https://patch.msgid.link/20260706-a733-rtc-v4-2-f330728db3d3@baylibre.com Signed-off-by: Chen-Yu Tsai --- Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml | 1 + include/dt-bindings/clock/sun6i-rtc.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml index 959a012c626f..f2b91186ed37 100644 --- a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml @@ -33,6 +33,7 @@ properties: - enum: - allwinner,sun20i-d1-rtc - allwinner,sun55i-a523-rtc + - allwinner,sun60i-a733-rtc - const: allwinner,sun50i-r329-rtc reg: diff --git a/include/dt-bindings/clock/sun6i-rtc.h b/include/dt-bindings/clock/sun6i-rtc.h index 3bd3aa3d57ce..5132a393ca4b 100644 --- a/include/dt-bindings/clock/sun6i-rtc.h +++ b/include/dt-bindings/clock/sun6i-rtc.h @@ -6,5 +6,9 @@ #define CLK_OSC32K 0 #define CLK_OSC32K_FANOUT 1 #define CLK_IOSC 2 +#define CLK_HOSC_UFS 8 +#define CLK_HOSC_HDMI 9 +#define CLK_HOSC_SERDES0 10 +#define CLK_HOSC_SERDES1 11 #endif /* _DT_BINDINGS_CLK_SUN6I_RTC_H_ */ -- cgit From dd21d1844aa096216e1be551d1ae5e57c27c837c Mon Sep 17 00:00:00 2001 From: P Praneesh Date: Sun, 14 Jun 2026 10:47:34 +0530 Subject: wifi: cfg80211: Fragment per-link station stats in nl80211_dump_station() In MLO scenarios, stations may have multiple links, each with distinct statistics. When userspace tools like iw or hostapd request station dumps, attempting to pack all per-link stats into a single netlink message can easily exceed the default 4KB buffer limit, especially when more than two links are active. This results in -EMSGSIZE errors and incomplete data delivery. To address this, fragment per-link station statistics across multiple netlink messages to ensure reliable delivery of complete MLO station information. Extend the stateful context with a two-phase dump mechanism: phase 0 (AGGREGATED) sends combined MLO-level statistics and phase 1 (PER_LINK) sends individual per-link statistics for each active link. The dump loop is structured to produce exactly one netlink message per iteration, with a common header (ifindex, wdev, mac, generation) built once and phase-specific payload added via a switch statement. This keeps header construction in one place and makes the EMSGSIZE bail-out uniform. Add a new request flag attribute, NL80211_ATTR_STA_DUMP_LINK_STATS (NLA_FLAG), for NL80211_CMD_GET_STATION dump. Userspace can set this flag to request per-link station statistics for MLO stations. Extract this flag during the first dump invocation by passing an attrbuf to nl80211_prepare_wdev_dump(); use __free(kfree) to avoid scattered manual kfree() calls. Cache the boolean in the dump context to avoid repeated parsing on subsequent invocations. Per-link messages carry a single NL80211_ATTR_MLO_LINKS nest with the link ID, link-specific MAC, and per-link NL80211_ATTR_STA_INFO payload. The link-specific validity (is_valid_ether_addr) and null pointer guard are checked in nl80211_put_link_station_payload() before any message construction begins. Also fix all nla_nest_start_noflag() calls in nl80211_fill_link_station() for nested attribute types (STA_INFO, BSS_PARAM, TID_STATS, per-tid) to use nla_nest_start() so the NLA_F_NESTED flag is set correctly. Propagate the actual return value from nl80211_put_sta_info_common() in the AGGREGATED phase rather than returning skb->len. Returning skb->len signals netlink to re-invoke the dump with the same sta_idx, causing an infinite loop when the aggregated payload is too large to fit; returning the real error code (-EMSGSIZE or otherwise) terminates the dump cleanly. Backward compatibility is seamlessly preserved for non-MLO stations. Signed-off-by: P Praneesh Link: https://patch.msgid.link/20260614051739.3979947-5-praneesh.p@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 19 +++++ net/wireless/nl80211.c | 170 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 157 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d9a8c693457f..020387d76412 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3168,6 +3168,23 @@ enum nl80211_commands { * @NL80211_ATTR_NPCA_PRIMARY_FREQ: NPCA primary channel (u32) * @NL80211_ATTR_NPCA_PUNCT_BITMAP: NPCA puncturing bitmap (u32) * + * @NL80211_ATTR_STA_DUMP_LINK_STATS: Request flag for %NL80211_CMD_GET_STATION + * (dump mode only). When set on an MLD station, the dump produces two + * %NL80211_CMD_NEW_STATION messages per station per dump call: + * + * 1. An aggregated-stats message whose top-level %NL80211_ATTR_STA_INFO + * contains MLO-combined statistics (same content as a dump without + * this flag). + * + * 2. For each active link, a per-link message containing + * %NL80211_ATTR_MLO_LINKS with a single link entry. Each entry holds + * %NL80211_ATTR_MLO_LINK_ID, the link-specific %NL80211_ATTR_MAC, + * and %NL80211_ATTR_STA_INFO with per-link statistics (see + * &enum nl80211_sta_info). + * + * The aggregated message always precedes the per-link messages for the + * same station within a dump sequence. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3766,6 +3783,8 @@ enum nl80211_attrs { NL80211_ATTR_NPCA_PRIMARY_FREQ, NL80211_ATTR_NPCA_PUNCT_BITMAP, + NL80211_ATTR_STA_DUMP_LINK_STATS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ccc4341a0aea..be16a40132ff 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1093,6 +1093,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NPCA_PRIMARY_FREQ] = { .type = NLA_U32 }, [NL80211_ATTR_NPCA_PUNCT_BITMAP] = NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range), + [NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -7870,7 +7871,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg, goto nla_put_failure; \ } while (0) - link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO); + link_sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!link_sinfoattr) goto nla_put_failure; @@ -7936,8 +7937,8 @@ static int nl80211_fill_link_station(struct sk_buff *msg, PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32); if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { - bss_param = nla_nest_start_noflag(msg, - NL80211_STA_INFO_BSS_PARAM); + bss_param = nla_nest_start(msg, + NL80211_STA_INFO_BSS_PARAM); if (!bss_param) goto nla_put_failure; @@ -7979,8 +7980,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg, struct nlattr *tidsattr; int tid; - tidsattr = nla_nest_start_noflag(msg, - NL80211_STA_INFO_TID_STATS); + tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS); if (!tidsattr) goto nla_put_failure; @@ -7993,7 +7993,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg, if (!tidstats->filled) continue; - tidattr = nla_nest_start_noflag(msg, tid + 1); + tidattr = nla_nest_start(msg, tid + 1); if (!tidattr) goto nla_put_failure; @@ -8464,21 +8464,74 @@ static void cfg80211_sta_set_mld_sinfo(struct station_info *sinfo) sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); } +enum nl80211_dump_station_phase { + NL80211_DUMP_STA_PHASE_AGGREGATED = 0, + NL80211_DUMP_STA_PHASE_PER_LINK = 1, +}; + struct nl80211_dump_station_ctx { int sta_idx; + int link_idx; + enum nl80211_dump_station_phase phase; + bool dump_link_stats; u8 mac_addr[ETH_ALEN]; struct station_info sinfo; }; +static int nl80211_put_link_station_payload(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct station_info *sinfo, + int link_idx) +{ + struct link_station_info *link_sinfo = sinfo->links[link_idx]; + struct nlattr *links, *link; + + if (WARN_ON_ONCE(!link_sinfo)) + return -ENOENT; + + if (!is_valid_ether_addr(link_sinfo->addr)) + return -EADDRNOTAVAIL; + + links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); + if (!links) + return -EMSGSIZE; + + link = nla_nest_start(msg, link_idx + 1); + if (!link) + goto nla_put_failure; + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_idx) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, link_sinfo->addr)) + goto nla_put_failure; + + if (nl80211_fill_link_station(msg, rdev, link_sinfo)) + goto nla_put_failure; + + nla_nest_end(msg, link); + nla_nest_end(msg, links); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, links); + return -EMSGSIZE; +} + static int nl80211_dump_station(struct sk_buff *skb, struct netlink_callback *cb) { struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; struct nl80211_dump_station_ctx *ctx = (void *)cb->args[2]; + struct nlattr **attrbuf __free(kfree) = NULL; int err; - err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL); + if (!ctx) { + attrbuf = kzalloc_objs(*attrbuf, NUM_NL80211_ATTR); + if (!attrbuf) + return -ENOMEM; + } + + err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, attrbuf); if (err) return err; /* nl80211_prepare_wdev_dump acquired it in the successful case */ @@ -8490,6 +8543,9 @@ static int nl80211_dump_station(struct sk_buff *skb, err = -ENOMEM; goto out_err; } + ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED; + ctx->dump_link_stats = + !!attrbuf[NL80211_ATTR_STA_DUMP_LINK_STATS]; cb->args[2] = (long)ctx; } @@ -8505,34 +8561,53 @@ static int nl80211_dump_station(struct sk_buff *skb, while (true) { void *hdr; + int ret; - memset(&ctx->sinfo, 0, sizeof(ctx->sinfo)); - for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - ctx->sinfo.links[i] = - kzalloc_obj(*ctx->sinfo.links[0]); - if (!ctx->sinfo.links[i]) { - err = -ENOMEM; + /* AGGREGATED phase: fetch sinfo from driver once per station */ + if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) { + memset(&ctx->sinfo, 0, sizeof(ctx->sinfo)); + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + ctx->sinfo.links[i] = + kzalloc_obj(*ctx->sinfo.links[0]); + if (!ctx->sinfo.links[i]) { + err = -ENOMEM; + goto out_err_release; + } + } + + err = rdev_dump_station(rdev, wdev, ctx->sta_idx, + ctx->mac_addr, &ctx->sinfo); + if (err == -ENOENT) { + err = skb->len; goto out_err_release; } - } + if (err) + goto out_err_release; - err = rdev_dump_station(rdev, wdev, ctx->sta_idx, - ctx->mac_addr, &ctx->sinfo); - if (err == -ENOENT) { - err = skb->len; - goto out_err_release; + if (ctx->sinfo.valid_links) + cfg80211_sta_set_mld_sinfo(&ctx->sinfo); + } else { + /* PER_LINK phase: advance to next valid link */ + while (ctx->link_idx < IEEE80211_MLD_MAX_NUM_LINKS && + !(ctx->sinfo.valid_links & BIT(ctx->link_idx))) + ctx->link_idx++; + + if (ctx->link_idx >= IEEE80211_MLD_MAX_NUM_LINKS) { + cfg80211_sinfo_release_content(&ctx->sinfo); + ctx->sta_idx++; + ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED; + continue; + } } - if (err) - goto out_err_release; - - if (ctx->sinfo.valid_links) - cfg80211_sta_set_mld_sinfo(&ctx->sinfo); + /* Build common header for both phases */ hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, NL80211_CMD_NEW_STATION); if (!hdr) { err = skb->len; + if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK) + goto out_err; goto out_err_release; } @@ -8546,18 +8621,49 @@ static int nl80211_dump_station(struct sk_buff *skb, ctx->sinfo.generation)) { genlmsg_cancel(skb, hdr); err = skb->len; + if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK) + goto out_err; goto out_err_release; } - if (nl80211_put_sta_info_common(skb, rdev, &ctx->sinfo)) { - genlmsg_cancel(skb, hdr); - err = skb->len; - goto out_err_release; - } + switch (ctx->phase) { + case NL80211_DUMP_STA_PHASE_AGGREGATED: + ret = nl80211_put_sta_info_common(skb, rdev, &ctx->sinfo); + if (ret) { + genlmsg_cancel(skb, hdr); + err = ret; + goto out_err_release; + } + genlmsg_end(skb, hdr); + + if (ctx->dump_link_stats && ctx->sinfo.valid_links) { + ctx->phase = NL80211_DUMP_STA_PHASE_PER_LINK; + ctx->link_idx = 0; + } else { + cfg80211_sinfo_release_content(&ctx->sinfo); + ctx->sta_idx++; + } + break; - genlmsg_end(skb, hdr); - cfg80211_sinfo_release_content(&ctx->sinfo); - ctx->sta_idx++; + case NL80211_DUMP_STA_PHASE_PER_LINK: + ret = nl80211_put_link_station_payload(skb, rdev, + &ctx->sinfo, + ctx->link_idx); + if (ret == -EMSGSIZE) { + genlmsg_cancel(skb, hdr); + err = skb->len; + goto out_err; + } + if (ret) { + /* skip invalid link, do not abort the dump */ + genlmsg_cancel(skb, hdr); + ctx->link_idx++; + continue; + } + genlmsg_end(skb, hdr); + ctx->link_idx++; + break; + } } out_err_release: -- cgit From e2904ddb14a4198ad31eb12a072a6923f0c8ca09 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 5 Jul 2026 14:38:04 +0200 Subject: timekeeping: Document monotonic raw timestamps in snapshots correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comments related to raw monotonic timestamps for the various snapshot mechanisms in code and struct documentation are ambiguous. They reference them as CLOCK_MONOTONIC_RAW timestamps, but with the arrival of AUX clocks that's not longer correct. The raw monotonic timestamps only represent CLOCK_MONOTONIC_RAW for the system time clock IDs, i.e. REALTIME, MONOTONIC, BOOTTIME, TAI. For AUX clocks they refer to the monotonic raw clock which is related to the individual AUX clocks. These monotonic raw timestamps have the same conversion factor as CLOCK_MONOTONIC_RAW, but differ from that by an offset: MONORAW(AUX$N) = MONORAW(SYSTEM) + OFFSET(AUX$N) The offset is established when a AUX clock is enabled and stays constant for the lifetime of the AUX clock. Update the comments so they reflect reality. Reported-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Reviewed-by: Thomas Weißschuh Link: https://patch.msgid.link/87wlv9k3wz.ffs@fw13 --- include/linux/timekeeping.h | 10 +++++++++- kernel/time/timekeeping.c | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 984a866d293b..b4191a873c39 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -276,7 +276,7 @@ static inline bool ktime_get_aux_ts64(clockid_t id, struct timespec64 *kt) { ret #endif /** - * struct system_time_snapshot - Simultaneous time capture of CLOCK_MONOTONIC_RAW, + * struct system_time_snapshot - Simultaneous time capture of monotonic raw time, * a selected CLOCK_* and the clocksource counter value * @cycles: Clocksource counter value to produce the system times * @hw_cycles: For derived clocksources, the hardware counter value from @@ -289,6 +289,10 @@ static inline bool ktime_get_aux_ts64(clockid_t id, struct timespec64 *kt) { ret * @clock_was_set_seq: The sequence number of clock-was-set events * @cs_was_changed_seq: The sequence number of clocksource change events * @valid: True if the snapshot is valid + * + * @monoraw is CLOCK_MONOTONIC_RAW for system time CLOCK ids. For CLOCK_AUX$N + * clock ids it's the monotonic raw time related to the AUX clock, which is + * CLOCK_MONOTONIC_RAW plus a AUX clock specific offset. */ struct system_time_snapshot { u64 cycles; @@ -326,6 +330,10 @@ struct system_counterval_t { * @sys_counter: Clocksource counter value simultaneous with device time * @sys_systime: System time for @clock_id * @sys_monoraw: Monotonic raw simultaneous with device time + * + * @sys_monoraw is CLOCK_MONOTONIC_RAW for system time CLOCK ids. For + * CLOCK_AUX$N clock ids it's the monotonic raw time related to the AUX clock, + * which is CLOCK_MONOTONIC_RAW plus a AUX clock specific offset. */ struct system_device_crosststamp { clockid_t clock_id; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index b1b5ec43c0f2..5985d6652c1d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1202,10 +1202,21 @@ static inline u64 tk_clock_read_snapshot(const struct tk_read_base *tkr, /** * ktime_get_snapshot_id - Simultaneously snapshot a given clock ID with - * CLOCK_MONOTONIC_RAW and the underlying + * the corresponding monotonic raw and the underlying * clocksource counter value. * @clock_id: The clock ID to snapshot * @systime_snapshot: Pointer to struct receiving the system time snapshot + * + * For the system time keeping clocks (REALTIME, MONOTONIC and BOOTTIME) the + * monotonic raw clock is CLOCK_MONOTONIC_RAW. For AUX clocks this is the + * monotonic raw clock related to the AUX clock. These AUX clock related + * monotonic raw clocks have a strict linear offset to the system time + * CLOCK_MONOTONIC_RAW: + * + * MONOTONIC_RAW(AUX$N) = CLOCK_MONOTONIC_RAW(system) + offset(AUX$N) + * + * The offset is established when a AUX clock is initialized, but it is + * currently not accessible. */ void ktime_get_snapshot_id(clockid_t clock_id, struct system_time_snapshot *systime_snapshot) { @@ -1512,6 +1523,9 @@ EXPORT_SYMBOL_GPL(ktime_real_to_base_clock); * @xtstamp: Receives simultaneously captured system and device time * * Reads a timestamp from a device and correlates it to system time + * + * See documentation for ktime_get_snapshot_id() for information about the raw + * monotonic time stamp which is used here. */ int get_device_system_crosststamp(int (*get_time_fn) (ktime_t *device_time, -- cgit From 4fe024eeba34b87b7e7388139d7836cde9928c6a Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Mon, 22 Jun 2026 11:32:54 +0800 Subject: nvme: fix typos in reservation related constants Fix the following spelling errors: - NVMET_PR_NOTIFI_MASK_ALL -> NVMET_PR_NOTIFY_MASK_ALL - NVME_PR_LOG_RESERVATOIN_PREEMPTED -> NVME_PR_LOG_RESERVATION_PREEMPTED - NVME_AEN_RESV_LOG_PAGE_AVALIABLE -> NVME_AEN_RESV_LOG_PAGE_AVAILABLE Signed-off-by: Guixin Liu Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch --- drivers/nvme/target/pr.c | 10 +++++----- include/linux/nvme.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/nvme/target/pr.c b/drivers/nvme/target/pr.c index c71ae46244ff..5dd2f3553d8c 100644 --- a/drivers/nvme/target/pr.c +++ b/drivers/nvme/target/pr.c @@ -8,7 +8,7 @@ #include #include "nvmet.h" -#define NVMET_PR_NOTIFI_MASK_ALL \ +#define NVMET_PR_NOTIFY_MASK_ALL \ (1 << NVME_PR_NOTIFY_BIT_REG_PREEMPTED | \ 1 << NVME_PR_NOTIFY_BIT_RESV_RELEASED | \ 1 << NVME_PR_NOTIFY_BIT_RESV_PREEMPTED) @@ -44,7 +44,7 @@ u16 nvmet_set_feat_resv_notif_mask(struct nvmet_req *req, u32 mask) unsigned long idx; u16 status; - if (mask & ~(NVMET_PR_NOTIFI_MASK_ALL)) { + if (mask & ~(NVMET_PR_NOTIFY_MASK_ALL)) { req->error_loc = offsetof(struct nvme_common_command, cdw11); return NVME_SC_INVALID_FIELD | NVME_STATUS_DNR; } @@ -169,7 +169,7 @@ static void nvmet_pr_resv_released(struct nvmet_pr *pr, uuid_t *hostid) nvmet_pr_add_resv_log(ctrl, NVME_PR_LOG_RESERVATION_RELEASED, ns->nsid); nvmet_add_async_event(ctrl, NVME_AER_CSS, - NVME_AEN_RESV_LOG_PAGE_AVALIABLE, + NVME_AEN_RESV_LOG_PAGE_AVAILABLE, NVME_LOG_RESERVATION); } } @@ -188,7 +188,7 @@ static void nvmet_pr_send_event_to_host(struct nvmet_pr *pr, uuid_t *hostid, if (uuid_equal(hostid, &ctrl->hostid)) { nvmet_pr_add_resv_log(ctrl, log_type, ns->nsid); nvmet_add_async_event(ctrl, NVME_AER_CSS, - NVME_AEN_RESV_LOG_PAGE_AVALIABLE, + NVME_AEN_RESV_LOG_PAGE_AVAILABLE, NVME_LOG_RESERVATION); } } @@ -201,7 +201,7 @@ static void nvmet_pr_resv_preempted(struct nvmet_pr *pr, uuid_t *hostid) return; nvmet_pr_send_event_to_host(pr, hostid, - NVME_PR_LOG_RESERVATOIN_PREEMPTED); + NVME_PR_LOG_RESERVATION_PREEMPTED); } static void nvmet_pr_registration_preempted(struct nvmet_pr *pr, diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 041f30931a90..91ce434a7e8d 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -2272,14 +2272,14 @@ struct nvme_completion { #define NVME_TERTIARY(ver) ((ver) & 0xff) enum { - NVME_AEN_RESV_LOG_PAGE_AVALIABLE = 0x00, + NVME_AEN_RESV_LOG_PAGE_AVAILABLE = 0x00, }; enum { NVME_PR_LOG_EMPTY_LOG_PAGE = 0x00, NVME_PR_LOG_REGISTRATION_PREEMPTED = 0x01, NVME_PR_LOG_RESERVATION_RELEASED = 0x02, - NVME_PR_LOG_RESERVATOIN_PREEMPTED = 0x03, + NVME_PR_LOG_RESERVATION_PREEMPTED = 0x03, }; enum { -- cgit From 7803dfda924f3c892ca93aab34a93619a67b7fe2 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Wed, 17 Jun 2026 16:37:53 +0530 Subject: dt-bindings: clock: qcom: Add EVA clock and reset controller for Glymur SoC Add the device tree bindings for the enhanced video analytics(EVA) clock controller which is required on Qualcomm Glymur SoC. The controller provides clocks, resets and power domains for the EVA subsystem. Reviewed-by: Rob Herring (Arm) Signed-off-by: Taniya Das Link: https://lore.kernel.org/r/20260617-evacc_glymur-v2-2-905108dacaaa@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,glymur-evacc.yaml | 72 ++++++++++++++++++++++ include/dt-bindings/clock/qcom,glymur-evacc.h | 38 ++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,glymur-evacc.yaml create mode 100644 include/dt-bindings/clock/qcom,glymur-evacc.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,glymur-evacc.yaml b/Documentation/devicetree/bindings/clock/qcom,glymur-evacc.yaml new file mode 100644 index 000000000000..fb0bc1acc920 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,glymur-evacc.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,glymur-evacc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm EVA Clock & Reset Controller on Glymur SoC + +maintainers: + - Taniya Das + +description: | + Qualcomm EVA clock control module which supports the clocks, resets and + power domains for the EVA instances on Glymur SoC. + + See also: + - include/dt-bindings/clock/qcom,glymur-evacc.h + +properties: + compatible: + const: qcom,glymur-evacc + + clocks: + items: + - description: Interface clock from GCC + - description: Board XO source + - description: Sleep clock source + + power-domains: + items: + - description: MMCX power domain + - description: MXC power domain + + required-opps: + description: + Required OPP nodes for the MMCX and MXC power domains. + items: + - description: MMCX performance point + - description: MXC performance point + +required: + - compatible + - clocks + - power-domains + - required-opps + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + clock-controller@ab00000 { + compatible = "qcom,glymur-evacc"; + reg = <0x0ab00000 0x10000>; + clocks = <&gcc GCC_EVA_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>; + power-domains = <&rpmhpd RPMHPD_MMCX>, + <&rpmhpd RPMHPD_MXC>; + required-opps = <&rpmhpd_opp_low_svs>, + <&rpmhpd_opp_low_svs>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; +... diff --git a/include/dt-bindings/clock/qcom,glymur-evacc.h b/include/dt-bindings/clock/qcom,glymur-evacc.h new file mode 100644 index 000000000000..35a7b4550351 --- /dev/null +++ b/include/dt-bindings/clock/qcom,glymur-evacc.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_EVACC_GLYMUR_H +#define _DT_BINDINGS_CLK_QCOM_EVACC_GLYMUR_H + +/* EVA_CC clocks */ +#define EVA_CC_AHB_CLK 0 +#define EVA_CC_AHB_CLK_SRC 1 +#define EVA_CC_MVS0_CLK 2 +#define EVA_CC_MVS0_CLK_SRC 3 +#define EVA_CC_MVS0_DIV_CLK_SRC 4 +#define EVA_CC_MVS0_FREERUN_CLK 5 +#define EVA_CC_MVS0_SHIFT_CLK 6 +#define EVA_CC_MVS0C_CLK 7 +#define EVA_CC_MVS0C_DIV2_DIV_CLK_SRC 8 +#define EVA_CC_MVS0C_FREERUN_CLK 9 +#define EVA_CC_MVS0C_SHIFT_CLK 10 +#define EVA_CC_PLL0 11 +#define EVA_CC_SLEEP_CLK 12 +#define EVA_CC_SLEEP_CLK_SRC 13 +#define EVA_CC_XO_CLK 14 +#define EVA_CC_XO_CLK_SRC 15 + +/* EVA_CC power domains */ +#define EVA_CC_MVS0_GDSC 0 +#define EVA_CC_MVS0C_GDSC 1 + +/* EVA_CC resets */ +#define EVA_CC_INTERFACE_BCR 0 +#define EVA_CC_MVS0_BCR 1 +#define EVA_CC_MVS0C_CLK_ARES 2 +#define EVA_CC_MVS0C_BCR 3 +#define EVA_CC_MVS0C_FREERUN_CLK_ARES 4 + +#endif /* _DT_BINDINGS_CLK_QCOM_EVACC_GLYMUR_H */ -- cgit From d9ef4ed45866531d37a2e9c62dc795cd678b5b0c Mon Sep 17 00:00:00 2001 From: Lin Li Date: Thu, 2 Jul 2026 10:08:07 -0700 Subject: dt-bindings: clock: qcom: Add Hawi video clock controller Add device tree bindings for the video clock controller on Qualcomm Hawi SoC. Signed-off-by: Lin Li Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260702-hawi-videocc-v1-1-6c1e640b0954@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,sm8450-videocc.yaml | 3 + include/dt-bindings/clock/qcom,hawi-videocc.h | 64 ++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,hawi-videocc.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml index 5d77029bfaf8..a6fd1992d6d2 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml @@ -16,6 +16,7 @@ description: | See also: include/dt-bindings/clock/qcom,glymur-videocc.h + include/dt-bindings/clock/qcom,hawi-videocc.h include/dt-bindings/clock/qcom,kaanapali-videocc.h include/dt-bindings/clock/qcom,sm8450-videocc.h include/dt-bindings/clock/qcom,sm8650-videocc.h @@ -26,6 +27,7 @@ properties: compatible: enum: - qcom,glymur-videocc + - qcom,hawi-videocc - qcom,kaanapali-videocc - qcom,sm8450-videocc - qcom,sm8475-videocc @@ -68,6 +70,7 @@ allOf: contains: enum: - qcom,glymur-videocc + - qcom,hawi-videocc - qcom,kaanapali-videocc - qcom,sm8450-videocc - qcom,sm8550-videocc diff --git a/include/dt-bindings/clock/qcom,hawi-videocc.h b/include/dt-bindings/clock/qcom,hawi-videocc.h new file mode 100644 index 000000000000..8c97079ff1a7 --- /dev/null +++ b/include/dt-bindings/clock/qcom,hawi-videocc.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_VIDEO_CC_HAWI_H +#define _DT_BINDINGS_CLK_QCOM_VIDEO_CC_HAWI_H + +/* VIDEO_CC clocks */ +#define VIDEO_CC_AHB_CLK 0 +#define VIDEO_CC_AHB_CLK_SRC 1 +#define VIDEO_CC_CX_AXI0_CLK 2 +#define VIDEO_CC_CX_DBGCH_XO_CLK 3 +#define VIDEO_CC_CX_XO_CLK 4 +#define VIDEO_CC_DBGCH_XO_CLK 5 +#define VIDEO_CC_MVS0_CLK 6 +#define VIDEO_CC_MVS0_CLK_SRC 7 +#define VIDEO_CC_MVS0_SHIFT_CLK 8 +#define VIDEO_CC_MVS0_VPP0_CLK 9 +#define VIDEO_CC_MVS0_VPP0_VPP1_GATING_CLK 10 +#define VIDEO_CC_MVS0_VPP1_CLK 11 +#define VIDEO_CC_MVS0A_CLK 12 +#define VIDEO_CC_MVS0A_CLK_SRC 13 +#define VIDEO_CC_MVS0B_CLK 14 +#define VIDEO_CC_MVS0B_CLK_SRC 15 +#define VIDEO_CC_MVS0C_CLK 16 +#define VIDEO_CC_MVS0C_CLK_SRC 17 +#define VIDEO_CC_MVS0C_CTL_FREERUN_CLK 18 +#define VIDEO_CC_MVS0C_DEBUG_CLK 19 +#define VIDEO_CC_MVS0C_FREERUN_CLK 20 +#define VIDEO_CC_MVS0C_SHIFT_CLK 21 +#define VIDEO_CC_PLL0 22 +#define VIDEO_CC_PLL0_OUT_EVEN 23 +#define VIDEO_CC_PLL1 24 +#define VIDEO_CC_PLL2 25 +#define VIDEO_CC_PLL3 26 +#define VIDEO_CC_SLEEP_CLK 27 +#define VIDEO_CC_XO_CLK 28 +#define VIDEO_CC_XO_CLK_SRC 29 + +/* VIDEO_CC power domains */ +#define VIDEO_CC_AXI0_CX_INT_GDSC 0 +#define VIDEO_CC_MM_INT_GDSC 1 +#define VIDEO_CC_MVS0_GDSC 2 +#define VIDEO_CC_MVS0_VPP0_GDSC 3 +#define VIDEO_CC_MVS0_VPP1_GDSC 4 +#define VIDEO_CC_MVS0A_GDSC 5 +#define VIDEO_CC_MVS0C_GDSC 6 + +/* VIDEO_CC resets */ +#define VIDEO_CC_AXI0_CX_INT_BCR 0 +#define VIDEO_CC_INTERFACE_BCR 1 +#define VIDEO_CC_MM_INT_BCR 2 +#define VIDEO_CC_MVS0_BCR 3 +#define VIDEO_CC_MVS0_VPP0_BCR 4 +#define VIDEO_CC_MVS0_VPP1_BCR 5 +#define VIDEO_CC_MVS0A_BCR 6 +#define VIDEO_CC_MVS0C_CLK_ARES 7 +#define VIDEO_CC_MVS0C_BCR 8 +#define VIDEO_CC_MVS0C_CTL_FREERUN_CLK_ARES 9 +#define VIDEO_CC_MVS0C_FREERUN_CLK_ARES 10 +#define VIDEO_CC_XO_CLK_ARES 11 + +#endif -- cgit From 08412b8c707fdbccb7bf2116f0554fe09113cd53 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Mon, 6 Jul 2026 17:37:49 +0200 Subject: x86/setup: do not include kexec_handover.h from asm/setup.h x86 asm/setup.h includes linux/kexec_handover.h. This is because it is used by setup.c and kaslr.c. But this inclusion is problematic. The header is included in many places, so it results in the KHO header being propagated there. Also, the setup header is used by realmode code. If KHO header includes things like mm.h, it causes a big dump of compilation failures. Nothing in setup.h uses anything from KHO. Remove the header from setup.h, and directly include it in setup.c. which does use things from KHO. Since kaslr.c is a part of the decompressor, avoid including linux headers there directly. Instead, split out struct kho_scratch, which is the only thing the kaslr.c uses, and move it to include/asm-generic/kexec_handover.h. This should also help reduce files recompiled when kexec_handover.h changes. Signed-off-by: Pratyush Yadav (Google) Acked-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260706153751.1166003-1-pratyush@kernel.org Signed-off-by: Mike Rapoport (Microsoft) --- MAINTAINERS | 1 + arch/x86/boot/compressed/kaslr.c | 2 ++ arch/x86/include/asm/Kbuild | 1 + arch/x86/include/asm/setup.h | 2 -- arch/x86/kernel/setup.c | 1 + include/asm-generic/kexec_handover.h | 12 ++++++++++++ include/linux/kexec_handover.h | 6 +----- 7 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 include/asm-generic/kexec_handover.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..a3ed337e827d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14334,6 +14334,7 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git F: Documentation/admin-guide/mm/kho.rst F: Documentation/core-api/kho/* +F: include/asm-generic/kexec_handover.h F: include/linux/kexec_handover.h F: include/linux/kho/ F: include/linux/kho_block.h diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c index 8e4bf5365ac6..22267a83e064 100644 --- a/arch/x86/boot/compressed/kaslr.c +++ b/arch/x86/boot/compressed/kaslr.c @@ -32,6 +32,8 @@ #include /* For COMMAND_LINE_SIZE */ #undef _SETUP +#include + extern unsigned long get_cmd_line_ptr(void); /* Simplified build-specific string for starting entropy. */ diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild index 078fd2c0d69d..47ef8cb482e3 100644 --- a/arch/x86/include/asm/Kbuild +++ b/arch/x86/include/asm/Kbuild @@ -15,3 +15,4 @@ generic-y += fprobe.h generic-y += mcs_spinlock.h generic-y += mmzone.h generic-y += ring_buffer.h +generic-y += kexec_handover.h diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h index 914eb32581c7..895d09faaf83 100644 --- a/arch/x86/include/asm/setup.h +++ b/arch/x86/include/asm/setup.h @@ -69,8 +69,6 @@ extern void x86_ce4100_early_setup(void); static inline void x86_ce4100_early_setup(void) { } #endif -#include - #ifndef _SETUP #include diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 46882ce79c3a..5ebb521e136d 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/include/asm-generic/kexec_handover.h b/include/asm-generic/kexec_handover.h new file mode 100644 index 000000000000..50839fb5ee8e --- /dev/null +++ b/include/asm-generic/kexec_handover.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_GENERIC_KEXEC_HANDOVER_H +#define __ASM_GENERIC_KEXEC_HANDOVER_H + +#include + +struct kho_scratch { + phys_addr_t addr; + phys_addr_t size; +}; + +#endif /* __ASM_GENERIC_KEXEC_HANDOVER_H */ diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h index 8968c56d2d73..48a9793c2b76 100644 --- a/include/linux/kexec_handover.h +++ b/include/linux/kexec_handover.h @@ -5,11 +5,7 @@ #include #include #include - -struct kho_scratch { - phys_addr_t addr; - phys_addr_t size; -}; +#include struct kho_vmalloc; -- cgit From ac798f757d6475dc6fee2ec899980d6740714596 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 6 Jul 2026 22:29:31 +0300 Subject: wifi: mac80211: Route (Re)association req/response to per-STA queue An association request or response frame is generally delivered to the driver without a TX queue object. However, with drivers that do encryption offload and couple the key with a transmit queue, this means that the frames are not being encrypted. Fix this by routing the association frames to the management TXQ. This will allow the driver to set up the required resources before transmitting the association frame, e.g., set up keys etc. Signed-off-by: Ilan Peer Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260706222925.febcc62f485c.Iff932880e4b98232cbf6ba405fbb90d650a85381@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 11 +++++++++++ net/mac80211/ieee80211_i.h | 4 +--- net/mac80211/tx.c | 7 +++++++ 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 084ad45aa2d8..26e674038865 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -556,6 +556,17 @@ static inline bool ieee80211_is_reassoc_resp(__le16 fc) cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); } +/** + * ieee80211_is_assoc - check if (Re)association request/response frame + * @fc: frame control bytes in little-endian byteorder + * Return: whether or not the frame is an (re)association request or response + */ +static inline bool ieee80211_is_assoc(__le16 fc) +{ + return ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || + ieee80211_is_assoc_resp(fc) || ieee80211_is_reassoc_resp(fc); +} + /** * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ * @fc: frame control bytes in little-endian byteorder diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 34a9ea8b6f85..d585820245dd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2470,9 +2470,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, static inline bool ieee80211_require_encrypted_assoc(__le16 fc, struct sta_info *sta) { - return (sta && sta->sta.epp_peer && - (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || - ieee80211_is_assoc_resp(fc) || ieee80211_is_reassoc_resp(fc))); + return sta && sta->sta.epp_peer && ieee80211_is_assoc(fc); } /* sta_out needs to be checked for ERR_PTR() before using */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c13b209fad47..42cfd76850b8 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1309,10 +1309,17 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)) return NULL; + /* + * While (re)association request/response frames are not considered + * bufferable MMPDUs, use the TXQ abstraction for the transmission of + * these frames. This is specifically useful for drivers that might + * associate other resources with the TXQ, e.g., encryption keys etc. + */ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && unlikely(!ieee80211_is_data_present(hdr->frame_control))) { if ((!ieee80211_is_mgmt(hdr->frame_control) || ieee80211_is_bufferable_mmpdu(skb) || + ieee80211_is_assoc(hdr->frame_control) || vif->type == NL80211_IFTYPE_STATION || vif->type == NL80211_IFTYPE_NAN || vif->type == NL80211_IFTYPE_NAN_DATA) && -- cgit From 6041421dd0876356794d49db00b65bb831066ad6 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 2 Jul 2026 04:44:14 +0000 Subject: ipv4: fib: Define fib_table_hash_lock under CONFIG_IP_MULTIPLE_TABLES. When CONFIG_IP_MULTIPLE_TABLES is disabled, fib_new_table() is fib_get_table(), and no new table is created. Let's move net->ipv4.fib_table_hash_lock under CONFIG_IP_MULTIPLE_TABLES. While at it, netns_ipv4_sysctl.rst is updated. Suggested-by: Ido Schimmel Signed-off-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260702044437.591864-2-kuniyu@google.com Signed-off-by: Paolo Abeni --- Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst | 1 + include/net/netns/ipv4.h | 2 +- net/ipv4/fib_frontend.c | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst b/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst index 6dbd97d435e9..3dc03bff739e 100644 --- a/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst +++ b/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst @@ -22,6 +22,7 @@ struct_mutex ra_mutex struct_fib_rules_ops* rules_ops struct_fib_table fib_main struct_fib_table fib_default +spinlock_t fib_table_hash_lock unsigned_int fib_rules_require_fldissect bool fib_has_custom_rules bool fib_has_custom_local_routes diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 59506320558a..cb7f8bf15671 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -118,6 +118,7 @@ struct netns_ipv4 { struct fib_rules_ops *rules_ops; struct fib_table __rcu *fib_main; struct fib_table __rcu *fib_default; + spinlock_t fib_table_hash_lock; unsigned int fib_rules_require_fldissect; bool fib_has_custom_rules; #endif @@ -127,7 +128,6 @@ struct netns_ipv4 { atomic_t fib_num_tclassid_users; #endif struct hlist_head *fib_table_hash; - spinlock_t fib_table_hash_lock; struct sock *fibnl; struct hlist_head *fib_info_hash; unsigned int fib_info_hash_bits; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index a5e739d32d59..8a3dc04e8cac 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1584,7 +1584,10 @@ static int __net_init ip_fib_net_init(struct net *net) net->ipv4.sysctl_fib_multipath_hash_fields = FIB_MULTIPATH_HASH_FIELD_DEFAULT_MASK; #endif + +#ifdef CONFIG_IP_MULTIPLE_TABLES spin_lock_init(&net->ipv4.fib_table_hash_lock); +#endif /* Avoid false sharing : Use at least a full cache line */ size = max_t(size_t, size, L1_CACHE_BYTES); -- cgit From b5f90fd4580ce71aa24ac9afcf5c9b4fa8121518 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Wed, 1 Jul 2026 10:32:45 +0300 Subject: devlink: Add parent dev to devlink API Upcoming changes to the rate commands need the parent devlink specified. This change adds a nested 'parent-dev' attribute to the API and helpers to obtain and put a reference to the parent devlink instance in info->ctx. To avoid deadlocks, the parent devlink is unlocked before obtaining the main devlink instance that is the target of the request. A reference to the parent is kept until the end of the request to avoid it suddenly disappearing. This means that this reference is of limited use without additional protection. Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Jiri Pirko Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260701073254.754518-6-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- Documentation/netlink/specs/devlink.yaml | 20 ++++++++++++++++++ include/uapi/linux/devlink.h | 2 ++ net/devlink/devl_internal.h | 3 +++ net/devlink/netlink.c | 36 +++++++++++++++++++++++++++----- 4 files changed, 56 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml index 52ad1e7805d1..13d960b3abb1 100644 --- a/Documentation/netlink/specs/devlink.yaml +++ b/Documentation/netlink/specs/devlink.yaml @@ -895,6 +895,16 @@ attribute-sets: resource-dump response. Bit 0 (dev) selects device-level resources; bit 1 (port) selects port-level resources. When absent all classes are returned. + - + name: parent-dev + type: nest + nested-attributes: dl-parent-dev + doc: | + Identifies the devlink instance which owns the parent rate node. + Used with rate-set and rate-new to parent a rate object to a node on + a different devlink instance, enabling cross-device rate scheduling. + When absent, the parent node is resolved on the same instance. + - name: dl-dev-stats subset-of: devlink @@ -1317,6 +1327,16 @@ attribute-sets: Specifies the bandwidth share assigned to the Traffic Class. The bandwidth for the traffic class is determined in proportion to the sum of the shares of all configured classes. + - + name: dl-parent-dev + subset-of: devlink + attributes: + - + name: bus-name + - + name: dev-name + - + name: index operations: enum-model: directional diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index ca713bcc47b9..a6801feb7744 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -648,6 +648,8 @@ enum devlink_attr { DEVLINK_ATTR_INDEX, /* uint */ DEVLINK_ATTR_RESOURCE_SCOPE_MASK, /* u32 */ + DEVLINK_ATTR_PARENT_DEV, /* nested */ + /* Add new attributes above here, update the spec in * Documentation/netlink/specs/devlink.yaml and re-generate * net/devlink/netlink_gen.c. diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h index 52c8bf359dd4..cdf894ba5a9d 100644 --- a/net/devlink/devl_internal.h +++ b/net/devlink/devl_internal.h @@ -154,6 +154,7 @@ int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink, struct devlink_nl_ctx { struct devlink *devlink; struct devlink_port *devlink_port; + struct devlink *parent_devlink; }; static inline struct devlink_nl_ctx * @@ -197,6 +198,8 @@ typedef int devlink_nl_dump_one_func_t(struct sk_buff *msg, struct devlink * devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs, bool dev_lock); +struct devlink * +devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs); int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb, devlink_nl_dump_one_func_t *dump_one); diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c index f0a857e286bc..5a057dc86b0f 100644 --- a/net/devlink/netlink.c +++ b/net/devlink/netlink.c @@ -12,6 +12,7 @@ #define DEVLINK_NL_FLAG_NEED_PORT BIT(0) #define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1) #define DEVLINK_NL_FLAG_NEED_DEV_LOCK BIT(2) +#define DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV BIT(3) static const struct genl_multicast_group devlink_nl_mcgrps[] = { [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME }, @@ -239,19 +240,39 @@ found: return ERR_PTR(-ENODEV); } +struct devlink * +devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info, u8 flags) { + bool parent_dev = flags & DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV; bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK; + struct devlink *devlink, *parent_devlink = NULL; + struct net *net = genl_info_net(info); + struct nlattr **attrs = info->attrs; struct devlink_port *devlink_port; - struct devlink *devlink; int err; - devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs, - dev_lock); - if (IS_ERR(devlink)) - return PTR_ERR(devlink); + if (parent_dev && attrs[DEVLINK_ATTR_PARENT_DEV]) { + parent_devlink = devlink_get_parent_from_attrs_lock(net, attrs); + if (IS_ERR(parent_devlink)) + return PTR_ERR(parent_devlink); + devlink_nl_ctx(info)->parent_devlink = parent_devlink; + /* Drop the parent devlink lock but don't release the reference. + * This will keep it alive until the end of the request. + */ + devl_unlock(parent_devlink); + } + devlink = devlink_get_from_attrs_lock(net, attrs, dev_lock); + if (IS_ERR(devlink)) { + err = PTR_ERR(devlink); + goto parent_put; + } devlink_nl_ctx(info)->devlink = devlink; if (flags & DEVLINK_NL_FLAG_NEED_PORT) { devlink_port = devlink_port_get_from_info(devlink, info); @@ -270,6 +291,9 @@ static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info, unlock: devl_dev_unlock(devlink, dev_lock); devlink_put(devlink); +parent_put: + if (parent_dev && parent_devlink) + devlink_put(parent_devlink); return err; } @@ -307,6 +331,8 @@ static void __devlink_nl_post_doit(struct sk_buff *skb, struct genl_info *info, devlink = devlink_nl_ctx(info)->devlink; devl_dev_unlock(devlink, dev_lock); devlink_put(devlink); + if (devlink_nl_ctx(info)->parent_devlink) + devlink_put(devlink_nl_ctx(info)->parent_devlink); } void devlink_nl_post_doit(const struct genl_split_ops *ops, -- cgit From 6bbd1bce3099eec42cb3e90099f5f9910c0dc84f Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Wed, 1 Jul 2026 10:32:47 +0300 Subject: devlink: Allow rate node parents from other devlinks This commit makes use of the building blocks previously added to implement cross-device rate nodes. A new 'supported_cross_device_rate_nodes' bool is added to devlink_ops which lets drivers advertise support for cross-device rate objects. If enabled and if there is a common shared devlink instance, then: - all rate objects will be stored in the top-most common nested instance and - rate objects can have parents from other devices sharing the same common instance. Storing rates in the common shared ancestor is safe, because it is reference counted by its nested devlink instances, so it's guaranteed to outlive them. Furthermore, the shared devlink infra guarantees a given nested devlink hierarchy is managed by the same driver. The parent devlink from info->ctx is not locked, so none of its mutable fields can be used. But parent setting only requires comparing devlink pointer comparisons. Additionally, since the shared devlink is locked, other rate operations cannot concurrently happen. Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Jiri Pirko Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260701073254.754518-8-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- Documentation/networking/devlink/devlink-port.rst | 2 + include/net/devlink.h | 9 +++ net/devlink/core.c | 4 +- net/devlink/rate.c | 86 ++++++++++++++++++++--- 4 files changed, 92 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 9374ebe70f48..18aca77006d5 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -420,6 +420,8 @@ API allows to configure following rate object's parameters: Parent node name. Parent node rate limits are considered as additional limits to all node children limits. ``tx_max`` is an upper limit for children. ``tx_share`` is a total bandwidth distributed among children. + If the device supports cross-function scheduling, the parent can be from a + different function of the same underlying device. ``tc_bw`` Allow users to set the bandwidth allocation per traffic class on rate diff --git a/include/net/devlink.h b/include/net/devlink.h index dd546dbd57cf..ffe1ad5fb70b 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1594,6 +1594,15 @@ struct devlink_ops { struct devlink_rate *parent, void *priv_child, void *priv_parent, struct netlink_ext_ack *extack); + /* Indicates if cross-device rate nodes are supported. + * This also requires a shared common ancestor object all devices that + * could share rate nodes are nested in. + * If enabled, rate operations may be called on an instance with only + * the common ancestor lock held and *without that instance lock held*. + * It is the driver's responsibility to ensure proper serialization + * with other operations. + */ + bool supported_cross_device_rate_nodes; /** * selftests_check() - queries if selftest is supported * @devlink: devlink instance diff --git a/net/devlink/core.c b/net/devlink/core.c index ee26c50b4118..c53a42e17a58 100644 --- a/net/devlink/core.c +++ b/net/devlink/core.c @@ -534,6 +534,9 @@ void devlink_free(struct devlink *devlink) { ASSERT_DEVLINK_NOT_REGISTERED(devlink); + devl_lock(devlink); + WARN_ON(devlink_rates_check(devlink, NULL, NULL)); + devl_unlock(devlink); devlink_rel_put(devlink); WARN_ON(!list_empty(&devlink->trap_policer_list)); @@ -544,7 +547,6 @@ void devlink_free(struct devlink *devlink) WARN_ON(!list_empty(&devlink->resource_list)); WARN_ON(!list_empty(&devlink->dpipe_table_list)); WARN_ON(!list_empty(&devlink->sb_list)); - WARN_ON(devlink_rates_check(devlink, NULL, NULL)); WARN_ON(!list_empty(&devlink->linecard_list)); WARN_ON(!xa_empty(&devlink->ports)); diff --git a/net/devlink/rate.c b/net/devlink/rate.c index 78a59d79c2ea..e727c8b8b33e 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -30,14 +30,42 @@ devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info) return devlink_rate ?: ERR_PTR(-ENODEV); } +/* Repeatedly walks the nested devlink chain while cross device rate nodes are + * supported and finds the topmost instance where rates should be stored. + * That instance is locked, referenced and returned. + * When cross device rate nodes aren't supported the original devlink instance + * is returned. + */ static struct devlink *devl_rate_lock(struct devlink *devlink) { - return devlink; + struct devlink *rate_devlink = devlink, *parent; + + devl_assert_locked(devlink); + + while (rate_devlink->ops && + rate_devlink->ops->supported_cross_device_rate_nodes) { + parent = devlink_nested_in_get_lock(rate_devlink); + if (!parent) + break; + if (rate_devlink != devlink) { + /* Unlock intermediate instances. */ + devl_unlock(rate_devlink); + devlink_put(rate_devlink); + } + rate_devlink = parent; + } + return rate_devlink; } +/* Unlocks and puts 'rate devlink' if different than 'devlink'. */ static void devl_rate_unlock(struct devlink *devlink, struct devlink *rate_devlink) { + if (devlink == rate_devlink) + return; + + devl_unlock(rate_devlink); + devlink_put(rate_devlink); } static struct devlink_rate * @@ -121,6 +149,25 @@ nla_put_failure: return -EMSGSIZE; } +static int devlink_nl_rate_parent_fill(struct sk_buff *msg, + struct devlink_rate *devlink_rate) +{ + struct devlink_rate *parent = devlink_rate->parent; + struct devlink *devlink = parent->devlink; + + if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME, + parent->name)) + return -EMSGSIZE; + + if (devlink != devlink_rate->devlink && + devlink_nl_put_nested_handle(msg, + devlink_net(devlink_rate->devlink), + devlink, DEVLINK_ATTR_PARENT_DEV)) + return -EMSGSIZE; + + return 0; +} + static int devlink_nl_rate_fill(struct sk_buff *msg, struct devlink_rate *devlink_rate, enum devlink_command cmd, u32 portid, u32 seq, @@ -165,10 +212,9 @@ static int devlink_nl_rate_fill(struct sk_buff *msg, devlink_rate->tx_weight)) goto nla_put_failure; - if (devlink_rate->parent) - if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME, - devlink_rate->parent->name)) - goto nla_put_failure; + if (devlink_rate->parent && + devlink_nl_rate_parent_fill(msg, devlink_rate)) + goto nla_put_failure; if (devlink_rate_put_tc_bws(msg, devlink_rate->tc_bw)) goto nla_put_failure; @@ -322,13 +368,14 @@ devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, struct genl_info *info, struct nlattr *nla_parent) { - struct devlink *devlink = devlink_rate->devlink; + struct devlink *devlink = devlink_rate->devlink, *parent_devlink; const char *parent_name = nla_data(nla_parent); const struct devlink_ops *ops = devlink->ops; size_t len = strlen(parent_name); struct devlink_rate *parent; int err = -EOPNOTSUPP; + parent_devlink = devlink_nl_ctx(info)->parent_devlink ? : devlink; parent = devlink_rate->parent; if (parent && !len) { @@ -346,7 +393,13 @@ devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, refcount_dec(&parent->refcnt); devlink_rate->parent = NULL; } else if (len) { - parent = devlink_rate_node_get_by_name(rate_devlink, devlink, + /* parent_devlink (when different than devlink) isn't locked, + * but the rate node devlink instance is, so nobody from the + * same group of devices sharing rates could change the used + * fields or unregister the parent. + */ + parent = devlink_rate_node_get_by_name(rate_devlink, + parent_devlink, parent_name); if (IS_ERR(parent)) return -ENODEV; @@ -633,9 +686,11 @@ static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info) { - struct devlink *rate_devlink, *devlink = devlink_nl_ctx(info)->devlink; + struct devlink_nl_ctx *ctx = devlink_nl_ctx(info); + struct devlink *devlink = ctx->devlink; struct devlink_rate *devlink_rate; const struct devlink_ops *ops; + struct devlink *rate_devlink; int err; rate_devlink = devl_rate_lock(devlink); @@ -652,6 +707,14 @@ int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info) goto unlock; } + if (ctx->parent_devlink && ctx->parent_devlink != devlink && + !ops->supported_cross_device_rate_nodes) { + NL_SET_ERR_MSG(info->extack, + "Cross-device rate parents aren't supported"); + err = -EOPNOTSUPP; + goto unlock; + } + err = devlink_nl_rate_set(devlink_rate, rate_devlink, ops, info); if (!err) @@ -679,6 +742,13 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info) if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE)) return -EOPNOTSUPP; + if (ctx->parent_devlink && ctx->parent_devlink != devlink && + !ops->supported_cross_device_rate_nodes) { + NL_SET_ERR_MSG(info->extack, + "Cross-device rate parents aren't supported"); + return -EOPNOTSUPP; + } + rate_devlink = devl_rate_lock(devlink); rate_node = devlink_rate_node_get_from_attrs(rate_devlink, devlink, info->attrs); -- cgit From 565de44266e64bec1554e86584083b41ab76d9bd Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Mon, 22 Jun 2026 06:34:45 +0000 Subject: dt-bindings: interconnect: qcom: document the RPMh Network-On-Chip interconnect in Maili SoC Document the RPMh Network-On-Chip interconnect for the Qualcomm Maili SoC. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Signed-off-by: Raviteja Laggyshetty Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260622-maili_icc-v2-1-18b5ac08c04f@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,maili-rpmh.yaml | 127 +++++++++++++++ include/dt-bindings/interconnect/qcom,maili-rpmh.h | 171 +++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml create mode 100644 include/dt-bindings/interconnect/qcom,maili-rpmh.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml new file mode 100644 index 000000000000..3db8d8b23219 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,maili-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Maili SoC + +maintainers: + - Raviteja Laggyshetty + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,maili-rpmh.h + +properties: + compatible: + enum: + - qcom,maili-aggre-noc + - qcom,maili-clk-virt + - qcom,maili-cnoc-main + - qcom,maili-gem-noc + - qcom,maili-llclpi-noc + - qcom,maili-lpass-ag-noc + - qcom,maili-lpass-lpiaon-noc + - qcom,maili-lpass-lpicx-noc + - qcom,maili-mc-virt + - qcom,maili-mmss-noc + - qcom,maili-nsp-noc + - qcom,maili-pcie-anoc + - qcom,maili-stdst-cfg + - qcom,maili-stdst-main + - qcom,maili-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 3 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-clk-virt + - qcom,maili-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-aggre-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB3 PRIM AXI clock + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-pcie-anoc + then: + properties: + clocks: + items: + - description: aggre-NOC PCIe AXI clock + - description: cfg-NOC PCIe a-NOC AHB clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-aggre-noc + - qcom,maili-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + gem_noc: interconnect@31100000 { + compatible = "qcom,maili-gem-noc"; + reg = <0x31100000 0x160200>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre_noc: interconnect@f00000 { + compatible = "qcom,maili-aggre-noc"; + reg = <0x00f00000 0x56200>; + #interconnect-cells = <2>; + clocks = <&gcc_phy_axi_clk>, + <&gcc_prim_axi_clk>, + <&rpmhcc_ipa_clk>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/include/dt-bindings/interconnect/qcom,maili-rpmh.h b/include/dt-bindings/interconnect/qcom,maili-rpmh.h new file mode 100644 index 000000000000..ae3e48b14eab --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,maili-rpmh.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MAILI_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MAILI_H + +#define MASTER_QSPI_0 0 +#define MASTER_QUP_2 1 +#define MASTER_QUP_3 2 +#define MASTER_QUP_4 3 +#define MASTER_QUP_5 4 +#define MASTER_CRYPTO 5 +#define MASTER_IPA 6 +#define MASTER_QUP_1 7 +#define MASTER_SOCCP_PROC 8 +#define MASTER_QDSS_ETR 9 +#define MASTER_QDSS_ETR_1 10 +#define MASTER_SDCC_2 11 +#define MASTER_SDCC_4 12 +#define MASTER_UFS_MEM 13 +#define MASTER_USB3 14 +#define SLAVE_A1NOC_SNOC 15 + +#define MASTER_DDR_EFF_VETO 0 +#define MASTER_QUP_CORE_0 1 +#define MASTER_QUP_CORE_1 2 +#define MASTER_QUP_CORE_2 3 +#define MASTER_QUP_CORE_3 4 +#define MASTER_QUP_CORE_4 5 +#define MASTER_QUP_CORE_5 6 +#define SLAVE_DDR_EFF_VETO 7 +#define SLAVE_QUP_CORE_0 8 +#define SLAVE_QUP_CORE_1 9 +#define SLAVE_QUP_CORE_2 10 +#define SLAVE_QUP_CORE_3 11 +#define SLAVE_QUP_CORE_4 12 +#define SLAVE_QUP_CORE_5 13 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AOSS 2 +#define SLAVE_IPA_CFG 3 +#define SLAVE_IPC_ROUTER_FENCE 4 +#define SLAVE_SOCCP 5 +#define SLAVE_TME_CFG 6 +#define SLAVE_CNOC_CFG 7 +#define SLAVE_DDRSS_CFG 8 +#define SLAVE_IMEM 9 +#define SLAVE_PCIE_0 10 +#define SLAVE_PCIE_1 11 + +#define MASTER_GIC 0 +#define MASTER_GPU_TCU 1 +#define MASTER_SYS_TCU 2 +#define MASTER_APPSS_PROC 3 +#define MASTER_GFX3D 4 +#define MASTER_LPASS_GEM_NOC 5 +#define MASTER_MSS_PROC 6 +#define MASTER_MNOC_HF_MEM_NOC 7 +#define MASTER_MNOC_SF_MEM_NOC 8 +#define MASTER_COMPUTE_NOC 9 +#define MASTER_ANOC_PCIE_GEM_NOC 10 +#define MASTER_QPACE 11 +#define MASTER_SNOC_SF_MEM_NOC 12 +#define MASTER_WLAN_Q6 13 +#define SLAVE_GEM_NOC_CNOC 14 +#define SLAVE_LLCC 15 +#define SLAVE_MEM_NOC_PCIE_SNOC 16 + +#define MASTER_LPIAON_NOC_LLCLPI_NOC 0 +#define SLAVE_LPASS_LPI_CC 1 +#define SLAVE_LLCC_ISLAND 2 +#define SLAVE_SERVICE_LLCLPI_NOC 3 +#define SLAVE_SERVICE_LLCLPI_NOC_CHIPCX 4 + +#define MASTER_LPIAON_NOC 0 +#define SLAVE_LPASS_GEM_NOC 1 + +#define MASTER_LPASS_LPINOC 0 +#define SLAVE_LPIAON_NOC_LLCLPI_NOC 1 +#define SLAVE_LPIAON_NOC_LPASS_AG_NOC 2 + +#define MASTER_LPASS_PROC 0 +#define SLAVE_LPICX_NOC_LPIAON_NOC 1 + +#define MASTER_LLCC 0 +#define MASTER_DDR_RT 1 +#define SLAVE_EBI1 2 +#define SLAVE_DDR_RT 3 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_NRT_ICP_SF 1 +#define MASTER_CAMNOC_RT_CDM_SF 2 +#define MASTER_CAMNOC_SF 3 +#define MASTER_MDP 4 +#define MASTER_MDSS_DCP 5 +#define MASTER_CDSP_HCP 6 +#define MASTER_VIDEO_CV_PROC 7 +#define MASTER_VIDEO_EVA 8 +#define MASTER_VIDEO_MVP 9 +#define MASTER_VIDEO_V_PROC 10 +#define SLAVE_MNOC_HF_MEM_NOC 11 +#define SLAVE_MNOC_SF_MEM_NOC 12 + +#define MASTER_CDSP_PROC 0 +#define SLAVE_CDSP_MEM_NOC 1 + +#define MASTER_PCIE_ANOC_CFG 0 +#define MASTER_PCIE_0 1 +#define MASTER_PCIE_1 2 +#define SLAVE_ANOC_PCIE_GEM_NOC 3 +#define SLAVE_SERVICE_PCIE_ANOC 4 + +#define MASTER_CFG_CENTER 0 +#define MASTER_CFG_EAST 1 +#define MASTER_CFG_MM_HF 2 +#define MASTER_CFG_MM_SF 3 +#define MASTER_CFG_NORTH 4 +#define MASTER_CFG_SOUTH 5 +#define MASTER_CFG_WEST 6 +#define SLAVE_AHB2PHY_SOUTH 7 +#define SLAVE_BOOT_ROM 8 +#define SLAVE_CAMERA_CFG 9 +#define SLAVE_CLK_CTL 10 +#define SLAVE_CRYPTO_CFG 11 +#define SLAVE_DISPLAY_CFG 12 +#define SLAVE_EVA_CFG 13 +#define SLAVE_GFX3D_CFG 14 +#define SLAVE_I2C 15 +#define SLAVE_IMEM_CFG 16 +#define SLAVE_IPC_ROUTER_CFG 17 +#define SLAVE_IRIS_CFG 18 +#define SLAVE_CNOC_MSS 19 +#define SLAVE_PCIE_0_CFG 20 +#define SLAVE_PCIE_1_CFG 21 +#define SLAVE_PRNG 22 +#define SLAVE_QSPI_0 23 +#define SLAVE_QUP_1 24 +#define SLAVE_QUP_2 25 +#define SLAVE_QUP_3 26 +#define SLAVE_QUP_4 27 +#define SLAVE_QUP_5 28 +#define SLAVE_SDCC_2 29 +#define SLAVE_SDCC_4 30 +#define SLAVE_TLMM 31 +#define SLAVE_UFS_MEM_CFG 32 +#define SLAVE_USB3 33 +#define SLAVE_VSENSE_CTRL_CFG 34 +#define SLAVE_PCIE_ANOC_CFG 35 +#define SLAVE_QDSS_CFG 36 +#define SLAVE_QDSS_STM 37 +#define SLAVE_TCSR 38 +#define SLAVE_TCU 39 + +#define MASTER_CNOC_STARDUST 0 +#define SLAVE_STARDUST_CENTER_CFG 1 +#define SLAVE_STARDUST_EAST_CFG 2 +#define SLAVE_STARDUST_MM_HF_CFG 3 +#define SLAVE_STARDUST_MM_SF_CFG 4 +#define SLAVE_STARDUST_NORTH_CFG 5 +#define SLAVE_STARDUST_SOUTH_CFG 6 +#define SLAVE_STARDUST_WEST_CFG 7 + +#define MASTER_A1NOC_SNOC 0 +#define MASTER_APSS_NOC 1 +#define MASTER_CNOC_SNOC 2 +#define SLAVE_SNOC_GEM_NOC_SF 3 + +#endif -- cgit From 9c9ee0324c774490ae953162aaaf4561d222bd93 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 30 Jun 2026 08:41:26 +0000 Subject: bpf: Reject MEM_ALLOC BTF accesses past object bounds BTF struct walks relax the struct-size check for accesses through a trailing flexible array. That is valid for ordinary BTF type walking, but PTR_TO_BTF_ID | MEM_ALLOC values point to objects allocated with the static BTF type size. When walking a MEM_ALLOC object, reject the access before applying the flexible-array relaxation if the access range extends past the struct size. Apply the same policy to struct ID matching so kfunc and kptr type checks do not walk past the allocated object bounds either. Fixes: 958cf2e273f0 ("bpf: Introduce bpf_obj_new") Fixes: 36d8bdf75a93 ("bpf: Add alloc/xchg/direct_access support for local percpu kptr") Signed-off-by: Yiyang Chen Reviewed-by: Amery Hung Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/4b8c8a81102ba4b595011434c881194f264ddc59.1782807039.git.chenyy23@mails.tsinghua.edu.cn Signed-off-by: Kumar Kartikeya Dwivedi --- include/linux/bpf.h | 2 +- kernel/bpf/btf.c | 17 +++++++++++------ kernel/bpf/verifier.c | 11 +++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index ba09795e0bfd..adf53f7edf28 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3146,7 +3146,7 @@ int btf_struct_access(struct bpf_verifier_log *log, bool btf_struct_ids_match(struct bpf_verifier_log *log, const struct btf *btf, u32 id, int off, const struct btf *need_btf, u32 need_type_id, - bool strict); + bool strict, bool walk_flex_arrays); int btf_distill_func_proto(struct bpf_verifier_log *log, struct btf *btf, diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 64572f85edc8..dff5c0d91641 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -7108,7 +7108,7 @@ enum bpf_struct_walk_result { static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, int off, int size, u32 *next_btf_id, enum bpf_type_flag *flag, - const char **field_name) + const char **field_name, bool walk_flex_arrays) { u32 i, moff, mtrue_end, msize = 0, total_nelems = 0; const struct btf_type *mtype, *elem_type = NULL; @@ -7135,11 +7135,14 @@ again: *flag |= PTR_UNTRUSTED; if (off + size > t->size) { + struct btf_array *array_elem; + + if (!walk_flex_arrays) + goto error; + /* If the last element is a variable size array, we may * need to relax the rule. */ - struct btf_array *array_elem; - if (vlen == 0) goto error; @@ -7404,7 +7407,8 @@ int btf_struct_access(struct bpf_verifier_log *log, t = btf_type_by_id(btf, id); do { - err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag, field_name); + err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag, + field_name, !type_is_alloc(reg->type)); switch (err) { case WALK_PTR: @@ -7463,7 +7467,7 @@ bool btf_types_are_same(const struct btf *btf1, u32 id1, bool btf_struct_ids_match(struct bpf_verifier_log *log, const struct btf *btf, u32 id, int off, const struct btf *need_btf, u32 need_type_id, - bool strict) + bool strict, bool walk_flex_arrays) { const struct btf_type *type; enum bpf_type_flag flag = 0; @@ -7482,7 +7486,8 @@ again: type = btf_type_by_id(btf, id); if (!type) return false; - err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL); + err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL, + walk_flex_arrays); if (err != WALK_STRUCT) return false; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d46f7db20d8f..a0f292635c59 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4379,7 +4379,8 @@ static int map_kptr_match_type(struct bpf_verifier_env *env, */ if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, reg->var_off.value, kptr_field->kptr.btf, kptr_field->kptr.btf_id, - kptr_field->type != BPF_KPTR_UNREF)) + kptr_field->type != BPF_KPTR_UNREF, + !type_is_alloc(reg->type))) goto bad_type; return 0; bad_type: @@ -7970,7 +7971,7 @@ found: if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, reg->var_off.value, btf_vmlinux, *arg_btf_id, - strict_type_match)) { + strict_type_match, !type_is_alloc(reg->type))) { verbose(env, "%s is of type %s but %s is expected\n", reg_arg_name(env, argno), btf_type_name(reg->btf, reg->btf_id), @@ -11436,7 +11437,8 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env, reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, ®_ref_id); reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off); struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, reg->var_off.value, - meta->btf, ref_id, strict_type_match); + meta->btf, ref_id, strict_type_match, + !type_is_alloc(reg->type)); /* If kfunc is accepting a projection type (ie. __sk_buff), it cannot * actually use it -- it must cast to the underlying type. So we allow * caller to pass in the underlying type. @@ -11883,7 +11885,8 @@ __process_kf_arg_ptr_to_graph_node(struct bpf_verifier_env *env, et = btf_type_by_id(field->graph_root.btf, field->graph_root.value_btf_id); t = btf_type_by_id(reg->btf, reg->btf_id); if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, 0, field->graph_root.btf, - field->graph_root.value_btf_id, true)) { + field->graph_root.value_btf_id, true, + !type_is_alloc(reg->type))) { verbose(env, "operation on %s expects arg#1 %s at offset=%d " "in struct %s, but arg is at offset=%d in struct %s\n", btf_field_type_name(head_field_type), -- cgit From 15c1f17979712407a4a71f2129f89ecd625ccbe8 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 3 Jul 2026 06:57:02 +0000 Subject: cred: delete task_euid() task_euid() is a very weird operation. You can see how weird it is by grepping for task_euid() - binder is its only user. task_euid() obtains the objective effective UID - it looks at the credentials of the task for purposes of acting on it as an object, but then accesses the effective UID (which the credentials.7 man page describes as "[...] used by the kernel to determine the permissions that the process will have when accessing shared resources [...]"). Since usage in Binder has now been removed, get rid of the resulting dead code. Changes to the zh_CN translation was carried out with the help of Gemini and Google Translate, and since adjusted as per Alex Shi's feedback. Suggested-by: Jann Horn Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Signed-off-by: Paul Moore --- Documentation/security/credentials.rst | 6 ++---- Documentation/translations/zh_CN/security/credentials.rst | 4 +--- include/linux/cred.h | 1 - rust/helpers/task.c | 5 ----- rust/kernel/task.rs | 10 ---------- 5 files changed, 3 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/Documentation/security/credentials.rst b/Documentation/security/credentials.rst index 4996838491b1..a39a2a2f67aa 100644 --- a/Documentation/security/credentials.rst +++ b/Documentation/security/credentials.rst @@ -393,16 +393,14 @@ the credentials so obtained when they're finished with. The result of ``__task_cred()`` should not be passed directly to ``get_cred()`` as this may race with ``commit_cred()``. -There are a couple of convenience functions to access bits of another task's -credentials, hiding the RCU magic from the caller:: +There is a convenience function to access bits of another task's credentials, +hiding the RCU magic from the caller:: uid_t task_uid(task) Task's real UID - uid_t task_euid(task) Task's effective UID If the caller is holding the RCU read lock at the time anyway, then:: __task_cred(task)->uid - __task_cred(task)->euid should be used instead. Similarly, if multiple aspects of a task's credentials need to be accessed, RCU read lock should be used, ``__task_cred()`` called, diff --git a/Documentation/translations/zh_CN/security/credentials.rst b/Documentation/translations/zh_CN/security/credentials.rst index 88fcd9152ffe..20c8696f8198 100644 --- a/Documentation/translations/zh_CN/security/credentials.rst +++ b/Documentation/translations/zh_CN/security/credentials.rst @@ -337,15 +337,13 @@ const指针上操作,因此不需要进行类型转换,但需要临时放弃 ``__task_cred()`` 的结果不应直接传递给 ``get_cred()`` , 因为这可能与 ``commit_cred()`` 发生竞争条件。 -还有一些方便的函数可以访问另一个任务凭据的特定部分,将RCU操作对调用方隐藏起来:: +有一个方便的函数可用于访问另一个任务凭据的特定部分,从而对调用方隐藏RCU机制:: uid_t task_uid(task) Task's real UID - uid_t task_euid(task) Task's effective UID 如果调用方在此时已经持有RCU读锁,则应使用:: __task_cred(task)->uid - __task_cred(task)->euid 类似地,如果需要访问任务凭据的多个方面,应使用RCU读锁,调用 ``__task_cred()`` 函数,将结果存储在临时指针中,然后从临时指针中调用凭据的各个方面,最后释放锁。 diff --git a/include/linux/cred.h b/include/linux/cred.h index c6676265a985..6ef1750c93e2 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -371,7 +371,6 @@ DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_NULL(_T)) put_cred(_T)) }) #define task_uid(task) (task_cred_xxx((task), uid)) -#define task_euid(task) (task_cred_xxx((task), euid)) #define task_ucounts(task) (task_cred_xxx((task), ucounts)) #define current_cred_xxx(xxx) \ diff --git a/rust/helpers/task.c b/rust/helpers/task.c index c0e1a06ede78..b46b1433a67e 100644 --- a/rust/helpers/task.c +++ b/rust/helpers/task.c @@ -28,11 +28,6 @@ __rust_helper kuid_t rust_helper_task_uid(struct task_struct *task) return task_uid(task); } -__rust_helper kuid_t rust_helper_task_euid(struct task_struct *task) -{ - return task_euid(task); -} - #ifndef CONFIG_USER_NS __rust_helper uid_t rust_helper_from_kuid(struct user_namespace *to, kuid_t uid) { diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index eabd65bfde12..c2b3457b700c 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -217,16 +217,6 @@ impl Task { Kuid::from_raw(unsafe { bindings::task_uid(self.as_ptr()) }) } - /// Returns the objective effective UID of the given task. - /// - /// You should probably not be using this; the effective UID is normally - /// only relevant in subjective credentials. - #[inline] - pub fn euid(&self) -> Kuid { - // SAFETY: It's always safe to call `task_euid` on a valid task. - Kuid::from_raw(unsafe { bindings::task_euid(self.as_ptr()) }) - } - /// Determines whether the given task has pending signals. #[inline] pub fn signal_pending(&self) -> bool { -- cgit From 57d12209790d3f006cd215d073780f3162d507c4 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Sun, 5 Jul 2026 17:09:50 -0400 Subject: genirq: Remove unnecessary NULL check of the kstat_irqs field The kstat_irqs field of struct irq_desc is used to store a per-cpu count of interrupt events. It is initialized in init_desc(), along with all the other fields in struct irq_desc that need explicit initialization, and therefore it's always available (non-NULL) for any valid interrupt descriptor (with a caveat - see below). When CONFIG_SPARSE_IRQ is enabled, all interrupt descriptors are always allocated dynamically via alloc_desc(), which calls init_desc(), so in that case kstat_irqs is guaranteed to be non-NULL before a valid struct irq_desc pointer is even returned. By contrast, when CONFIG_SPARSE_IRQ is disabled, interrupt descriptors are allocated statically in the irq_desc[] array, and kstat_irqs is initialized implicitly to NULL. The per-cpu pointer is initialized only later, for all descriptors, via start_kernel() -> early_irq_init() -> init_desc(). The kstat_irqs field is used mostly for printing interrupt statistics (i.e. reading /proc/interrupts), and that cannot happen until much later, when user-space is fully initialized. So, there is no concern with that use case. The list below includes all functions where the NULL check is removed, along with a list of all possible call chains and/or a brief explanation of why it's safe to remove the NULL check in that case. * irq_desc_kstat_cpu() [include/linux/irqdesc.h] - All direct call sites use it for printing IRQ statistics. - Indirect call site: per_cpu_count_show() - also used for printing interruptstatistics. * kstat_irqs_cpu() [kernel/irq/irqdesc.c] - Called by sun3_int7() and sun3_int5() [arch/m68k/sun3/sun3ints.c] These are interrupt handlers and cannot be called until their corresponding interrupts are initialized in sun3_init_IRQ(). The call chain leading to that is: start_kernel() -> init_IRQ() [arch/m68k/kernel/ints.c] -> mach_init_IRQ = sun3_init_IRQ() The init_IRQ() call happens right *after* the early_irq_init() call, which means the descriptors are already fully initialized by the time the interrupt handlers are even registered. - Called by show_interrupts() [arch/s390/kernel/irq.c] - used for printing interrupt statistics. * kstat_irqs() [kernel/irq/irqdesc.c] The only possible call chain is via fs/proc/stat.c: stat_open() -> show_stat() -> show_all_irqs() -> kstat_irqs_usr() -> kstat_irqs() It is used for printing interrupt statistics. * kstat_snapshot_irqs() The only possible call chain is via kernel/watchdog.c: watchdog_timer_fn() -> is_softlockup() -> start_counting_irqs() -> kstat_snapshot_irqs() The watchdog timer cannot fire early, before early_irq_init(). * kstat_get_irq_since_snapshot() The only possible call chain is via kernel/watchdog.c: watchdog_timer_fn() -> report_cpu_status() -> print_irq_counts() -> kstat_get_irq_since_snapshot() The watchdog timer cannot fire early, before early_irq_init(). Signed-off-by: Radu Rendec Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260705210951.2717741-2-radu@rendec.net --- include/linux/irqdesc.h | 2 +- kernel/irq/irqdesc.c | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 8080db17c1b1..779d6023c5c2 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -146,7 +146,7 @@ extern struct irq_desc irq_desc[NR_IRQS]; static inline unsigned int irq_desc_kstat_cpu(struct irq_desc *desc, unsigned int cpu) { - return desc->kstat_irqs ? per_cpu(desc->kstat_irqs->cnt, cpu) : 0; + return per_cpu(desc->kstat_irqs->cnt, cpu); } static inline struct irq_desc *irq_data_to_desc(struct irq_data *data) diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 80ef4e27dcf4..3a818f07a101 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -1004,7 +1004,7 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) { struct irq_desc *desc = irq_to_desc(irq); - return desc && desc->kstat_irqs ? per_cpu(desc->kstat_irqs->cnt, cpu) : 0; + return desc ? irq_desc_kstat_cpu(desc, cpu) : 0; } static unsigned int kstat_irqs_desc(struct irq_desc *desc, const struct cpumask *cpumask) @@ -1026,7 +1026,7 @@ static unsigned int kstat_irqs(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); - if (!desc || !desc->kstat_irqs) + if (!desc) return 0; return kstat_irqs_desc(desc, cpu_possible_mask); } @@ -1038,18 +1038,15 @@ void kstat_snapshot_irqs(void) struct irq_desc *desc; unsigned int irq; - for_each_irq_desc(irq, desc) { - if (!desc->kstat_irqs) - continue; + for_each_irq_desc(irq, desc) this_cpu_write(desc->kstat_irqs->ref, this_cpu_read(desc->kstat_irqs->cnt)); - } } unsigned int kstat_get_irq_since_snapshot(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); - if (!desc || !desc->kstat_irqs) + if (!desc) return 0; return this_cpu_read(desc->kstat_irqs->cnt) - this_cpu_read(desc->kstat_irqs->ref); } -- cgit From 03b5d4c2798234d9ee3c4a719a2fefe785a6aec4 Mon Sep 17 00:00:00 2001 From: "Thomas Weißschuh (Schneider Electric)" Date: Thu, 2 Jul 2026 11:41:58 +0200 Subject: hrtimer: Rename hrtimer_defs.h to hrtimer_bases.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header was originally added for #defines and was later extended with the hrtimer base structures. All the #defines have been removed in the meantime, so the naming is off now. Rename the header to fit its contents more. This will also make the upcoming addition of some functions nicer. Signed-off-by: Thomas Weißschuh (Schneider Electric) Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260702-hrtimer-header-dependencies-v1-1-c50b19bda473@linutronix.de --- include/linux/hrtimer.h | 2 +- include/linux/hrtimer_bases.h | 113 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hrtimer_defs.h | 113 ------------------------------------------ 3 files changed, 114 insertions(+), 114 deletions(-) create mode 100644 include/linux/hrtimer_bases.h delete mode 100644 include/linux/hrtimer_defs.h (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 6862dea0acc5..8aa58520a816 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -12,7 +12,7 @@ #ifndef _LINUX_HRTIMER_H #define _LINUX_HRTIMER_H -#include +#include #include #include #include diff --git a/include/linux/hrtimer_bases.h b/include/linux/hrtimer_bases.h new file mode 100644 index 000000000000..8c10f45dc469 --- /dev/null +++ b/include/linux/hrtimer_bases.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_HRTIMER_BASES_H +#define _LINUX_HRTIMER_BASES_H + +#include +#include +#include + +#ifdef CONFIG_64BIT +# define __hrtimer_clock_base_align ____cacheline_aligned +#else +# define __hrtimer_clock_base_align +#endif + +/** + * struct hrtimer_clock_base - the timer base for a specific clock + * @cpu_base: per cpu clock base + * @index: clock type index for per_cpu support when moving a + * timer to a base on another cpu. + * @clockid: clock id for per_cpu support + * @seq: seqcount around __run_hrtimer + * @expires_next: Absolute time of the next event in this clock base + * @running: pointer to the currently running hrtimer + * @active: red black tree root node for the active timers + * @offset: offset of this clock to the monotonic base + */ +struct hrtimer_clock_base { + struct hrtimer_cpu_base *cpu_base; + const unsigned int index; + const clockid_t clockid; + seqcount_raw_spinlock_t seq; + ktime_t expires_next; + struct hrtimer *running; + struct timerqueue_linked_head active; + ktime_t offset; +} __hrtimer_clock_base_align; + +enum hrtimer_base_type { + HRTIMER_BASE_MONOTONIC, + HRTIMER_BASE_REALTIME, + HRTIMER_BASE_BOOTTIME, + HRTIMER_BASE_TAI, + HRTIMER_BASE_MONOTONIC_SOFT, + HRTIMER_BASE_REALTIME_SOFT, + HRTIMER_BASE_BOOTTIME_SOFT, + HRTIMER_BASE_TAI_SOFT, + HRTIMER_MAX_CLOCK_BASES +}; + +/** + * struct hrtimer_cpu_base - the per cpu clock bases + * @lock: lock protecting the base and associated clock bases and timers + * @cpu: cpu number + * @active_bases: Bitfield to mark bases with active timers + * @clock_was_set_seq: Sequence counter of clock was set events + * @hres_active: State of high resolution mode + * @deferred_rearm: A deferred rearm is pending + * @deferred_needs_update: The deferred rearm must re-evaluate the first timer + * @hang_detected: The last hrtimer interrupt detected a hang + * @softirq_activated: displays, if the softirq is raised - update of softirq + * related settings is not required then. + * @nr_events: Total number of hrtimer interrupt events + * @nr_retries: Total number of hrtimer interrupt retries + * @nr_hangs: Total number of hrtimer interrupt hangs + * @max_hang_time: Maximum time spent in hrtimer_interrupt + * @softirq_expiry_lock: Lock which is taken while softirq based hrtimer are expired + * @online: CPU is online from an hrtimers point of view + * @timer_waiters: A hrtimer_cancel() waiters for the timer callback to finish. + * @expires_next: Absolute time of the next event, is required for remote + * hrtimer enqueue; it is the total first expiry time (hard + * and soft hrtimer are taken into account) + * @next_timer: Pointer to the first expiring timer + * @softirq_expires_next: Time to check, if soft queues needs also to be expired + * @softirq_next_timer: Pointer to the first expiring softirq based timer + * @deferred_expires_next: Cached expires next value for deferred rearm + * @clock_base: Array of clock bases for this cpu + * + * Note: next_timer is just an optimization for __remove_hrtimer(). + * Do not dereference the pointer because it is not reliable on + * cross cpu removals. + */ +struct hrtimer_cpu_base { + raw_spinlock_t lock; + unsigned int cpu; + unsigned int active_bases; + unsigned int clock_was_set_seq; + bool hres_active; + bool deferred_rearm; + bool deferred_needs_update; + bool hang_detected; + bool softirq_activated; + bool online; +#ifdef CONFIG_HIGH_RES_TIMERS + unsigned int nr_events; + unsigned short nr_retries; + unsigned short nr_hangs; + unsigned int max_hang_time; +#endif +#ifdef CONFIG_PREEMPT_RT + spinlock_t softirq_expiry_lock; + atomic_t timer_waiters; +#endif + ktime_t expires_next; + struct hrtimer *next_timer; + ktime_t softirq_expires_next; + struct hrtimer *softirq_next_timer; + ktime_t deferred_expires_next; + struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; + call_single_data_t csd; +} ____cacheline_aligned; + + +#endif diff --git a/include/linux/hrtimer_defs.h b/include/linux/hrtimer_defs.h deleted file mode 100644 index 52ed9e46ff13..000000000000 --- a/include/linux/hrtimer_defs.h +++ /dev/null @@ -1,113 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _LINUX_HRTIMER_DEFS_H -#define _LINUX_HRTIMER_DEFS_H - -#include -#include -#include - -#ifdef CONFIG_64BIT -# define __hrtimer_clock_base_align ____cacheline_aligned -#else -# define __hrtimer_clock_base_align -#endif - -/** - * struct hrtimer_clock_base - the timer base for a specific clock - * @cpu_base: per cpu clock base - * @index: clock type index for per_cpu support when moving a - * timer to a base on another cpu. - * @clockid: clock id for per_cpu support - * @seq: seqcount around __run_hrtimer - * @expires_next: Absolute time of the next event in this clock base - * @running: pointer to the currently running hrtimer - * @active: red black tree root node for the active timers - * @offset: offset of this clock to the monotonic base - */ -struct hrtimer_clock_base { - struct hrtimer_cpu_base *cpu_base; - const unsigned int index; - const clockid_t clockid; - seqcount_raw_spinlock_t seq; - ktime_t expires_next; - struct hrtimer *running; - struct timerqueue_linked_head active; - ktime_t offset; -} __hrtimer_clock_base_align; - -enum hrtimer_base_type { - HRTIMER_BASE_MONOTONIC, - HRTIMER_BASE_REALTIME, - HRTIMER_BASE_BOOTTIME, - HRTIMER_BASE_TAI, - HRTIMER_BASE_MONOTONIC_SOFT, - HRTIMER_BASE_REALTIME_SOFT, - HRTIMER_BASE_BOOTTIME_SOFT, - HRTIMER_BASE_TAI_SOFT, - HRTIMER_MAX_CLOCK_BASES -}; - -/** - * struct hrtimer_cpu_base - the per cpu clock bases - * @lock: lock protecting the base and associated clock bases and timers - * @cpu: cpu number - * @active_bases: Bitfield to mark bases with active timers - * @clock_was_set_seq: Sequence counter of clock was set events - * @hres_active: State of high resolution mode - * @deferred_rearm: A deferred rearm is pending - * @deferred_needs_update: The deferred rearm must re-evaluate the first timer - * @hang_detected: The last hrtimer interrupt detected a hang - * @softirq_activated: displays, if the softirq is raised - update of softirq - * related settings is not required then. - * @nr_events: Total number of hrtimer interrupt events - * @nr_retries: Total number of hrtimer interrupt retries - * @nr_hangs: Total number of hrtimer interrupt hangs - * @max_hang_time: Maximum time spent in hrtimer_interrupt - * @softirq_expiry_lock: Lock which is taken while softirq based hrtimer are expired - * @online: CPU is online from an hrtimers point of view - * @timer_waiters: A hrtimer_cancel() waiters for the timer callback to finish. - * @expires_next: Absolute time of the next event, is required for remote - * hrtimer enqueue; it is the total first expiry time (hard - * and soft hrtimer are taken into account) - * @next_timer: Pointer to the first expiring timer - * @softirq_expires_next: Time to check, if soft queues needs also to be expired - * @softirq_next_timer: Pointer to the first expiring softirq based timer - * @deferred_expires_next: Cached expires next value for deferred rearm - * @clock_base: Array of clock bases for this cpu - * - * Note: next_timer is just an optimization for __remove_hrtimer(). - * Do not dereference the pointer because it is not reliable on - * cross cpu removals. - */ -struct hrtimer_cpu_base { - raw_spinlock_t lock; - unsigned int cpu; - unsigned int active_bases; - unsigned int clock_was_set_seq; - bool hres_active; - bool deferred_rearm; - bool deferred_needs_update; - bool hang_detected; - bool softirq_activated; - bool online; -#ifdef CONFIG_HIGH_RES_TIMERS - unsigned int nr_events; - unsigned short nr_retries; - unsigned short nr_hangs; - unsigned int max_hang_time; -#endif -#ifdef CONFIG_PREEMPT_RT - spinlock_t softirq_expiry_lock; - atomic_t timer_waiters; -#endif - ktime_t expires_next; - struct hrtimer *next_timer; - ktime_t softirq_expires_next; - struct hrtimer *softirq_next_timer; - ktime_t deferred_expires_next; - struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; - call_single_data_t csd; -} ____cacheline_aligned; - - -#endif -- cgit From d3dc7fabd4c4a3baaa7e7bfa84558fedad699580 Mon Sep 17 00:00:00 2001 From: "Thomas Weißschuh (Schneider Electric)" Date: Thu, 2 Jul 2026 11:41:59 +0200 Subject: hrtimer: Move hrtimer_callback_running() to hrtimer_bases.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The usage of the hrtimer base introduces a dependency on the timer base structure definitions from the widely-used hrtimer.h. Move the helper to hrtimer_bases.h to trim this dependency. Also adapt the two only callers to now include hrtimer_bases.h. Signed-off-by: Thomas Weißschuh (Schneider Electric) Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260702-hrtimer-header-dependencies-v1-2-c50b19bda473@linutronix.de --- include/linux/hrtimer.h | 9 --------- include/linux/hrtimer_bases.h | 10 ++++++++++ kernel/sched/fair.c | 2 ++ sound/drivers/dummy.c | 1 + 4 files changed, 13 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 8aa58520a816..7bf154cf3079 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -287,15 +287,6 @@ static inline bool hrtimer_is_queued(struct hrtimer *timer) return READ_ONCE(timer->is_queued); } -/* - * Helper function to check, whether the timer is running the callback - * function - */ -static inline int hrtimer_callback_running(struct hrtimer *timer) -{ - return timer->base->running == timer; -} - /** * hrtimer_update_function - Update the timer's callback function * @timer: Timer to update diff --git a/include/linux/hrtimer_bases.h b/include/linux/hrtimer_bases.h index 8c10f45dc469..70b99e651168 100644 --- a/include/linux/hrtimer_bases.h +++ b/include/linux/hrtimer_bases.h @@ -2,6 +2,7 @@ #ifndef _LINUX_HRTIMER_BASES_H #define _LINUX_HRTIMER_BASES_H +#include #include #include #include @@ -110,4 +111,13 @@ struct hrtimer_cpu_base { } ____cacheline_aligned; +/* + * Helper function to check, whether the timer is running the callback + * function + */ +static inline int hrtimer_callback_running(struct hrtimer *timer) +{ + return timer->base->running == timer; +} + #endif diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d78467ec6ee1..09197f8e4b76 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 7283f0f18813..ce7ab986dee6 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include -- cgit From a116c7582d7f7701736801a47e3432eb7cabd674 Mon Sep 17 00:00:00 2001 From: "Thomas Weißschuh (Schneider Electric)" Date: Thu, 2 Jul 2026 11:42:00 +0200 Subject: hrtimer: Move hrtimer_update_function() to hrtimer.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The usage of the hrtimer base forces hrtimer.h to also expose the base structure definitions. Move the function to hrtimer.c to avoid this. Signed-off-by: Thomas Weißschuh (Schneider Electric) Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260702-hrtimer-header-dependencies-v1-3-c50b19bda473@linutronix.de --- include/linux/hrtimer.h | 24 ++---------------------- kernel/time/hrtimer.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 7bf154cf3079..81ab9849e83b 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -287,28 +287,8 @@ static inline bool hrtimer_is_queued(struct hrtimer *timer) return READ_ONCE(timer->is_queued); } -/** - * hrtimer_update_function - Update the timer's callback function - * @timer: Timer to update - * @function: New callback function - * - * Only safe to call if the timer is not enqueued. Can be called in the callback function if the - * timer is not enqueued at the same time (see the comments above HRTIMER_STATE_ENQUEUED). - */ -static inline void hrtimer_update_function(struct hrtimer *timer, - enum hrtimer_restart (*function)(struct hrtimer *)) -{ -#ifdef CONFIG_PROVE_LOCKING - guard(raw_spinlock_irqsave)(&timer->base->cpu_base->lock); - - if (WARN_ON_ONCE(hrtimer_is_queued(timer))) - return; - - if (WARN_ON_ONCE(!function)) - return; -#endif - ACCESS_PRIVATE(timer, function) = function; -} +void hrtimer_update_function(struct hrtimer *timer, + enum hrtimer_restart (*function)(struct hrtimer *)); /* Forward a hrtimer so it expires after now: */ extern u64 diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 697816d0dc26..0ac8899e2617 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1039,6 +1039,30 @@ static inline void unlock_hrtimer_base(const struct hrtimer *timer, unsigned lon raw_spin_unlock_irqrestore(&timer->base->cpu_base->lock, *flags); } +/** + * hrtimer_update_function - Update the timer's callback function + * @timer: Timer to update + * @function: New callback function + * + * Only safe to call if the timer is not enqueued. Can be called in the callback function if the + * timer is not enqueued at the same time (see the comments above HRTIMER_STATE_ENQUEUED). + */ +void hrtimer_update_function(struct hrtimer *timer, + enum hrtimer_restart (*function)(struct hrtimer *)) +{ +#ifdef CONFIG_PROVE_LOCKING + guard(raw_spinlock_irqsave)(&timer->base->cpu_base->lock); + + if (WARN_ON_ONCE(hrtimer_is_queued(timer))) + return; + + if (WARN_ON_ONCE(!function)) + return; +#endif + ACCESS_PRIVATE(timer, function) = function; +} +EXPORT_SYMBOL_GPL(hrtimer_update_function); + /** * hrtimer_forward() - forward the timer expiry * @timer: hrtimer to forward -- cgit From 071993aac72ea8e6f9986bf41101974e8faa2eca Mon Sep 17 00:00:00 2001 From: "Thomas Weißschuh (Schneider Electric)" Date: Thu, 2 Jul 2026 11:42:03 +0200 Subject: hrtimer: Explicitly include some necessary headers in hrtimer_rearm.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple used types and symbols are only visible through transitive dependency chains. Include the headers explicitly as those chains are going to go away. Signed-off-by: Thomas Weißschuh (Schneider Electric) Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260702-hrtimer-header-dependencies-v1-6-c50b19bda473@linutronix.de --- include/linux/hrtimer_rearm.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/hrtimer_rearm.h b/include/linux/hrtimer_rearm.h index a6f2e5d5e1c7..17a81826bd9a 100644 --- a/include/linux/hrtimer_rearm.h +++ b/include/linux/hrtimer_rearm.h @@ -2,7 +2,12 @@ #ifndef _LINUX_HRTIMER_REARM_H #define _LINUX_HRTIMER_REARM_H +#include + #ifdef CONFIG_HRTIMER_REARM_DEFERRED +#include +#include +#include #include void __hrtimer_rearm_deferred(void); -- cgit From faef65e45a2a03f1fa32bc4e55c11d79f6aaae6f Mon Sep 17 00:00:00 2001 From: "Thomas Weißschuh (Schneider Electric)" Date: Thu, 2 Jul 2026 11:42:05 +0200 Subject: hrtimer: Remove inclusion of hrtimer_bases.h remove from hrtimer.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hrtimer.h is used all over the kernel. Any change to hrtimer_bases.h effectively triggers a full rebuild. As all logical dependencies from hrtimer.h to hrtimer_bases.h have been removed, the inclusion is now unncessary. Remove it. Signed-off-by: Thomas Weißschuh (Schneider Electric) Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260702-hrtimer-header-dependencies-v1-8-c50b19bda473@linutronix.de --- include/linux/hrtimer.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 81ab9849e83b..29072d89e5cb 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -12,7 +12,6 @@ #ifndef _LINUX_HRTIMER_H #define _LINUX_HRTIMER_H -#include #include #include #include -- cgit From 6e435911394b05c91b92d4c332c455ec569e22fb Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 7 Jul 2026 08:07:14 +0200 Subject: timekeeping: Fold vdso_time_update_aux() declarations into the generic ifdeffery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only caller of vdso_time_update_aux() is already gated behind CONFIG_POSIX_AUX. The additional check in the header files is not necessary. Remove it and then fold the declarations into the existing CONFIG_GENERIC_GETTIMEOFDAY ifdeffery. Signed-off-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260707-timekeeping-header-cleanup-v1-1-e85ad96409a9@linutronix.de --- include/linux/timekeeper_internal.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 4486dfd5d0de..58c83d6e65b3 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -194,6 +194,7 @@ struct timekeeper { extern void update_vsyscall(struct timekeeper *tk); extern void update_vsyscall_tz(void); +extern void vdso_time_update_aux(struct timekeeper *tk); #else @@ -203,12 +204,9 @@ static inline void update_vsyscall(struct timekeeper *tk) static inline void update_vsyscall_tz(void) { } -#endif - -#if defined(CONFIG_GENERIC_GETTIMEOFDAY) && defined(CONFIG_POSIX_AUX_CLOCKS) -extern void vdso_time_update_aux(struct timekeeper *tk); -#else -static inline void vdso_time_update_aux(struct timekeeper *tk) { } +static inline void vdso_time_update_aux(struct timekeeper *tk) +{ +} #endif #endif /* _LINUX_TIMEKEEPER_INTERNAL_H */ -- cgit From 79bd39c58f2c6fdbc5fb6300d309606f3cb84ab8 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 7 Jul 2026 08:07:15 +0200 Subject: timekeeping: Move the vDSO update declarations into a private header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All architectures are now fully using the generic vDSO infrastructure. They don't need these declarations anymore to implement the functions in architecture-specific code. Move them to the private header. Signed-off-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260707-timekeeping-header-cleanup-v1-2-e85ad96409a9@linutronix.de --- include/linux/timekeeper_internal.h | 19 ------------------- kernel/time/time.c | 1 + kernel/time/timekeeping_internal.h | 21 +++++++++++++++++++++ 3 files changed, 22 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 58c83d6e65b3..264db7c9c071 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -190,23 +190,4 @@ struct timekeeper { s32 tai_offset; }; -#ifdef CONFIG_GENERIC_GETTIMEOFDAY - -extern void update_vsyscall(struct timekeeper *tk); -extern void update_vsyscall_tz(void); -extern void vdso_time_update_aux(struct timekeeper *tk); - -#else - -static inline void update_vsyscall(struct timekeeper *tk) -{ -} -static inline void update_vsyscall_tz(void) -{ -} -static inline void vdso_time_update_aux(struct timekeeper *tk) -{ -} -#endif - #endif /* _LINUX_TIMEKEEPER_INTERNAL_H */ diff --git a/kernel/time/time.c b/kernel/time/time.c index 0dd63a91e7c5..d1a7efd80bf5 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -42,6 +42,7 @@ #include #include "timekeeping.h" +#include "timekeeping_internal.h" /* * The timezone where the local system is located. Used as a default by some diff --git a/kernel/time/timekeeping_internal.h b/kernel/time/timekeeping_internal.h index 973ede670a36..6d719b8e5ea2 100644 --- a/kernel/time/timekeeping_internal.h +++ b/kernel/time/timekeeping_internal.h @@ -6,6 +6,8 @@ #include #include +struct timekeeper; + /* * timekeeping debug functions */ @@ -48,4 +50,23 @@ void timekeeper_unlock_irqrestore(unsigned long flags); /* NTP specific interface to access the current seconds value */ long ktime_get_ntp_seconds(unsigned int id); +#ifdef CONFIG_GENERIC_GETTIMEOFDAY + +extern void update_vsyscall(struct timekeeper *tk); +extern void update_vsyscall_tz(void); +extern void vdso_time_update_aux(struct timekeeper *tk); + +#else + +static inline void update_vsyscall(struct timekeeper *tk) +{ +} +static inline void update_vsyscall_tz(void) +{ +} +static inline void vdso_time_update_aux(struct timekeeper *tk) +{ +} +#endif + #endif /* _TIMEKEEPING_INTERNAL_H */ -- cgit From 79ced850e549e8c86b772a79ea417a1425b5c04b Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Mon, 4 May 2026 08:32:26 +0200 Subject: y2038: uapi: Use 64-bit __kernel_old_timespec::tv_nsec on x32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'struct __kernel_old_timespec' represents the 'native' time ABI of the kernel. On 32-bit systems it uses 32-bit fields and on 64-bit systems it uses 64-bit fields. However the x86 x32 ABI uses the 64-bit time ABI natively. This is correctly handled for the 'tv_sec' fields, through the typedefs of '__kernel_old_time_t' -> '__kernel_long_t' -> 'long long'. The same treatment was missed for 'tv_nsec'. In practice this might not make much of a difference as the value of 'tv_nsec' will always fit into 32 bits and the missing bits fall into the padding of the structure. When introspecting the structure however, a difference can be observed. Switch to 64-bit tv_nsec on x32. No other architectures or ABIs are affected. While this could be interpreted as violating the POSIX requirement of 'timespec::tv_nsec' being 'long': * __kernel_old_timespec is not actually the POSIX timespec type * the requirement is gone in newer versions of POSIX * this matches glibc Fixes: 94c467ddb273 ("y2038: add __kernel_old_timespec and __kernel_old_time_t") Signed-off-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260504-timespec-x32-v2-1-0739c9047fc4@linutronix.de --- include/uapi/linux/time_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/time_types.h b/include/uapi/linux/time_types.h index bcc0002115d3..03a0d8aaadca 100644 --- a/include/uapi/linux/time_types.h +++ b/include/uapi/linux/time_types.h @@ -30,7 +30,7 @@ struct __kernel_old_timeval { struct __kernel_old_timespec { __kernel_old_time_t tv_sec; /* seconds */ - long tv_nsec; /* nanoseconds */ + __kernel_long_t tv_nsec; /* nanoseconds */ }; struct __kernel_old_itimerval { -- cgit From 037d3e9a44e52dba5508a84095db8d90f343628c Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Wed, 18 Mar 2026 16:19:05 +1100 Subject: mmc: sdio: add Morse Micro vendor ids Add the Morse Micro mm81x series vendor ids. Acked-by: Ulf Hansson Signed-off-by: Lachlan Hodges --- include/linux/mmc/sdio_ids.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 0685dd717e85..bbffad9ae88e 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -117,6 +117,9 @@ #define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296 #define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347 +#define SDIO_VENDOR_ID_MORSEMICRO 0x325b +#define SDIO_DEVICE_ID_MORSEMICRO_MM8108 0x0809 + #define SDIO_VENDOR_ID_NXP 0x0471 #define SDIO_DEVICE_ID_NXP_IW61X 0x0205 -- cgit From f390a1fac199b03d8971b84a531d1a978a2b20cc Mon Sep 17 00:00:00 2001 From: Imran Shaik Date: Mon, 8 Jun 2026 17:51:50 +0530 Subject: dt-bindings: clock: qcom: Add Qualcomm Shikra SoC Global Clock Controller Add device tree bindings for the global clock controller on Qualcomm Shikra SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Imran Shaik Link: https://lore.kernel.org/r/20260608-shikra-gcc-rpmcc-clks-v5-2-94cefe092ee3@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/clock/qcom,shikra-gcc.yaml | 70 ++++++ include/dt-bindings/clock/qcom,shikra-gcc.h | 263 +++++++++++++++++++++ 2 files changed, 333 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,shikra-gcc.yaml create mode 100644 include/dt-bindings/clock/qcom,shikra-gcc.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,shikra-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,shikra-gcc.yaml new file mode 100644 index 000000000000..da6eebfa84c2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,shikra-gcc.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,shikra-gcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Global Clock & Reset Controller on Qualcomm Shikra SoC + +maintainers: + - Imran Shaik + - Taniya Das + +description: | + Global clock control module provides the clocks, resets and power + domains on Qualcomm Shikra SoC platform. + + See also: include/dt-bindings/clock/qcom,shikra-gcc.h + +properties: + compatible: + const: qcom,shikra-gcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + - description: EMAC0 sgmiiphy mac rclk source + - description: EMAC0 sgmiiphy mac tclk source + - description: EMAC1 sgmiiphy mac rclk source + - description: EMAC1 sgmiiphy mac tclk source + - description: PCIE Pipe clock source + - description: USB3 phy wrapper pipe clock source + + power-domains: + items: + - description: CX domain + +required: + - compatible + - clocks + - power-domains + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + clock-controller@1400000 { + compatible = "qcom,shikra-gcc"; + reg = <0x01400000 0x1f0000>; + clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>, + <&sleep_clk>, + <&emac0_sgmiiphy_rclk>, + <&emac0_sgmiiphy_tclk>, + <&emac1_sgmiiphy_rclk>, + <&emac1_sgmiiphy_tclk>, + <&pcie_pipe_clk>, + <&usb3_phy_wrapper_gcc_usb30_pipe_clk>; + power-domains = <&rpmpd RPMPD_VDDCX>; + #clock-cells = <1>; + #power-domain-cells = <1>; + #reset-cells = <1>; + }; + +... diff --git a/include/dt-bindings/clock/qcom,shikra-gcc.h b/include/dt-bindings/clock/qcom,shikra-gcc.h new file mode 100644 index 000000000000..656c959c7e12 --- /dev/null +++ b/include/dt-bindings/clock/qcom,shikra-gcc.h @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_GCC_SHIKRA_H +#define _DT_BINDINGS_CLK_QCOM_GCC_SHIKRA_H + +/* GCC clocks */ +#define GPLL0 0 +#define GPLL0_OUT_AUX2 1 +#define GPLL1 2 +#define GPLL10 3 +#define GPLL11 4 +#define GPLL12 5 +#define GPLL12_OUT_AUX2 6 +#define GPLL3 7 +#define GPLL3_OUT_MAIN 8 +#define GPLL4 9 +#define GPLL5 10 +#define GPLL6 11 +#define GPLL6_OUT_MAIN 12 +#define GPLL7 13 +#define GPLL8 14 +#define GPLL8_OUT_MAIN 15 +#define GPLL9 16 +#define GPLL9_OUT_MAIN 17 +#define GCC_AHB2PHY_CSI_CLK 18 +#define GCC_AHB2PHY_USB_CLK 19 +#define GCC_BOOT_ROM_AHB_CLK 20 +#define GCC_CAM_THROTTLE_NRT_CLK 21 +#define GCC_CAM_THROTTLE_RT_CLK 22 +#define GCC_CAMERA_AHB_CLK 23 +#define GCC_CAMERA_XO_CLK 24 +#define GCC_CAMSS_AXI_CLK 25 +#define GCC_CAMSS_AXI_CLK_SRC 26 +#define GCC_CAMSS_CAMNOC_ATB_CLK 27 +#define GCC_CAMSS_CAMNOC_DRAGONLINK_ATB_CLK 28 +#define GCC_CAMSS_CAMNOC_NTS_XO_CLK 29 +#define GCC_CAMSS_CCI_0_CLK 30 +#define GCC_CAMSS_CCI_CLK_SRC 31 +#define GCC_CAMSS_CPHY_0_CLK 32 +#define GCC_CAMSS_CPHY_1_CLK 33 +#define GCC_CAMSS_CSI0PHYTIMER_CLK 34 +#define GCC_CAMSS_CSI0PHYTIMER_CLK_SRC 35 +#define GCC_CAMSS_CSI1PHYTIMER_CLK 36 +#define GCC_CAMSS_CSI1PHYTIMER_CLK_SRC 37 +#define GCC_CAMSS_MCLK0_CLK 38 +#define GCC_CAMSS_MCLK0_CLK_SRC 39 +#define GCC_CAMSS_MCLK1_CLK 40 +#define GCC_CAMSS_MCLK1_CLK_SRC 41 +#define GCC_CAMSS_MCLK2_CLK 42 +#define GCC_CAMSS_MCLK2_CLK_SRC 43 +#define GCC_CAMSS_MCLK3_CLK 44 +#define GCC_CAMSS_MCLK3_CLK_SRC 45 +#define GCC_CAMSS_NRT_AXI_CLK 46 +#define GCC_CAMSS_OPE_AHB_CLK 47 +#define GCC_CAMSS_OPE_AHB_CLK_SRC 48 +#define GCC_CAMSS_OPE_CLK 49 +#define GCC_CAMSS_OPE_CLK_SRC 50 +#define GCC_CAMSS_RT_AXI_CLK 51 +#define GCC_CAMSS_TFE_0_CLK 52 +#define GCC_CAMSS_TFE_0_CLK_SRC 53 +#define GCC_CAMSS_TFE_0_CPHY_RX_CLK 54 +#define GCC_CAMSS_TFE_0_CSID_CLK 55 +#define GCC_CAMSS_TFE_0_CSID_CLK_SRC 56 +#define GCC_CAMSS_TFE_1_CLK 57 +#define GCC_CAMSS_TFE_1_CLK_SRC 58 +#define GCC_CAMSS_TFE_1_CPHY_RX_CLK 59 +#define GCC_CAMSS_TFE_1_CSID_CLK 60 +#define GCC_CAMSS_TFE_1_CSID_CLK_SRC 61 +#define GCC_CAMSS_TFE_CPHY_RX_CLK_SRC 62 +#define GCC_CAMSS_TOP_AHB_CLK 63 +#define GCC_CAMSS_TOP_AHB_CLK_SRC 64 +#define GCC_CFG_NOC_USB2_PRIM_AXI_CLK 65 +#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 66 +#define GCC_DDRSS_GPU_AXI_CLK 67 +#define GCC_DDRSS_MEMNOC_PCIE_SF_CLK 68 +#define GCC_DISP_AHB_CLK 69 +#define GCC_DISP_GPLL0_CLK_SRC 70 +#define GCC_DISP_GPLL0_DIV_CLK_SRC 71 +#define GCC_DISP_HF_AXI_CLK 72 +#define GCC_DISP_THROTTLE_CORE_CLK 73 +#define GCC_DISP_XO_CLK 74 +#define GCC_EMAC0_AHB_CLK 75 +#define GCC_EMAC0_AXI_CLK 76 +#define GCC_EMAC0_AXI_CLK_SRC 77 +#define GCC_EMAC0_AXI_SYS_NOC_CLK 78 +#define GCC_EMAC0_CC_SGMIIPHY_RX_CLK 79 +#define GCC_EMAC0_CC_SGMIIPHY_RX_CLK_SRC 80 +#define GCC_EMAC0_CC_SGMIIPHY_TX_CLK 81 +#define GCC_EMAC0_CC_SGMIIPHY_TX_CLK_SRC 82 +#define GCC_EMAC0_PHY_AUX_CLK 83 +#define GCC_EMAC0_PHY_AUX_CLK_SRC 84 +#define GCC_EMAC0_PTP_CLK 85 +#define GCC_EMAC0_PTP_CLK_SRC 86 +#define GCC_EMAC0_RGMII_CLK 87 +#define GCC_EMAC0_RGMII_CLK_SRC 88 +#define GCC_EMAC1_AHB_CLK 89 +#define GCC_EMAC1_AXI_CLK 90 +#define GCC_EMAC1_AXI_CLK_SRC 91 +#define GCC_EMAC1_AXI_SYS_NOC_CLK 92 +#define GCC_EMAC1_CC_SGMIIPHY_RX_CLK 93 +#define GCC_EMAC1_CC_SGMIIPHY_RX_CLK_SRC 94 +#define GCC_EMAC1_CC_SGMIIPHY_TX_CLK 95 +#define GCC_EMAC1_CC_SGMIIPHY_TX_CLK_SRC 96 +#define GCC_EMAC1_PHY_AUX_CLK 97 +#define GCC_EMAC1_PHY_AUX_CLK_SRC 98 +#define GCC_EMAC1_PTP_CLK 99 +#define GCC_EMAC1_PTP_CLK_SRC 100 +#define GCC_EMAC1_RGMII_CLK 101 +#define GCC_EMAC1_RGMII_CLK_SRC 102 +#define GCC_GP1_CLK 103 +#define GCC_GP1_CLK_SRC 104 +#define GCC_GP2_CLK 105 +#define GCC_GP2_CLK_SRC 106 +#define GCC_GP3_CLK 107 +#define GCC_GP3_CLK_SRC 108 +#define GCC_GPU_CFG_AHB_CLK 109 +#define GCC_GPU_GPLL0_CLK_SRC 110 +#define GCC_GPU_GPLL0_DIV_CLK_SRC 111 +#define GCC_GPU_IREF_CLK 112 +#define GCC_GPU_MEMNOC_GFX_CLK 113 +#define GCC_GPU_SMMU_VOTE_CLK 114 +#define GCC_GPU_SNOC_DVM_GFX_CLK 115 +#define GCC_GPU_THROTTLE_CORE_CLK 116 +#define GCC_LPASS_CONFIG_CLK 117 +#define GCC_LPASS_CORE_AXIM_CLK 118 +#define GCC_MMU_TCU_VOTE_CLK 119 +#define GCC_PCIE_AUX_CLK 120 +#define GCC_PCIE_AUX_CLK_SRC 121 +#define GCC_PCIE_AUX_PHY_CLK_SRC 122 +#define GCC_PCIE_CFG_AHB_CLK 123 +#define GCC_PCIE_CLKREF_EN 124 +#define GCC_PCIE_MSTR_AXI_CLK 125 +#define GCC_PCIE_PIPE_CLK 126 +#define GCC_PCIE_PIPE_CLK_SRC 127 +#define GCC_PCIE_RCHNG_PHY_CLK 128 +#define GCC_PCIE_RCHNG_PHY_CLK_SRC 129 +#define GCC_PCIE_SLEEP_CLK 130 +#define GCC_PCIE_SLV_AXI_CLK 131 +#define GCC_PCIE_SLV_Q2A_AXI_CLK 132 +#define GCC_PCIE_TBU_CLK 133 +#define GCC_PCIE_THROTTLE_CORE_CLK 134 +#define GCC_PCIE_THROTTLE_XO_CLK 135 +#define GCC_PCIE_TILE_AXI_SYS_NOC_CLK 136 +#define GCC_PDM2_CLK 137 +#define GCC_PDM2_CLK_SRC 138 +#define GCC_PDM_AHB_CLK 139 +#define GCC_PDM_XO4_CLK 140 +#define GCC_PWM0_XO512_CLK 141 +#define GCC_QMIP_CAMERA_NRT_AHB_CLK 142 +#define GCC_QMIP_CAMERA_RT_AHB_CLK 143 +#define GCC_QMIP_DISP_AHB_CLK 144 +#define GCC_QMIP_GPU_CFG_AHB_CLK 145 +#define GCC_QMIP_PCIE_CFG_AHB_CLK 146 +#define GCC_QMIP_VIDEO_VCODEC_AHB_CLK 147 +#define GCC_QUPV3_WRAP0_CORE_2X_CLK 148 +#define GCC_QUPV3_WRAP0_CORE_CLK 149 +#define GCC_QUPV3_WRAP0_S0_CLK 150 +#define GCC_QUPV3_WRAP0_S0_CLK_SRC 151 +#define GCC_QUPV3_WRAP0_S1_CLK 152 +#define GCC_QUPV3_WRAP0_S1_CLK_SRC 153 +#define GCC_QUPV3_WRAP0_S2_CLK 154 +#define GCC_QUPV3_WRAP0_S2_CLK_SRC 155 +#define GCC_QUPV3_WRAP0_S3_CLK 156 +#define GCC_QUPV3_WRAP0_S3_CLK_SRC 157 +#define GCC_QUPV3_WRAP0_S4_CLK 158 +#define GCC_QUPV3_WRAP0_S4_CLK_SRC 159 +#define GCC_QUPV3_WRAP0_S5_CLK 160 +#define GCC_QUPV3_WRAP0_S5_CLK_SRC 161 +#define GCC_QUPV3_WRAP0_S6_CLK 162 +#define GCC_QUPV3_WRAP0_S6_CLK_SRC 163 +#define GCC_QUPV3_WRAP0_S7_CLK 164 +#define GCC_QUPV3_WRAP0_S7_CLK_SRC 165 +#define GCC_QUPV3_WRAP0_S8_CLK 166 +#define GCC_QUPV3_WRAP0_S8_CLK_SRC 167 +#define GCC_QUPV3_WRAP0_S9_CLK 168 +#define GCC_QUPV3_WRAP0_S9_CLK_SRC 169 +#define GCC_QUPV3_WRAP_0_M_AHB_CLK 170 +#define GCC_QUPV3_WRAP_0_S_AHB_CLK 171 +#define GCC_SDCC1_AHB_CLK 172 +#define GCC_SDCC1_APPS_CLK 173 +#define GCC_SDCC1_APPS_CLK_SRC 174 +#define GCC_SDCC1_ICE_CORE_CLK 175 +#define GCC_SDCC1_ICE_CORE_CLK_SRC 176 +#define GCC_SDCC2_AHB_CLK 177 +#define GCC_SDCC2_APPS_CLK 178 +#define GCC_SDCC2_APPS_CLK_SRC 179 +#define GCC_SYS_NOC_CPUSS_AHB_CLK 180 +#define GCC_SYS_NOC_USB2_PRIM_AXI_CLK 181 +#define GCC_SYS_NOC_USB3_PRIM_AXI_CLK 182 +#define GCC_TSCSS_AHB_CLK 183 +#define GCC_TSCSS_CLK_SRC 184 +#define GCC_TSCSS_CNTR_CLK 185 +#define GCC_TSCSS_ETU_CLK 186 +#define GCC_UFS_CLKREF_EN 187 +#define GCC_USB20_MASTER_CLK 188 +#define GCC_USB20_MASTER_CLK_SRC 189 +#define GCC_USB20_MOCK_UTMI_CLK 190 +#define GCC_USB20_MOCK_UTMI_CLK_SRC 191 +#define GCC_USB20_MOCK_UTMI_POSTDIV_CLK_SRC 192 +#define GCC_USB20_SLEEP_CLK 193 +#define GCC_USB30_PRIM_MASTER_CLK 194 +#define GCC_USB30_PRIM_MASTER_CLK_SRC 195 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK 196 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 197 +#define GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC 198 +#define GCC_USB30_PRIM_SLEEP_CLK 199 +#define GCC_USB3_PRIM_CLKREF_EN 200 +#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 201 +#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 202 +#define GCC_USB3_PRIM_PHY_PIPE_CLK 203 +#define GCC_USB3_PRIM_PHY_PIPE_CLK_SRC 204 +#define GCC_VCODEC0_AXI_CLK 205 +#define GCC_VENUS_AHB_CLK 206 +#define GCC_VENUS_CTL_AXI_CLK 207 +#define GCC_VIDEO_AHB_CLK 208 +#define GCC_VIDEO_AXI0_CLK 209 +#define GCC_VIDEO_THROTTLE_CORE_CLK 210 +#define GCC_VIDEO_VCODEC0_SYS_CLK 211 +#define GCC_VIDEO_VENUS_CLK_SRC 212 +#define GCC_VIDEO_VENUS_CTL_CLK 213 +#define GCC_VIDEO_XO_CLK 214 + +/* GCC power domains */ +#define GCC_CAMSS_TOP_GDSC 0 +#define GCC_EMAC0_GDSC 1 +#define GCC_EMAC1_GDSC 2 +#define GCC_PCIE_GDSC 3 +#define GCC_USB20_GDSC 4 +#define GCC_USB30_PRIM_GDSC 5 +#define GCC_VCODEC0_GDSC 6 +#define GCC_VENUS_GDSC 7 + +/* GCC resets */ +#define GCC_CAMSS_OPE_BCR 0 +#define GCC_CAMSS_TFE_BCR 1 +#define GCC_CAMSS_TOP_BCR 2 +#define GCC_EMAC0_BCR 3 +#define GCC_EMAC1_BCR 4 +#define GCC_GPU_BCR 5 +#define GCC_MMSS_BCR 6 +#define GCC_PCIE_BCR 7 +#define GCC_PCIE_PHY_BCR 8 +#define GCC_PDM_BCR 9 +#define GCC_QUPV3_WRAPPER_0_BCR 10 +#define GCC_QUSB2PHY_PRIM_BCR 11 +#define GCC_QUSB2PHY_SEC_BCR 12 +#define GCC_SDCC1_BCR 13 +#define GCC_SDCC2_BCR 14 +#define GCC_TSCSS_BCR 15 +#define GCC_USB20_BCR 16 +#define GCC_USB30_PRIM_BCR 17 +#define GCC_USB3PHY_PHY_PRIM_SP0_BCR 18 +#define GCC_USB3_DP_PHY_PRIM_BCR 19 +#define GCC_USB3_PHY_PRIM_SP0_BCR 20 +#define GCC_USB_PHY_CFG_AHB2PHY_BCR 21 +#define GCC_VCODEC0_BCR 22 +#define GCC_VENUS_BCR 23 +#define GCC_VIDEO_INTERFACE_BCR 24 + +#endif -- cgit From ee935e8dc757614006ac29a7d98a15882cdcaeb2 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Mon, 6 Jul 2026 10:00:18 -0400 Subject: syscall_user_dispatch: Make it configurable in Kconfig Syscall User Dispatch is presently built under CONFIG_GENERIC_SYSCALL and cannot be disabled independently. Add CONFIG_SYSCALL_USER_DISPATCH to make it an optional feature. Signed-off-by: Gregory Price Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260706140020.873735-2-gourry@gourry.net --- arch/Kconfig | 10 ++++++++ include/linux/entry-common.h | 6 ++--- include/linux/syscall_user_dispatch.h | 28 ++++++++++++++++++++-- include/linux/syscall_user_dispatch_types.h | 2 +- kernel/entry/Makefile | 3 ++- .../testing/selftests/syscall_user_dispatch/config | 2 +- 6 files changed, 42 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index fa7507ac8e13..0c01521c2f3f 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -114,6 +114,16 @@ config GENERIC_ENTRY select GENERIC_IRQ_ENTRY select GENERIC_SYSCALL +config SYSCALL_USER_DISPATCH + bool "Syscall User Dispatch" + depends on GENERIC_ENTRY + default y + help + Syscall User Dispatch lets a thread have its own system calls + intercepted and redirected to a userspace signal handler based + on a prctl() configured instruction pointer range. + If unsure, say Y. + config KPROBES bool "Kprobes" depends on HAVE_KPROBES diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h index 416a3352261f..9336516430a1 100644 --- a/include/linux/entry-common.h +++ b/include/linux/entry-common.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -55,7 +56,6 @@ static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs } #endif -bool syscall_user_dispatch(struct pt_regs *regs); long trace_syscall_enter(struct pt_regs *regs, long syscall); void trace_syscall_exit(struct pt_regs *regs, long ret); @@ -232,10 +232,8 @@ static __always_inline void syscall_exit_work(struct pt_regs *regs, unsigned lon * of these syscalls is unknown. */ if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) { - if (unlikely(current->syscall_dispatch.on_dispatch)) { - current->syscall_dispatch.on_dispatch = false; + if (syscall_user_dispatch_clear_on_dispatch()) return; - } } audit_syscall_exit(regs); diff --git a/include/linux/syscall_user_dispatch.h b/include/linux/syscall_user_dispatch.h index 3858a6ffdd5c..3dd30f4b2799 100644 --- a/include/linux/syscall_user_dispatch.h +++ b/include/linux/syscall_user_dispatch.h @@ -6,9 +6,23 @@ #define _SYSCALL_USER_DISPATCH_H #include +#include #include -#ifdef CONFIG_GENERIC_ENTRY +struct pt_regs; + +#ifdef CONFIG_SYSCALL_USER_DISPATCH + +bool syscall_user_dispatch(struct pt_regs *regs); + +static __always_inline bool syscall_user_dispatch_clear_on_dispatch(void) +{ + if (likely(!current->syscall_dispatch.on_dispatch)) + return false; + + current->syscall_dispatch.on_dispatch = false; + return true; +} int set_syscall_user_dispatch(unsigned long mode, unsigned long offset, unsigned long len, char __user *selector); @@ -24,6 +38,16 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz #else +static __always_inline bool syscall_user_dispatch(struct pt_regs *regs) +{ + return false; +} + +static __always_inline bool syscall_user_dispatch_clear_on_dispatch(void) +{ + return false; +} + static inline int set_syscall_user_dispatch(unsigned long mode, unsigned long offset, unsigned long len, char __user *selector) { @@ -46,6 +70,6 @@ static inline int syscall_user_dispatch_set_config(struct task_struct *task, return -EINVAL; } -#endif /* CONFIG_GENERIC_ENTRY */ +#endif /* CONFIG_SYSCALL_USER_DISPATCH */ #endif /* _SYSCALL_USER_DISPATCH_H */ diff --git a/include/linux/syscall_user_dispatch_types.h b/include/linux/syscall_user_dispatch_types.h index 3be36b06c7d7..c0bdd4f760d3 100644 --- a/include/linux/syscall_user_dispatch_types.h +++ b/include/linux/syscall_user_dispatch_types.h @@ -4,7 +4,7 @@ #include -#ifdef CONFIG_GENERIC_ENTRY +#ifdef CONFIG_SYSCALL_USER_DISPATCH struct syscall_user_dispatch { char __user *selector; diff --git a/kernel/entry/Makefile b/kernel/entry/Makefile index 2333d70802e4..f220bae86b12 100644 --- a/kernel/entry/Makefile +++ b/kernel/entry/Makefile @@ -13,5 +13,6 @@ CFLAGS_REMOVE_common.o = -fstack-protector -fstack-protector-strong CFLAGS_common.o += -fno-stack-protector obj-$(CONFIG_GENERIC_IRQ_ENTRY) += common.o -obj-$(CONFIG_GENERIC_SYSCALL) += syscall-common.o syscall_user_dispatch.o +obj-$(CONFIG_GENERIC_SYSCALL) += syscall-common.o +obj-$(CONFIG_SYSCALL_USER_DISPATCH) += syscall_user_dispatch.o obj-$(CONFIG_VIRT_XFER_TO_GUEST_WORK) += virt.o diff --git a/tools/testing/selftests/syscall_user_dispatch/config b/tools/testing/selftests/syscall_user_dispatch/config index 039e303e59d7..22c4dfe167ca 100644 --- a/tools/testing/selftests/syscall_user_dispatch/config +++ b/tools/testing/selftests/syscall_user_dispatch/config @@ -1 +1 @@ -CONFIG_GENERIC_ENTRY=y +CONFIG_SYSCALL_USER_DISPATCH=y -- cgit From 03e1dd35c206f24b6bc987198ac58e5138176cb7 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Tue, 7 Jul 2026 15:04:49 -0700 Subject: iomap: add helper to mark folio uptodate Add an exported helper iomap_folio_mark_uptodate() to mark a folio as uptodate and update its uptodate bitmap if the folio has iomap state data attached. This is needed because there are some filesystems (eg fuse) that have paths outside of conventional iomap calls that need to mark a folio as uptodate (eg writing server-pushed data directly into the page cache) and need the iomap-internal uptodate bitmap to be in sync with the uptodate state of the folio. Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi --- fs/iomap/buffered-io.c | 6 ++++++ include/linux/iomap.h | 1 + 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 276720bc18dc..af415293e265 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -105,6 +105,12 @@ static void iomap_set_range_uptodate(struct folio *folio, size_t off, folio_mark_uptodate(folio); } +void iomap_folio_mark_uptodate(struct folio *folio) +{ + iomap_set_range_uptodate(folio, 0, folio_size(folio)); +} +EXPORT_SYMBOL_GPL(iomap_folio_mark_uptodate); + /* * Find the next dirty block in the folio. end_blk is inclusive. * If no dirty block is found, this will return end_blk + 1. diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 56b43d594e6e..40aa3476a351 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -365,6 +365,7 @@ struct folio *iomap_get_folio(struct iomap_iter *iter, loff_t pos, size_t len); bool iomap_release_folio(struct folio *folio, gfp_t gfp_flags); void iomap_invalidate_folio(struct folio *folio, size_t offset, size_t len); bool iomap_dirty_folio(struct address_space *mapping, struct folio *folio); +void iomap_folio_mark_uptodate(struct folio *folio); int iomap_file_unshare(struct inode *inode, loff_t pos, loff_t len, const struct iomap_ops *ops, const struct iomap_write_ops *write_ops); -- cgit From f6ec46b7e2b227499200fb071752ea653f145f3d Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Thu, 2 Jul 2026 14:17:25 +0300 Subject: devlink: print controller prefix for non-zero controller The controller prefix (c) in phys_port_name is currently restricted to external host controllers. This layout sufficed when DPUs only had a single local controller and one or more external host controllers. However, newer devices can have multiple controllers within the DPU itself, even within a single host environment. To support these topologies, allow drivers to report the controller number regardless of the "external" flag status. Any non-zero controller number will now be explicitly reported, even for single-host or local DPU controllers. Existing ports with controller=0 are unaffected. Update documentation and kdoc to clarify that a non-zero controller number does not require the external flag to be set. Signed-off-by: Moshe Shemesh Reviewed-by: Parav Pandit Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260702111726.816985-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- Documentation/networking/devlink/devlink-port.rst | 9 +++++++++ include/net/devlink.h | 6 +++--- net/devlink/port.c | 6 +++--- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 18aca77006d5..fe2cfee3e2a6 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -107,6 +107,15 @@ doesn't have the eswitch. Local controller (identified by controller number = 0) has the eswitch. The Devlink instance on the local controller has eswitch devlink ports for both the controllers. +A non-zero controller number may also be used for ports that are not external. +For example, a SmartNIC may have additional local PCI physical functions +that are managed by the eswitch but are not on an external host. These +ports use a non-zero controller number to distinguish them from the eswitch +manager's own functions, while the external flag remains unset. + +The ``phys_port_name`` includes the controller prefix (``c``) +whenever the controller number is non-zero, regardless of the external flag. + Function configuration ====================== diff --git a/include/net/devlink.h b/include/net/devlink.h index ffe1ad5fb70b..4830aba4087a 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -36,7 +36,7 @@ struct devlink_port_phys_attrs { * struct devlink_port_pci_pf_attrs - devlink port's PCI PF attributes * @controller: Associated controller number * @pf: associated PCI function number for the devlink port instance - * @external: when set, indicates if a port is for an external controller + * @external: when set, indicates if a port is for an external host controller. */ struct devlink_port_pci_pf_attrs { u32 controller; @@ -50,7 +50,7 @@ struct devlink_port_pci_pf_attrs { * @pf: associated PCI function number for the devlink port instance * @vf: associated PCI VF number of a PF for the devlink port instance; * VF number starts from 0 for the first PCI virtual function - * @external: when set, indicates if a port is for an external controller + * @external: when set, indicates if a port is for an external host controller. */ struct devlink_port_pci_vf_attrs { u32 controller; @@ -64,7 +64,7 @@ struct devlink_port_pci_vf_attrs { * @controller: Associated controller number * @sf: associated SF number of a PF for the devlink port instance * @pf: associated PCI function number for the devlink port instance - * @external: when set, indicates if a port is for an external controller + * @external: when set, indicates if a port is for an external host controller. */ struct devlink_port_pci_sf_attrs { u32 controller; diff --git a/net/devlink/port.c b/net/devlink/port.c index c268afefaed7..dc82cac68e7d 100644 --- a/net/devlink/port.c +++ b/net/devlink/port.c @@ -1529,7 +1529,7 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port, WARN_ON(1); return -EINVAL; case DEVLINK_PORT_FLAVOUR_PCI_PF: - if (attrs->pci_pf.external) { + if (attrs->pci_pf.external || attrs->pci_pf.controller) { n = snprintf(name, len, "c%u", attrs->pci_pf.controller); if (n >= len) return -EINVAL; @@ -1539,7 +1539,7 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port, n = snprintf(name, len, "pf%u", attrs->pci_pf.pf); break; case DEVLINK_PORT_FLAVOUR_PCI_VF: - if (attrs->pci_vf.external) { + if (attrs->pci_vf.external || attrs->pci_vf.controller) { n = snprintf(name, len, "c%u", attrs->pci_vf.controller); if (n >= len) return -EINVAL; @@ -1550,7 +1550,7 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port, attrs->pci_vf.pf, attrs->pci_vf.vf); break; case DEVLINK_PORT_FLAVOUR_PCI_SF: - if (attrs->pci_sf.external) { + if (attrs->pci_sf.external || attrs->pci_sf.controller) { n = snprintf(name, len, "c%u", attrs->pci_sf.controller); if (n >= len) return -EINVAL; -- cgit From f3117a847ad82b4b54a0e62f430228da16420be6 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 1 Jul 2026 14:03:18 +0200 Subject: drm/of: Implement drm_of_get_panel_orientation() Implement drm_of_get_panel_orientation() to retrieve a panel's rotation property as enum drm_panel_orientation. The code has been taken from of_drm_get_panel_orientation(), so convert that helper over. Callers of the old helper can be converted as well. Signed-off-by: Thomas Zimmermann Reviewed-by: Maxime Ripard Reviewed-by: Javier Martinez Canillas Reviewed-by: Thierry Reding Link: https://patch.msgid.link/20260701121055.192475-2-tzimmermann@suse.de --- drivers/gpu/drm/drm_of.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_panel.c | 26 ++------------------------ include/drm/drm_of.h | 11 +++++++++++ 3 files changed, 57 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index d03ada82eac9..96eef327bf7e 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -220,6 +221,49 @@ int drm_of_encoder_active_endpoint(struct device_node *node, } EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); +/** + * drm_of_get_panel_orientation - look up the orientation of the panel through + * the "rotation" binding from a device tree node + * @np: device tree node of the panel + * @orientation: orientation enum to be filled in + * + * Looks up the rotation of a panel in the device tree. The orientation of the + * panel is expressed as a property name "rotation" in the device tree. The + * rotation in the device tree is counter clockwise. + * + * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the + * rotation property doesn't exist. Return a negative error code on failure. + */ +int drm_of_get_panel_orientation(const struct device_node *np, + enum drm_panel_orientation *orientation) +{ + int rotation, ret; + + ret = of_property_read_u32(np, "rotation", &rotation); + if (ret == -EINVAL) { + /* Don't return an error if there's no rotation property. */ + *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; + return 0; + } + + if (ret < 0) + return ret; + + if (rotation == 0) + *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; + else if (rotation == 90) + *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; + else if (rotation == 180) + *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; + else if (rotation == 270) + *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; + else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation); + /** * drm_of_find_panel_or_bridge - return connected panel or bridge device * @np: device tree node containing encoder output ports diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 2c5649e433df..eddd13a34c03 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -508,30 +509,7 @@ EXPORT_SYMBOL(of_drm_find_panel); int of_drm_get_panel_orientation(const struct device_node *np, enum drm_panel_orientation *orientation) { - int rotation, ret; - - ret = of_property_read_u32(np, "rotation", &rotation); - if (ret == -EINVAL) { - /* Don't return an error if there's no rotation property. */ - *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; - return 0; - } - - if (ret < 0) - return ret; - - if (rotation == 0) - *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; - else if (rotation == 90) - *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; - else if (rotation == 180) - *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; - else if (rotation == 270) - *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; - else - return -EINVAL; - - return 0; + return drm_of_get_panel_orientation(np, orientation); } EXPORT_SYMBOL(of_drm_get_panel_orientation); #endif diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 7bcc0ccfe0f4..ebebed14c611 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -20,6 +20,8 @@ struct device_node; struct mipi_dsi_device_info; struct mipi_dsi_host; +enum drm_panel_orientation; + /** * enum drm_lvds_dual_link_pixels - Pixel order of an LVDS dual-link connection * @DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated @@ -47,6 +49,8 @@ int drm_of_component_probe(struct device *dev, int drm_of_encoder_active_endpoint(struct device_node *node, struct drm_encoder *encoder, struct of_endpoint *endpoint); +int drm_of_get_panel_orientation(const struct device_node *np, + enum drm_panel_orientation *orientation); int drm_of_find_panel_or_bridge(const struct device_node *np, int port, int endpoint, struct drm_panel **panel, @@ -101,6 +105,13 @@ static inline int drm_of_encoder_active_endpoint(struct device_node *node, { return -EINVAL; } + +static inline int drm_of_get_panel_orientation(const struct device_node *np, + enum drm_panel_orientation *orientation) +{ + return -EINVAL; +} + static inline int drm_of_find_panel_or_bridge(const struct device_node *np, int port, int endpoint, struct drm_panel **panel, -- cgit From b5a8b1f2a97360649fc67e5e28cc4f2132122422 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 1 Jul 2026 14:03:20 +0200 Subject: drm/panel: Use drm_of_get_panel_orientation() The old of_drm_get_panel_orientation() function was replaced by the drm_of_get_panel_orientation() in the core DRM OF helpers. Replace all uses of the old helper and remove it. Changes in v5: - also convert r63419 panel Changes in v4: - also convert anbernic, chipone and ili9488 panels Changes in v2: - include drm_of.h in all drivers to make sure the new symbol is defined Signed-off-by: Thierry Reding Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patch.msgid.link/20260701121055.192475-4-tzimmermann@suse.de --- drivers/gpu/drm/drm_panel.c | 20 -------------------- drivers/gpu/drm/panel/panel-anbernic-td4310.c | 3 ++- drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c | 3 ++- drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c | 3 ++- drivers/gpu/drm/panel/panel-chipone-icna35xx.c | 3 ++- drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c | 3 ++- drivers/gpu/drm/panel/panel-edp.c | 3 ++- drivers/gpu/drm/panel/panel-elida-kd35t133.c | 3 ++- drivers/gpu/drm/panel/panel-focaltech-ota7290b.c | 3 ++- drivers/gpu/drm/panel/panel-himax-hx83102.c | 3 ++- drivers/gpu/drm/panel/panel-himax-hx8394.c | 3 ++- drivers/gpu/drm/panel/panel-ilitek-ili9488.c | 3 ++- drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c | 3 ++- drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 3 ++- drivers/gpu/drm/panel/panel-ilitek-ili9882t.c | 3 ++- drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c | 2 +- drivers/gpu/drm/panel/panel-lvds.c | 2 +- drivers/gpu/drm/panel/panel-novatek-nt36523.c | 3 ++- drivers/gpu/drm/panel/panel-renesas-r63419.c | 3 ++- drivers/gpu/drm/panel/panel-simple.c | 2 +- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 3 ++- drivers/gpu/drm/panel/panel-sitronix-st7703.c | 3 ++- drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 3 ++- include/drm/drm_panel.h | 8 -------- 24 files changed, 41 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index eddd13a34c03..d7c6f4824b2d 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -492,26 +492,6 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) return ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL(of_drm_find_panel); - -/** - * of_drm_get_panel_orientation - look up the orientation of the panel through - * the "rotation" binding from a device tree node - * @np: device tree node of the panel - * @orientation: orientation enum to be filled in - * - * Looks up the rotation of a panel in the device tree. The orientation of the - * panel is expressed as a property name "rotation" in the device tree. The - * rotation in the device tree is counter clockwise. - * - * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the - * rotation property doesn't exist. Return a negative error code on failure. - */ -int of_drm_get_panel_orientation(const struct device_node *np, - enum drm_panel_orientation *orientation) -{ - return drm_of_get_panel_orientation(np, orientation); -} -EXPORT_SYMBOL(of_drm_get_panel_orientation); #endif /* Find panel by fwnode. This should be identical to of_drm_find_panel(). */ diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c index 9a1b4525423c..3b6de1b2fbc6 100644 --- a/drivers/gpu/drm/panel/panel-anbernic-td4310.c +++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -165,7 +166,7 @@ static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi) if (!ctx->panel_info) return -EINVAL; - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret < 0) return dev_err_probe(dev, ret, "Failed to get panel orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c b/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c index 01b4458e55ad..a70a2e58f88c 100644 --- a/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c +++ b/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -380,7 +381,7 @@ static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), "Failed to get reset GPIO\n"); - ret = of_drm_get_panel_orientation(dsi->dev.of_node, + ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); if (ret) return dev_err_probe(&dsi->dev, ret, diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index 658ce64c71eb..150dff3ab6c3 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include