7.2.11-rc1 review
Subsystems (2)
Greg Kroah-Hartman
maintainerPATCH 140/281 drm/amdgpu: serialize vblank counter reads against GPU reset
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vitaly Prosyak <[email protected]>
commit 1a6023edfbb5673f55fab6348f135fb3b1428ea5 upstream.
The vblank counter callback reads display registers and scanout position without holding reset_domain->sem. During GPU recovery, display commit work can call the callback while reset owns the semaphore for writing. The hardware access then triggers the lockdep_assert_held() in amdgpu_device_skip_hw_access() and sets TAINT_WARN when lockdep is enabled.
Try to acquire the reset semaphore for reading before accessing the hardware, and hold it across the counter and scanout-position reads. If reset is already in progress, return DRM's cached software vblank count instead. This avoids blocking behind reset and avoids accessing display hardware while it is being reset.
The cached count is less accurate than the hardware count, but is used only when reset prevents a safe hardware read. This addresses the reset-time warning from this vblank path; it does not suppress or fix unrelated lockdep warnings.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +5 -0
@@ -30,6 +30,7 @@#include <drm/amdgpu_drm.h> #include <drm/drm_drv.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_vblank.h> #include "amdgpu_uvd.h" #include "amdgpu_vce.h" #include "atom.h"
@@ -1631,6 +1632,9 @@ u32 amdgpu_get_vblank_counter_kms(structDRM_ERROR ("Invalid crtc %u\n" , pipe);return -EINVAL ; } +if (!down_read_trylock (&adev->reset_domain->sem)) +return drm_crtc_vblank_count (crtc); +/* The hw increments its frame counter at start of vsync, not at start * of vblank, as is required by DRM core vblank counter handling.
@@ -1676,6 +1680,7 @@ u32 amdgpu_get_vblank_counter_kms(structDRM_DEBUG_VBL ("NULL mode info! Returned count may be wrong.\n" ); } +up_read (&adev->reset_domain->sem);return count; }
Greg Kroah-Hartman
maintainerPATCH 141/281 drm/amdgpu: make Mac FB workaround generic
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit c7f44ec5c4ec760e74351f1843dcb8322f67cbf6 upstream.
Rather than adding a case of every chip where this is seen just check if the FB is at 0 and the ssid is apple. This should cover all chips which fall into this case.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +7 -10
@@ -1089,20 +1089,17 @@ void amdgpu_gmc_init_vga_resv_regions(stcase CHIP_RENOIR : adev->mman.keep_stolen_vga_memory = true;break ; -case CHIP_POLARIS10 : -case CHIP_POLARIS11 : -case CHIP_POLARIS12 : -/* MacBookPros with switchable graphics put VRAM at 0 when -* the iGPU is enabled which results in cursor issues if -* the cursor ends up at 0. Reserve vram at 0 in that case. -*/ -if (adev->gmc.vram_start ==0 ) - adev->mman.keep_stolen_vga_memory = true; -break ;default : adev->mman.keep_stolen_vga_memory = false;break ; } +/* MacBookPros with switchable graphics put VRAM at 0 when +* the iGPU is enabled which results in cursor issues if +* the cursor ends up at 0. Reserve vram at 0 in that case. +*/ +if ((adev->pdev->subsystem_vendor ==PCI_VENDOR_ID_APPLE ) && + (adev->gmc.vram_start ==0 )) + adev->mman.keep_stolen_vga_memory = true;if (amdgpu_sriov_vf (adev) || !amdgpu_device_has_display_hardware (adev)) {
Greg Kroah-Hartman
maintainerPATCH 142/281 drm/amdgpu: copy debugfs register data outside the GRBM/SRBM locks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Gavrilov <[email protected]>
commit 16c64495859f13f75602f439588883f2a4a4d6a6 upstream.
Reading or writing a register through the amdgpu_regs2 debugfs file with GRBM bank selection, which umr does for every banked access (a single "umr -b x x x -r <reg>" is enough, as is the wave dump with "-O halt_waves"), makes lockdep report a circular locking dependency, after which lockdep stays off until reboot:
WARNING: possible circular locking dependency detected
umr/63306 is trying to acquire lock:
ffff8882cf4b8350 (&mm->mmap_lock){++++}-{4:4}, at: __might_fault+0x9b/0x140
but task is already holding lock:
ffff8881aec010d0 (&amdgpu_grbm_lock_key){+.+.}-{4:4}, at: amdgpu_debugfs_regs2_op.isra.0+0x20d/0xd00 [amdgpu]
...
Chain exists of:
&mm->mmap_lock --> &amdgpu_srbm_lock_key --> &amdgpu_grbm_lock_key
...
__might_fault+0xc5/0x140
amdgpu_debugfs_regs2_op.isra.0+0x441/0xd00 [amdgpu]
full_proxy_write+0x10e/0x1d0
vfs_write+0x1d0/0xf50
ksys_write+0x116/0x250amdgpu_debugfs_regs2_op() and amdgpu_debugfs_process_reg_op() call get_user()/put_user() for every dword while holding grbm_idx_mutex or srbm_mutex, and pm.mutex if requested, so a fault there takes mmap_lock inside those locks. The lock hierarchy in amdgpu_lockdep.c puts srbm_mutex and grbm_idx_mutex below notifier_lock, which amdgpu_cs_submit() takes with the BO reservations held, and dma_resv_lockdep() orders the reservations inside mmap_lock. So mmap_lock is outside grbm_idx_mutex and srbm_mutex, and these two debugfs paths nest it the other way round. The copies under the locks are as old as the bank selection in both files; commit 1d0f5838b126 ("drm/amdgpu: Add lockdep annotations for lock ordering validation") only made them visible to lockdep.
Copy the user buffer in before any of the locks is taken and the result out after all of them are dropped, as amdgpu_debugfs_wave_read(), amdgpu_debugfs_gpr_read() and amdgpu_debugfs_gprwave_read() already do. The buffer is sized by the count passed to read() or write(), so an access larger than PAGE_SIZE is now rejected with -EINVAL; umr reads and writes one register at a time. A fault on the user buffer of a write now fails with -EFAULT before any register is written, instead of after a partial write.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 77 ++++++++++++++++------------ 1 file changed, 46 insertions(+), 31 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +46 -31
@@ -120,6 +120,20 @@ static int amdgpu_debugfs_process_reg_o *pos &= (1UL <<22 ) -1 ; +if (size >PAGE_SIZE ) +return -EINVAL ; + +/* +* Access the user buffer only while none of the locks below are held: +* a fault takes mmap_lock, and grbm_idx_mutex and srbm_mutex nest +* inside it. +*/ + u32 *data __free(kfree) = kmalloc(size, GFP_KERNEL); +if (!data) +return -ENOMEM ; +if (!read &©_from_user (data, buf, size)) +return -EFAULT ; + r =pm_runtime_get_sync (adev_to_drm (adev)->dev);if (r <0 ) {pm_runtime_put_autosuspend (adev_to_drm (adev)->dev);
@@ -151,28 +165,16 @@ static int amdgpu_debugfs_process_reg_omutex_lock (&adev->pm.mutex);while (size) { -uint32_t value; - -if (read) { - value =RREG32 (*pos >>2 ); - r =put_user (value, (uint32_t *)buf); - }else { - r =get_user (value, (uint32_t *)buf); -if (!r) -amdgpu_mm_wreg_mmio_rlc (adev, *pos >>2 , value,0 ); - } -if (r) { - result = r; - goto end; - } +if (read) + data[result >>2 ] =RREG32 (*pos >>2 ); +else +amdgpu_mm_wreg_mmio_rlc (adev, *pos >>2 , data[result >>2 ],0 ); result +=4 ; - buf +=4 ; *pos +=4 ; size -=4 ; } -end:if (use_bank) {amdgpu_gfx_select_se_sh (adev,0xffffffff ,0xffffffff ,0xffffffff ,0 );mutex_unlock (&adev->grbm_idx_mutex);
@@ -187,6 +189,10 @@ end:pm_runtime_put_autosuspend (adev_to_drm (adev)->dev);amdgpu_virt_disable_access_debugfs (adev); + +if (read &©_to_user (buf, data, result)) +return -EFAULT ; +return result; }
@@ -237,11 +243,24 @@ static ssize_t amdgpu_debugfs_regs2_op(sstruct amdgpu_device *adev = rd->adev;ssize_t result =0 ;int r; -uint32_t value;if (size &0x3 || offset &0x3 )return -EINVAL ; +if (size >PAGE_SIZE ) +return -EINVAL ; + +/* +* Access the user buffer only while none of the locks below are held: +* a fault takes mmap_lock, and grbm_idx_mutex and srbm_mutex nest +* inside it. +*/ + u32 *data __free(kfree) = kmalloc(size, GFP_KERNEL); +if (!data) +return -ENOMEM ; +if (write_en &©_from_user (data, buf, size)) +return -EFAULT ; + r =pm_runtime_get_sync (adev_to_drm (adev)->dev);if (r <0 ) {pm_runtime_put_autosuspend (adev_to_drm (adev)->dev);
@@ -280,24 +299,16 @@ static ssize_t amdgpu_debugfs_regs2_op(smutex_lock (&adev->pm.mutex);while (size) { -if (!write_en) { - value =RREG32 (offset >>2 ); - r =put_user (value, (uint32_t *)buf); - }else { - r =get_user (value, (uint32_t *)buf); -if (!r) -amdgpu_mm_wreg_mmio_rlc (adev, offset >>2 , value, rd->id.xcc_id); - } -if (r) { - result = r; - goto end; - } +if (!write_en) + data[result >>2 ] =RREG32 (offset >>2 ); +else +amdgpu_mm_wreg_mmio_rlc (adev, offset >>2 , data[result >>2 ], + rd->id.xcc_id); offset +=4 ; size -=4 ; result +=4 ; - buf +=4 ; } -end: +if (rd->id.use_grbm) {amdgpu_gfx_select_se_sh (adev,0xffffffff ,0xffffffff ,0xffffffff , rd->id.xcc_id);mutex_unlock (&adev->grbm_idx_mutex);
@@ -316,6 +327,10 @@ end:pm_runtime_put_autosuspend (adev_to_drm (adev)->dev);amdgpu_virt_disable_access_debugfs (adev); + +if (!write_en &©_to_user (buf, data, result)) +return -EFAULT ; +return result; }
Greg Kroah-Hartman
maintainerPATCH 143/281 drm/amdgpu/userq: only accept doorbell BOs as queue doorbell
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jesse Zhang <[email protected]>
commit 7f7c88dbf4d543f6703e53bdeed380e886733167 upstream.
amdgpu_userq_get_doorbell_index() pins any BO passed as doorbell_handle into the doorbell domain. For a regular BO, TTM moves it with ttm_bo_move_null() and its contents are lost. Since an importer on the same device gets the exporter's GEM object, a client can do this to a buffer another client shared with it.
Reject BOs that were not created in the doorbell domain.
v2: drop the error message, use a local abo variable (Christian)
--- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +12 -1
@@ -439,6 +439,7 @@ amdgpu_userq_get_doorbell_index(struct au64 doorbell_index;struct drm_gem_object *gobj;struct amdgpu_userq_obj *db_obj = db_info->db_obj; +struct amdgpu_bo *abo;int r, db_size; gobj =drm_gem_object_lookup (filp, db_info->doorbell_handle);
@@ -447,7 +448,17 @@ amdgpu_userq_get_doorbell_index(struct areturn -EINVAL ; } - db_obj->obj =amdgpu_bo_ref (gem_to_amdgpu_bo (gobj)); +/* +* Pinning a regular BO into the doorbell domain would discard its +* contents, possibly those of a buffer shared by another client. +*/ + abo =gem_to_amdgpu_bo (gobj); +if (!(abo->preferred_domains &AMDGPU_GEM_DOMAIN_DOORBELL )) { +drm_gem_object_put (gobj); +return -EINVAL ; + } + + db_obj->obj =amdgpu_bo_ref (abo);drm_gem_object_put (gobj); r =amdgpu_bo_reserve (db_obj->obj, true);
Greg Kroah-Hartman
maintainerPATCH 144/281 drm/amdgpu/userq: return the memdup_user() error for the user MQD
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jesse Zhang <[email protected]>
commit 76ceb6ed610cbf14341a9ba78c5c0b2199b9030a upstream.
mes_userq_mqd_create() returns -ENOMEM when copying the user MQD fails, even though memdup_user() returns -EFAULT for a bad pointer or -EINTR when interrupted by a signal. Return PTR_ERR() instead.
While at it drop the error messages. Neither case is a driver error and userspace shouldn't be able to spam the log with invalid IOCTL parameters.
v2: drop the error messages (Christian)
--- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +3 -6
@@ -304,8 +304,7 @@ static int mes_userq_mqd_create(struct a compute_mqd =memdup_user (u64_to_user_ptr (mqd_user->mqd), mqd_user->mqd_size);if (IS_ERR (compute_mqd)) { -DRM_ERROR ("Failed to read user MQD\n" ); - r = -ENOMEM ; + r =PTR_ERR (compute_mqd); goto free_mqd; }
@@ -349,8 +348,7 @@ static int mes_userq_mqd_create(struct a mqd_gfx_v11 =memdup_user (u64_to_user_ptr (mqd_user->mqd), mqd_user->mqd_size);if (IS_ERR (mqd_gfx_v11)) { -DRM_ERROR ("Failed to read user MQD\n" ); - r = -ENOMEM ; + r =PTR_ERR (mqd_gfx_v11); goto free_mqd; }
@@ -394,8 +392,7 @@ static int mes_userq_mqd_create(struct a mqd_sdma_v11 =memdup_user (u64_to_user_ptr (mqd_user->mqd), mqd_user->mqd_size);if (IS_ERR (mqd_sdma_v11)) { -DRM_ERROR ("Failed to read sdma user MQD\n" ); - r = -ENOMEM ; + r =PTR_ERR (mqd_sdma_v11); goto free_mqd; }
Greg Kroah-Hartman
maintainerPATCH 145/281 drm/amd/display: Fix direct scanout alpha on some DRM_FORMATs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leo Li <[email protected]>
commit 7744262f5d63f9faa4d59dec127ae1c8b2d56495 upstream.
[Why]
DM was declaring it's own list of has_alpha DRM formats to check against for enabling alpha blending, when the format->has_alpha already does that for us.
The self-declared list missed some has_alpha formats, causing direct-scanout planes with such formats to appear opaque.
[How]
Drop the self-declared list, instead use the format->has_alpha field directly.
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c | 20 ---------------- 1 file changed, 1 insertion(+), 19 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +1 -19
@@ -110,25 +110,7 @@ void amdgpu_dm_plane_fill_blending_from_if (plane_state->pixel_blend_mode ==DRM_MODE_BLEND_PREMULTI || plane_state->pixel_blend_mode ==DRM_MODE_BLEND_COVERAGE ) { -static const uint32_t alpha_formats[] = { -DRM_FORMAT_ARGB8888 , -DRM_FORMAT_RGBA8888 , -DRM_FORMAT_ABGR8888 , -DRM_FORMAT_ARGB2101010 , -DRM_FORMAT_ABGR2101010 , -DRM_FORMAT_ARGB16161616 , -DRM_FORMAT_ABGR16161616 , -DRM_FORMAT_ARGB16161616F , - }; -uint32_t format = plane_state->fb->format->format; -unsigned int i; - -for (i =0 ; i <ARRAY_SIZE (alpha_formats); ++i) { -if (format == alpha_formats[i]) { - *per_pixel_alpha = true; -break ; - } - } + *per_pixel_alpha = plane_state->fb->format->has_alpha;if (*per_pixel_alpha && plane_state->pixel_blend_mode ==DRM_MODE_BLEND_COVERAGE ) *pre_multiplied_alpha = false;
Greg Kroah-Hartman
maintainerPATCH 146/281 drm/amd/display: disable self-refresh on tearing flips
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leo Li <[email protected]>
commit 2e54a92e0136c3ff5c7b542b95bf0940ad6687bf upstream.
[Why & How]
Immediate (tearing) flips breaks self refresh state machine assumptions, so disable PSR/Replay for them.
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +19 -0
@@ -10397,6 +10397,13 @@ static void amdgpu_dm_enable_self_refresstruct amdgpu_dm_connector *aconn = (struct amdgpu_dm_connector *)acrtc_state->stream->dm_stream_context; +if (!acrtc_state->base.async_flip) { +amdgpu_dm_psr_set_event (dm, acrtc_state->stream, false, + psr_event_immediate_flip, false); +amdgpu_dm_replay_set_event (dm, acrtc_state->stream, false, + replay_event_immediate_flip, false); + } +/* Decrement skip count when SR is enabled and we're doing fast updates. */ if (acrtc_state->update_type ==UPDATE_TYPE_FAST && (psr->psr_feature_enabled || pr->replay_feature_enabled)) {
@@ -10810,6 +10817,18 @@ static void amdgpu_dm_commit_planes(struspin_unlock_irqrestore (&pcrtc->dev->event_lock, flags); }mutex_lock (&dm->dc_lock); +/* +* Tearing (immediate) flips cannot work with panel self-refresh +* features. The event is cleared by +* amdgpu_dm_enable_self_refresh() once async flips stop. +*/ +if (immediate_flip) { +dc_exit_ips_for_hw_access (dm->dc); +amdgpu_dm_psr_set_event (dm, acrtc_state->stream, true, + psr_event_immediate_flip, true); +amdgpu_dm_replay_set_event (dm, acrtc_state->stream, true, + replay_event_immediate_flip, true); + } update_planes_and_stream_adapter(dm->dc, acrtc_state->update_type, planes_count,
Greg Kroah-Hartman
maintainerPATCH 147/281 mount: keep a copied mount unbindable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 1ab8904ae61b076d1242e1f48537698c77074b5c upstream.
It's groundhog day.
MNT_UNBINDABLE used to be set in in mnt->mnt.mnt_flags and was not part of MNT_INTERNAL_FLAGS. clone_mnt() copied it with all the other flags to the new mount. This guaranteed that the copy of an unbindable mount became unbindable as well. Unbindable copies that ended up as shared lost the unbindable property.
But then commit 406fea799925 ("mount: separate the flags accessed only under namespace_sem") moved MNT_UNBINDABLE from mnt->mnt.mnt_flags into mnt->mnt_t_flags as T_UNBINDABLE.
clone_mnt() doesn't copy from mnt_t_flags and specifically doesn't copy T_UNBINDABLE. The only place a mount gets marked unbindable is in change_mnt_propagation() for MS_UNBINDABLE.
This reintroduced an earlier bug we had already fixed. It resurfaces in copy_mnt_ns() which clones unbindable mounts via CL_COPY_UNBINDABLE. So since v6.17 a mount namespace created via clone(CLONE_NEWNS) or unshare(CLONE_NEWNS) contain private and bindable copies of every unbindable mount. The following snippet:
mount --make-unbindable /mnt unshare -m --propagation unchanged mount --bind /mnt /tmp/x
succeeds. This used to fail with EINVAL. This also applies to recursively binding a tree that contains an unbindable the mount. Such subtrees used to be pruned when they were moved onto a shared mount.
And Documentation/filesystems/sharedsubtree.rst still says that the copy of an unbindable mount is unbindable.
So once again let's fix this. Copy T_UNBINDABLE in clone_mnt(). Let set_mnt_shared() mask it off whenever a mount is made shared. This restores what the old MNT_INTERNAL_FLAGS mask did.
--- fs/namespace.c | 1 + 1 file changed, 1 insertion(+)
fs/namespace.c +1 -0
@@ -1254,6 +1254,7 @@ static struct mount *clone_mnt(struct mo mnt->mnt.mnt_flags =READ_ONCE (old->mnt.mnt_flags) & ~MNT_INTERNAL_FLAGS ; + mnt->mnt_t_flags = old->mnt_t_flags &T_UNBINDABLE ;if (flag & (CL_SLAVE |CL_PRIVATE )) mnt->mnt_group_id =0 ;/* not a peer of original */
Greg Kroah-Hartman
maintainerPATCH 148/281 fs: dont silently unmount busy mounts
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 6857e209015f95be7a8f19a9cd5dac345b9b9859 upstream.
The propagate_mount_busy() helper exists to decide whether a synchronous can suceed. It does that by looking at the copies of the victims in the mounts its parent propagates to. One of those functions from propagation hell.
It skips a copy that has child mounts except for the case of a single child mount covering the copy's root. That algorithm used to be exact. It isn't anymore.
Originally only childless copies were unmounted during propagation. And 1064f874abc0 ("mnt: Tuck mounts under others instead of creating shadow/side mounts.") ended up adding covered mounts to both sides in one go.
Along came 99b19d16471e ("mnt: In propgate_umount handle visiting mounts in any order") and that started to unmount more than before:
[1]: A copy of a mount is unmounted when each of its children is either
its overmount or a copy of the victim itself. In the former case the overmount gets reparented. In the latter case the copy of the victim will end up being unmounted.
So propagate_umount() ended up being rewritten without adjusting propagate_mount_busy(). And currently propagate_mount_busy() doesn't look at copies of the victim that have two or more children nor a single child that is not the overmount.
So any such copy in [1] is not checked for
references. The result is that a synchronous unmount succeeds, pulling out a mount that is still in use.
A container can run into this with its own mounts. The host shares a tree with the container and the host has a mount in that tree:
mount -t tmpfs none /mnt mount --make-shared /mnt mount -t tmpfs none /mnt/a
The container's /mnt is a slave copy of the host mount. The container now takes a detached copy of the tree and mounts that copy beneath its propagated copy of /mnt/a and keeps the file descriptor to the tree open:
unshare -m --propagation unchanged mount --make-rslave / fd = open_tree(AT_FDCWD, "/mnt", OPEN_TREE_CLONE | AT_RECURSIVE) move_mount(fd, "", AT_FDCWD, "/mnt/a", MOVE_MOUNT_BENEATH)
grep /mnt /proc/self/mountinfo
775 355 ... /mnt ... master:646 777 775 ... /mnt/a ... master:646 776 777 ... /mnt/a ... master:647
778 777 ... /mnt/a/a ... master:647
The now attached tree (777) is now mounted beneath the container's copy of /mnt/a. That's also where the host mount sits (as seen from the host mount namespace). So the propagated copy (776) of the host's mount is on top of its root and the copy of that (778) is inside it. Now the host runs:
umount /mnt/a
and it succeeds. The moved tree tree and the copy inside of it are gone from the container. Now only the propagated copy is left and it got reparented to where it was before:
grep /mnt /proc/self/mountinfo
775 355 ... /mnt ... master:646
776 775 ... /mnt/a ...
But the file descriptor still refers to the moved tree. So a synchronous umount detached a mount that is still in use.
Teach propagate_mount_busy() the same checks that propagate_umount() does. It now checks for a single victim mount without children for which it can assume that MNT_LOCKED is cleared on every copy like propagate_umount() does. The copies form chains. IOW, when a copy itself receives propagation from the victim's parent then the mount at the same mountpoint inside that copy is a copy as well. Whether a mount receives propagation from the victim's can simply be checked by walking its masters. Walk each chain once from its top so we are sure that nested copies stay linear. Check every reference once of every copy that gets unmounted.
--- fs/pnode.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 19 deletions(-)
fs/pnode.c +91 -19
@@ -410,19 +410,99 @@ bool propagation_would_overmount(const sreturn false; } +/* Does @m receive propagation from @parent? */ +static bool receives_from (struct mount *m,struct mount *parent) +{ +if (m == parent) +return false; +for (; m; m = m->mnt_master) +if (m == parent ||peers (m, parent)) +return true; +return false; +} + +/* +* Does @m receive propagation from the victim's parent as well? If so, then +* the mount at the victim's mountpoint inside of @m is a umount candidate as +* well. So it's the next candidate in the chain. Otherwise the chain ends at +* @m. +*/ +static struct mount *next_candidate (struct mount *m,struct mount *victim) +{ +if (!receives_from (m, victim->mnt_parent)) +return NULL ; +return __lookup_mnt (&m->mnt, victim->mnt_mountpoint); +} + +/* +* Would propagate_umount() pull out a mount of the chain of candidates that +* starts at @c, and does that mount have references beyond its own? +* +* This mirrors how trim_one(), trim_ancestors() and handle_locked() handle a +* synchronous umount: +* +* - single victim +* - without children +* - with MNT_LOCKED already cleared on every candidate by propagate_mount_unlock() +* +* A copy of the victim gets unmounted when each of its children is +* the next candidate in the chain or its overmount, unless the next +* unmount candidate is not its overmount and some unmount candidate further +* down has a child outside the chain. Keep this in sync with +* Documentation/filesystems/propagate_umount.txt. +*/ +static bool chain_busy (struct mount *c,struct mount *victim) +{ +struct mount *m, *n, *next, *deepest =NULL ; +bool above; + +/* the deepest candidate with a child outside the chain */ +for (m = c; m; m = next) { + next =next_candidate (m, victim); +list_for_each_entry (n, &m->mnt_mounts, mnt_child) { +if (n != next && n != victim) { + deepest = m; +break ; + } + } + } + + above = deepest !=NULL ;/* @deepest is at or below @m */ +for (m = c; m; m = next) { +bool goes = true; + + next =next_candidate (m, victim); +list_for_each_entry (n, &m->mnt_mounts, mnt_child) { +if (n != next && n != m->overmount && n != victim) { + goes = false; +break ; + } + } +if (goes && next && next != m->overmount && above && m != deepest) + goes = false; +if (m == deepest) + above = false; +if (goes &&do_refcount_check (m,1 )) +return true; + } +return false; +} +/* * check if the mount 'mnt' can be unmounted successfully. * @mnt: the mount to be checked for unmount * NOTE: unmounting 'mnt' would naturally propagate to all * other mounts its parent propagates to. -* Check if any of these mounts that **do not have submounts** -* have more references than 'refcnt'. If so return busy. +* Check if any of the mounts that propagate_umount() would pull out +* along with it have more references than their own. If so return busy. * * vfsmount lock must be held for write */ int propagate_mount_busy (struct mount *mnt,int refcnt) {struct mount *parent = mnt->mnt_parent; +struct dentry *mp = mnt->mnt_mountpoint; +struct mount *m;/* * quickly check if the current mount can be unmounted.
@@ -435,24 +515,16 @@ int propagate_mount_busy(struct mount *mif (mnt == parent)return 0 ; -for (struct mount *m =propagation_next (parent, parent); m; - m =propagation_next (m, parent)) { -struct list_head *head; -struct mount *child =__lookup_mnt (&m->mnt, mnt->mnt_mountpoint); - -if (!child) +/* the candidates are the mounts at @mp below the receivers */ +for (m =propagation_next (parent, parent); m; + m =propagation_next (m, parent)) { +struct mount *c =__lookup_mnt (&m->mnt, mp); + +/* each chain once, from its top: skip receivers that are candidates */ +if (!c || (mnt_has_parent (m) && m->mnt_mountpoint == mp && +receives_from (m->mnt_parent, parent)))continue ; - - head = &child->mnt_mounts; -if (!list_empty (head)) { -/* -* a mount that covers child completely wouldn't prevent -* it being pulled out; any other would. -*/ -if (!list_is_singular (head) || !child->overmount) -continue ; - } -if (do_refcount_check (child,1 )) +if (chain_busy (c, mnt))return 1 ; }return 0 ;
Greg Kroah-Hartman
maintainerPATCH 149/281 namespace: reset the old parents ->overmount in mnt_change_mountpoint()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 476ef286a7a5db8bda51325dc0c45887b4d99d9a upstream.
__umount_mnt() resets ->overmount of the parent when it unmounts the mount that overmounts it. mnt_change_mountpoint() doesn't. It moves the mount to its new parent and leaves the old one pointing at it.
For a mount that is moved beneath in attach_recursive_mnt() that doesn't matter. The new mount has already reset ->overmount of the old parent by the time the old top mount is moved onto it.
It matters for reparent() though. propagate_umount() moves a surviving overmount onto the first ancestor that isn't going to be unmounted. The mount it was taken from is unmounted immediately afterwards. If a file descriptor keeps that unmounted mount alive ->overmount points at the moved mount. That's a dangling pointer and UAF as the moved mount is unmounted and freed.
do_lock_mount() follows ->overmount from the target path under mount_lock. With a file descriptor on such a mount as the target of a mount beneath topmost_overmount() walks into the freed mount and do_lock_mount() takes references on whatever it finds there.
So reset ->overmount of the old parent in mnt_change_mountpoint() the same way __umount_mnt() does. A mount's ->overmount is then always a mount that is attached to it.
--- fs/namespace.c | 3 +++ 1 file changed, 3 insertions(+)
fs/namespace.c +3 -0
@@ -1058,7 +1058,10 @@ static void attach_mnt(struct mount *mntvoid mnt_change_mountpoint (struct mount *parent,struct mountpoint *mp,struct mount *mnt) {struct mountpoint *old_mp = mnt->mnt_mp; +struct mount *old_parent = mnt->mnt_parent; +if (old_parent->overmount == mnt) + old_parent->overmount =NULL ;list_del_init (&mnt->mnt_child);hlist_del_init (&mnt->mnt_mp_list);hlist_del_init_rcu (&mnt->mnt_hash);
Greg Kroah-Hartman
maintainerPATCH 150/281 statmount: read the parent of an unmounted mount under mount_lock
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit bb46094057520304073130920c223451c2a694a7 upstream.
statmount_mnt_basic() reads the ids of the parent through m->mnt_parent with namespace_sem held for reading. That's fine for a mount that is in a namespace. Everything above it is pinned by that namespace.
STATMOUNT_BY_FD also accepts a file descriptor on a mount that has been unmounted on purpose and that only the file descriptor keeps alive. If that mount stayed attached to its parent when it was unmounted the parent is unmounted as well and nothing pins it for the child.
The final mntput() unmounts the children under mount_lock alone and frees the parent afterwards. Concurrently, statmount_mnt_basic() reads m->mnt_parent twice while that can happen and can easily dereference a mount that's already gone.
Read the parent of an unmounted mount under mount_lock. The parent can't be put while it is held and both ids come from the same mount. A mounted mount's parent is pinned by the namespace and a mount can't leave its namespace while namespace_sem is held, so the common case takes no global lock.
--- fs/namespace.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
fs/namespace.c +12 -2
@@ -5316,15 +5316,25 @@ static void statmount_sb_basic(struct ks s->sm.sb_flags = sb->s_flags & (SB_RDONLY |SB_SYNCHRONOUS |SB_DIRSYNC |SB_LAZYTIME ); } +static void statmount_mnt_parent (struct kstatmount *s,const struct mount *m) +{ + s->sm.mnt_parent_id = m->mnt_parent->mnt_id_unique; + s->sm.mnt_parent_id_old = m->mnt_parent->mnt_id; +} +static void statmount_mnt_basic (struct kstatmount *s) {struct mount *m =real_mount (s->mnt); s->sm.mask |=STATMOUNT_MNT_BASIC ; s->sm.mnt_id = m->mnt_id_unique; - s->sm.mnt_parent_id = m->mnt_parent->mnt_id_unique; s->sm.mnt_id_old = m->mnt_id; - s->sm.mnt_parent_id_old = m->mnt_parent->mnt_id; +/* An unmounted mount is cut loose from its parent under mount_lock alone. */ +if (likely (is_mounted (s->mnt))) +statmount_mnt_parent (s, m); +else + scoped_guard(mount_locked_reader) +statmount_mnt_parent (s, m); s->sm.mnt_attr =mnt_to_attr_flags (&m->mnt); s->sm.mnt_propagation =mnt_to_propagation_flags (m); s->sm.mnt_peer_group = m->mnt_group_id;
Greg Kroah-Hartman
maintainerPATCH 151/281 namespace: dont inherit MNT_UMOUNT in clone_mnt()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit e2c6d326216642011da318b9edfd5d57ebd5a56f upstream.
MNT_UMOUNT gets raised on a mount that umount_tree() or umount_one() has taken out of its namespace. It's never cleared. __detach_mounts() unhooks a mount that carries it instead of unmounting it.
disconnect_mount() lets an unmounted child stay attached only if its parent carries it. propagate_umount() takes it as the sign that a mount is going to be unmounted when it decides what else can be unmounted, where a chain of locked mounts ends and where a for reparenting an overmount.
clone_mnt() copies the flags of the source and only masks MNT_INTERNAL_FLAGS which doesn't include MNT_UMOUNT. A mount that was lazily unmounted but is kept alive by a file descriptor can still be copied when its root is an nsfs or pidfs file because may_copy_tree() accepts those before checking whether the source is mounted at all:
mount --bind /proc/self/ns/net /tmp/x exec 3< /tmp/x umount -l /tmp/x mount --bind /proc/self/fd/3 /tmp/y
The mount on /tmp/y is attached to the caller's namespace with MNT_UMOUNT already set and every copy of it inherits the flag again.
That causes a number of bugs:
- Unlink its mountpoint from a mount namespace where it isn't one. __detach_mounts() takes it for a mount that was unmounted earlier, unhooks it from its parent and drops the reference the namespace holds on it. It stays in the namespace with one reference too few and as its own parent, so it can't be unmounted through the file descriptor anymore and the namespace never finds it again. When the namespace goes away it's leaked and points to a freed struct mnt_namespace that may_change_propagation() and anon_ns_root() read on the next propagation change or mount_setattr() through the file descriptor.
- Make it shared and unmount a peer. trace_transfers() follows the flag from the peer into it and strips it from its peer group.
- Let a propagated umount reach it. gather_candidates() marks it with T_UMOUNT_CANDIDATE but never puts it on the candidate list, so the mark is never cleared and every later propagated umount skips it. If it's the parent of a locked mount handle_locked() takes it as the cutoff and commits the locked child that should have stayed and disconnect_mount() leaves that child attached to it, unmounted under a mounted parent, which is what disconnect_mount() exists to prevent. When the namespace goes away umount_tree() visits the child a second time. And once __detach_mounts() has made it its own parent reparent() never finds the end of the chain of unmounted parents.
A clone is a new mount that has never been unmounted. Add MNT_UMOUNT to MNT_INTERNAL_FLAGS so clone_mnt() never copies it. And warn when umount_tree() reaches a mount that already has it set.
--- fs/namespace.c | 2 ++ include/linux/mount.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-)
fs/namespace.c +2 -0
@@ -1787,6 +1787,8 @@ static void umount_tree(struct mount *mn/* Gather the mounts to umount */ for (p = mnt; p; p =next_mnt (p, mnt)) { +/* A mount is unmounted once. */ +VFS_WARN_ON_ONCE (p->mnt.mnt_flags &MNT_UMOUNT ); p->mnt.mnt_flags |=MNT_UMOUNT ;if (mnt_ns_attached (p))move_from_ns (p);
include/linux/mount.h +1 -1
@@ -52,7 +52,7 @@ enum mount_flags {MNT_ATIME_MASK =MNT_NOATIME |MNT_NODIRATIME |MNT_RELATIME ,MNT_INTERNAL_FLAGS =MNT_INTERNAL |MNT_DOOMED | -MNT_SYNC_UMOUNT |MNT_LOCKED +MNT_SYNC_UMOUNT |MNT_LOCKED |MNT_UMOUNT }; struct vfsmount {
Greg Kroah-Hartman
maintainerPATCH 152/281 namespace: dont copy an unmounted mount tree
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 8bacbf6be959973b28b691713a872c12eae69eec upstream.
may_copy_tree() accepts a mount whose root is an nsfs or pidfs file before it checks whether that mount is mounted. This exception exists for nsfs and pidfs mounts. They are part of no mount namespace so neither check_mnt() nor is_mounted() would let them through and bind-mounting a nsfsfd or a pidfd wouldn't work.
Problem is that exception also admits a bind mount of such a file when it was lazily unmounted and is only kept alive by a file descriptor:
mount --bind /proc/self/ns/net /tmp/x exec 3< /tmp/x umount -l /tmp/x mount --rbind /proc/self/fd/3 /tmp/y
With MS_REC or AT_RECURSIVE copy_tree() doesn't stop at the unmounted mount. It copies every mount that stayed attached to it as well. So if you have a bunch of overmounts over the nsfsfd or the pidfd the whole unmounted stack is copied. That's a bug.
So refuse to walk an unmounted tree. Copying its root alone stays possible. So a bind mount of a namespace that is gone can still be recreated from a file descriptor on the old one. The file descriptor pins that mount and its root and __has_locked_children() only reads the direct children which don't leave the parent's list without namespace_sem. The internal mounts carry MNT_NS_INTERNAL instead of NULL and are still walked. Copying an unmounted tree recursively fails with EINVAL.
--- fs/namespace.c | 3 +++ 1 file changed, 3 insertions(+)
fs/namespace.c +3 -0
@@ -2992,6 +2992,9 @@ static struct mount *__do_loopback(constif (!may_copy_tree (old_path))return ERR_PTR (-EINVAL ); +if (recurse && !old->mnt_ns) +return ERR_PTR (-EINVAL ); +if (!recurse &&__has_locked_children (old, old_path->dentry))return ERR_PTR (-EINVAL );
Greg Kroah-Hartman
maintainerPATCH 153/281 fs: refuse fspick() on internal superblocks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 02587a4af82adccaef8e3b3624b4e7f1c401632d upstream.
There's a few instances where a internal superblock is reachable by fspick(). Block that. graft_tree() and fsmount() already refuse them. fanotify refuses mount and superblock marks on them for the same reason.
--- fs/fsopen.c | 3 +++ 1 file changed, 3 insertions(+)
fs/fsopen.c +3 -0
@@ -190,6 +190,9 @@ SYSCALL_DEFINE3(fspick, int, dfd, const ret = -EINVAL ;if (target.mnt->mnt_root != target.dentry) goto err_path; +/* kernel-internal superblocks are nobody's to reconfigure */ +if (target.dentry->d_sb->s_flags &SB_NOUSER ) + goto err_path; fc =fs_context_for_reconfigure (target.dentry,0 ,0 );if (IS_ERR (fc)) {
Greg Kroah-Hartman
maintainerPATCH 154/281 namespace: check the target before walking it in do_mount_setattr()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 08f4288a37ef4b3d19b4a9599ec3048219cac828 upstream.
When mount_setattr() changes the propagation type to MS_SHARED do_mount_setattr() takes namespace_sem and calls invent_group_ids() which walks the whole target tree with next_mnt() and allocates a peer group id for every mount that doesn't have one.
Whether the caller may touch the target at all is only checked afterwards under mount_lock. If it may not, cleanup_group_ids() walks the tree a second time on the way out to free the ids again.
A detached tree that a file descriptor keeps alive, a mount in another mount namespace or a mount below the root of an anonymous mount namespace does a bunch of allocation work that is completely useless because it will fail and revert right after.
Do the right thing and check the target right after taking namespace_sem instead. A mount leaves its namespace only under namespace_sem so the mount stays stable. The check under mount_lock stays for the case where no propagation change is requested and namespace_sem isn't taken. Callers get the same EINVAL as before.
--- fs/namespace.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/namespace.c +6 -0
@@ -4957,6 +4957,11 @@ static int do_mount_setattr(const struct* propagation. */ namespace_lock (); +/* invent_group_ids() walks the tree, only walk a mounted one */ +if (!anon_ns_root (mnt) && !check_mnt (mnt)) { +namespace_unlock (); +return -EINVAL ; + }if (kattr->propagation ==MS_SHARED ) { err =invent_group_ids (mnt, kattr->kflags &MOUNT_KATTR_RECURSE );if (err) {
@@ -4969,6 +4974,7 @@ static int do_mount_setattr(const struct err = -EINVAL ;lock_mount_hash (); +/* Checked under namespace_sem already if the propagation changes. */ if (!anon_ns_root (mnt) && !check_mnt (mnt)) goto out;
Greg Kroah-Hartman
maintainerPATCH 155/281 namespace: queue a mount only once for mount notifications
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit bd2feb7796b6585938a4241e7b7c4148a6f7b762 upstream.
mnt_notify_add() puts a mount on notify_list. It doesn't check whether the mount is on the list already. That's fine as long as every mount is queued at most once per namespace_sem hold. It isn't.
propagate_umount() moves a surviving overmount off a stack of mounts that are going away and queues it as moved. shrink_submounts() and mark_mounts_for_expiry() call umount_tree() for several mounts under a single namespace_sem hold. So the second umount_tree() can take down exactly the mount the first one reparented, reparent it once more and so end up queueing it a second time.
The second list_add_tail() cuts the mounts queued in between out of the list while the head still points at the last of them. notify_mnt_list() then only visits that one mount and the others are freed after the grace period with notify_list still pointing at them. Every later mount operation on the host walks freed memory:
BUG: KASAN: slab-use-after-free in __list_add_valid_or_report Read of size 8 at addr ffff8881003d57e0 by task notify_dq/162 __list_add_valid_or_report+0x15c/0x1a0 mnt_add_to_ns+0x322/0x890 attach_recursive_mnt.isra.0+0xf2a/0x1a00 path_mount+0x139c/0x1d40
This needs a mount with MNT_SHRINKABLE to bind from. That's what finish_automount() creates (submounts of NFS, AFS and CIFS and the tracefs mount below debugfs). Bind mounts inherit the flag. A mount namespace with a FAN_MARK_MNTNS mark does the rest and root in a user namespace can have both.
Initialize to_notify in alloc_vfsmnt() and leave a mount alone that is queued already. mnt_notify() looks at the state the mount has when the list is drained so a single entry per mount is enough. The move event for a mount that is unmounted under the same hold is lost. That's fine. The detach is what matters.
--- fs/mount.h | 3 +++ fs/namespace.c | 3 +++ 2 files changed, 6 insertions(+)
fs/mount.h +3 -0
@@ -233,6 +233,9 @@ static inline struct mnt_namespace *to_m#ifdef CONFIG_FSNOTIFY static inline void mnt_notify_add (struct mount *m) { +/* queued already under this namespace_sem hold */ +if (!list_empty (&m->to_notify)) +return ;/* Optimize the case where there are no watches */ if ((m->mnt_ns && m->mnt_ns->n_fsnotify_marks) || (m->prev_ns && m->prev_ns->n_fsnotify_marks))
fs/namespace.c +3 -0
@@ -322,6 +322,9 @@ static struct mount *alloc_vfsmnt(constINIT_HLIST_NODE (&mnt->mnt_mp_list);INIT_HLIST_HEAD (&mnt->mnt_stuck_children);INIT_HLIST_NODE (&mnt->mnt_ns_visible); +#ifdef CONFIG_FSNOTIFY +INIT_LIST_HEAD (&mnt->to_notify); +#endif RB_CLEAR_NODE (&mnt->mnt_node); mnt->mnt.mnt_idmap = &nop_mnt_idmap; }
Greg Kroah-Hartman
maintainerPATCH 156/281 namespace: check a submount for references right before unmounting it
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 32bf92416614a1e3ff3524fc6b780259c7aad3ef upstream.
shrink_submounts() and mark_mounts_for_expiry() first collect all the mounts they are allowed to unmount and then unmount them.
Whether a mount is busy is decided by propagate_mount_busy() on the tree. But unmounting the first mount changes the tree that the second one propagates into. propagate_umount()
moves a surviving overmount off a copy it unmounts and mounts it where the copy was mounted. If that's where the propagated copy of the second victim is looked up the overmount becomes a candidate of the second umount_tree(). It's childless and so trim_one() commits it without looking at its reference count.
So a synchronous umount pulls out a busy mount that is in use somewhere else and marks it MNT_SYNC_UMOUNT while it has users:
umount2(/tmp/plshrink/p1, 0) = 0 errno 0
cwd is (unreachable)
The same two umounts requested one after the other fail with EBUSY. This needs a mount with MNT_SHRINKABLE, i.e., automounted submounts of NFS, AFS and CIFS or the tracefs mount below debugfs. Bind mounts inherit the flag.
Check each mount right before it is unmounted under the same hold of namespace_sem and mount_lock as the umount itself. Make sure that the algorithm stays linear.
--- fs/namespace.c | 78 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 25 deletions(-)
fs/namespace.c +53 -25
@@ -3965,6 +3965,11 @@ void mark_mounts_for_expiry(struct list_ }while (!list_empty (&graveyard)) { mnt = list_first_entry(&graveyard, struct mount, mnt_expire); +/* an earlier umount_tree() may have moved a busy mount here */ +if (propagate_mount_busy (mnt,1 )) { +list_move (&mnt->mnt_expire, mounts); +continue ; + }touch_mnt_namespace (mnt->mnt_ns);umount_tree (mnt,UMOUNT_PROPAGATE |UMOUNT_SYNC ); }
@@ -3973,16 +3978,36 @@ void mark_mounts_for_expiry(struct list_EXPORT_SYMBOL_GPL (mark_mounts_for_expiry);/* +* Unmount @mnt if it's a shrinkable mount without children that nobody uses. +* +* mount_lock must be held for write +*/ +static bool shrink_submount (struct mount *mnt) +{ +if (propagate_mount_busy (mnt,1 )) +return false; +touch_mnt_namespace (mnt->mnt_ns); +umount_tree (mnt,UMOUNT_PROPAGATE |UMOUNT_SYNC ); +return true; +} + +/* * Ripoff of 'select_parent()' * -* search the list of submounts for a given mountpoint, and move any -* shrinkable submounts to the 'graveyard' list. +* unmount the shrinkable submounts of @parent that aren't busy, children +* before their parent, and say whether anything went +* +* The cursor into the children of @this_parent survives the umount of a +* child mount without child mounts. The mounts that get umounted together with +* it are located under receiving mounts of @this_parent and never under +* @this_parent itself. The one exception is @this_parent getting unmounted +* then the walk starts over. */ -static int select_submounts (struct mount *parent,struct list_head *graveyard) +static bool __shrink_submounts (struct mount *parent) {struct mount *this_parent = parent;struct list_head *next; -int found =0 ; +bool shrunk = false; repeat: next = this_parent->mnt_mounts.next;
@@ -4001,42 +4026,45 @@ resume: this_parent = mnt; goto repeat; } - -if (!propagate_mount_busy (mnt,1 )) { -list_move_tail (&mnt->mnt_expire, graveyard); - found++; - } +if (!shrink_submount (mnt)) +continue ; + shrunk = true; +if (unlikely (this_parent->mnt.mnt_flags &MNT_UMOUNT )) +return true; }/* * All done at this level ... ascend and resume the search */ if (this_parent != parent) { - next = this_parent->mnt_child.next; - this_parent = this_parent->mnt_parent; +struct mount *mnt = this_parent; + + next = mnt->mnt_child.next; + this_parent = mnt->mnt_parent; +/* its children are gone, maybe it can go as well */ +if (shrink_submount (mnt)) { + shrunk = true; +if (unlikely (this_parent->mnt.mnt_flags &MNT_UMOUNT )) +return true; + } goto resume; } -return found; +return shrunk; }/* -* process a list of expirable mountpoints with the intent of discarding any -* submounts of a specific parent mountpoint +* unmount the shrinkable submounts of @mnt that aren't busy +* +* The busy check and the umount of a mount are adjacent. An umount can +* still empty or move a mount in a part of the tree that was walked +* already, so walk again until nothing goes. * * mount_lock must be held for write */ static void shrink_submounts (struct mount *mnt) { -LIST_HEAD (graveyard); -struct mount *m; - -/* extract submounts of 'mountpoint' from the expiration list */ -while (select_submounts (mnt, &graveyard)) { -while (!list_empty (&graveyard)) { - m = list_first_entry(&graveyard, struct mount, - mnt_expire); -touch_mnt_namespace (m->mnt_ns); -umount_tree (m,UMOUNT_PROPAGATE |UMOUNT_SYNC ); - } +for (;;) { +if (!__shrink_submounts (mnt)) +break ; } }
Greg Kroah-Hartman
maintainerPATCH 157/281 namespace: check a recursive bind mount for mount namespace loops
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit c640a01a78e42302879006ed7b7d5a9a943ba815 upstream.
do_loopback() refuses to bind mount the file of a mount namespace that is as old as the caller's or older because a mount namespace that holds a mount of its own file, or of an ancestor's, would cause a cycle. But it only checks the dentry the bind mount starts from. With MS_REC everything below it is copied and do_loopback() passes CL_COPY_MNT_NS_FILE so mount namespace files below the source are copied.
That's fine for a source in the caller's own mount namespace. Every mount namespace file in there passed the same check when it was bind-mounted. It isn't fine for a source in another mount namespace. may_copy_tree() accepts a bind mount of any nsfs or pidfs file no matter what mount namespace it lives in so that /proc/<pid>/ns/<ns> and pidfds can be bind mounted. If we create a bind-mount stack of mount namespaces file descriptors the kernel will copy them irrespective of their ancestoral relationship to the mount namespace in question:
151 146 0:7 net:[4026531833] /tmp/nrc/y rw - nsfs nsfs rw 152 151 0:7 mnt:[4026532293] /tmp/nrc/y rw - nsfs nsfs rw
Mount 152 is a mount of the mount namespace file of mount namespace 4026532293 inside mount namespace 4026532293. That namespace and every mount in it are leaked... All it takes is a process in an older mount namespace that stacks the file and repeating it leaks without limit:
after control: Shmem: 380 kB
child: mount(/proc/self/fd/6, MS_BIND|MS_REC): ok
after cycle: Shmem: 65916 kB
do_move_mount() runs check_for_nsfs_mounts() over a detached tree for exactly that reason. Do the same for the copy before it is grafted.
--- fs/namespace.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
fs/namespace.c +5 -1
@@ -3037,7 +3037,11 @@ static int do_loopback(const struct pathif (IS_ERR (mnt))return PTR_ERR (mnt); - err =graft_tree (mnt, &mp); +/* the copy may carry mount namespace files from below the source */ +if (recurse && !check_for_nsfs_mounts (mnt)) + err = -EINVAL ; +else + err =graft_tree (mnt, &mp);if (err) {lock_mount_hash ();umount_tree (mnt,UMOUNT_SYNC );
Greg Kroah-Hartman
maintainerPATCH 158/281 namespace: keep covered mounts covered in OPEN_TREE_NAMESPACE
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 0febe278ec45b1ccdbe9bd5ff1ba365d8fd55e2a upstream.
open_tree(OPEN_TREE_NAMESPACE) creates a new mount namespace from a copy of the tree at the given path. The caller needs to be privileged over its current user namespace because the new mount namespace will be owned by it. It doesn't need to be privileged over the mount namespace the tree is copied from. An unprivileged user just needs to create a user namespace first.
The copy follows the rules of a detached bind mount. Without AT_RECURSIVE only the mount itself is cloned and its children are left out. With AT_RECURSIVE unbindable mounts are skipped. Both is fine when the caller has privileges over the source mount namespace because it could unmount those mounts anyway. Not so for an unprivileged user in that mount namespace. For them a mount namespace copy is what unshare() does. copy_mnt_ns() copies everything including unbindable mounts and lock_mnt_tree() makes sure nothing can be unmounted in the copy. Nothing that was covered gets revealed.
create_new_namespace() only does the locking and so the covered content is right there in the new mount namespace:
# mount -t tmpfs none /tmp/otn; mkdir /tmp/otn/covered # echo hidden > /tmp/otn/covered/under.txt # mount -t tmpfs none /tmp/otn/covered
uid 1000, after unshare(CLONE_NEWUSER):
fd = open_tree(AT_FDCWD, "/tmp/otn", OPEN_TREE_NAMESPACE);
setns(fd, CLONE_NEWNS);
open("/covered/under.txt", O_RDONLY) -> "hidden"Covering paths with mounts is how container runtimes mask parts of /proc and /sys and how admins hide things.
When the caller's user namespace doesn't own the source mount namespace copy the way unshare() does and refuse a non-recursive copy of a mount that has anything mounted below the requested directory and copy unbindable mounts in a recursive copy. A caller that is privileged over the source mount namespace sees no change.
--- fs/namespace.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-)
fs/namespace.c +26 -4
@@ -2354,6 +2354,18 @@ bool has_locked_children(struct mount *mreturn __has_locked_children (mnt, dentry); } +/* locks: namespace_shared && pinned(mnt) || mount_locked_reader */ +static bool __has_children (struct mount *mnt,struct dentry *dentry) +{ +struct mount *child; + +list_for_each_entry (child, &mnt->mnt_mounts, mnt_child) { +if (is_subdir (child->mnt_mountpoint, dentry)) +return true; + } +return false; +} +/* * Check that there aren't references to earlier/same mount namespaces in the * specified subtree. Such references can act as pins for mount namespaces
@@ -3123,12 +3135,18 @@ static struct mnt_namespace *create_new_struct mount *mnt;unsigned int copy_flags =0 ;bool locked = false, recurse = flags &MOUNT_COPY_RECURSIVE ; +bool foreign = user_ns != ns->user_ns;if (unlikely (!d_can_lookup (path->dentry)))return ERR_PTR (-ENOTDIR ); -if (user_ns != ns->user_ns) - copy_flags |=CL_SLAVE ; +/* +* Without privileges over the mount namespace the copy is made from +* nothing mounted below @path may be left out. It would reveal what +* it covers. That's what unshare() gives such a caller as well. +*/ +if (foreign) + copy_flags |=CL_SLAVE |CL_COPY_UNBINDABLE ; new_ns =alloc_mnt_ns (user_ns, false);if (IS_ERR (new_ns))
@@ -3162,10 +3180,14 @@ static struct mnt_namespace *create_new_/* * We don't emulate unshare()ing a mount namespace. We stick to * the restrictions of creating detached bind-mounts. It has a -* lot saner and simpler semantics. +* lot saner and simpler semantics. A caller without privileges +* over the mount namespace can't leave out any child though. */ if (flags &MOUNT_COPY_NEW ) mnt =clone_mnt (real_mount (path->mnt), path->dentry, copy_flags); +else if (foreign && !recurse && +__has_children (real_mount (path->mnt), path->dentry)) + mnt =ERR_PTR (-EINVAL );else mnt =__do_loopback (path, recurse, copy_flags);scoped_guard (mount_writer) {
@@ -3182,7 +3204,7 @@ static struct mnt_namespace *create_new_* of the real rootfs we created. */ attach_mnt (mnt, new_ns_root, mp.mp); -if (user_ns != ns->user_ns) +if (foreign)lock_mnt_tree (new_ns_root); }
Greg Kroah-Hartman
maintainerPATCH 159/281 namespace: look at the topmost mount for a mount namespace file
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 8714b44adac7b10506e1db3ef1710689751f0f10 upstream.
attach_recursive_mnt() preallocates a mountpoint for the root of the topmost mount of the source so that a mount that already sits at the destination can be put on top of the source or on top of one of its propagated copies.
The copies are made without CL_COPY_MNT_NS_FILE so their chain of overmounts is shorter than the source's. It ends below the first bind mount of a mount namespace file. So while the loop walks up the chain of the source it remembers the mountpoint of that mount in "shorter" and the existing mount is put there for the copies.
But the loop ends at the topmost mount without ever looking at it. If the topmost mount is the only mount namespace file in the chain "shorter" stays NULL and mnt_change_mountpoint() attaches the existing mount of the copy below the root of the mount namespace file. That's a dentry of nsfs. No path walk in the copy's mount namespace ever gets there. The mount is gone until its parent goes and the copy that took its place can't be unmounted synchronously because it has a child:
S bind mount of a file N bind mount of a mount namespace file on top of S A shared mount, B a slave of A, Q mounted on B/file
move_mount(S, A/file)
cat B/file -> the content of S instead of Q
umount B/file -> EBUSY
Look at every mount of the chain including the topmost one.
--- fs/namespace.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/namespace.c +3 -1
@@ -2602,9 +2602,11 @@ static int attach_recursive_mnt(struct m* Preallocate a mountpoint in case the new mounts need to be * mounted beneath mounts on the same mountpoint. */ -for (top = source_mnt;unlikely (top->overmount); top = top->overmount) { +for (top = source_mnt; ; top = top->overmount) {if (!shorter &&is_mnt_ns_file (top->mnt.mnt_root)) shorter = top->mnt_mp; +if (likely (!top->overmount)) +break ; } err =get_mountpoint (top->mnt.mnt_root, &root);if (err)
Greg Kroah-Hartman
maintainerPATCH 160/281 namespace: dont reconfigure internal superblocks via remount and umount
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit d791606bb915a8f27e36657696fd1d9a216c5c08 upstream.
Commit 02587a4af82a ("fs: refuse fspick() on internal superblocks") blocked fspick() on SB_NOUSER superblocks. But that's not the only way to reconfigure_super(). mount(MS_REMOUNT) and umount() of the caller's root without MNT_DETACH. The root of an empty mount namespace is a nullfs mount and all mount namespaces share that superblock:
nullfs: fspick: FAIL errno=22 (Invalid argument)
nullfs: mount(MS_REMOUNT|MS_RDONLY): ok(0)
nullfs after remount: statfs(/): magic=0x4e554c4c flags=0x21 RDONLY
nullfs: umount2("/", 0): ok(0)
Refuse both like fspick() does. Only root in the initial user namespace can do this and nullfs is empty and immutable so the flags don't buy anything. But they show up in statfs() for the root of every mount namespace on the host.
--- fs/fs_context.c | 4 ++++ fs/fsopen.c | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-)
fs/fs_context.c +4 -0
@@ -315,6 +315,10 @@ struct fs_context *fs_context_for_reconfigure(struct dentry *dentry, unsigned int sb_flags, unsigned int sb_flags_mask) { +/* kernel-internal superblocks are nobody's to reconfigure */ +if (dentry->d_sb->s_flags &SB_NOUSER ) +return ERR_PTR (-EINVAL ); +return alloc_fs_context (dentry->d_sb->s_type, dentry, sb_flags, sb_flags_mask,FS_CONTEXT_FOR_RECONFIGURE ); }
fs/fsopen.c +0 -3
@@ -190,9 +190,6 @@ SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags ret = -EINVAL ;if (target.mnt->mnt_root != target.dentry) goto err_path; -/* kernel-internal superblocks are nobody's to reconfigure */ -if (target.dentry->d_sb->s_flags &SB_NOUSER ) - goto err_path; fc =fs_context_for_reconfigure (target.dentry,0 ,0 );if (IS_ERR (fc)) {
Signature · 1 line
2.56.0
Greg Kroah-Hartman
maintainerPATCH 161/281 namespace: remove the fsnotify marks of a mount namespace in process context
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 1a116222acdcc1bc1a8350727ae6f28907d44594 upstream.
mnt_ns_release() drops the last passive reference of a mount namespace and removes its fanotify marks via fsnotify_mntns_delete(). That takes the mutex of every group with a mark on the namespace and the spinlock of the connector. Fine from process context. But mnt_ns_tree_remove() hands the reference the namespace was allocated with to call_rcu() and so the marks are removed from the RCU softirq:
BUG: sleeping function called from invalid context at kernel/locking/mutex.c:623 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 0, name: swapper/3 __mutex_lock+0x113/0x24b0 fsnotify_destroy_marks+0x11b/0x3d0 mnt_ns_release_rcu+0x57/0xa0 rcu_core+0x6b6/0x1e40
and lockdep complains about the connector lock being taken from softirq context. A fanotify group with a FAN_MARK_MNTNS mark on the mount namespace of another task is all that's needed. Root in a user namespace can do that for a mount namespace it owns. The task exits and the namespace is freed with the mark still on it.
Remove the marks in free_mnt_ns() before the namespace is handed to RCU. That runs in process context once the last active reference is gone. A mark is added through a file descriptor to the namespace which holds an active reference so no mark can show up after that.
--- fs/namespace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/namespace.c +2 -1
@@ -130,7 +130,6 @@ static void mnt_ns_release(struct mnt_na {/* keep alive for {list,stat}mount() */ if (ns &&refcount_dec_and_test (&ns->passive)) { -fsnotify_mntns_delete (ns);put_user_ns (ns->user_ns);kfree (ns); }
@@ -4257,6 +4256,8 @@ static void free_mnt_ns(struct mnt_namesif (!is_anon_ns (ns))ns_common_free (ns);dec_mnt_namespaces (ns->ucounts); +/* the last active reference is gone, no mark can show up anymore */ +fsnotify_mntns_delete (ns);mnt_ns_tree_remove (ns); }
Greg Kroah-Hartman
maintainerPATCH 162/281 namespace: check the mounts before reading their parents in pivot_root()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 6b7c001eb5857fa09041f47673666060a2e5fc1c upstream.
path_pivot_root() reads the parents of new_root and of the caller's root and checks whether they are shared before it checks that either mount is in the caller's mount namespace. Only namespace_sem is held. That's fine for a mount that is in the caller's mount namespace.
But new_root can be a file descriptor to a mount that has been unmounted and that only the file descriptor keeps alive. If that mount stayed attached to its parent when it was unmounted nothing pins the parent for it. The final mntput() of the parent unhooks the children under mount_lock alone and frees the parent afterwards:
pivot_root() close(fd), last ref on the parent
------------ ---------------------------------
ex_parent = new_mnt->mnt_parent
mntput_no_expire_slowpath()
__umount_mnt(new_mnt)
cleanup_mnt()
call_rcu() IS_MNT_SHARED(ex_parent)
BUG: KASAN: slab-use-after-free in path_pivot_root+0xf1a/0x1840 Read of size 4 at addr ffff8881047382f8 by task pivot_widen/157 path_pivot_root+0xf1a/0x1840 __x64_sys_pivot_root+0x165/0x190
The same goes for the caller's root via chroot(). Both outcomes of the check end in EINVAL so nothing but the read itself goes wrong. It's the same thing commit bb4609405752 ("statmount: read the parent of an unmounted mount under mount_lock") fixed for statmount().
Check that both mounts are in the caller's namespace before their parents are read. Every path returns EINVAL either way.
--- fs/namespace.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
fs/namespace.c +3 -2
@@ -4759,14 +4759,15 @@ int path_pivot_root(struct path *new, st new_mnt =real_mount (new->mnt); root_mnt =real_mount (root.mnt); +/* only a mounted mount has a parent that namespace_sem pins */ +if (!check_mnt (root_mnt) || !check_mnt (new_mnt)) +return -EINVAL ; ex_parent = new_mnt->mnt_parent; root_parent = root_mnt->mnt_parent;if (IS_MNT_SHARED (old_mnt) ||IS_MNT_SHARED (ex_parent) ||IS_MNT_SHARED (root_parent))return -EINVAL ; -if (!check_mnt (root_mnt) || !check_mnt (new_mnt)) -return -EINVAL ;if (new_mnt->mnt.mnt_flags &MNT_LOCKED )return -EINVAL ;if (d_unlinked (new->dentry))
Greg Kroah-Hartman
maintainerPATCH 163/281 dcache: dont put a mountpoint on a dentry thats being removed
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 75f4197f1e11d2eb75c2d6f916a7d4764db621e5 upstream.
rmdir(), unlink() and rename() call dont_mount() on the victim and then detach_mounts() with the victim's inode locked. do_lock_mount() takes the inode lock of the mountpoint and checks cant_mount() so a mount can't show up after detach_mounts().
But attach_recursive_mnt() makes a second mountpoint for the root of the source mount so that the mounts already located at the destination can be put on top of it. No inode is locked for that one and d_set_mounted() only refuses a dentry that is unlinked. Between detach_mounts() and d_delete() the victim is still hashed:
rmrace: b passed dont_mount() and detach_mounts(), sleeping
T2: move_mount(S, /tmp/plcant/x, BENEATH) = 0 errno 0 ()
T1: rmdir(/tmp/plcant/d/b) = 0 errno 0
109 107 0:61 /d/b//deleted /tmp/plcant/x rw,relatime - tmpfs tmpfs rw
108 109 0:63 / /tmp/plcant/x rw,relatime - tmpfs T rwA bind mount of the directory that's being removed is moved beneath an existing mount while the rmdir() is located between the two calls. The mount ends up on the removed directory and nothing will ever detach it.
Check cant_mount() in d_set_mounted() as well. dont_mount() raises the flag under d_lock before detach_mounts() runs so either the mountpoint is set first and detach_mounts() finds the mount or the flag is seen and the mount is refused.
--- fs/dcache.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/dcache.c +3 -1
@@ -1583,6 +1583,8 @@ EXPORT_SYMBOL(path_has_submounts);* * Only one of d_invalidate() and d_set_mounted() must succeed. For * this reason take rename_lock and d_lock on dentry and ancestors. +* Likewise for dont_mount() which marks a dentry that is being removed +* under d_lock. */ int d_set_mounted (struct dentry *dentry) {
@@ -1599,7 +1601,7 @@ int d_set_mounted(struct dentry *dentry)spin_unlock (&p->d_lock); }spin_lock (&dentry->d_lock); -if (!d_unlinked (dentry)) { +if (!d_unlinked (dentry) && !cant_mount (dentry)) { ret = -EBUSY ;if (!d_mountpoint (dentry)) { dentry->d_flags |=DCACHE_MOUNTED ;
Greg Kroah-Hartman
maintainerPATCH 164/281 unshare: dont drop active namespace references that were never taken
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 0b6f31a3c0b16066f812e94b52a704174a6f23cd upstream.
Active references on the namespaces of an nsproxy are taken when the nsproxy is installed into a task in switch_task_namespaces() and copy_namespaces() and dropped again by put_nsproxy() through deactivate_nsproxy().
But ksys_unshare() calls put_nsproxy() on an nsproxy that was never installed when set_cred_ucounts() fails. The new namespaces of that nsproxy go from zero to minus one and the namespaces shared with the caller lose a reference that belongs to the nsproxy the caller keeps using:
WARNING: kernel/nscommon.c:171 at __ns_ref_active_put+0x1cd/0x230 nsproxy_ns_active_put deactivate_nsproxy ksys_unshare
Afterwards the namespaces the caller lives in aren't listed by listns() anymore. set_cred_ucounts() only fails when alloc_ucounts() can't allocate and it only allocates when the real uid of the caller differs from its effective uid.
Commit cefd55bd2159 ("nsproxy: fix free_nsproxy() and simplify create_new_namespaces()") separated the two cases on purpose. nsproxy_free() frees an nsproxy that was prepared but never installed and that's what a failed setns() uses in put_nsset(). Export it and use it for a failed unshare() as well.
--- include/linux/nsproxy.h | 1 + kernel/fork.c | 3 ++- kernel/nsproxy.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-)
include/linux/nsproxy.h +1 -0
@@ -100,6 +100,7 @@ void exit_cred_namespaces(struct task_stvoid switch_task_namespaces (struct task_struct *tsk,struct nsproxy *new);int exec_task_namespaces (void );void deactivate_nsproxy (struct nsproxy *ns); +void nsproxy_free (struct nsproxy *ns);int unshare_nsproxy_namespaces (unsigned long ,struct nsproxy **,struct cred *,struct fs_struct *);int __init nsproxy_cache_init (void );
kernel/fork.c +2 -1
@@ -3326,8 +3326,9 @@ int ksys_unshare(unsigned long unshare_fperf_event_namespaces (current); bad_unshare_cleanup_nsproxy: +/* never installed, so no active references to drop */ if (new_nsproxy) -put_nsproxy (new_nsproxy); +nsproxy_free (new_nsproxy); bad_unshare_cleanup_cred:if (new_cred)put_cred (new_cred);
kernel/nsproxy.c +1 -1
@@ -61,7 +61,7 @@ static inline struct nsproxy *create_nspreturn nsproxy; } -static inline void nsproxy_free (struct nsproxy *ns) +void nsproxy_free (struct nsproxy *ns) {put_mnt_ns (ns->mnt_ns);put_uts_ns (ns->uts_ns);
Greg Kroah-Hartman
maintainerPATCH 165/281 namespace: dont let a pseudo dentry become the root of a mount
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit b138e16fb1e81376cea89f727dda7bfccd140de4 upstream.
d_alloc_pseudo() hands out dentries for pipes, sockets and other files that are never anyone's child or parent. They carry DCACHE_NORCU and dentry_free() frees them right away because no lockless path walk can ever reach them. That holds as long as such a dentry isn't the root of a mount. But bind mounting /proc/self/fd/<fd> of such a file does exactly that.
Most callers of alloc_file_pseudo() put their files on kernel-internal mounts and may_copy_tree() refuses those. bpf_token_create() doesn't. It places the token file on the bpffs mount the caller handed it and that mount is in the caller's mount namespace so the clone goes through:
mount --bind /proc/self/fd/<token> <file> open_tree(tokfd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE) + move_mount()
__follow_mount_rcu() then loads ->mnt_root of that mount, reads d_seq and d_flags of the dentry and only then checks mount_lock. The dentry can be gone by then. The final mntput() of an unmounted parent unhooks a child that stayed attached to it under mount_lock alone. When the child's holder does the final put right after that the root is dput() and freed immediately while a walker that found the mount hashed still looks at it.
Refuse to clone a mount with a DCACHE_NORCU dentry as its root. Nothing sensible can be done with a bind mount of a bpf token anyway.
--- fs/namespace.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/namespace.c +4 -0
@@ -3008,6 +3008,10 @@ static struct mount *__do_loopback(constif (!may_copy_tree (old_path))return ERR_PTR (-EINVAL ); +/* a pseudo dentry is freed without an RCU delay, no walk may find it */ +if (old_path->dentry->d_flags &DCACHE_NORCU ) +return ERR_PTR (-EINVAL ); +if (recurse && !old->mnt_ns)return ERR_PTR (-EINVAL );
Greg Kroah-Hartman
maintainerPATCH 166/281 namespace: unhash a dentry before detaching the mounts on it
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 528b383dc35d1e5caf64ddf5d35f983ae543b0d0 upstream.
unlink(), rmdir() and rename() remove the entry from the filesystem, call detach_mounts() on the entry with the inode locked and then call d_delete() once the inode is unlocked.
The thing is that between detach_mounts() and d_delete() the dentry is still hashed and positive. But after detach_mounts() nothing covers the dentry anymore. A lookup that finds this dentry in the dcache can uncover what the mounts hid.
That's a problem when unlinking files or directories that are mountpoints in other mount namespaces. Everybody who had the underlying entry covered can race the detach_mount() call until d_delete() has run.
The race window isn't all that small. It encompasses namespace_unlock() with a full synchronize_rcu_expedited() grace period and the inode_unlock() of the entry.
In my experiments three walkers that kept trying read an overmounted file in 11938 of the 16140 unlinks that removed its mountpoint from a bind mount of the filesystem within a minute.
Fun fact, d_invalidate() has the same ordering problem but gets it right. It unhashes the dentry first and detaches the mounts afterwards. Let's do the same in __detach_mounts():
- A lookup that hasn't found the dentry yet misses it in the dcache and waits for the directory lock that the caller holds until the name is gone for good.
- A lockless lookup that found it already fails the mount_lock check while the dentry still counts as a mountpoint and the d_seq check once it doesn't, and retries.
This fixes the lockless path. We still need to fix the reference count lookup in a follow-up patch.
So d_drop() the dentry. The dentry stays positive and held but can't be found anymore. Then proceed with the detach and unlink.
Reproducer:
The reads were counted with the program below, three walkers for 60 s in a VM with 4 CPUs. The race window is stretched with the debug patch pasted here. fs.detach_race_walk_us sleeps in lookup_fast() once it found a mountpoint dentry and before its mounts are crossed. fs.detach_race_unlink_us sleeps in vfs_unlink() between detach_mounts() and d_delete().
// SPDX-License-Identifier: GPL-2.0 /* * unlink_covered: unlink a file that is a mountpoint in a detached copy of * its mount, against lookups that open it through that copy. * * A is a tmpfs with the file f1 (content SECRET_F) and the plain file * MARK_A. A' is an open_tree(OPEN_TREE_CLONE) copy of A with MARK_A bound * on A'/f1, held through an O_PATH fd on its root once the tree fd is * closed. Walkers open f1 through that fd while the driver unlinks A/f1, * where nothing is mounted on it. A walker may read MARK_A or get ENOENT. * A read of SECRET_F is a hit: the name was found after its mount was * gone. The lockless walks use openat2(RESOLVE_CACHED). * * usage: unlink_covered [-t seconds] [-w walkers] */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/syscall.h> #include <linux/openat2.h> #ifndef __NR_open_tree #define __NR_open_tree 428 #endif #ifndef __NR_move_mount #define __NR_move_mount 429 #endif #ifndef __NR_openat2 #define __NR_openat2 437 #endif #ifndef OPEN_TREE_CLONE #define OPEN_TREE_CLONE 1 #endif #ifndef OPEN_TREE_CLOEXEC #define OPEN_TREE_CLOEXEC O_CLOEXEC #endif #ifndef MOVE_MOUNT_F_EMPTY_PATH #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 #endif #define WORK "/tmp/uc" #define ADIR WORK "/A"
static int duration = 60, nwalkers = 3;
static atomic_int stop, writer_waiting;
static pthread_rwlock_t cur_lock = PTHREAD_RWLOCK_INITIALIZER;
static int cur_fd = -1; /* the root of A', -1 while there is none */
static atomic_long n_unlink, n_walk, n_mark, n_enoent, n_other, n_secret,
n_secret_cached;
static void die(const char *what)
{
fprintf(stderr, "FATAL %s: %s\n", what, strerror(errno));
exit(2);
} static void put_file(int dfd, const char *name, const char *content) {
int fd = openat(dfd, name, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
0644);
if (fd < 0 || write(fd, content, strlen(content)) < 0)
die(name);
close(fd);
}
/* "plain/../" @n times, then f1: a longer walk that checks nothing on its way */
static char *longpath(int n)
{
char *p = malloc(n * 9 + 3), *q = p;
for (int i = 0; i < n; i++, q += 9)
memcpy(q, "plain/../", 9);
strcpy(q, "f1");
return p;
}
static void try_read(int dfd, const char *path, int cached)
{
struct open_how how = { .flags = O_RDONLY | O_CLOEXEC,
.resolve = RESOLVE_CACHED };
char buf[32] = "";
long n;
int fd;
if (cached)
fd = syscall(__NR_openat2, dfd, path, &how, sizeof(how));
else
fd = openat(dfd, path, O_RDONLY | O_CLOEXEC);
atomic_fetch_add(&n_walk, 1);
if (fd < 0) {
if (errno == ENOENT)
atomic_fetch_add(&n_enoent, 1);
else if (!cached || errno != EAGAIN)
atomic_fetch_add(&n_other, 1);
return;
}
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n >= 6 && !strncmp(buf, "SECRET", 6)) {
if (cached)
atomic_fetch_add(&n_secret_cached, 1);
if (!atomic_fetch_add(&n_secret, 1))
printf("HIT: read \"%s\" through %s (%s walk)\n", buf,
path, cached ? "lockless" : "any");
} else {
atomic_fetch_add(&n_mark, 1);
}
}
static void *walker(void *arg)
{
unsigned int r = (long)arg * 2654435761u;
while (!atomic_load(&stop)) {
char *p_long, *p_short;
int dfd;
while (atomic_load(&writer_waiting) && !atomic_load(&stop))
usleep(20);
pthread_rwlock_rdlock(&cur_lock);
dfd = cur_fd;
if (dfd < 0) {
pthread_rwlock_unlock(&cur_lock);
usleep(100);
continue;
}
r = r * 1103515245u + 12345u;
p_long = longpath(1 + (r >> 8) % 400);
p_short = longpath(0);
try_read(dfd, p_long, 0);
try_read(dfd, p_short, 0);
try_read(dfd, p_long, 1);
pthread_rwlock_unlock(&cur_lock);
free(p_long);
free(p_short);
}
return NULL;
} /* hand the walkers a new A' (or none), close the old one */ static void publish(int fd)
{
int old;
atomic_fetch_add(&writer_waiting, 1);
pthread_rwlock_wrlock(&cur_lock);
old = cur_fd;
cur_fd = fd;
pthread_rwlock_unlock(&cur_lock);
atomic_fetch_sub(&writer_waiting, 1);
if (old >= 0)
close(old);
}
static void *driver(void *arg __attribute__((unused)))
{
int a;
if (mkdir(ADIR, 0755) && errno != EEXIST)
die("mkdir A");
if (mount("A", ADIR, "tmpfs", 0, "size=4M"))
die("mount A");
a = open(ADIR, O_PATH | O_DIRECTORY | O_CLOEXEC);
if (a < 0 || mkdirat(a, "plain", 0755))
die("A/plain");
put_file(a, "MARK_A", "MARK_A");
while (!atomic_load(&stop)) {
int t, m, fd_a;
put_file(a, "f1", "SECRET_F");
t = syscall(__NR_open_tree, a, "",
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH);
if (t < 0)
die("open_tree A");
m = syscall(__NR_open_tree, a, "MARK_A",
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
if (m < 0)
die("open_tree MARK_A");
if (syscall(__NR_move_mount, m, "", t, "f1",
MOVE_MOUNT_F_EMPTY_PATH))
die("move_mount");
close(m);
fd_a = openat(t, ".", O_PATH | O_DIRECTORY | O_CLOEXEC);
if (fd_a < 0)
die("open A'");
publish(fd_a);
usleep(200 + rand() % 1000);
close(t); /* A' is unmounted, fd_a holds it */
usleep(200 + rand() % 1000);
if (unlinkat(a, "f1", 0)) /* through A, a plain file there */
die("unlink f1");
atomic_fetch_add(&n_unlink, 1);
usleep(rand() % 300);
publish(-1);
}
close(a);
umount2(ADIR, MNT_DETACH);
return NULL;
}
int main(int argc, char **argv)
{
pthread_t d, *w;
int c, i;
setvbuf(stdout, NULL, _IOLBF, 0);
while ((c = getopt(argc, argv, "t:w:")) != -1) {
switch (c) {
case 't':
duration = atoi(optarg);
break;
case 'w':
nwalkers = atoi(optarg);
break;
default:
fprintf(stderr, "usage: unlink_covered [-t seconds] [-w walkers]\n");
return 2;
}
}
if (unshare(CLONE_NEWNS) ||
mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL))
die("unshare");
if (mkdir(WORK, 0755) && errno != EEXIST)
die("mkdir");
w = calloc(nwalkers, sizeof(*w));
for (i = 0; i < nwalkers; i++)
if (pthread_create(&w[i], NULL, walker, (void *)(long)i))
die("pthread_create");
if (pthread_create(&d, NULL, driver, NULL))
die("pthread_create");
sleep(duration);
atomic_store(&stop, 1);
pthread_join(d, NULL);
for (i = 0; i < nwalkers; i++)
pthread_join(w[i], NULL);
printf("unlink_covered: %d s, %d walkers: unlinks %ld walks %ld mark %ld enoent %ld other %ld SECRET %ld (lockless %ld)\n",
duration, nwalkers, atomic_load(&n_unlink), atomic_load(&n_walk),
atomic_load(&n_mark), atomic_load(&n_enoent),
atomic_load(&n_other), atomic_load(&n_secret),
atomic_load(&n_secret_cached));
return atomic_load(&n_secret) ? 1 : 0;
}# diff --git a/fs/namei.c b/fs/namei.c
# --- a/fs/namei.c
# +++ b/fs/namei.c
# @@ -35,6 +35,7 @@
# #include <linux/fcntl.h>
# #include <linux/device_cgroup.h>
# #include <linux/fs_struct.h>
# +#include <linux/delay.h>
# #include <linux/posix_acl.h>
# #include <linux/hash.h>
# #include <linux/bitops.h>
# @@ -1205,9 +1206,26 @@ static int sysctl_protected_symlinks __read_mostly;
# static int sysctl_protected_hardlinks __read_mostly;
# static int sysctl_protected_fifos __read_mostly;
# static int sysctl_protected_regular __read_mostly;
# +/* debug: widen the two windows of the detach_mounts() race */
# +static int sysctl_detach_race_walk_us __read_mostly;
# +static int sysctl_detach_race_unlink_us __read_mostly;
#
# #ifdef CONFIG_SYSCTL
# static const struct ctl_table namei_sysctls[] = {
# + {
# + .procname = "detach_race_walk_us",
# + .data = &sysctl_detach_race_walk_us,
# + .maxlen = sizeof(int),
# + .mode = 0644,
# + .proc_handler = proc_dointvec,
# + },
# + {
# + .procname = "detach_race_unlink_us",
# + .data = &sysctl_detach_race_unlink_us,
# + .maxlen = sizeof(int),
# + .mode = 0644,
# + .proc_handler = proc_dointvec,
# + },
# {
# .procname = "protected_symlinks",
# .data = &sysctl_protected_symlinks,
# @@ -1878,6 +1896,10 @@ static struct dentry *lookup_fast(struct nameidata *nd)
# dentry = __d_lookup(parent, &nd->last);
# if (unlikely(!dentry))
# return NULL;
# + /* debug: between finding a mountpoint and crossing its mounts */
# + if (unlikely(sysctl_detach_race_walk_us) && d_mountpoint(dentry))
# + usleep_range(sysctl_detach_race_walk_us,
# + sysctl_detach_race_walk_us + 10);
# status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
# }
# if (unlikely(status <= 0)) {
# @@ -5693,6 +5715,10 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
# if (!error) {
# dont_mount(dentry);
# detach_mounts(dentry);
# + /* debug: the mounts are gone, d_delete() is still to come */
# + if (unlikely(sysctl_detach_race_unlink_us))
# + usleep_range(sysctl_detach_race_unlink_us,
# + sysctl_detach_race_unlink_us + 10);
# }
# }
# }--- fs/namespace.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/namespace.c +5 -0
@@ -1984,6 +1984,9 @@ out:* detach_mounts allows lazily unmounting those mounts instead of * leaking them. * +* The dentry is unhashed before the mounts go so that no lookup finds +* what they covered. The caller removes it for good afterwards. +* * The caller may hold dentry->d_inode->i_rwsem. */ void __detach_mounts (struct dentry *dentry)
@@ -1997,6 +2000,8 @@ void __detach_mounts(struct dentry *dentif (!lookup_mountpoint (dentry, &mp))return ; +/* the name goes first, what covered it goes second */ +d_drop (dentry); event++;while (mp.node.next) { mnt = hlist_entry(mp.node.next, struct mount, mnt_mp_list);
Greg Kroah-Hartman
maintainerPATCH 167/281 namei: dont reveal overmounted entries in refwalk
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 8189ab688f5a2c745dc90b63374da02f213d6994 upstream.
In rcuwalk the dentry is validated before it is accepted. step_into() step_into() rechecks d_seq and __follow_mount_rcu() rechecks mount_lock. An entry that gets unlinked in between causes the lookup to retry and miss.
A refwalk doesn't do this. lookup_fast() takes a reference on the hashed dentry and simply accepts it. So an unlink that happens after the reference was taken isn't seen by refwalk. That's fine for a simple file. We just happened to open it before it was unlinked, no problem.
For a mountpoint and specifically a locked mountpoint it very much isn't. The unlink detaches all mounts and then removes the name. Any refwalk that hasn't traversed the mounts yet simply reveals the underlying entry. It's a very narrow window but it can be hit:
reads of a covered file in 60 s, 3 walkers, ~15000 unlinks
no widening 27
that step + 200 us 3741
See the appended patch for a more reliable reproducer.
So check the entry after step_into(). unlink(), rmdir() and rename() mark the dentry with dont_mount() before they detach the mounts and remove the dentry.
So a dentry that is marked with DCACHE_CANT_MOUNT and is unhashed by the time its mounts were looked at is a name that was unlinked under the refwalk. DCACHE_CANT_MOUNT is read after the mounts were looked up and it is set before they are detached by detach_mounts(). A refwalk that missed the mounts will see DCACHE_CANT_MOUNT.
Plain d_unlinked() is fine. A rename takes the dentry off its hash chain but ___d_drop() leaves d_hash.pprev set. So only __d_drop() and a rename over the dentry unhash it. The only move that flips IS_ROOT splices in a disconnected alias. That doesn't have DCACHE_CANT_MOUNT set.
Add the to step_into_slowpath(). ".." and LOOKUP_DOWN may legitimately land on an unhashed directory. A refwalk that crossed onto a mount has path.mnt different from nd->path.mnt. A dentry that a filesystem dropped on its own (d_invalidate(), d_drop()) doesn't have the flag set and is treated as before.
// SPDX-License-Identifier: GPL-2.0 /* * unlink_covered: unlink a file that is a mountpoint in a detached copy of * its mount, against walkers that open it through that copy. * * A is a tmpfs with the file f1 (content SECRET_F) and the plain file * MARK_A. A' is an open_tree(OPEN_TREE_CLONE) copy of A with MARK_A bound * on A'/f1, held through an O_PATH fd on its root once the tree fd is * closed. Walkers open f1 through that fd while the driver unlinks A/f1, * where nothing is mounted on it. A walker may read MARK_A or get ENOENT. * A read of SECRET_F is a hit: the name was found after its mount was * gone. The lockless walks use openat2(RESOLVE_CACHED). * * usage: unlink_covered [-t seconds] [-w walkers] */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/syscall.h> #include <linux/openat2.h> #ifndef __NR_open_tree #define __NR_open_tree 428 #endif #ifndef __NR_move_mount #define __NR_move_mount 429 #endif #ifndef __NR_openat2 #define __NR_openat2 437 #endif #ifndef OPEN_TREE_CLONE #define OPEN_TREE_CLONE 1 #endif #ifndef OPEN_TREE_CLOEXEC #define OPEN_TREE_CLOEXEC O_CLOEXEC #endif #ifndef MOVE_MOUNT_F_EMPTY_PATH #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 #endif #define WORK "/tmp/uc" #define ADIR WORK "/A"
static int duration = 60, nwalkers = 3;
static atomic_int stop, writer_waiting;
static pthread_rwlock_t cur_lock = PTHREAD_RWLOCK_INITIALIZER;
static int cur_fd = -1; /* the root of A', -1 while there is none */
static atomic_long n_unlink, n_walk, n_mark, n_enoent, n_other, n_secret,
n_secret_cached;
static void die(const char *what)
{
fprintf(stderr, "FATAL %s: %s\n", what, strerror(errno));
exit(2);
} static void put_file(int dfd, const char *name, const char *content) {
int fd = openat(dfd, name, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
0644);
if (fd < 0 || write(fd, content, strlen(content)) < 0)
die(name);
close(fd);
}
/* "plain/../" @n times, then f1: a longer walk that checks nothing on its way */
static char *longpath(int n)
{
char *p = malloc(n * 9 + 3), *q = p;
for (int i = 0; i < n; i++, q += 9)
memcpy(q, "plain/../", 9);
strcpy(q, "f1");
return p;
}
static void try_read(int dfd, const char *path, int cached)
{
struct open_how how = { .flags = O_RDONLY | O_CLOEXEC,
.resolve = RESOLVE_CACHED };
char buf[32] = "";
long n;
int fd;
if (cached)
fd = syscall(__NR_openat2, dfd, path, &how, sizeof(how));
else
fd = openat(dfd, path, O_RDONLY | O_CLOEXEC);
atomic_fetch_add(&n_walk, 1);
if (fd < 0) {
if (errno == ENOENT)
atomic_fetch_add(&n_enoent, 1);
else if (!cached || errno != EAGAIN)
atomic_fetch_add(&n_other, 1);
return;
}
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n >= 6 && !strncmp(buf, "SECRET", 6)) {
if (cached)
atomic_fetch_add(&n_secret_cached, 1);
if (!atomic_fetch_add(&n_secret, 1))
printf("HIT: read \"%s\" through %s (%s walk)\n", buf,
path, cached ? "lockless" : "any");
} else {
atomic_fetch_add(&n_mark, 1);
}
}
static void *walker(void *arg)
{
unsigned int r = (long)arg * 2654435761u;
while (!atomic_load(&stop)) {
char *p_long, *p_short;
int dfd;
while (atomic_load(&writer_waiting) && !atomic_load(&stop))
usleep(20);
pthread_rwlock_rdlock(&cur_lock);
dfd = cur_fd;
if (dfd < 0) {
pthread_rwlock_unlock(&cur_lock);
usleep(100);
continue;
}
r = r * 1103515245u + 12345u;
p_long = longpath(1 + (r >> 8) % 400);
p_short = longpath(0);
try_read(dfd, p_long, 0);
try_read(dfd, p_short, 0);
try_read(dfd, p_long, 1);
pthread_rwlock_unlock(&cur_lock);
free(p_long);
free(p_short);
}
return NULL;
} /* hand the walkers a new A' (or none), close the old one */ static void publish(int fd)
{
int old;
atomic_fetch_add(&writer_waiting, 1);
pthread_rwlock_wrlock(&cur_lock);
old = cur_fd;
cur_fd = fd;
pthread_rwlock_unlock(&cur_lock);
atomic_fetch_sub(&writer_waiting, 1);
if (old >= 0)
close(old);
}
static void *driver(void *arg __attribute__((unused)))
{
int a;
if (mkdir(ADIR, 0755) && errno != EEXIST)
die("mkdir A");
if (mount("A", ADIR, "tmpfs", 0, "size=4M"))
die("mount A");
a = open(ADIR, O_PATH | O_DIRECTORY | O_CLOEXEC);
if (a < 0 || mkdirat(a, "plain", 0755))
die("A/plain");
put_file(a, "MARK_A", "MARK_A");
while (!atomic_load(&stop)) {
int t, m, fd_a;
put_file(a, "f1", "SECRET_F");
t = syscall(__NR_open_tree, a, "",
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH);
if (t < 0)
die("open_tree A");
m = syscall(__NR_open_tree, a, "MARK_A",
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
if (m < 0)
die("open_tree MARK_A");
if (syscall(__NR_move_mount, m, "", t, "f1",
MOVE_MOUNT_F_EMPTY_PATH))
die("move_mount");
close(m);
fd_a = openat(t, ".", O_PATH | O_DIRECTORY | O_CLOEXEC);
if (fd_a < 0)
die("open A'");
publish(fd_a);
usleep(200 + rand() % 1000);
close(t); /* A' is unmounted, fd_a holds it */
usleep(200 + rand() % 1000);
if (unlinkat(a, "f1", 0)) /* through A, a plain file there */
die("unlink f1");
atomic_fetch_add(&n_unlink, 1);
usleep(rand() % 300);
publish(-1);
}
close(a);
umount2(ADIR, MNT_DETACH);
return NULL;
}
int main(int argc, char **argv)
{
pthread_t d, *w;
int c, i;
setvbuf(stdout, NULL, _IOLBF, 0);
while ((c = getopt(argc, argv, "t:w:")) != -1) {
switch (c) {
case 't':
duration = atoi(optarg);
break;
case 'w':
nwalkers = atoi(optarg);
break;
default:
fprintf(stderr, "usage: unlink_covered [-t seconds] [-w walkers]\n");
return 2;
}
}
if (unshare(CLONE_NEWNS) ||
mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL))
die("unshare");
if (mkdir(WORK, 0755) && errno != EEXIST)
die("mkdir");
w = calloc(nwalkers, sizeof(*w));
for (i = 0; i < nwalkers; i++)
if (pthread_create(&w[i], NULL, walker, (void *)(long)i))
die("pthread_create");
if (pthread_create(&d, NULL, driver, NULL))
die("pthread_create");
sleep(duration);
atomic_store(&stop, 1);
pthread_join(d, NULL);
for (i = 0; i < nwalkers; i++)
pthread_join(w[i], NULL);
printf("unlink_covered: %d s, %d walkers: unlinks %ld walks %ld mark %ld enoent %ld other %ld SECRET %ld (lockless %ld)\n",
duration, nwalkers, atomic_load(&n_unlink), atomic_load(&n_walk),
atomic_load(&n_mark), atomic_load(&n_enoent),
atomic_load(&n_other), atomic_load(&n_secret),
atomic_load(&n_secret_cached));
return atomic_load(&n_secret) ? 1 : 0;
}# diff --git a/fs/namei.c b/fs/namei.c
# --- a/fs/namei.c
# +++ b/fs/namei.c
# @@ -35,6 +35,7 @@
# #include <linux/fcntl.h>
# #include <linux/device_cgroup.h>
# #include <linux/fs_struct.h>
# +#include <linux/delay.h>
# #include <linux/posix_acl.h>
# #include <linux/hash.h>
# #include <linux/bitops.h>
# @@ -1205,9 +1206,26 @@ static int sysctl_protected_symlinks __read_mostly;
# static int sysctl_protected_hardlinks __read_mostly;
# static int sysctl_protected_fifos __read_mostly;
# static int sysctl_protected_regular __read_mostly;
# +/* debug: widen the two windows of the detach_mounts() race */
# +static int sysctl_detach_race_walk_us __read_mostly;
# +static int sysctl_detach_race_unlink_us __read_mostly;
#
# #ifdef CONFIG_SYSCTL
# static const struct ctl_table namei_sysctls[] = {
# + {
# + .procname = "detach_race_walk_us",
# + .data = &sysctl_detach_race_walk_us,
# + .maxlen = sizeof(int),
# + .mode = 0644,
# + .proc_handler = proc_dointvec,
# + },
# + {
# + .procname = "detach_race_unlink_us",
# + .data = &sysctl_detach_race_unlink_us,
# + .maxlen = sizeof(int),
# + .mode = 0644,
# + .proc_handler = proc_dointvec,
# + },
# {
# .procname = "protected_symlinks",
# .data = &sysctl_protected_symlinks,
# @@ -1878,6 +1896,10 @@ static struct dentry *lookup_fast(struct nameidata *nd)
# dentry = __d_lookup(parent, &nd->last);
# if (unlikely(!dentry))
# return NULL;
# + /* debug: between finding a mountpoint and crossing its mounts */
# + if (unlikely(sysctl_detach_race_walk_us) && d_mountpoint(dentry))
# + usleep_range(sysctl_detach_race_walk_us,
# + sysctl_detach_race_walk_us + 10);
# status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
# }
# if (unlikely(status <= 0)) {
# @@ -5693,6 +5715,10 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
# if (!error) {
# dont_mount(dentry);
# detach_mounts(dentry);
# + /* debug: the mounts are gone, d_delete() is still to come */
# + if (unlikely(sysctl_detach_race_unlink_us))
# + usleep_range(sysctl_detach_race_unlink_us,
# + sysctl_detach_race_unlink_us + 10);
# }
# }
# }--- fs/namei.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
fs/namei.c +29 -0
@@ -2087,6 +2087,33 @@ all_done: // pure jump }/* +* Be careful in case the dentry is unlinked or renamed. Any mounts +* stacked on top of it are going away. We need to make sure that we +* don't reveal the underyling dentry during refwalk. In rcuwalk we +* catch this via d_seq and another lookup for the name. Give the same +* guarantee in refwalk. +*/ +static bool unlink_may_reveal (struct nameidata *nd,int flags, +struct path *path,struct dentry *dentry) +{ +/* ".." and LOOKUP_DOWN may land on an unhashed directory */ +if (flags &WALK_NOFOLLOW ) +return false; +if (nd->flags &LOOKUP_REVAL ) +return false; +/* We crossed onto a mount and the name led us here while it still existed */ +if (path->mnt != nd->path.mnt) +return false; +/* only a name on its way out is flagged */ +if (likely (!cant_mount (dentry))) +return false; +if (!d_unlinked (dentry)) +return false; +dput (no_free_ptr (path->dentry)); +return true; +} + +/* * Do we need to follow links? We _really_ want to be able * to do this check without having to look at inode->i_op, * so we keep a cache of "no, this doesn't need follow_link"
@@ -2115,6 +2142,8 @@ static noinline const char *step_into_slif (unlikely (!inode))return ERR_PTR (-ENOENT ); }else { +if (unlikely (unlink_may_reveal (nd, flags, &path, dentry))) +return ERR_PTR (-ESTALE );dput (nd->path.dentry);if (nd->path.mnt != path.mnt)mntput (nd->path.mnt);
Greg Kroah-Hartman
maintainerPATCH 168/281 fcntl: refuse F_SET_RW_HINT on an immutable inode
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit b0d1022b9bdb1e98aa7fb3244fdd50a58c61766c upstream.
An immutable inode is never written to so write hints are pointless. Refuse the hint on an immutable inode the way setattr(), fallocate() and etxattr() refuse their changes.
--- fs/fcntl.c | 3 +++ 1 file changed, 3 insertions(+)
fs/fcntl.c +3 -0
@@ -372,6 +372,9 @@ static long fcntl_set_rw_hint(struct filu64 __user *argp = (u64 __user *)arg;u64 hint; +/* nothing is ever written to it */ +if (IS_IMMUTABLE (inode)) +return -EPERM ;if (!inode_owner_or_capable (file_mnt_idmap (file), inode))return -EPERM ;
Greg Kroah-Hartman
maintainerPATCH 169/281 namespace: refuse an automount below a mount that is in no namespace
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit ba985ce08471c555af2a2fbe217c1bc2e72a6d8c upstream.
It's possible to add autmounts even when the parent mount isn't in the mount namespace of the caller. The only requirement we have is that the parent mount namespace must not be NULL, i.e., unmounted.
Problem is that clone_private_mount() has MNT_NS_INTERNAL which makes that trivially true. So that passes the test and attach_recursive_mnt() accepts that as a mount point and funny enough, count_mounts() dereferences MNT_NS_INTERNAL. The problem is it is an error pointer...
So we can reach this in userspace via fanotify. A filesystem mark on the lower filesystem of an overlay reports paths on the layer clone and reading the event hands out a descriptor on it. For example with debugfs as the lower layer it goes kaboom:
openat(evfd, "tracing", O_DIRECTORY)
Oops: general protection fault KASAN: null-ptr-deref in range [0x1d0-0x1d7] RIP: 0010:count_mounts+0x35/0x200 attach_recursive_mnt finish_automount
And since that sleeping beauty happens under namespace_sem held for writing every mount operation on the system blocks from then on. Congrats.
Use is_mounted() instead which rejects unmounted and internal mounts alike. The open fails with EINVAL just as it did before clone_private_mount() used MNT_NS_INTERNAL.
--- fs/namespace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/namespace.c +1 -1
@@ -3784,7 +3784,7 @@ static int do_add_mount(struct mount *neif (!(mnt_flags &MNT_SHRINKABLE ))return -EINVAL ;/* ... and for those we'd better have mountpoint still alive */ -if (!parent->mnt_ns) +if (!is_mounted (&parent->mnt))return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 170/281 nullfs: dont update the access time
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit caf16f9b4aa66e5092f95e98cda54f2bb7482ed2 upstream.
There's no point in updating access times of the nullfs instance. Raise S_IMMUTABLE.
--- fs/nullfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/nullfs.c +2 -2
@@ -30,8 +30,8 @@ static int nullfs_fs_fill_super(struct smake_empty_dir_inode (inode);simple_inode_init_ts (inode); inode->i_ino =1 ; -/* ... and immutable. */ - inode->i_flags |=S_IMMUTABLE ; +/* ... and immutable, reading it leaves no trace either. */ + inode->i_flags |=S_IMMUTABLE |S_NOATIME ; s->s_root =d_make_root (inode);if (!s->s_root)
Greg Kroah-Hartman
maintainerPATCH 171/281 namespace: never expire a locked mount
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit f78ddcc872248c7aa1d7e904928441a0be4c6d5d upstream.
Locked mounts are special. They protect the underlying files and directories from being revealed. do_umount() refuses to unmount locked mounts but shrink_submounts() doesn't.
A shrinkable mount can become locked once the owner of a user namespace puts it beneath a locked mount with MOVE_MOUNT_BENEATH. The locked property now moves to the mount at the bottom making it possible to unmount the top mount.
So umount() of an unlocked ancestor now expires the bottom mount first and the covered directory is revealed:
move_mount(c -> x/hidden, MOVE_MOUNT_BENEATH) = 0
the cover after the lock moved: umount2(x/hidden) = 0 the holder of the lock: umount2(x/hidden) = EINVAL the root of the copy, busy: umount2(x) = EBUSY
reads x/hidden/secret: "covered-by-root"
So leave a locked mount alone as it dies together with its parent. A plain umount() of an unlocked mount with a locked one below it is EBUSY from now on. It's the same for any other locked child. A lazy umount still takes the whole tree.
mark_mounts_for_expiry() never sees a locked mount. lock_mnt_tree() leaves a mount on an expiry list alone and nothing else puts a locked one on such a list. Add an assert for this.
--- fs/namespace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
fs/namespace.c +4 -1
@@ -3994,6 +3994,8 @@ void mark_mounts_for_expiry(struct list_list_for_each_entry_safe (mnt, next, mounts, mnt_expire) {if (!is_mounted (&mnt->mnt))continue ; +/* lock_mnt_tree() leaves expirable mounts alone */ +VFS_WARN_ON_ONCE (IS_MNT_LOCKED (mnt));if (!xchg (&mnt->mnt_expiry_mark,1 ) ||propagate_mount_busy (mnt,1 ))continue ;
@@ -4020,7 +4022,8 @@ EXPORT_SYMBOL_GPL(mark_mounts_for_expiry*/ static bool shrink_submount (struct mount *mnt) { -if (propagate_mount_busy (mnt,1 )) +/* not the kernel's to remove either */ +if (IS_MNT_LOCKED (mnt) ||propagate_mount_busy (mnt,1 ))return false;touch_mnt_namespace (mnt->mnt_ns);umount_tree (mnt,UMOUNT_PROPAGATE |UMOUNT_SYNC );
Greg Kroah-Hartman
maintainerPATCH 172/281 namespace: handle mount locking for automounts correctly
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 33aadc7c72055fca93cb5a3691d836ebf8b31c9b upstream.
If mounts are propagated across user namespaces, attach_recursive_mnt() locks every copy of the source mount to protect overmounts from vanishing and revealing the underlying files or directories.
The user namespace is taken from the caller's mount namespaces since this is where the mounts end up. Except, that's not always true. Automounts may legitimately get popped in by tasks located in a different mount and user namespace during path lookup.
Then check doesn't make sense at that point. The copy in the namespace of the parent - which may be the host's - is now locked and the host cannot change the flags of its own mounts anymore.
Here's the reproducer:
- host has debugfs mounted nosuid,nodev,noexec and shared
- tracefs automount below it is not yet active
- hand a child process a directory descriptor on that mount
- child process enters auser and mount namespace
- child process' namespace now holds a locked copy of the host's debugfs mount that receives propagation from it
- child process stats "tracing/." through the descriptor
- lookup runs on the host's mount so the automount lands below the host's mount
- propagation puts a copy below the child's copy
- both try to clear the flags on the mount they got, with a bind remount:
host, on its own automount: MS_REMOUNT|MS_BIND = EPERM
child, on the copy in its namespace: MS_REMOUNT|MS_BIND = 0So the lock landed on the host's mount instead of the child's copy. Congrats. So we need to compare with the owner of the namespace the mount actually gets mounted on. For all regular cases that is the caller's mount namespace and so nothing changes.
Detached trees in anonymous mount namespaces by be handed over via SCM_RIGHTS or inherited in other ways on purpose so the attaching task's mount namespace is authoritative, not the creator of the detached tree.
--- fs/namespace.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
fs/namespace.c +17 -1
@@ -2589,11 +2589,11 @@ enum mnt_tree_flags_t {static int attach_recursive_mnt (struct mount *source_mnt,const struct pinned_mountpoint *dest) { -struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;struct mount *dest_mnt = dest->parent;struct mountpoint *dest_mp = dest->mp;HLIST_HEAD (tree_list);struct mnt_namespace *ns = dest_mnt->mnt_ns; +struct user_namespace *user_ns = ns->user_ns;struct pinned_mountpoint root = {};struct mountpoint *shorter =NULL ;struct mount *child, *p;
@@ -2603,6 +2603,22 @@ static int attach_recursive_mnt(struct mbool moving =mnt_has_parent (source_mnt);/* +* A caller in an unprivileged mount namespaces may trigger an +* automount and propagate locked mounts into privileged mount +* namespaces. Take ownership from the target mount namespace. +* It's equivalent for everything but the automount case. +* +* Detached trees in anonymous mount namespaces by be handed +* over via SCM_RIGHTS or inherited in other ways on purpose +* the attaching task's mount namespace is authoritative, not +* the creator of the detached tree. +*/ +if (is_anon_ns (ns)) + user_ns = current->nsproxy->mnt_ns->user_ns; +else + user_ns = ns->user_ns; + +/* * Preallocate a mountpoint in case the new mounts need to be * mounted beneath mounts on the same mountpoint. */
Greg Kroah-Hartman
maintainerPATCH 173/281 namespace: keep the lock on a mount that a propagated copy is moved beneath
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit d74d66aa708ccf2bc0b1e265a83fed0f2308d5c4 upstream.
attach_recursive_mnt() transfers MNT_LOCKED from the top mount to the mount that is moved beneath it with MOVE_MOUNT_BENEATH. This allows the owner of a user namespace to replace its locked /proc or its root. The mount beneath takes on the job of covering the underlying mountpoint allowing the top mount to be unmounted.
Consider two mount namespaces:
(H) The host H has a shared mount P with a secret in P/d
(Z) Z is a user namespace made from M. Its copy of P receives propagation from the host's P and its copy of M's cover on P/d is locked Z's owner must not get to see P/d.
Now (H) mounts X on P/d. This propagates into (Z). The copy of X lands beneath (Z)'s locked cover. The locked property is transfered from (Z)'s cover to the copy of X propagated beneath it. The cover is now unlocked.
Now (H) unmounts X again. The copy of X in (Z) gets unmounted and the covering mount is left unlocked on top of P/d. (Z) can now unmount it:
Z: umount2(P/d) = EINVAL /* the cover is locked */
H: mount X on P/d, umount X /* both propagate into Z /*
Z: umount2(P/d) = 0 /* the cover is now unlocked */Z: read P/d/secret = "covered-by-root" /* secret revealed */
So only transfer the locked property to the mount beneath for mounts the caller has placed. A propagated copy that lands beneath a locked mount is locked as well so that the mount at the bottom of the stack carries a lock the way every check expects. The mount on top of it remains locked to ensure that it keeps covering even if the propagated mount is unmounted again.
--- fs/namespace.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
fs/namespace.c +8 -6
@@ -2697,15 +2697,17 @@ static int attach_recursive_mnt(struct m/* * If @q was locked it was meant to hide * whatever was under it. Let @child take over -* that job and lock it, then we can unlock @q. -* That'll allow another namespace to shed @q -* and reveal @child. Clearly, that mounter -* consented to this by not severing the mount -* relationship. Otherwise, what's the point. +* that job and lock it. If @child is the mount +* the caller placed we can then unlock @q: +* nothing another namespace does removes it +* again. A propagated copy goes away when the +* mounter of the original unmounts it, so @q +* keeps its lock. */ if (IS_MNT_LOCKED (q)) { child->mnt.mnt_flags |=MNT_LOCKED ; - q->mnt.mnt_flags &= ~MNT_LOCKED ; +if (child == source_mnt) + q->mnt.mnt_flags &= ~MNT_LOCKED ; }mnt_change_mountpoint (r, mp, q); }
Greg Kroah-Hartman
maintainerPATCH 174/281 xfs: stop exchanging reflink flags when exchanging mappings
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
[ Upstream commit d84d6c1a7f1e2de9e601703a326a4c1b7b421943 ]
When an exchange covers both files from offset 0 to i_disk_size, xmi_can_exchange_reflink_flags() assumes every mapping moved and hands the reflink flag over to the other file.
That assumption is wrong. A file can still own shared blocks past i_disk_size, e.g. after EXCHANGE_RANGE_TO_EOF sets a smaller size without unmapping anything. The flag is cleared anyway, and the next write to such a block happens in place instead of CoW, corrupting the other file that shares it.
Commit a23eca88448e ("xfs: fix exchange-range reflink flag clearing issue with INO1_WRITTEN") fixed one instance of this. Rather than add another special case, stop moving the flags. xfs_exchmaps_ensure_reflink() already sets the flag on both inodes before the exchange, so both just keep it.
Nothing sets CLEAR_INO{1,2}_REFLINK anymore. Intents logged by older kernels may still carry them; recovery already masks them off, so they stay in the log format mask and are ignored.
XFS_IOC_SWAPEXT has the same problem: it moves the reflink flag from one inode to the other along with the extents. Just removing that is not enough here: the exchange path flags both inodes up front in xfs_exchmaps_ensure_reflink(), but swapext has no such step, so the inode receiving the shared extents would be left without the flag. Set it on both inodes instead, and make sure both have a CoW fork, since an inode with the reflink flag is expected to have one.
The flag now stays until FALLOC_FL_UNSHARE_RANGE, xfs_scrub, or a truncate to zero removes it.
--- fs/xfs/libxfs/xfs_exchmaps.c | 90 +---------------------------------------- fs/xfs/libxfs/xfs_log_format.h | 4 - fs/xfs/xfs_bmap_util.c | 15 ++---- 3 files changed, 10 insertions(+), 99 deletions(-)
fs/xfs/libxfs/xfs_exchmaps.c +3 -87
@@ -49,7 +49,7 @@ struct xfs_exchmaps_adjacent { .right2 = { .br_startblock = HOLESTARTBLOCK }, \ } -/* Information to reset reflink flag / CoW fork state after an exchange. */ +/* Information to reset CoW fork state after an exchange. */ /* * If the reflink flag is set on either inode, make sure it has an incore CoW
@@ -127,9 +127,7 @@ xmi_has_more_exchange_work(const structstatic inline bool xmi_has_postop_work (const struct xfs_exchmaps_intent *xmi) { -return xmi->xmi_flags & (XFS_EXCHMAPS_CLEAR_INO1_REFLINK | -XFS_EXCHMAPS_CLEAR_INO2_REFLINK | - __XFS_EXCHMAPS_INO2_SHORTFORM); +return xmi->xmi_flags & __XFS_EXCHMAPS_INO2_SHORTFORM; }/* Check all mappings to make sure we can actually exchange them. */
@@ -525,18 +523,6 @@ free:return error; } -/* Clear the reflink flag after an exchange. */ -static inline void -xfs_exchmaps_clear_reflink ( -struct xfs_trans *tp, -struct xfs_inode *ip) -{ -trace_xfs_reflink_unset_inode_flag (ip); - - ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK ; -xfs_trans_log_inode (tp, ip,XFS_ILOG_CORE ); -} -/* Finish whatever work might come after an exchange operation. */ static int xfs_exchmaps_do_postop_work(
@@ -557,16 +543,6 @@ xfs_exchmaps_do_postop_work(return error; } -if (xmi->xmi_flags &XFS_EXCHMAPS_CLEAR_INO1_REFLINK ) { -xfs_exchmaps_clear_reflink (tp, xmi->xmi_ip1); - xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO1_REFLINK ; - } - -if (xmi->xmi_flags &XFS_EXCHMAPS_CLEAR_INO2_REFLINK ) { -xfs_exchmaps_clear_reflink (tp, xmi->xmi_ip2); - xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO2_REFLINK ; - } -return 0 ; }
@@ -948,56 +924,13 @@ xfs_exchmaps_intent_destroy_cache(void) }/* -* Decide if we will exchange the reflink flags between the two files after the -* exchange. The only time we want to do this is if we're exchanging all -* mappings under EOF and the inode reflink flags have different states. +* Allocate and initialize a new incore intent item from a request. */ -static inline bool -xmi_can_exchange_reflink_flags ( -const struct xfs_exchmaps_req *req, -unsigned int reflink_state) -{ -struct xfs_mount *mp = req->ip1->i_mount; - -/* -* The INO1_WRITTEN optimization can skip exchanging hole and -* unwritten mappings, which means we cannot guarantee that all -* shared extents actually moved to the other file. Clearing the -* reflink flag of an inode that still holds shared extents breaks -* the CoW write path, so refuse to exchange the flags in that case. -*/ -if (req->flags &XFS_EXCHMAPS_INO1_WRITTEN ) -return false; - -/* -* The INO1_WRITTEN optimization can skip exchanging hole and -* unwritten mappings, which means we cannot guarantee that all -* shared extents actually moved to the other file. Clearing the -* reflink flag of an inode that still holds shared extents breaks -* the CoW write path, so refuse to exchange the flags in that case. -*/ -if (req->flags &XFS_EXCHMAPS_INO1_WRITTEN ) -return false; - -if (hweight32 (reflink_state) !=1 ) -return false; -if (req->startoff1 !=0 || req->startoff2 !=0 ) -return false; -if (req->blockcount !=XFS_B_TO_FSB (mp, req->ip1->i_disk_size)) -return false; -if (req->blockcount !=XFS_B_TO_FSB (mp, req->ip2->i_disk_size)) -return false; -return true; -} - - -/* Allocate and initialize a new incore intent item from a request. */ struct xfs_exchmaps_intent *xfs_exchmaps_init_intent (const struct xfs_exchmaps_req *req) {struct xfs_exchmaps_intent *xmi; -unsigned int rs =0 ; xmi =kmem_cache_zalloc (xfs_exchmaps_intent_cache,GFP_NOFS | __GFP_NOFAIL);
@@ -1021,23 +954,6 @@ xfs_exchmaps_init_intent( xmi->xmi_isize2 = req->ip1->i_disk_size; } -/* Record the state of each inode's reflink flag before the op. */ -if (xfs_is_reflink_inode (req->ip1)) - rs |=1 ; -if (xfs_is_reflink_inode (req->ip2)) - rs |=2 ; - -/* -* Figure out if we're clearing the reflink flags (which effectively -* exchanges them) after the operation. -*/ -if (xmi_can_exchange_reflink_flags (req, rs)) { -if (rs &1 ) - xmi->xmi_flags |=XFS_EXCHMAPS_CLEAR_INO1_REFLINK ; -if (rs &2 ) - xmi->xmi_flags |=XFS_EXCHMAPS_CLEAR_INO2_REFLINK ; - } -if (S_ISDIR (VFS_I (xmi->xmi_ip2)->i_mode) ||S_ISLNK (VFS_I (xmi->xmi_ip2)->i_mode)) xmi->xmi_flags |= __XFS_EXCHMAPS_INO2_SHORTFORM;
fs/xfs/libxfs/xfs_log_format.h +1 -3
@@ -927,10 +927,8 @@ struct xfs_xmi_log_format {*/ #define XFS_EXCHMAPS_INO1_WRITTEN (1ULL << 2) -/* Clear the reflink flag from inode1 after the operation. */ +/* No longer set, but older intents may carry them; keep them valid. */ #define XFS_EXCHMAPS_CLEAR_INO1_REFLINK (1ULL << 3) - -/* Clear the reflink flag from inode2 after the operation. */ #define XFS_EXCHMAPS_CLEAR_INO2_REFLINK (1ULL << 4)#define XFS_EXCHMAPS_LOGGED_FLAGS (XFS_EXCHMAPS_ATTR_FORK | \
fs/xfs/xfs_bmap_util.c +6 -9
@@ -1514,7 +1514,6 @@ xfs_swap_extents(struct xfs_bstat *sbp = &sxp->sx_stat;int src_log_flags, target_log_flags;int error =0 ; -uint64_t f;int resblks =0 ;unsigned int flags =0 ;struct timespec64 ctime, mtime;
@@ -1670,14 +1669,12 @@ xfs_swap_extents(if (error) goto out_trans_cancel; -/* Do we have to swap reflink flags? */ -if ((ip->i_diflags2 &XFS_DIFLAG2_REFLINK ) ^ - (tip->i_diflags2 &XFS_DIFLAG2_REFLINK )) { - f = ip->i_diflags2 &XFS_DIFLAG2_REFLINK ; - ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK ; - ip->i_diflags2 |= tip->i_diflags2 &XFS_DIFLAG2_REFLINK ; - tip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK ; - tip->i_diflags2 |= f &XFS_DIFLAG2_REFLINK ; +/* Either inode may now own shared blocks; flag both, with CoW forks. */ +if ((ip->i_diflags2 | tip->i_diflags2) &XFS_DIFLAG2_REFLINK ) { + ip->i_diflags2 |=XFS_DIFLAG2_REFLINK ; + tip->i_diflags2 |=XFS_DIFLAG2_REFLINK ; +xfs_ifork_init_cow (ip); +xfs_ifork_init_cow (tip); }/* Swap the cow forks. */
Greg Kroah-Hartman
maintainerPATCH 175/281 xfs: fix exchange-range-to-eof file size exchange
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Darrick J. Wong" <[email protected]>
[ Upstream commit 7dfb218c829c1acb626e0fe92c5fddb9a26a03e9 ]
Norbert Szetei posted a patch containing an incomplete description of a bug in XFS_IOC_EXCHANGE_RANGE's TO_EOF flag. The bug tricks exchange-range into modifying a file so that it has shared blocks starting beyond EOF, which is never allowed because files never have written data beyond EOF, and you can only share written data blocks. This is key to all the incorrect behavior that follows.
Eventually I got from Norbert a description of what's going wrong:
Quoted 37 lines
Fair. Here it is with physical block numbers, 4k blocks. peer is the file that gets rewritten and file1 is the one that ends up with the flag cleared.
Start, nothing shared:
peer size 4096 -> [100] file1 size 12288 -> [200] [201] [202] donor1 size 4096 -> [300] donor2 size 4096 -> [400]1. FICLONERANGE peer's block into file1's middle slot:
peer size 4096 -> [100] file1 size 12288 -> [200] [100] [202] reflink flag set ^^^^^ both files own block 1002.
XFS_IOC_EXCHANGE_RANGEfile1 against donor1,file1_offset0,file2_offset8192, length 0,TO_EOF. One block is exchanged, and the sizes are exchanged with it:file1 size 4096 -> [200] [100] [300] reflink flag set ^^^^^^^^^^^ still owned, now past the size donor1 size 12288 -> [202]file1 says it is one block long while it still owns three. Nothing was unmapped.
file2_offsetis block 2, so this exchange cannot clear a flag.3.
XFS_IOC_EXCHANGE_RANGEfile1 against donor2, both offsets 0, length 4096. Byi_disk_sizethis is two one-block files exchanging everything, soxmi_can_exchange_reflink_flags()agrees and the flag is cleared:file1 size 4096 -> [400] [100] [300] reflink flag CLEARED donor2 size 4096 -> [200]Block 100 is still shared with peer.
Quoted 8 lines
4.
pwrite(file1, 4096, 4096), which is file1's second slot, block 100.xfs_is_cow_inode(file1)is false, so no CoW:peer size 4096 -> [100] same block, new contentsBlock 100 is the only one that matters here. PoC below, it reads a file called peer in the current directory. The filesystem needs reflink and exchange-range, which mkfs.xfs 7.x turns on by default.
So yes, TO_EOF is screwing up the file size even if it's actually exchanging the data block correctly. The following is the intended behavior of XFS_EXCHANGE_RANGE_TO_EOF, as described by its manpage:
"Ignore the length parameter. All bytes in file1_fd from file1_offset
to EOF are moved to file2_fd, and file2's size is set to (file2_offset+(file1_length-file1_offset)). Meanwhile, all bytes in file2 from file2_offset to EOF are moved to file1 and file1's size is set to (file1_offset+(file2_length-file2_offset))."
In other words, if we only exchange a portion of the file data, then we should only exchange the portion of the file size that relates to the exchanged data. Unfortunately, the code swaps the ondisk file size and the incore file sizes, which is incorrect if the caller passes in a nonzero offset.
Having confused the file sizes, the PoC uses a second exchange-range call on what looks like a non-reflinked single-block file. Because the file size is set incorrectly, exchange-range thinks it's exchanging the full contents of two files and clears the reflink flag on the broken file. That enables an extending write of the broken file to rewrite the shared block that's just past EOF. This has become known colloquially as refluxfs.
Once the file sizes are set correctly, the second exchange-range no longer thinks that it's doing a full-contents swap, so it won't clear the reflink flag on either of its file arguments.
Norbert followed up to an earlier version of this patch with a second complaint about us not handling the i_disk_size state correctly:
Quoted 6 lines
The offsets are bounded against the incore
i_sizebut subtracted from the ondiski_disk_size, so a file that has not been written back givesxmi_isize1= 0 + (0 - 12288).For instance, you can do:
Quoted 9 lines
$ xfs_io -f -c "pwrite -q 0 4096" -c fsync file1 $ xfs_io -f -c "pwrite -q 0 12288" \ -c "exchangerange -s 0 -d 12288 file1" dirty $ stat -c %s file1 0 $ stat -c %i file1 131 $ sudo xfs_io -r -c "bulkstat_single 131" . | grep bs_size bs_size = 18446744073709539328
That's wrong, and the new file size update logic depends on the ondisk i_disk_size matching the incore i_size, so flush the pagecache as needed to push it upwards.
--- fs/xfs/libxfs/xfs_exchmaps.c | 13 +++++++++- fs/xfs/xfs_exchrange.c | 51 +++++++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 16 deletions(-)
fs/xfs/libxfs/xfs_exchmaps.c +11 -2
@@ -931,6 +931,7 @@ xfs_exchmaps_init_intent( const struct xfs_exchmaps_req *req) {struct xfs_exchmaps_intent *xmi; +struct xfs_mount *mp = req->ip1->i_mount; xmi =kmem_cache_zalloc (xfs_exchmaps_intent_cache,GFP_NOFS | __GFP_NOFAIL);
@@ -948,10 +949,18 @@ xfs_exchmaps_init_intent(return xmi; } +/* +* If the caller wanted, set each file's size to that file's exchange +* offset + length exchanged from the other file because the ranges in +* each file might be different lengths. +*/ if (req->flags &XFS_EXCHMAPS_SET_SIZES ) { +loff_t off1 =XFS_FSB_TO_B (mp, xmi->xmi_startoff1); +loff_t off2 =XFS_FSB_TO_B (mp, xmi->xmi_startoff2); + xmi->xmi_flags |=XFS_EXCHMAPS_SET_SIZES ; - xmi->xmi_isize1 = req->ip2->i_disk_size; - xmi->xmi_isize2 = req->ip1->i_disk_size; + xmi->xmi_isize1 = off1 + (req->ip2->i_disk_size - off2); + xmi->xmi_isize2 = off2 + (req->ip1->i_disk_size - off1); } if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode) ||
fs/xfs/xfs_exchrange.c +37 -14
75 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 176/281 clk: qcom: gcc-eliza: Fix always-enabling PCIE_RSCC clocks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luca Weiss <[email protected]>
[ Upstream commit 5ffd02d16f4e2f0566b8f31d5610538987e1944c ]
The critical_cbrcrs array will get qcom_branch_set_clk_en() called on each entry which will set BIT(0) to enable the clock. This is however wrong for these two PCIE_RSCC clocks which need BIT(20) and BIT(21) set respectively to enable them.
--- drivers/clk/qcom/gcc-eliza.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/clk/qcom/gcc-eliza.c +4 -2
@@ -3010,8 +3010,6 @@ static const u32 gcc_eliza_critical_cbcrs[] = {0x26034 ,/* GCC_CAMERA_XO_CLK */ 0x27004 ,/* GCC_DISP_AHB_CLK */ 0x71004 ,/* GCC_GPU_CFG_AHB_CLK */ -0x52010 ,/* GCC_PCIE_RSCC_CFG_AHB_CLK */ -0x52010 ,/* GCC_PCIE_RSCC_XO_CLK */ 0x32004 ,/* GCC_VIDEO_AHB_CLK */ 0x32038, /* GCC_VIDEO_XO_CLK */ };
@@ -3045,6 +3043,10 @@ static const struct regmap_config gcc_eliza_regmap_config = {static void clk_eliza_regs_configure (struct device *dev,struct regmap *regmap) { +/* Keep clocks always enabled */ +regmap_update_bits (regmap,0x52010 ,BIT (20 ),BIT (20 ));/* GCC_PCIE_RSCC_CFG_AHB_CLK */ +regmap_update_bits (regmap,0x52010 ,BIT (21 ),BIT (21 ));/* GCC_PCIE_RSCC_XO_CLK */ +/* FORCE_MEM_CORE_ON for ufs phy ice core and gcc ufs phy axi clocks */ qcom_branch_set_force_mem_core (regmap, gcc_ufs_phy_ice_core_clk, true);qcom_branch_set_force_mem_core (regmap, gcc_ufs_phy_axi_clk, true);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 177/281 clk: qcom: gcc-kaanapali: Fix always-enabling PCIE_RSCC clocks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luca Weiss <[email protected]>
[ Upstream commit 0bfb542fc3368371cefb4cf45ef21c3ba2d37c3f ]
The critical_cbrcrs array will get qcom_branch_set_clk_en() called on each entry which will set BIT(0) to enable the clock. This is however wrong for these two PCIE_RSCC clocks which need BIT(20) and BIT(21) set respectively to enable them.
--- drivers/clk/qcom/gcc-kaanapali.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/clk/qcom/gcc-kaanapali.c +4 -2
@@ -3464,8 +3464,6 @@ static const u32 gcc_kaanapali_critical_cbcrs[] = {0x9f004 ,/* GCC_EVA_AHB_CLK */ 0x9f024 ,/* GCC_EVA_XO_CLK */ 0x71004 ,/* GCC_GPU_CFG_AHB_CLK */ -0x52010 ,/* GCC_PCIE_RSCC_CFG_AHB_CLK */ -0x52010 ,/* GCC_PCIE_RSCC_XO_CLK */ 0x32004 ,/* GCC_VIDEO_AHB_CLK */ 0x32040, /* GCC_VIDEO_XO_CLK */ };
@@ -3480,6 +3478,10 @@ static const struct regmap_config gcc_kaanapali_regmap_config = {static void clk_kaanapali_regs_configure (struct device *dev,struct regmap *regmap) { +/* Keep clocks always enabled */ +regmap_update_bits (regmap,0x52010 ,BIT (20 ),BIT (20 ));/* GCC_PCIE_RSCC_CFG_AHB_CLK */ +regmap_update_bits (regmap,0x52010 ,BIT (21 ),BIT (21 ));/* GCC_PCIE_RSCC_XO_CLK */ +/* FORCE_MEM_CORE_ON for ufs phy ice core clocks */ qcom_branch_set_force_mem_core (regmap, gcc_ufs_phy_ice_core_clk, true); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 178/281 clk: qcom: gpucc-glymur: Mark the GPU CX GDSC as votable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Taniya Das <[email protected]>
[ Upstream commit 8f3f88309fa4e975ac27368ee945c2c202251a7c ]
The GPU CX GDSC on Glymur is a votable power domain and hence should not be polled for power-off status during GDSC disable. Also, its power-on status should be read from the GDS hardware control register.
Accommodate this by removing POLL_CFG_GDSCR and adding the VOTABLE flag for the GPU CX GDSC, similar to other platforms (e.g. SM8750 and Milos).
--- drivers/clk/qcom/gpucc-glymur.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clk/qcom/gpucc-glymur.c +1 -1
@@ -510,7 +510,7 @@ static struct gdsc gpu_cc_cx_gdsc = { .name = "gpu_cc_cx_gdsc", }, .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, + .flags = RETAIN_FF_ENABLE | VOTABLE, }; static struct clk_regmap *gpu_cc_glymur_clocks[] = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 179/281 clk: qcom: gpucc-kaanapali: Mark the GPU CX GDSC as votable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Taniya Das <[email protected]>
[ Upstream commit 55981579b27b6541aeeab81c768f248dba0491ac ]
The GPU CX GDSC on Kaanapali is a votable power domain and hence should not be polled for power-off status during GDSC disable. Also, its power-on status should be read from the GDS hardware control register.
Accommodate this by removing POLL_CFG_GDSCR and adding the VOTABLE flag for the GPU CX GDSC, similar to other platforms (e.g. SM8750 and Milos).
--- drivers/clk/qcom/gpucc-kaanapali.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clk/qcom/gpucc-kaanapali.c +1 -1
@@ -380,7 +380,7 @@ static struct gdsc gpu_cc_cx_gdsc = { .name = "gpu_cc_cx_gdsc", }, .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, + .flags = RETAIN_FF_ENABLE | VOTABLE, }; static struct clk_regmap *gpu_cc_kaanapali_clocks[] = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 180/281 arm64: dts: lx2160a: fix incorrect pinmux
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Jardin <[email protected]>
[ Upstream commit 8188a59dd6782786556b18f515090b79cb701c45 ]
For gpio1 value set for pinctrl is outside the mask and so will do nothing.
Add the missing offset for value field to be the same as the mask as expected.
--- arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi +3 -3
@@ -1803,7 +1803,7 @@ fspi_data74_pins: xspi1-data74-pins { }; gpio1_31_28_pins: xspi1-data74-gpio-pins { - pinctrl-single,bits = <0x0 0x1 (0x7 << 15)>; + pinctrl-single,bits = <0x0 (0x1 << 15) (0x7 << 15)>; }; fspi_data30_pins: xspi1-data30-pins {
@@ -1811,7 +1811,7 @@ fspi_data30_pins: xspi1-data30-pins { }; gpio1_27_24_pins: xspi1-data30-gpio-pins { - pinctrl-single,bits = <0x0 0x1 (0x7 << 18)>; + pinctrl-single,bits = <0x0 (0x1 << 18) (0x7 << 18)>; }; fspi_dqs_sck_cs10_pins: xspi1-base-pins {
@@ -1819,7 +1819,7 @@ fspi_dqs_sck_cs10_pins: xspi1-base-pins { }; gpio1_23_20_pins: xspi1-base-gpio-pins { - pinctrl-single,bits = <0x0 0x1 (0x7 << 21)>; + pinctrl-single,bits = <0x0 (0x1 << 21) (0x7 << 21)>; }; esdhc0_cmd_data30_clk_vsel_pins: sdhc1-base-sdhc-vsel-pins {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 181/281 arm64: dts: lx2160a: fix IIC1 pinmux submask rejected by pinctrl-single
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Jardin <[email protected]>
[ Upstream commit 6affdda171ca8f2e082e8c4fba56233a2f8c9910 ]
IIC1_PMUX is a single bit field at RCWSR14 bit 10, so the mask for the two iic1 pinmux nodes was narrowed from 0x7 to 0x1 to avoid writing the two reserved bits next to it.
With pinctrl-single,bit-per-mux the driver does not take the supplied mask as the bits to modify. It derives the field from the node's pinctrl-single,function-mask, which is 0x7 here, and then requires the supplied mask to match it exactly:
bit_pos = __ffs(mask); /* 10 */
mask_pos = ((pcs->fmask) << bit_pos); /* 0x7 << 10 = 0x1c00 */
submask = mask & mask_pos; /* 0x400 */
...
if (submask != mask_pos) {
dev_warn(pcs->dev,
"Invalid submask 0x%x for %pOFn at 0x%x\n",
submask, np, offset);
continue;
}A one bit mask gives submask 0x400 against mask_pos 0x1c00, so both nodes are rejected and the pins are never muxed with this message:
pinctrl-single 70010012c.pinmux: Invalid submask 0x400 for iic1-i2c-pins pinctrl-single 70010012c.pinmux: Invalid submask 0x400 for iic1-gpio-pins
The values are not changed:
- 0x0 selects IIC1
- 0x1 << 10 selects GPIO_1[3:2].
--- arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi +9 -2
@@ -1868,12 +1868,19 @@ i2c6_i2c7_pins: iic8-iic7-i2c-pins { }; /* RCWSR14 */ + /* + * IIC1_PMUX is a single bit at RCWSR14[10], but bit-per-mux + * uses the mask for the field to clear before applying the + * value. + * So bits 11 and 12, which are reserved, are written as 0 + * on every mux switch, it is harmless. + */ i2c0_pins: iic1-i2c-pins { - pinctrl-single,bits = <0x8 0x0 (0x1 << 10)>; + pinctrl-single,bits = <0x8 0x0 (0x7 << 10)>; }; gpio0_3_2_pins: iic1-gpio-pins { - pinctrl-single,bits = <0x8 (0x1 << 10) (0x1 << 10)>; + pinctrl-single,bits = <0x8 (0x1 << 10) (0x7 << 10)>; }; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 182/281 arm64: dts: lx2160a: fix the iic5 spi3 pinmux offset and value
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Jardin <[email protected]>
[ Upstream commit f14f4dc8ea3810f8ed4f0f3ea13f8bba3e2b0f04 ]
The iic5-spi3-pins node has both of its first two cells wrong.
The offset selects the register within the node's reg range, where:
0x0 is RCWSR12,
0x4 is RCWSR13,
0x8 is RCWSR14.
An offset of 0x3 is not one of them and is not even aligned for the declared 32 bit register width. It is the only node in this pinmux that does not use 0x0, 0x4 neither 0x8.
The value is wrong too:
IIC5_PMUX is RCWSR12[11:9] and it encodes 0b000 for IIC5, 0b001 for GPIO_1[25:24],
0b010 for SDHC1_CLK_SYNC_OUT/IN,
0b011 for {IIC5_SCL,IIC5_SDA} = {SPI3_SOUT,SPI3_SIN}.
This node selects the last of those, so it needs 0x3, not the 0x2, which is the value of the neighbouring sdhc1 clock sync node it was copied from.
--- arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi +1 -1
@@ -1783,7 +1783,7 @@ esdhc0_clksync_pins: iic5-sdhc-clk-pins { }; dspi2_miso_mosi_pins: iic5-spi3-pins { - pinctrl-single,bits = <0x3 (0x2 << 9) (0x7 << 9)>; + pinctrl-single,bits = <0x0 (0x3 << 9) (0x7 << 9)>; }; i2c5_pins: iic6-i2c-pins {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 183/281 iommu/dma: skip swiotlb bounce for DMA_ATTR_MMIO in iommu_dma_map_phys
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peng Fan <[email protected]>
[ Upstream commit 057e5e07420c753f248f9ce148040ab6dcf8359f ]
MMIO resources mapped via dma_map_resource() carry DMA_ATTR_MMIO. These never need swiotlb bounce buffering, but the current code enters the swiotlb path when the physical address is not page-aligned, then rejects the mapping because MMIO cannot be bounced.
Move the DMA_ATTR_MMIO check before the swiotlb block so the entire bounce path is skipped for MMIO, falling through directly to __iommu_dma_map() which creates the IOVA mapping.
--- drivers/iommu/dma-iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/iommu/dma-iommu.c +2 -2
@@ -1228,9 +1228,9 @@ dma_addr_t iommu_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size,* If both the physical buffer start address and size are page aligned, * we don't need to use a bounce page. */ -if (dev_use_swiotlb (dev, size, dir) && +if (!(attrs &DMA_ATTR_MMIO ) &&dev_use_swiotlb (dev, size, dir) &&iova_unaligned (iovad, phys, size)) { -if (attrs & (DMA_ATTR_MMIO |DMA_ATTR_REQUIRE_COHERENT )) +if (attrs &DMA_ATTR_REQUIRE_COHERENT )return DMA_MAPPING_ERROR ; phys =iommu_dma_map_swiotlb (dev, phys, size, dir, attrs);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 184/281 mount: refuse MOVE_MOUNT_SET_GROUP on an unbindable mount
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit a2b42babc912e4fb428cbb42adb4f6e1e067113d ]
do_set_group() only accepts mounts as targets that are neither shared nor a slave. That encompasses undindable mounts.
If the source mount is a slave mount the target mount will end up with source's master as its master. It keeps T_UNBINDABLE. That means we get a mount that is both unbindable and a slave:
mount --bind /tmp/src /tmp/src mount --make-shared /tmp/src mount --bind /tmp/src /tmp/b mount --make-slave /tmp/b mount --bind /tmp/src /tmp/c mount --make-unbindable /tmp/c move_mount(b, "", c, "", MOVE_MOUNT_SET_GROUP) grep /tmp/c /proc/self/mountinfo ... /tmp/c ... master:646 unbindable ...
This property cannot be produced any other way. change_mnt_propagation() drops the master when it makes a mount unbindable (iow, it becomes private) and doesn't touch an unbindable mount when it is supposed to turned into a slave mount.
End-result is that the unbindable-slave mount receives everything propagated from its master's peer group while it can't be bind mounted itself. Not sure what that's supposed to do.
On the other side: if the source mount is shared, the target mount drops T_UNBINDABLE because because set_mnt_shared() clears T_SHARED_MASK.
So teach do_set_group() to refuse an unbindable target mount the same way a shared or slave target mount is refused.
The main user of MOVE_MOUNT_SET_GROUP is CRIU which restores sharing first and applies MS_UNBINDABLE afterwards. It never hits this.
--- fs/namespace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/namespace.c +1 -1
@@ -3530,7 +3530,7 @@ static int do_set_group(const struct path *from_path, const struct path *to_pathreturn -EINVAL ;/* Setting sharing groups is only allowed on private mounts */ -if (IS_MNT_SHARED (to) ||IS_MNT_SLAVE (to)) +if (IS_MNT_SHARED (to) ||IS_MNT_SLAVE (to) ||IS_MNT_UNBINDABLE (to))return -EINVAL ;/* From should not be private */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 185/281 fs: dont let a migrating task hide its reference from do_umount()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 7eb84d54fac5002bd975d1de8fd61d57e89172ab ]
propagate_mount_busy() uses mnt_get_count() via do_refcount_check() to figure out whether a synchronous umount may proceed or not by summing the per-CPU mnt_count counter under mount_lock.
The fastpath for mntget() and mntput() doesn't require that lock. A task that already holds a reference and takes another one on a cpu the loop has summed can get migrated and drops that reference again on another cpu. The loop might not have reached that cpu yet. That ends up undercounting. The original reference of the task is missed and umount() ends up succeeding with the file still open. The mount is destroyed later when the task mntput()s so it degrades to a lazy umount.
That's basically the umount(2) variant of commit 9ea0a46ca2c3 ("fix mntput/mntput race") closed for the final mntput(). The same cpu-migration caused a UAF.
Luckily this isn't a UAF. It's just a hidden reference pinning the mount but it's wrong nonetheless.
Like the other race it's a narrow window but I can reproduce it reliably with some patching. The debug patch below prints the sum the loop computed next to an immediate re-sum of the same counters. It reports who drops the last reference to that mount later:
# --- a/fs/namespace.c # +++ b/fs/namespace.c # @@ -34,6 +34,8 @@ # #include <linux/mnt_idmapping.h> # #include <linux/pidfs.h> # #include <linux/nstree.h>
# +#include <linux/debugfs.h>
# +#include <linux/delay.h>
#
# #include "pnode.h"
# #include "internal.h"
# @@ -263,17 +265,48 @@
# #endif
# }
#
# +static u32 mnt_get_count_delay_ms;
# +static int mnt_get_count_debug_id;
# +
# +static int __init mnt_get_count_debug_init(void)
# +{
# + debugfs_create_u32("mnt_get_count_delay_ms", 0600, NULL,
# + &mnt_get_count_delay_ms);
# + return 0;
# +}
# +late_initcall(mnt_get_count_debug_init);
# +
# /*
# * vfsmount lock must be held for write
# */
# int mnt_get_count(struct mount *mnt)
# {
# #ifdef CONFIG_SMP
# - int count = 0;
# + static int seen[NR_CPUS];
# + bool slow = mnt_get_count_delay_ms &&
# + !(mnt->mnt.mnt_flags & MNT_UMOUNT);
# + int count = 0, again = 0;
# int cpu;
#
# for_each_possible_cpu(cpu) {
# - count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
# + seen[cpu] = per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
# + count += seen[cpu];
# + if (slow)
# + mdelay(mnt_get_count_delay_ms);
# + }
# + if (slow) {
# + for_each_possible_cpu(cpu) {
# + int now;
# +
# + now = per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
# + again += now;
# + pr_err("mount %d: cpu%d read %d, now %d\n",
# + mnt->mnt_id, cpu, seen[cpu], now);
# + }
# + mnt_get_count_debug_id = mnt->mnt_id;
# + pr_err("mount %d: sum %d, re-sum %d, %ps\n",
# + mnt->mnt_id, count, again,
# + __builtin_return_address(0));# }
#
# return count;
# @@ -1350,6 +1383,9 @@
# smp_mb();
# mnt_add_count(mnt, -1);
# count = mnt_get_count(mnt);
# + if (count == 0 && mnt->mnt_id == mnt_get_count_debug_id)
# + pr_err("mount %d: last put by %s (%d)\n", mnt->mnt_id,
# + current->comm, task_pid_nr(current));
# if (count != 0) {
# WARN_ON(count < 0);
# rcu_read_unlock();A task holds open a directory fd on a tmpfs. A synchronous rumount() of the tmpfs sums up. Concurrently a task calls fchdir() on that fd on CPU 0 and fchdir()s back to / on CPU 3. This will cause a path_get() and a path_put() with a migration in between without mount_lock held.
Adding a delay per CPU makes the count loop miss the get in a cpu it already visited but count the put in one it hasn't.
The reproducer, run as root with debugfs mounted:
/* * gcc -static -o umount-torn umount-torn.c */ #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <sched.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/wait.h> #define KNOB "/sys/kernel/debug/mnt_get_count_delay_ms" #define MNT "/mnt/x"
static int kmsg;
static void pin(int cpu)
{
cpu_set_t s;
CPU_ZERO(&s);
CPU_SET(cpu, &s);
sched_setaffinity(0, sizeof(s), &s);
}
static void knob(const char *v)
{
int fd = open(KNOB, O_WRONLY);
write(fd, v, strlen(v));
close(fd);
}
int main(void)
{
int last = sysconf(_SC_NPROCESSORS_ONLN) - 1;
int p[2], fd, ret;
pid_t pid;
char c;
kmsg = open("/dev/kmsg", O_WRONLY);
mkdir(MNT, 0755);
mount("none", MNT, "tmpfs", 0, NULL);
fd = open(MNT "/f", O_WRONLY | O_CREAT, 0644);
write(fd, "still here\n", 11);
close(fd);
pipe(p);
pid = fork();
if (pid == 0) {
int dirfd = open(MNT, O_RDONLY | O_DIRECTORY);
int rootfd = open("/", O_RDONLY | O_DIRECTORY);
struct stat st;
char buf[16];
/* one get/put pair while the umount is summing */
usleep(150000);
pin(0);
fchdir(dirfd); /* mntget() */
dprintf(kmsg, "holder %d: get on cpu %d\n", getpid(),
sched_getcpu());
pin(last);
fchdir(rootfd); /* mntput() */
dprintf(kmsg, "holder %d: put on cpu %d\n", getpid(),
sched_getcpu());
/* the umount has returned, is the fd still good? */
read(p[0], &c, 1);
fd = openat(dirfd, "f", O_RDONLY);
ret = read(fd, buf, sizeof(buf));
dprintf(kmsg, "holder %d: fstat %d openat %d read %d\n",
getpid(), fstat(dirfd, &st), fd, ret);
_exit(0);
}
pin(1);
knob("100");
ret = umount2(MNT, 0);
knob("0");
if (ret)
dprintf(kmsg, "umount: %s\n", strerror(errno));
else
dprintf(kmsg, "umount: 0\n");
write(p[1], "x", 1);
waitpid(pid, NULL, 0);
return 0;
}The log shows the busy check summing 2 and when read right after it reads 3. The loop read CPU 0 before the get landed there and CPU 3 after the put did. The umount succeeds while there's still someone with an fd. The mount is then dropped when the fd goes:
[ 2.511944] holder 90: get on cpu 0 [ 2.512023] holder 90: put on cpu 3 [ 2.671388] mount 28: cpu0 read 1, now 2 [ 2.671398] mount 28: cpu1 read 2, now 2 [ 2.671401] mount 28: cpu2 read 0, now 0 [ 2.671404] mount 28: cpu3 read -1, now -1 [ 2.671407] mount 28: sum 2, re-sum 3, propagate_mount_busy [ 2.671773] umount: 0 [ 2.672963] holder 90: fstat 0 openat 8 read 11 [ 2.673663] mount 28: last put by umount-torn (90)
Easy fix would be to order mntget() against the loop that sums up. But that's a full barrier and a flag test in mntget() afaict. No bueno... That would cost every path_get().
So learn from SRCU. Keep gets and puts in separate per-cpu counters and make mnt_get_count() sum all puts first and all gets second with a full barrier in between.
A put is always after its own get (hopefully...) independent of the cpu the task is on. So a put the first pass counted must have its get visible to the second pass. Ergo, we can't undercount.
This is the two pass sum srcu_readers_active_idx_check() does for srcu_locks and srcu_unlocks. mntget() keeps a single this_cpu_inc(). mntput() gains an smp_wmb() before its increment, a compiler barrier on x86 and a store fence on the weakly ordered architectures, since the two counters are two words and a put stored after a get could otherwise become visible before it. The counters are unsigned so the difference stays right after they wrap.
The count can still come out high when a get is counted whose put the first pass did not see. We don't care. A task can only take a new reference if:
(1) it holds one already
(2) through __legitimize_mnt() which the seqcount orders against the write side
So a get in this window means someone has a reference and -EBUSY is correct. The final mntput() is unchanged. Once ->mnt_ns is NULL every put takes the slow path under mount_lock and so no put is in flight while it counts. Here's an attempted proof for that crap:
(i) A put never becomes visible before its get. Within one task the smp_wmb() in mntput_no_expire() orders every earlier store before the increment of the puts.
Any task that migrated in between had its stores flushed by the scheduler. When that reference is handed to another task that's trivially true.
(ii) The double count in mnt_get_count() is ordered via smp_mb(). That
keeps every read of the puts counter before every read of the gets counter.
(iii) We never undercount by (ii).
(iv) There are no spurious counts/differences. A get is counted only if
it happened and a put is left out only if it hasn't happened when the counter was read. With (iii) this means it's equivalent to the old count whenever nothing is taken or dropped during the loop.
(v) do_umount() holds a reference and mount_lock hence. New references come from: (v.a) __legitimize_mnt() managed by the seqcount. Either it's smp_mb() made the get visible to the loop or it takes the lock and waits for the loop. By (iii) a mount with a additional references is refused. By (iv) refusing the umount just means that any additional reference existed, same as the old counter.
(vi) mntput_no_expire_slowpath() runs with ->mnt_ns already NULL. Hence,
every other put must take the slowpath as well and hangs on mount_lock.
The grace period in namespace_unlock() will make every fast path do a put that still saw ->mnt_ns.
No put is in flight during the loop.
A get that is in flight comes from a task holding another reference. That must be included by the count or from __legitimize_mnt() as in (v).
Ergo, zero means the last reference and non-zero means a reference is live.
(vii) may_umount() uses propagate_mount_busy() as well and
may_umount_tree() sums mnt_get_count() itself.
With this patch the race is dead:
[ 3.066786] mount 28: sum 3, re-sum 3, propagate_mount_busy [ 3.105867] umount: Device or resource busy [ 3.106047] holder 90: fstat 0 openat 8 read 11
--- fs/mount.h | 3 ++- fs/namespace.c | 48 ++++++++++++++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 19 deletions(-)
fs/mount.h +2 -1
@@ -33,7 +33,8 @@ struct mnt_namespace { } __randomize_layout;struct mnt_pcp { -int mnt_count; +unsigned int mnt_gets; +unsigned int mnt_puts;int mnt_writers; };
fs/namespace.c +30 -18
101 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 186/281 scsi: ufs: core: Hold a clock reference across the probe
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Naomi Chu <[email protected]>
[ Upstream commit 02a3178319639f650428164d4d003de4dc03e326 ]
Clock gating becomes possible as soon as ufshcd_init_clk_gating() has run, and from that point on the probe keeps accessing host registers without ever taking a clock reference. This has been safe only because of the state check in __ufshcd_release(): gate_work is not queued unless hba->ufshcd_state is UFSHCD_STATE_OPERATIONAL, and the promotion to that state used to happen after the last register access of the probe, at the end of ufshcd_probe_hba().
That is fragile: it only works while the promotion happens after the register accesses. Commit a390e6677f41 ("scsi: ufs: core: Expand the ufshcd_device_init(hba, true) call") changed that ordering by moving the promotion into ufshcd_init(), which schedules ufshcd_async_scan() afterwards. ufshcd_probe_hba() therefore now runs with the state already promoted, and it accesses host registers without holding a clock reference:
- On hosts with UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH it calls ufshcd_hba_stop() and ufshcd_hba_enable() before ufshcd_device_init() sets the state back to UFSHCD_STATE_RESET, and both read REG_CONTROLLER_ENABLE, so gated clocks stall there instead of just losing a write.
- It ends with an ufshcd_configure_auto_hibern8() write, which its other callers do take a clock reference for.
Take a clock reference as soon as clock gating has been initialised and keep it until the probe is over. It then does not matter who drops a clock reference while the probe is running, and the register accesses of the probe no longer depend on hba->ufshcd_state. The reference is dropped by ufshcd_async_scan() once the scan has finished, or by the new out_release label if the probe fails after it was taken.
--- drivers/ufs/core/ufshcd.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
drivers/ufs/core/ufshcd.c +14 -8
@@ -9484,6 +9484,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie) ret =ufshcd_add_lus (hba); out: +ufshcd_release (hba);pm_runtime_put_sync (hba->dev);if (ret)
@@ -11206,6 +11207,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)ufshcd_init_clk_gating (hba); +/* Released by ufshcd_async_scan(), or by out_release on failure. */ +ufshcd_hold (hba); +ufshcd_init_clk_scaling (hba);/*
@@ -11226,7 +11230,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) err =devm_request_irq (dev, irq, ufshcd_intr,IRQF_SHARED ,UFSHCD , hba);if (err) {dev_err (hba->dev,"request irq failed\n" ); - goto out_disable; + goto out_release; }else { hba->is_irq_enabled = true; }
@@ -11242,7 +11246,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)dev_err (hba->dev,"Host controller enable failed\n" );ufshcd_print_evt_hist (hba);ufshcd_print_host_state (hba); - goto out_disable; + goto out_release; }INIT_DELAYED_WORK (&hba->rpm_dev_flush_recheck_work, ufshcd_rpm_dev_flush_recheck_work);
@@ -11256,7 +11260,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) err =ufshcd_add_scsi_host (hba);if (err) - goto out_disable; + goto out_release;/* Hold auto suspend until async scan completes */ pm_runtime_get_sync (dev);
@@ -11276,7 +11280,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) err =ufshcd_link_startup (hba);if (err) - goto out_disable; + goto out_release;if (hba->mcq_enabled)ufshcd_config_mcq (hba);
@@ -11293,23 +11297,23 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)/* Verify device initialization by sending NOP OUT UPIU */ err =ufshcd_verify_dev_init (hba);if (err) - goto out_disable; + goto out_release;/* Initiate UFS initialization, and waiting until completion */ err =ufshcd_complete_dev_init (hba);if (err) - goto out_disable; + goto out_release; err =ufshcd_device_params_init (hba);if (err) - goto out_disable; + goto out_release; err =ufshcd_post_device_init (hba); initialized:ufshcd_process_probe_result (hba, probe_start, err);if (err) - goto out_disable; + goto out_release;ufs_sysfs_add_nodes (hba->dev); hba->dme_qos_sysfs_handle = sysfs_get_dirent(hba->dev->kobj.sd,
@@ -11320,6 +11324,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)ufshcd_pm_qos_init (hba);return 0 ; +out_release: +ufshcd_release (hba); out_disable: hba->is_irq_enabled = false;ufshcd_hba_exit (hba);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 187/281 Bluetooth: btintel_pcie: fix TX descriptor bounds check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ravindra <[email protected]>
[ Upstream commit baa53af641c9e3644e717b8dfd1b95a55a0c111b ]
btintel_pcie_prepare_tx() uses tfd_index to access the TFD and data buffer arrays before the index is advanced modulo txq->count. An index equal to txq->count is one past the end of both arrays, so reject it in the bounds check.
--- drivers/bluetooth/btintel_pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btintel_pcie.c +1 -1
@@ -401,7 +401,7 @@ static int btintel_pcie_send_sync(struct btintel_pcie_data *data, tfd_index = data->ia.tr_hia[BTINTEL_PCIE_TXQ_NUM ]; -if (tfd_index > txq->count) +if (tfd_index >= txq->count)return -ERANGE ;if (skb->len >BTINTEL_PCIE_BUFFER_SIZE -BTINTEL_PCIE_HCI_TYPE_LEN ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 188/281 Bluetooth: btintel_pcie: Add shared HW reset for clean start
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sai Teja Aluvala <[email protected]>
[ Upstream commit 1465494e8d07426fa41c0021530cccb2a1ac38fa ]
Reset shared Bluetooth hardware after mapping the BAR and before setting up MSI-X interrupts, so stale device state is cleared before IRQ handlers and interrupt masks are configured.
Propagate reset errors so probe aborts rather than continuing when the reset fails.
--- drivers/bluetooth/btintel_pcie.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/bluetooth/btintel_pcie.c +7 -0
@@ -1819,6 +1819,13 @@ static int btintel_pcie_config_pcie(struct pci_dev *pdev,if (IS_ERR (data->base_addr))return PTR_ERR (data->base_addr); +/* Do shared hardware reset to ensure a clean start before +* configuring interrupts. +*/ + err =btintel_pcie_reset_bt (data); +if (err) +return err; + err =btintel_pcie_setup_irq (data);if (err)return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 189/281 drm/bridge: Fix unlocked list_del in drm_bridge_add()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cristian Ciocaltea <[email protected]>
[ Upstream commit b62e3db8cdc1680e1c3390591e872f485148541a ]
When re-adding a bridge that was previously removed, drm_bridge_add() drops it from bridge_lingering_list without holding bridge_lock.
Both bridge_list and bridge_lingering_list are protected by bridge_lock, as they are concurrently modified by drm_bridge_remove() and __drm_bridge_free(), and walked by the debugfs 'bridges' file. Running the list_empty() test and the list_del_init() outside of the lock may therefore corrupt either list.
Perform both under bridge_lock.
--- drivers/gpu/drm/drm_bridge.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/drm_bridge.c +2 -0
@@ -414,8 +414,10 @@ void drm_bridge_add(struct drm_bridge *bridge)* in bridge_lingering_list. Remove it or bridge_lingering_list will be * corrupted when adding this bridge to bridge_list below. */ +mutex_lock (&bridge_lock);if (!list_empty (&bridge->list))list_del_init (&bridge->list); +mutex_unlock (&bridge_lock);mutex_init (&bridge->hpd_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 190/281 drm/bridge: Fix unlocked list access in drm_bridge_attach()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cristian Ciocaltea <[email protected]>
[ Upstream commit 57889076afd1be890150c32ffde3b691fe5707c8 ]
drm_bridge_attach() inspects bridge->list to warn about bridges being attached without a previous drm_bridge_add() call, but it does so without holding bridge_lock.
The list head can be concurrently written by drm_bridge_add(), drm_bridge_remove() and __drm_bridge_free() while they operate on neighbouring entries of bridge_list or bridge_lingering_list, all of them under bridge_lock. Hence the unlocked read is a data race, even though it is unlikely to change the outcome of list_empty() in practice.
Perform the check with bridge_lock held.
--- drivers/gpu/drm/drm_bridge.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/drm_bridge.c +4 -2
@@ -558,8 +558,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,if (!bridge->container)DRM_WARN ("DRM bridge corrupted or not allocated by devm_drm_bridge_alloc()\n" ); -if (list_empty (&bridge->list)) -DRM_WARN ("Missing drm_bridge_add() before attach\n" ); + scoped_guard(mutex, &bridge_lock) { +if (list_empty (&bridge->list)) +DRM_WARN ("Missing drm_bridge_add() before attach\n" ); + }drm_bridge_get (bridge);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 191/281 cgroup/cpuset: Dont access cpuset_cgrp_subsys.root in is_in_v2_mode()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waiman Long <[email protected]>
[ Upstream commit e06b678e3b4e12fdaa0b51a7a5a54abc5dbd6469 ]
After seeing the patch [1] to guard is_in_v2_mode() with RCU, it makes me realize that is_in_v2_mode() may be called in a context where a new cgroup filesystem is being rebound with stale cpuset_cgrp_subsys.root pointer. Avoid this potential UaF situation by adding a new cpuset_v2_mode flag which is set when the cpuset_v2_mode mount option is used. This flag is written into only when cpuset_bind() is being called with a stable cpuset_cgrp_subsys.root value. The is_in_v2_mode() helper is modified to read the new cpuset_v2_mode flag instead of accessing cpuset_cgrp_subsys.root directly.
[1] https://lore.kernel.org/lkml/[email protected]
--- kernel/cgroup/cpuset.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
kernel/cgroup/cpuset.c +9 -2
@@ -151,6 +151,12 @@ static cpumask_var_t isolated_cpus; /* CSCB */*/ static bool update_housekeeping;/* RWCS */ +/* +* Set if "cpuset_v2_mode" mount option is used +* Cached at bind time and not lock protected; accessed via {READ,WRITE}_ONCE +*/ +static bool cpuset_v2_mode; +/* * Copy of isolated_cpus to be passed to housekeeping_update() */
@@ -403,8 +409,7 @@ static inline bool cpuset_v2(void)*/ static inline bool is_in_v2_mode (void ) { -return cpuset_v2 () || - (cpuset_cgrp_subsys.root->flags &CGRP_ROOT_CPUSET_V2_MODE ); +return cpuset_v2 () ||READ_ONCE (cpuset_v2_mode); }/**
@@ -3602,6 +3607,8 @@ static void cpuset_bind(struct cgroup_subsys_state *root_css)mutex_lock (&cpuset_mutex);spin_lock_irq (&callback_lock); +WRITE_ONCE (cpuset_v2_mode, + !!(cpuset_cgrp_subsys.root->flags &CGRP_ROOT_CPUSET_V2_MODE ));if (is_in_v2_mode ()) {cpumask_copy (top_cpuset.cpus_allowed, cpu_possible_mask);cpumask_copy (top_cpuset.effective_xcpus, cpu_possible_mask);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 192/281 drm/dp: dont mark the AUX backlight enabled when enabling failed
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleg Keri <[email protected]>
[ Upstream commit 707ce102521496c67860d81221b20c56d8255f2f ]
If drm_edp_backlight_enable() fails, dp_aux_backlight_update_status() still marks the backlight as enabled. From then on every brightness change only updates the level and the enable is never retried, so the panel stays dark until something blanks and unblanks the backlight.
Return the error instead, so the next update tries to enable again.
--- drivers/gpu/drm/display/drm_dp_helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/display/drm_dp_helper.c +3 -1
@@ -4634,7 +4634,9 @@ static int dp_aux_backlight_update_status(struct backlight_device *bd)if (!backlight_is_blank (bd)) {if (!bl->enabled) { -drm_edp_backlight_enable (bl->aux, &bl->info, brightness); + ret =drm_edp_backlight_enable (bl->aux, &bl->info, brightness); +if (ret) +return ret; bl->enabled = true;return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 193/281 gpio: exar: initialize the ID before registering its cleanup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sang-Hoon Choi <[email protected]>
[ Upstream commit 4d69e74034b208588636ecc177e0dd43a7696af3 ]
devm_add_action_or_reset() invokes the cleanup action immediately when registering the action fails. gpio_exar_probe() currently stores the allocated ID in exar_gpio only after registering the action, so that failure makes exar_devm_ida_free() release ID 0. If the allocated ID is nonzero, this leaks that ID and can free ID 0 belonging to another device.
Store the ID before registering the cleanup action so both the immediate failure path and normal device teardown release the correct allocation.
--- drivers/gpio/gpio-exar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpio/gpio-exar.c +1 -1
@@ -199,6 +199,7 @@ static int gpio_exar_probe(struct platform_device *pdev) index =ida_alloc (&ida_index,GFP_KERNEL );if (index <0 )return index; + exar_gpio->index = index; ret =devm_add_action_or_reset (dev, exar_devm_ida_free, exar_gpio);if (ret)
@@ -214,7 +215,6 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->gpio_chip.set = exar_set_value; exar_gpio->gpio_chip.base =-1 ; exar_gpio->gpio_chip.ngpio = ngpios; - exar_gpio->index = index; exar_gpio->first_pin = first_pin; ret =devm_gpiochip_add_data (dev, &exar_gpio->gpio_chip, exar_gpio);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 194/281 drm/aspeed: Balance the display clock enable on teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
[ Upstream commit ecce445c6489895463b7ddec2af66338287fee4c ]
aspeed_gfx_load() enables the display clock without checking the return value and never disables it on probe failure or removal. devm_clk_get() only releases the clock reference, leaving the enable unbalanced.
Use devm_clk_get_enabled() to handle enable failures and automatically disable and unprepare the clock on probe failure or removal.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +2 -3
@@ -189,13 +189,12 @@ static int aspeed_gfx_load(struct drm_device *drm) }reset_control_deassert (priv->rst); - priv->clk =devm_clk_get (drm->dev,NULL ); + priv->clk =devm_clk_get_enabled (drm->dev,NULL );if (IS_ERR (priv->clk)) {dev_err (&pdev->dev, -"missing or invalid clk device tree entry" ); +"failed to get and enable clock\n" );return PTR_ERR (priv->clk); } -clk_prepare_enable (priv->clk);/* Sanitize control registers */ writel (0 , priv->base +CRT_CTRL1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 195/281 drm/vc4: Free the BO cache size list on teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
[ Upstream commit b41ff8b529278216cba22a5dcdc93fea2a3a9619 ]
vc4_bo_cache_destroy() purges the cached BOs but never frees the size list, leaking it on every unbind.
--- drivers/gpu/drm/vc4/vc4_bo.c | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/vc4/vc4_bo.c +1 -0
@@ -1052,6 +1052,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused)cancel_work_sync (&vc4->bo_cache.time_work);vc4_bo_cache_purge (dev); +kfree (vc4->bo_cache.size_list);for (i =0 ; i < vc4->num_labels; i++) {if (vc4->bo_labels[i].num_allocated) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 196/281 drm/vc4: Use kvmalloc_objs() for the BO cache size list
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
[ Upstream commit bca45af5998a05f34b13a2ef11e639bac9c62643 ]
The BO cache size list has one entry per BO page count, so its size scales with the largest BO ever freed. A 64 MiB BO already needs at least an order-6 allocation, which fails under fragmentation and triggers a warn_alloc() splat on BO close:
page allocation failure: order:7, mode:0x40cc0(GFP_KERNEL|__GFP_COMP)
Call trace: ... warn_alloc+0x118/0x190 __alloc_pages_slowpath+0x4a8/0x520 ... __kmalloc_noprof+0x3ac/0x5d8 vc4_free_object+0x128/0x370 [vc4] drm_gem_object_handle_put_unlocked+0xb8/0x158 [drm] drm_gem_object_release_handle+0x74/0xe8 [drm] drm_gem_handle_delete+0x6c/0xa8 [drm] drm_gem_close_ioctl+0x34/0x50 [drm]
The list doesn't need physically contiguous memory. Use kvmalloc_objs() so large lists can fall back to vmalloc(), avoiding the allocation failure.
--- drivers/gpu/drm/vc4/vc4_bo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/vc4/vc4_bo.c +3 -3
@@ -204,7 +204,7 @@ static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev,struct list_head *new_list;uint32_t i; - new_list = kmalloc_objs(struct list_head, new_size); + new_list = kvmalloc_objs(struct list_head, new_size);if (!new_list)return NULL ;
@@ -224,7 +224,7 @@ static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev,for (i = vc4->bo_cache.size_list_size; i < new_size; i++)INIT_LIST_HEAD (&new_list[i]); -kfree (vc4->bo_cache.size_list); +kvfree (vc4->bo_cache.size_list); vc4->bo_cache.size_list = new_list; vc4->bo_cache.size_list_size = new_size; }
@@ -1052,7 +1052,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused)cancel_work_sync (&vc4->bo_cache.time_work);vc4_bo_cache_purge (dev); -kfree (vc4->bo_cache.size_list); +kvfree (vc4->bo_cache.size_list);for (i =0 ; i < vc4->num_labels; i++) {if (vc4->bo_labels[i].num_allocated) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 197/281 net/sched: em_text: reject unterminated algo before autoload
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit c9728fcf2e7fb4a7a8d08ebd9524c3b02ae1a021 ]
This is a follow-up to commit 9c572a83037a ("net/sched: fix potential stack infoleak in em_text_dump()"), which zero-initialised the dump-side struct. Sashiko pointed at a pre-existing bug that exposed the change-side input-validation gap open.
em_text_change() casts the raw netlink attribute payload to struct tcf_em_text and passes conf->algo, a 16-byte array with no enforced NUL terminator, to textsearch_prepare(). On the TS_AUTOLOAD retry textsearch_prepare() calls request_module("ts_%s", algo); vsnprintf() walks %s until it finds a NUL, so an algo[] with no NUL reads past the array into the adjacent struct fields and payload and feeds those bytes to the usermode helper command line. This is an out-of-bounds read and a kernel memory disclosure.
Reject the attribute when no NUL exists within TC_EM_TEXT_ALGOSIZ bytes, matching the check xt_string has had since 3ab720881b6e.
Conditions to recreate the bug:
- CAP_NET_ADMIN on the target netns (namespace-local via unshare -Urn is enough)
- install clsact on an interface, then add a basic filter with one text ematch whose algo[] is 16 bytes with no NUL (e.g. "A"*16) and pattern_len at least 1
- the add fails with -ENOENT and the kernel invokes modprobe with a module name made of "ts_" plus the 16 bytes and the adjacent payload
--- net/sched/em_text.c | 3 +++ 1 file changed, 3 insertions(+)
net/sched/em_text.c +3 -0
@@ -58,6 +58,9 @@ static int em_text_change(struct net *net, void *data, int len,if (len <sizeof (*conf) || len < (sizeof (*conf) + conf->pattern_len))return -EINVAL ; +if (!memchr (conf->algo,'\0' ,sizeof (conf->algo))) +return -EINVAL ; +if (conf->from_layer > conf->to_layer)return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 198/281 amt: send the relays General Query directly from the receive path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Omar Ramadan <[email protected]>
[ Upstream commit afae89de73dd693cfa329580ba0fba63bd3f0a11 ]
An skb queued in a qdisc can outlive the tunnel it references through a raw pointer in skb->cb. For example, with igmp_qrv set to 1 on the relay a tunnel lives for 135s, so a netem delay of 180s on the amt device outlives it; when the tunnel expires and is freed, the subsequent dequeue triggers a use-after-free in amt_dev_xmit().
BUG: KASAN: slab-use-after-free in amt_dev_xmit+0x2763/0x2e20 Call Trace: amt_dev_xmit+0x2763/0x2e20 [drivers/net/amt.c:1262] dev_hard_start_xmit+0x22f/0x620 sch_direct_xmit+0x12e/0xac0 netem_dequeue+0x333/0xc50 net_tx_action+0x35c/0xa60
amt_send_igmp_gq() and amt_send_mld_gq() are only called from amt_request_handler(), inside the rcu_read_lock_bh() section of amt_rcv(). amt_request_handler() already has the tunnel the query is
for: it found or created it inside that section. Queuing the query with dev_queue_xmit() only leads back into amt_dev_xmit(), which strips the Ethernet header and calls amt_send_membership_query() for that tunnel.
Make that call directly from the two senders instead, the same way amt_send_advertisement() transmits from the receive path. The query never waits in a qdisc, the tunnel is only dereferenced inside the RCU section that found or created it, and nothing is stored in skb->cb, so no lookup or refcount is needed. Remove the query branch of amt_dev_xmit(), amt_skb_cb() and struct amt_skb_cb, which have no users left.
Behaviour changes:
- The relay's own General Queries no longer pass through the amt device's egress path: its qdisc, tc egress (clsact/tcx), the netfilter egress hook and packet taps. They are still visible as UDP on the underlay.
- A query that is sent successfully is no longer counted as tx_dropped. The old query branch left through the unlock label, which counted every sent query as dropped.
- A query that reaches amt_dev_xmit() on a relay from elsewhere, such as a userspace querier, is now dropped at the IGMP/MLD type switch. Before, it trusted whatever skb->cb held, and a NULL tunnel hit the WARN_ON(1).
--- drivers/net/amt.c | 48 +++++++++++++++-------------------------------- include/net/amt.h | 4 ---- 2 files changed, 15 insertions(+), 37 deletions(-)
drivers/net/amt.c +15 -33
@@ -80,15 +80,6 @@ static struct in6_addr mld2_all_node = MLD2_ALL_NODE_INIT;static struct mld2_grec mldv2_zero_grec;#endif -static struct amt_skb_cb *amt_skb_cb (struct sk_buff *skb) -{ -BUILD_BUG_ON (sizeof (struct amt_skb_cb ) +sizeof (struct tc_skb_cb ) > - sizeof_field(struct sk_buff, cb)); - -return (struct amt_skb_cb *)((void *)skb->cb + -sizeof (struct tc_skb_cb )); -} -static void __amt_source_gc_work (void ) {struct amt_source_node *snode;
@@ -789,6 +780,11 @@ static void amt_send_request(struct amt_dev *amt, bool v6)rcu_read_unlock (); } +static bool amt_send_membership_query (struct amt_dev *amt, +struct sk_buff *skb, +struct amt_tunnel_list *tunnel, +bool v6); +static void amt_send_igmp_gq (struct amt_dev *amt,struct amt_tunnel_list *tunnel) {
@@ -798,8 +794,11 @@ static void amt_send_igmp_gq(struct amt_dev *amt,if (!skb)return ; -amt_skb_cb (skb)->tunnel = tunnel; -dev_queue_xmit (skb); +skb_pull (skb,sizeof (struct ethhdr )); +if (amt_send_membership_query (amt, skb, tunnel, false)) { + amt->dev->stats.tx_dropped++; +kfree_skb (skb); + } }#if IS_ENABLED (CONFIG_IPV6 )
@@ -883,8 +882,11 @@ static void amt_send_mld_gq(struct amt_dev *amt, struct amt_tunnel_list *tunnel)if (!skb)return ; -amt_skb_cb (skb)->tunnel = tunnel; - dev_queue_xmit(skb); +skb_pull (skb,sizeof (struct ethhdr )); +if (amt_send_membership_query (amt, skb, tunnel, true)) { + amt->dev->stats.tx_dropped++; +kfree_skb (skb); + } }#else static void amt_send_mld_gq (struct amt_dev *amt,struct amt_tunnel_list *tunnel)
@@ -1183,7 +1185,6 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)#endif bool report = false;struct igmphdr *ih; -bool query = false;struct iphdr *iph;bool data = false;bool v6 = false;
@@ -1201,9 +1202,6 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)case IGMP_HOST_MEMBERSHIP_REPORT : report = true;break ; -case IGMP_HOST_MEMBERSHIP_QUERY : - query = true; -break ;default : goto free; }
@@ -1225,9 +1223,6 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)case ICMPV6_MLD2_REPORT : report = true;break ; -case ICMPV6_MGM_QUERY : - query = true; -break ;default : goto free; }
@@ -1258,19 +1253,6 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) goto free; goto unlock; }else if (amt->mode ==AMT_MODE_RELAY ) { -if (query) { - tunnel =amt_skb_cb (skb)->tunnel; -if (!tunnel) { -WARN_ON (1 ); - goto free; - } - -/* Do not forward unexpected query */ -if (amt_send_membership_query (amt, skb, tunnel, v6)) - goto free; - goto unlock; - } -if (!data) goto free;list_for_each_entry_rcu (tunnel, &amt->tunnel_list, list) {
include/net/amt.h +0 -4
@@ -231,10 +231,6 @@ struct amt_relay_headers { }; } __packed; -struct amt_skb_cb { -struct amt_tunnel_list *tunnel; -}; -struct amt_tunnel_list {struct list_head list;/* Protect All resources under an amt_tunne_list */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 199/281 lib/dim: fix 32-bit overflow in dim_calc_stats() rates
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shashank Mohan Jain <[email protected]>
[ Upstream commit 0843b23890642a02ff85c36406224e8874e30664 ]
dim_calc_stats() computes the per-millisecond rates as
DIV_ROUND_UP(nbytes * USEC_PER_MSEC, delta_us)
where nbytes is a u32 and USEC_PER_MSEC is 1000L. On 64-bit the product is done in 64-bit long arithmetic, but on 32-bit architectures long is 32 bits wide and the product wraps as soon as a measurement window carries more than 4294967 bytes (about 4.3 MB). The same applies to the packet and completion counts, although those need more than 4.29 million packets or completions per window.
A DIM window spans DIM_NEVENTS (64) events. Drivers count events per interrupt or per NAPI poll, so under sustained load a window can easily carry more than 4.3 MB: 64 full NAPI polls of 64 MTU-sized frames are already 6.2 MB, and drivers such as mtk_eth_soc count one event per interrupt while NAPI keeps polling with the interrupt masked. On 32-bit users of the library (for example mtk_eth_soc on MT7621, bcmgenet and bcmsysport on 32-bit ARM, or virtio_net in a 32-bit guest) bpms then becomes the product modulo 2^32 divided by the window length, and net_dim_stats_compare() makes its BETTER/WORSE decisions on a value that has little to do with the real throughput.
For example, a 1 Gbit/s link at line rate that moves 5 MB in a 40 ms window gives bpms = 125000 on 64-bit but 17626 on 32-bit, and 5 million packets in 2 s gives ppms = 353 instead of 2500.
Widen the products to 64 bits and divide with DIV_ROUND_UP_ULL(). The results are unchanged on 64-bit.
--- lib/dim/dim.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
lib/dim/dim.c +7 -3
@@ -69,11 +69,15 @@ bool dim_calc_stats(const struct dim_sample *start,if (!delta_us)return false; - curr_stats->ppms =DIV_ROUND_UP (npkts *USEC_PER_MSEC , delta_us); - curr_stats->bpms =DIV_ROUND_UP (nbytes *USEC_PER_MSEC , delta_us); +/* u32 * USEC_PER_MSEC overflows a 32-bit long */ + curr_stats->ppms =DIV_ROUND_UP_ULL ((u64 )npkts *USEC_PER_MSEC , + delta_us); + curr_stats->bpms =DIV_ROUND_UP_ULL ((u64 )nbytes *USEC_PER_MSEC , + delta_us); curr_stats->epms =DIV_ROUND_UP (DIM_NEVENTS *USEC_PER_MSEC , delta_us); - curr_stats->cpms =DIV_ROUND_UP (ncomps *USEC_PER_MSEC , delta_us); + curr_stats->cpms =DIV_ROUND_UP_ULL ((u64 )ncomps *USEC_PER_MSEC , + delta_us);if (curr_stats->epms !=0 ) curr_stats->cpe_ratio =DIV_ROUND_DOWN_ULL ( curr_stats->cpms *100 , curr_stats->epms);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 200/281 cxgb4/ch_ktls: disable softirqs around tid_list erases
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sang-Hoon Choi <[email protected]>
[ Upstream commit 4c9dc0faca98084922caeb689579d338cf3e1c29 ]
chcr_ktls_cpl_act_open_rpl() inserts into tid_list from the receive path using xa_insert_bh(). The array is shared by the adapter's connections and initialized with XA_FLAGS_LOCK_BH. However, chcr_ktls_dev_del() and the chcr_ktls_dev_add() error path use xa_erase(), which takes the XArray lock without disabling softirqs.
When cleanup runs with softirqs enabled, a receive softirq for another connection on the same CPU can interrupt the erase and spin on the lock held by the interrupted task. Use xa_erase_bh() at both sites to match the insertion path.
--- .../net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c +2 -2
@@ -389,7 +389,7 @@ static void chcr_ktls_dev_del(struct net_device *netdev,cxgb4_remove_tid (&tx_info->adap->tids, tx_info->tx_chan, tx_info->tid, tx_info->ip_family); -xa_erase (&u_ctx->tid_list, tx_info->tid); +xa_erase_bh (&u_ctx->tid_list, tx_info->tid); } port_stats = &tx_info->adap->ch_ktls_stats.ktls_port[tx_info->port_id];
@@ -577,7 +577,7 @@ static int chcr_ktls_dev_add(struct net_device *netdev, struct sock *sk,cxgb4_remove_tid (&tx_info->adap->tids, tx_info->tx_chan, tx_info->tid, tx_info->ip_family); -xa_erase (&u_ctx->tid_list, tx_info->tid); +xa_erase_bh (&u_ctx->tid_list, tx_info->tid); put_module:/* release module refcount */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 201/281 wifi: mt76: mt7996: program a link again if the driver holds it
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit f5e0b20f6c6d548d985acdd620c5110776c915c2 ]
The firmware needs the driver to remove active links from the BSSINFO table when it destroys the interface. A link therefore stays registered from its first chanctx assignment until the interface goes away. If the driver holds the link already, a second add does nothing.
mac80211 gives a non-AP MLD a new address each time it sets a link up. A link that comes back after a failed attempt thus carries an address that the hardware does not know, on a BSS that the driver never programs again. The station then authenticates from an address that nothing answers. The failure repeats until the driver tears the interface down for good.
Program the address and the BSS again. This is the pair that the initial add sets up. For a station it does what mt7996_mcu_mld_link_oper() does for an AP.
--- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7996/main.c +8 -1
@@ -310,7 +310,14 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,if (vif->type ==NL80211_IFTYPE_AP )return mt7996_mcu_mld_link_oper (dev, link_conf, link, true); -return 0 ; + +/* update the link address */ + ret =mt7996_mcu_add_dev_info (phy, vif, link_conf, mlink, true); +if (ret) +return ret; + +return mt7996_mcu_add_bss_info (phy, vif, link_conf, mlink, + msta_link, true); } mlink->idx =__ffs64 (~dev->mt76.vif_mask);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 202/281 net: stmmac: propagate PTP addend and system time programming errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 232d49dd4b40a666283de9e722899f088ed581b2 ]
stmmac_update_subsecond_increment() ignores the error returned by stmmac_config_addend(), and stmmac_init_tstamp_counter() discards the addend and system time programming errors, always returning success. A failure to program the addend (PTP_TCR_TSADDREG) or to initialize the system time counter (PTP_TCR_TSINIT) is therefore silently swallowed, leaving the hardware timestamp counter in a non-running or partially configured state while the driver keeps operating as if timestamping were up. This matters for TAPRIO/EST offloading, which derives the gate base time from the hardware timestamp counter.
The same hooks are also called from the PHC callbacks: settime64 and adjfine drop the error and report success to clock_settime() and clock_adjtime(), so a dead PTP reference clock goes unnoticed by ptp4l/phc2sys.
Return error codes from stmmac_update_subsecond_increment(), stmmac_init_tstamp_counter(), stmmac_dl_ts_coarse_set() and the settime64/adjfine callbacks instead of silently returning success. On failure, roll back the partially applied configuration so the hardware and the driver bookkeeping stay consistent, and report the reason through the devlink extack. Also guard against a zero sub-second increment, which would otherwise divide by zero when computing the addend.
Reset the persistent timestamping state (hwts_tx_en, hwts_rx_en, tstamp_config, systime_flags and tsfupdt_coarse) when (re)initializing timestamping, so a failed init does not leave TX/RX timestamping enabled on a counter that never started.
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 106 +++++++++++++----- .../net/ethernet/stmicro/stmmac/stmmac_ptp.c | 10 +- 2 files changed, 84 insertions(+), 32 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +78 -28
@@ -601,31 +601,64 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, } } -static void stmmac_update_subsecond_increment (struct stmmac_priv *priv) +static void stmmac_restore_subsecond_increment (struct stmmac_priv *priv, +u32 default_addend) {bool xmac =dwmac_is_xmac (priv->plat->core_type);u32 sec_inc =0 ; -u64 temp =0 ; +stmmac_config_addend (priv, priv->ptpaddr, default_addend);stmmac_config_hw_tstamping (priv, priv->ptpaddr, priv->systime_flags); +stmmac_config_sub_second_increment (priv, priv->ptpaddr, + priv->plat->clk_ptp_rate, + xmac, &sec_inc); + priv->default_addend = default_addend; + priv->sub_second_inc = sec_inc; +} + +static int stmmac_update_subsecond_increment (struct stmmac_priv *priv, +u32 systime_flags) +{ +bool xmac =dwmac_is_xmac (priv->plat->core_type); +u32 sec_inc =0 , val; +u64 temp =0 ; +int ret; + +stmmac_config_hw_tstamping (priv, priv->ptpaddr, systime_flags);/* program Sub Second Increment reg */ stmmac_config_sub_second_increment (priv, priv->ptpaddr, priv->plat->clk_ptp_rate, xmac, &sec_inc); - temp =div_u64 (1000000000ULL , sec_inc); - -/* Store sub second increment for later use */ - priv->sub_second_inc = sec_inc; +if (!sec_inc) { + ret = -EINVAL ; + goto error; + }/* calculate default added value: * formula is : * addend = (2^32)/freq_div_ratio; * where, freq_div_ratio = 1e9ns/sec_inc */ + temp =div_u64 (1000000000ULL , sec_inc); temp = (u64)(temp <<32 ); - priv->default_addend =div_u64 (temp, priv->plat->clk_ptp_rate); -stmmac_config_addend (priv, priv->ptpaddr, priv->default_addend); + val =div_u64 (temp, priv->plat->clk_ptp_rate); + + ret =stmmac_config_addend (priv, priv->ptpaddr, val); +if (ret) + goto error; + + priv->sub_second_inc = sec_inc; + priv->default_addend = val; + +return 0 ; +error: +/* Restore previous configuration */ +stmmac_config_hw_tstamping (priv, priv->ptpaddr, priv->systime_flags); +stmmac_config_sub_second_increment (priv, priv->ptpaddr, + priv->plat->clk_ptp_rate, xmac, +NULL ); +return ret; }/**
@@ -854,35 +887,42 @@ static int stmmac_hwtstamp_get(struct net_device *dev,/** * stmmac_init_tstamp_counter - init hardware timestamping counter * @priv: driver private structure -* @systime_flags: timestamping flags * Description: * Initialize hardware counter for packet timestamping. * This is valid as long as the interface is open and not suspended. * Will be rerun after resuming from suspend, case in which the timestamping * flags updated by stmmac_hwtstamp_set() also need to be restored. */ -static int stmmac_init_tstamp_counter (struct stmmac_priv *priv, -u32 systime_flags) +static int stmmac_init_tstamp_counter (struct stmmac_priv *priv) { +u32 default_addend = priv->default_addend;struct timespec64 now; +int ret;if (!priv->plat->clk_ptp_rate) {netdev_err (priv->dev,"Invalid PTP clock rate" );return -EINVAL ; } -stmmac_config_hw_tstamping (priv, priv->ptpaddr, systime_flags); - priv->systime_flags = systime_flags; - -stmmac_update_subsecond_increment (priv); + ret =stmmac_update_subsecond_increment (priv, priv->systime_flags); +if (ret) +return ret;/* initialize system time */ ktime_get_real_ts64 (&now);/* lower 32 bits of tv_sec are safe until y2106 */ -stmmac_init_systime (priv, priv->ptpaddr, (u32 )now.tv_sec, now.tv_nsec); + ret =stmmac_init_systime (priv, priv->ptpaddr, (u32 )now.tv_sec, + now.tv_nsec); +if (ret) + goto error;return 0 ; +error: +/* Restore previous configuration */ +stmmac_restore_subsecond_increment (priv, default_addend); + +return ret; }/**
@@ -905,8 +945,14 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv)return -EOPNOTSUPP ; } - ret =stmmac_init_tstamp_counter (priv,STMMAC_HWTS_ACTIVE | -PTP_TCR_TSCFUPDT ); +/* Reset hw ts configuration */ +memset (&priv->tstamp_config,0 ,sizeof (priv->tstamp_config)); + priv->systime_flags =STMMAC_HWTS_ACTIVE |PTP_TCR_TSCFUPDT ; + priv->tsfupdt_coarse = false; + priv->hwts_tx_en =0 ; + priv->hwts_rx_en =0 ; + + ret =stmmac_init_tstamp_counter (priv);if (ret) {netdev_warn (priv->dev,"PTP init failed\n" );return ret;
@@ -927,10 +973,6 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv)netdev_info (priv->dev,"IEEE 1588-2008 Advanced Timestamp supported\n" ); -memset (&priv->tstamp_config,0 ,sizeof (priv->tstamp_config)); - priv->hwts_tx_en =0 ; - priv->hwts_rx_en =0 ; -if (priv->plat->flags &STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY )stmmac_hwtstamp_correct_latency (priv, priv);
@@ -7718,18 +7760,26 @@ static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id, {struct stmmac_devlink_priv *dl_priv =devlink_priv (dl);struct stmmac_priv *priv = dl_priv->stmmac_priv; +u32 systime_flags = priv->systime_flags; +int ret; - priv->tsfupdt_coarse = ctx->val.vbool; - -if (priv->tsfupdt_coarse) - priv->systime_flags &= ~PTP_TCR_TSCFUPDT ; +if (ctx->val.vbool) + systime_flags &= ~PTP_TCR_TSCFUPDT ;else - priv->systime_flags |=PTP_TCR_TSCFUPDT ; + systime_flags |=PTP_TCR_TSCFUPDT ;/* In Coarse mode, we can use a smaller subsecond increment, let's * reconfigure the systime, subsecond increment and addend. */ -stmmac_update_subsecond_increment (priv); + ret =stmmac_update_subsecond_increment (priv, systime_flags); +if (ret) { +NL_SET_ERR_MSG_MOD (extack, +"failed to reconfigure PTP adjustment" ); +return ret; + } + + priv->tsfupdt_coarse = ctx->val.vbool; + priv->systime_flags = systime_flags;return 0 ; }
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +6 -4
@@ -28,14 +28,15 @@ static int stmmac_adjust_freq(struct ptp_clock_info *ptp, long scaled_ppm) container_of(ptp, struct stmmac_priv, ptp_clock_ops);unsigned long flags;u32 addend; +int ret; addend =adjust_by_scaled_ppm (priv->default_addend, scaled_ppm);write_lock_irqsave (&priv->ptp_lock, flags); -stmmac_config_addend (priv, priv->ptpaddr, addend); + ret =stmmac_config_addend (priv, priv->ptpaddr, addend);write_unlock_irqrestore (&priv->ptp_lock, flags); -return 0 ; +return ret; }/**
@@ -153,12 +154,13 @@ static int stmmac_set_time(struct ptp_clock_info *ptp,struct stmmac_priv *priv = container_of(ptp, struct stmmac_priv, ptp_clock_ops);unsigned long flags; +int ret;write_lock_irqsave (&priv->ptp_lock, flags); -stmmac_init_systime (priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec); + ret =stmmac_init_systime (priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec);write_unlock_irqrestore (&priv->ptp_lock, flags); -return 0 ; +return ret; } static int stmmac_enable(struct ptp_clock_info *ptp,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 203/281 tee: optee: ffa: support shared memory offsets on large-page kernels
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mahantesh Salimath <[email protected]>
[ Upstream commit 3c0d2f1f431c51a11253f28763be723ac782fd7a ]
OP-TEE FF-A memory objects use 4 KiB pages, while the kernel page size may be larger. Consequently, tee_shm->offset can be greater than or equal to FFA_PAGE_SIZE, but OP-TEE rejects such a value in internal_offs.
Do not encode the excess page offset in offs_low/offs_high. Those fields describe the logical memref offset and are copied back into tee_param->shm_offs on return. Folding the page offset into them breaks parameter round trips when a memref is reused. They are also ignored by the OPTEE_RPC_CMD_SHM_ALLOC response path, which uses only global_id and internal_offs to construct the shared-memory mobj.
Instead, start the FF-A descriptor at the 4 KiB page containing the shared buffer, the same approach as optee_fill_pages_list() in the SMC ABI. Store the remaining in-page offset in internal_offs and preserve shm_offs in offs_low/offs_high. This keeps internal_offs within the FF-A page size, maps RPC allocations at the correct address, and preserves normal memref offsets across repeated invocations.
Tested on ARMv8-A with 64 KiB PAGE_SIZE. OP-TEE OS ran as a secure partition under Hafnium (SPMC) over FF-A. Verified registered shared memory with tee_shm->offset >= 4 KiB, memref reuse on the same TEEC_Operation, and RPC OPTEE_RPC_CMD_SHM_ALLOC (xtest regression 6007-6009). optee_hello_world, optee_aes, and xtest regression 1005, 1007, 1008, 4001-4003 and 6001-6003 also passed.
--- drivers/tee/optee/ffa_abi.c | 22 ++++++++++++++++++---- drivers/tee/optee/optee_msg.h | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-)
drivers/tee/optee/ffa_abi.c +18 -4
@@ -198,7 +198,8 @@ static int to_msg_param_ffa_mem(struct optee_msg_param *mp,if (shm) {u64 shm_offs = p->u.memref.shm_offs; - mp->u.fmem.internal_offs = shm->offset; + mp->u.fmem.internal_offs =tee_shm_get_page_offset (shm) & + (FFA_PAGE_SIZE -1 ); mp->u.fmem.offs_low = shm_offs; mp->u.fmem.offs_high = shm_offs >>32 ;
@@ -284,14 +285,26 @@ static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm, .nattrs = 1, };struct sg_table sgt; +size_t ffa_offs;int rc; +if (!num_pages) +return -EINVAL ; + rc =optee_check_mem_type (start, num_pages);if (rc)return rc; - rc =sg_alloc_table_from_pages (&sgt, pages, num_pages,0 , - num_pages *PAGE_SIZE ,GFP_KERNEL ); +/* +* Start the FF-A descriptor at the 4 KiB page containing the shared +* buffer, skipping unused leading 4 KiB pages when PAGE_SIZE is +* larger. Same approach as optee_fill_pages_list() in the SMC ABI. +* This leaves only the offset into that 4 KiB page for internal_offs. +*/ + ffa_offs =round_down (tee_shm_get_page_offset (shm),FFA_PAGE_SIZE ); + rc =sg_alloc_table_from_pages (&sgt, pages, num_pages, ffa_offs, + num_pages *PAGE_SIZE - ffa_offs, +GFP_KERNEL );if (rc)return rc; args.sg = sgt.sgl;
@@ -458,7 +471,8 @@ static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx, .attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT, .u.fmem.size = tee_shm_get_size(shm), .u.fmem.global_id = shm->sec_world_id, - .u.fmem.internal_offs = shm->offset, + .u.fmem.internal_offs = tee_shm_get_page_offset(shm) & + (FFA_PAGE_SIZE - 1), }; arg->ret =TEEC_SUCCESS ;
drivers/tee/optee/optee_msg.h +2 -2
@@ -136,8 +136,8 @@ struct optee_msg_param_rmem {* struct optee_msg_param_fmem - FF-A memory reference parameter * @offs_low: lower bits of offset into shared memory reference * @offs_high: higher bits of offset into shared memory reference -* @internal_offs: internal offset into the first page of shared memory -* reference +* @internal_offs: offset into the first 4 KiB page of the FF-A shared +* memory region * @size: size of the buffer * @global_id: global identifier of the shared memory */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 204/281 bonding: fix the program leak when XDP is replaced
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 184b5ddf82d45d6f75cc83719a2e453392512bd2 ]
We need to release the reference on the old BPF prog, whether we are going to no-prog, or replacing the prog with another.
Spotted by AI while running tests for the propagation series (leaked programs were accumulating). Selftest will come in the net-next series to avoid conflicts.
--- drivers/net/bonding/bond_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/net/bonding/bond_main.c +4 -4
@@ -5738,12 +5738,12 @@ static int bond_xdp_set(struct net_device *dev, struct bpf_prog *prog,bpf_prog_inc (prog); } -if (prog) { +if (prog && !old_prog)static_branch_inc (&bpf_master_redirect_enabled_key); - }else if (old_prog) { -bpf_prog_put (old_prog); +else if (!prog && old_prog)static_branch_dec (&bpf_master_redirect_enabled_key); - } +if (old_prog) +bpf_prog_put (old_prog);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 205/281 ice: Restore Ordered MMIO Writes for Tx Doorbells
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryan Fraschetti <[email protected]>
[ Upstream commit 7a579dec8423bba7bec96fcfcb2e30838b0858ee ]
The DQL accounting state which tracks the number of bytes queued for the NIC to transmit, namely dql->num_queued, is updated by the ICE driver when it invokes __netdev_tx_sent_queue(). Subsequently the driver updates the hardware queue tail allowing the NIC to begin processing the work queue. The queue accounting update should be ordered before the NIC begins transmitting descriptors.
The transmit path currently updates the hardware doorbell using writel_relaxed(), which (on arm64) does not provide the same ordering guarantees between writes to normal memory and writes to MMIO registers that writel() does. This introduces a potential race where the NIC begins transmitting descriptors before the dql->num_queued update is globally visible. If the NIC finishes before the update is observed by dql_completed(), it detects an invalid state where more bytes have been completed than have been queued. When this happens the following BUG_ON is triggered.
BUG_ON(count > num_queued - dql->num_completed);
This has been observed and manifests as the following crash (note that the trace has been trimmed) in an environment with sustained network load that uses an Intel Corporation Ethernet Controller E810-XXV for SFP (rev 02) on an arm64 machine. Replacing writel_relaxed() with writel() in a test kernel eliminated the crash in the user's workload, which previously reproduced the issue reliably.
kernel BUG at lib/dynamic_queue_limits.c:99
Internal error: Oops - BUG: 00000000f2000800 [#1] SMP pc : dql_completed+0x268/0x2a0 lr : ice_clean_tx_irq+0x1d4/0x620 [ice] Call trace: dql_completed+0x268/0x2a0 (P) ice_napi_poll+0x94/0x520 [ice] __napi_poll+0x48/0x3f0 net_rx_action+0x194/0x420
This restores the behaviour prior to commit ccde82e90946 ("ice: add E830 Earliest TxTime First Offload support"), which changed the notification mechanism from writel() to writel_relaxed().
--- drivers/net/ethernet/intel/ice/ice_txrx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/ice/ice_txrx.c +2 -2
@@ -1561,10 +1561,10 @@ ice_tx_map(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first, } } tstamp_ring->next_to_use = j; -writel_relaxed (j, tstamp_ring->tail); +writel (j, tstamp_ring->tail); }else { ring_kick: -writel_relaxed (i, tx_ring->tail); +writel (i, tx_ring->tail); }return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 206/281 nvmet: return Invalid Field for invalid NSIDs feature 82h
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guixin Liu <[email protected]>
[ Upstream commit d99bc5b456fd63be46c8bda20dbf6ede93d2fc11 ]
The Reservation Notification Mask feature (FID 82h) defines its own status code for namespaces that do not support reservations:
"If a Set Features command or a Get Features command attempts to access the Reservation Notification Mask on a namespace that does not support reservations or is invalid, then that command is aborted with status code of Invalid Field in Command."
-- NVMe Base Specification, section 5.2.26.1.33
The Set Features handler routes the namespace lookup through nvmet_req_find_ns(), which returns Invalid Namespace or Format for an unknown NSID. That is the correct status for the generic namespace-scoped admin commands, but not for this feature: a Set Features 82h naming a nonexistent NSID is aborted with Invalid Namespace or Format instead of Invalid Field in Command.
Translate the lookup failure to Invalid Field in Command. The broadcast NSID, the namespace-without-reservations case and everything else keep their current behavior.
--- drivers/nvme/target/pr.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/nvme/target/pr.c +4 -0
@@ -51,6 +51,8 @@ u16 nvmet_set_feat_resv_notif_mask(struct nvmet_req *req, u32 mask)if (nsid !=U32_MAX ) { status =nvmet_req_find_ns (req); +if (status == (NVME_SC_INVALID_NS |NVME_STATUS_DNR )) +return NVME_SC_INVALID_FIELD |NVME_STATUS_DNR ;if (status)return status;if (!req->ns->pr.enable)
@@ -75,6 +77,8 @@ u16 nvmet_get_feat_resv_notif_mask(struct nvmet_req *req)u16 status; status =nvmet_req_find_ns (req); +if (status == (NVME_SC_INVALID_NS |NVME_STATUS_DNR )) +return NVME_SC_INVALID_FIELD |NVME_STATUS_DNR ;if (status)return status;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 207/281 nvmet: use the local P2P device on metadata allocation failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sang-Hoon Choi <[email protected]>
[ Upstream commit 4aa9c0f377a4de5e8fddce76b34ee4529639a1ff ]
nvmet_req_alloc_p2pmem_sgls() assigns req->p2p_dev only after the data and metadata SGL allocations succeed. If the data allocation succeeds but the metadata allocation fails, out_free_sg passes req->p2p_dev to pci_p2pmem_free_sgl() before that assignment has taken place.
Pass the local p2p_dev argument instead, so cleanup uses the same PCI device as the successful data allocation.
--- drivers/nvme/target/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/core.c +1 -1
@@ -1310,7 +1310,7 @@ static int nvmet_req_alloc_p2pmem_sgls(struct pci_dev *p2p_dev,return 0 ; out_free_sg: -pci_p2pmem_free_sgl (req->p2p_dev, req->sg); +pci_p2pmem_free_sgl (p2p_dev, req->sg); out_err:return -ENOMEM ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 208/281 arm64/boot: Dont set PMUv3p9 FGT2 bits without PMUv3
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fuad Tabba <[email protected]>
[ Upstream commit b52ef450139f5632d7e53e6fd5f5ec46c7029dac ]
On a CPU with FGT2 and an IMPLEMENTATION DEFINED PMU (PMUVer 0b1111), __init_el2_fgt2 sets the PMUv3p9 bits in HDFGRTR2_EL2 and HDFGWTR2_EL2, which are RES0 there. The macro only checks that PMUVer is at least PMUv3p9, and 0b1111 passes.
Exclude 0b1111 before the compare, as __init_el2_debug does.
--- arch/arm64/include/asm/el2_setup.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
arch/arm64/include/asm/el2_setup.h +3 -2
@@ -421,8 +421,9 @@mov x2, xzr mrs x1, id_aa64dfr0_el1 ubfx x1, x1, #ID_AA64DFR0_EL1_PMUVer_SHIFT, #4 - cmp x1, #ID_AA64DFR0_EL1_PMUVer_V3P9 - b.lt .Lskip_pmuv3p9_\@ + cmp x1, #ID_AA64DFR0_EL1_PMUVer_IMP_DEF + ccmp x1, #ID_AA64DFR0_EL1_PMUVer_V3P9, #8, ne + b.lt .Lskip_pmuv3p9_\@ // Skip if < PMUv3p9 or IMP_DEF orr x0, x0, #HDFGRTR2_EL2_nPMICNTR_EL0 orr x0, x0, #HDFGRTR2_EL2_nPMICFILTR_EL0
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/281 spi: spi-nxp-fspi: exit stop mode before waiting for DLL lock
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haibo Chen <[email protected]>
[ Upstream commit 94b6df050433875f493fb8298b58d7b0dc32eb9c ]
For 8D-8D-8D (octal DTR) mode the FlexSPI samples read data using MCR0[RXCLKSRC] = 3 (flash-memory-provided read strobe on the DQS pad), which requires the DLL to be locked. Per the i.MX95 Reference Manual (FlexSPI chapter, "DLL configuration for sampling"), when the serial root clock is above 100 MHz the DLL is enabled (DLLxCR[DLLEN] = 1, OVRDEN = 0) and software must then wait for STS2[xREFLOCK] and STS2[xSLVLOCK] to be asserted before starting a read or write. This DLL lock is relative to the serial root clock.
While the module is in stop mode (MCR0[MDIS] = 1) the AHB and serial clocks are gated off internally, so the serial root clock the DLL locks against is not running. nxp_fspi_dll_calibration() both programs DLLxCR and then polls STS2 for lock. When it runs entirely in stop mode the STS2 REF/SLV lock bits never assert (observed as "DLL lock failed" with STS2 = 0).
The MCR0 bit fields (RXCLKSRC) and the serial root clock rate still have to be reconfigured in stop mode, so keep entering stop mode around that. Only move the exit from stop mode (clearing MCR0[MDIS]) ahead of the DLL calibration/override step, so that the serial root clock is running when the DLL is calibrated and STS2 can report lock.
--- drivers/spi/spi-nxp-fspi.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
drivers/spi/spi-nxp-fspi.c +14 -8
@@ -898,10 +898,10 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,dev_dbg (f->dev,"Target device [CS:%x] selected\n" ,spi_get_chipselect (spi,0 ));/* -* Per the FlexSPI reference manual (initialization sequence), MCR0 and -* the DLL control registers should be configured while the module is in -* stop mode (MCR0[MDIS] = 1). Enter stop mode before reconfiguring the -* RX sample clock source and the DLL, then exit stop mode afterwards. +* Per the FlexSPI reference manual (initialization sequence), MCR0 +* should be configured while the module is in stop mode (MCR0[MDIS] = 1). +* Enter stop mode before reconfiguring the RX sample clock source, then +* exit stop mode afterwards. */ reg =fspi_readl (f, f->iobase +FSPI_MCR0 );fspi_writel (f, reg |FSPI_MCR0_MDIS , f->iobase +FSPI_MCR0 );
@@ -936,6 +936,16 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,if (ret)return ret; +/* +* Exit stop mode before running the DLL calibration. The DLL +* reference clock is derived from the module clock domain, which is +* gated while MCR0[MDIS] = 1. Calibrating the DLL in stop mode leaves +* the STS2 lock bits stuck at 0 (the DLL never runs), so clear MDIS +* first and let the module and reference clock run. +*/ + reg =fspi_readl (f, f->iobase +FSPI_MCR0 ); +fspi_writel (f, reg & ~FSPI_MCR0_MDIS , f->iobase +FSPI_MCR0 ); +/* * If clock rate > 100MHz, then switch from DLL override mode to * DLL calibration mode.
@@ -945,10 +955,6 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,else nxp_fspi_dll_override (f); -/* Exit stop mode now that MCR0 and the DLL have been reconfigured. */ - reg =fspi_readl (f, f->iobase +FSPI_MCR0 ); -fspi_writel (f, reg & ~FSPI_MCR0_MDIS , f->iobase +FSPI_MCR0 ); - f->pre_op_rate = op->max_freq; f->selected =spi_get_chipselect (spi,0 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/281 zloop: zero out only the unread tail of short reads
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shin'ichiro Kawasaki <[email protected]>
[ Upstream commit 58304e139cb3fa640da74bc02101f0926d0607e2 ]
zloop_complete_rq() handles a short read from a zone's backing file by zero-filling every bio in the request. This is correct when the entire request is beyond the written part of the zone. When a read request straddles the boundary between written and unwritten data, read_iter() returns a partial byte count and zloop then zeroes the whole request even for the partially read area with valid data.
Such an unexpected zero data read was observed with fio test script t/zbd/test-zbd-support and its test case 69, failing with "bad magic header 0".
Avoid the unexpected zero data read by zeroing out only the unread tail part. When read data size cmd->ret is smaller than the request size, call the new helper function zloop_fill_zero_rq() that walks through each bio_vec and fills zeros from the specified start offset.
--- drivers/block/zloop.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-)
drivers/block/zloop.c +24 -7
@@ -799,6 +799,27 @@ static void zloop_cmd_workfn(struct work_struct *work) current->flags = orig_flags; } +static void zloop_fill_zero_rq (struct request *rq,unsigned int zero_from) +{ +struct req_iterator iter; +struct bio_vec bv; +unsigned int pos =0 ; +unsigned int end, off; + +rq_for_each_segment (bv, rq, iter) { + end = pos + bv.bv_len; +if (end > zero_from) { +if (pos < zero_from) + off = zero_from - pos; +else + off =0 ; +memzero_page (bv.bv_page, bv.bv_offset + off, + bv.bv_len - off); + } + pos = end; + } +} +static void zloop_complete_rq (struct request *rq) {struct zloop_cmd *cmd =blk_mq_rq_to_pdu (rq);
@@ -813,13 +834,9 @@ static void zloop_complete_rq(struct request *rq)pr_err ("Zone %u: failed read sector %llu, %llu sectors\n" , zone_no, cmd->sector, cmd->nr_sectors); -if (cmd->ret >=0 && cmd->ret !=blk_rq_bytes (rq)) { -/* short read */ -struct bio *bio; - -__rq_for_each_bio (bio, rq) -zero_fill_bio (bio); - } +/* Short read. Zero out the unread tail beyond cmd->ret. */ +if (cmd->ret >=0 && cmd->ret !=blk_rq_bytes (rq)) +zloop_fill_zero_rq (rq, cmd->ret);break ;case REQ_OP_WRITE :case REQ_OP_ZONE_APPEND :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 211/281 btrfs: always return -EIOCBQUEUED after btrfs_uring_read_extent_endio
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Xiuwei <[email protected]>
[ Upstream commit 80a8dbb30d2b00b9c671b02bd73f700f6d20100f ]
If all bios finish before btrfs_encoded_read_regular_fill_pages() returns, it calls btrfs_uring_read_extent_endio() and previously returned the I/O status. A negative errno then made btrfs_uring_read_extent() unlock and free while btrfs_uring_read_finished() did the same again.
Return -EIOCBQUEUED so only the deferred path cleans up.
--- fs/btrfs/inode.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
fs/btrfs/inode.c +3 -4
@@ -9622,7 +9622,6 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,struct completion sync_reads;unsigned long i =0 ;struct btrfs_bio *bbio; -int ret;/* * Fast path for synchronous reads which completes in this call, io_uring
@@ -9669,10 +9668,10 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,if (uring_ctx) {if (refcount_dec_and_test (&priv->pending_refs)) { - ret =blk_status_to_errno (READ_ONCE (priv->status)); -btrfs_uring_read_extent_endio (uring_ctx, ret); +int error =blk_status_to_errno (READ_ONCE (priv->status)); + +btrfs_uring_read_extent_endio (uring_ctx, error);kfree (priv); -return ret; }return -EIOCBQUEUED ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/281 btrfs: free iov when btrfs_uring_read_extent() fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Xiuwei <[email protected]>
[ Upstream commit a8ea92830b3e0e163d4e829bdcb17ca83c0f951c ]
After btrfs_uring_read_extent(), the caller always jumped to out_acct. That skips kfree(data->iov), which is only correct for -EIOCBQUEUED where the deferred path owns the iov. On failure, fall through to out_free instead.
--- fs/btrfs/ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/btrfs/ioctl.c +2 -2
@@ -4823,8 +4823,8 @@ static int btrfs_uring_encoded_read(struct io_uring_cmd *cmd, unsigned int issue cached_state, disk_bytenr, disk_io_size, count, data->args.compression, data->iov, cmd); - - goto out_acct; +if (ret == -EIOCBQUEUED ) + goto out_acct; } out_free:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/281 btrfs: unlock inode and extent in caller when io_uring read extent fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Xiuwei <[email protected]>
[ Upstream commit b6e8add5d79d1f6f819a054c833bab452bd7365d ]
btrfs_uring_read_extent() runs only after btrfs_encoded_read() has taken the inode shared lock and the extent lock. On failure it used to unlock in out_fail, and a pages-array allocation failure returned -ENOMEM without unlocking at all.
Unlock in the caller instead on all failure returns, matching the copy_to_user() error path. The deferred -EIOCBQUEUED path still unlocks in btrfs_uring_read_finished().
--- fs/btrfs/ioctl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
fs/btrfs/ioctl.c +3 -4
@@ -4559,7 +4559,7 @@ static void btrfs_uring_read_finished(struct io_tw_req tw_req, io_tw_token_t tw)size_t page_offset;ssize_t ret; -/* The inode lock has already been acquired in btrfs_uring_read_extent. */ +/* The inode lock has already been acquired in btrfs_encoded_read(). */ btrfs_lockdep_inode_acquire (inode, i_rwsem);if (priv->err) {
@@ -4625,7 +4625,6 @@ static int btrfs_uring_read_extent(struct kiocb *iocb, struct iov_iter *iter, struct iovec *iov, struct io_uring_cmd *cmd) { struct btrfs_inode *inode = BTRFS_I(file_inode(iocb->ki_filp)); -struct extent_io_tree *io_tree = &inode->io_tree;struct page **pages =NULL ;struct btrfs_uring_priv *priv =NULL ;unsigned long nr_pages;
@@ -4681,8 +4680,6 @@ static int btrfs_uring_read_extent(struct kiocb *iocb, struct iov_iter *iter,return -EIOCBQUEUED ; out_fail: -btrfs_unlock_extent (io_tree, start, lockend, &cached_state); -btrfs_inode_unlock (inode,BTRFS_ILOCK_SHARED );kfree (priv);for (int i =0 ; i < nr_pages; i++) {if (pages[i])
@@ -4825,6 +4822,8 @@ static int btrfs_uring_encoded_read(struct io_uring_cmd *cmd, unsigned int issue data->iov, cmd);if (ret == -EIOCBQUEUED ) goto out_acct; +btrfs_unlock_extent (io_tree, start, lockend, &cached_state); +btrfs_inode_unlock (inode,BTRFS_ILOCK_SHARED ); } out_free:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/281 btrfs: dont stash io_uring encoded data across -EAGAIN
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Xiuwei <[email protected]>
[ Upstream commit 51562a8cb11628d3a5e105b4c347ec5473e5745f ]
Returning -EAGAIN while leaving btrfs_uring_encoded_data in the cmd PDU leaks if the request is cancelled or the ring exits before reissue. io_uring does not free driver PDU allocations on cleanup.
Write: io_queue_sqe() always issues with IO_URING_F_NONBLOCK first, so return -EAGAIN before allocating and free data on every exit.
Read: free on nowait -EAGAIN too; only -EIOCBQUEUED keeps the allocation for btrfs_uring_read_finished().
--- fs/btrfs/ioctl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
fs/btrfs/ioctl.c +11 -9
@@ -4792,7 +4792,7 @@ static int btrfs_uring_encoded_read(struct io_uring_cmd *cmd, unsigned int issue ret =btrfs_encoded_read (&kiocb, &data->iter, &data->args, &cached_state, &disk_bytenr, &disk_io_size);if (ret == -EAGAIN ) - goto out_acct; + goto out_free;if (ret <0 && ret != -EIOCBQUEUED ) goto out_free;
@@ -4834,8 +4834,10 @@ static int btrfs_uring_encoded_read(struct io_uring_cmd *cmd, unsigned int issueadd_rchar (current, ret);inc_syscr (current); -if (ret != -EIOCBQUEUED && ret != -EAGAIN ) +if (ret != -EIOCBQUEUED ) {kfree (data); + bc->data =NULL ; + }return ret; }
@@ -4864,6 +4866,11 @@ static int btrfs_uring_encoded_write(struct io_uring_cmd *cmd, unsigned int issu goto out_acct; } +if (issue_flags &IO_URING_F_NONBLOCK ) { + ret = -EAGAIN ; + goto out_acct; + } +if (!data) { data =kzalloc_obj (*data,GFP_NOFS );if (!data) {
@@ -4932,11 +4939,6 @@ static int btrfs_uring_encoded_write(struct io_uring_cmd *cmd, unsigned int issu } } -if (issue_flags &IO_URING_F_NONBLOCK ) { - ret = -EAGAIN ; - goto out_acct; - } - pos = data->args.offset; ret =rw_verify_area (WRITE , file, &pos, data->args.len);if (ret <0 )
@@ -4962,8 +4964,8 @@ static int btrfs_uring_encoded_write(struct io_uring_cmd *cmd, unsigned int issuadd_wchar (current, ret);inc_syscw (current); -if (ret != -EAGAIN ) -kfree (data); +kfree (data); + bc->data =NULL ;return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 215/281 btrfs: fix xattr replace when multiple xattrs are packed in the same item
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 858bee1c77857aabcc2327cd32dd8266332de332 ]
If we have a btrfs_dir_item item that packs multiple xattrs and then we replace the value of one of them (with the setxattr(2) family of syscalls) with another value of a different size, we end up not having a fully initialized btrfs_dir_item, resulting in a corruption that the tree checker will detect at extent buffer writeback time.
This is because in btrfs_setxattr() when we find a btrfs_dir_item with multiple xattrs (due to the crc32c hash of their name being the same) we delete one of the xattr items (btrfs_dir_item) and then insert a new one, but the deletion and insertion results in shifting existing data in the leaf and therefore when the new value of a xattr has a different size, the new btrfs_dir_item is placed in a leaf section that was not initialized and we only copy the value's data and set the value's length in the new btrfs_dir_item, without setting the name, the name's length, the key (which must be all zeroes for xattrs), flags (BTRFS_FT_XATTR) and transaction ID.
The following script reproduces the issue:
$ cat test.sh #!/bin/bash
DEV=/dev/sdi
MNT=/mnt/sdi
mkfs.btrfs -f $DEV
mount $DEV $MNT
touch $MNT/testfile
# Add two xattrs that, on btrfs, have the same hash (crc32c) for their
# name and therefore are packed into the same btrfs_dir_item. setfattr -n user.foobar -v 123 $MNT/testfile setfattr -n user.WvG1c1Td -v qwerty $MNT/testfile
# Verify the xattrs are present. echo "xattrs before:" getfattr --absolute-names --dump $MNT/testfile
# Now replace the value of the foobar xattr with a significantly larger
# value. setfattr -n user.foobar -v abcdefghijklmnopqrstuvwxyz $MNT/testfile
# Check the xattrs have the expected values. echo "xattrs after:" getfattr --absolute-names --dump $MNT/testfile
umount $MNT
Running it:
$ ./test.sh (...) xattrs before: # file: /mnt/sdi/testfile user.WvG1c1Td="qwerty" user.foobar="123"
xattrs after:
# file: /mnt/sdi/testfile
user.WvG1c1Td="qwerty"
So the "user.foobar" xattr is missing and there was a transaction abort when unmounting the fs with the following traces in dmesg:
$ dmesg [869800.159271] BTRFS warning (device sdi): access to eb bytenr 30474240 len 16384 out of range start 16015 len 25964 [869800.159293] ------------[ cut here ]------------ [869800.159296] WARNING: fs/btrfs/extent_io.c:4408 at report_eb_range+0x44/0x60 [btrfs], CPU#8: getfattr/2605179 [869800.168961] Modules linked in: btrfs dm_thin_pool (...) [869800.190527] CPU: 8 UID: 0 PID: 2605179 Comm: getfattr Tainted: G W 7.3.0-rc3-btrfs-next-244+ #1 PREEMPT(full) [869800.193726] Tainted: [W]=WARN [869800.194502] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 [869800.197576] RIP: 0010:report_eb_range+0x44/0x60 [btrfs] [869800.198985] Code: 48 8b 7b 18 (...) [869800.203531] RSP: 0018:ffffce4541937d58 EFLAGS: 00010246 [869800.204609] RAX: 0000000000000000 RBX: ffff8dde054b8738 RCX: 0000000000000000 [869800.206137] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffffffffc04c92a0 [869800.207645] RBP: 0000000000003e8f R08: 0000000000000000 R09: 3fffffffffefffff [869800.226175] R10: ffffce4541937a88 R11: 0000000000000003 R12: 000000000000656c [869800.227594] R13: ffff8dde14be800f R14: 000000000000656c R15: 0000000000000069 [869800.229097] FS: 00007f59023bb780(0000) GS:ffff8de5788ed000(0000) knlGS:0000000000000000 [869800.231137] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [869800.232321] CR2: 0000558dc45e6a78 CR3: 0000000765b16004 CR4: 0000000000370ef0 [869800.233783] Call Trace: [869800.234330] <TASK> [869800.234791] read_extent_buffer+0x4d/0x100 [btrfs] [869800.235898] btrfs_listxattr+0x199/0x240 [btrfs] [869800.236912] vfs_listxattr+0x51/0xa0 [869800.237683] listxattr+0x7e/0x100 [869800.238398] path_listxattrat+0x9e/0x190 [869800.239117] do_syscall_64+0x89/0x470 [869800.239877] entry_SYSCALL_64_after_hwframe+0x76/0x7e [869800.240914] RIP: 0033:0x7f59024cdcb7 [869800.241686] Code: f0 ff ff 73 (...) [869800.245353] RSP: 002b:00007ffd056dcdb8 EFLAGS: 00000246 ORIG_RAX: 00000000000000c2 [869800.246892] RAX: ffffffffffffffda RBX: 00007ffd056df2e2 RCX: 00007f59024cdcb7 [869800.248853] RDX: 0000000000006600 RSI: 0000558dc45e0470 RDI: 00007ffd056df2e2 [869800.250514] RBP: 00007ffd056df2e2 R08: 0000000000006600 R09: 0000000000006600 [869800.252295] R10: 0000000000000004 R11: 0000000000000246 R12: 00000000ffffff9c [869800.254105] R13: 0000558dc45e0470 R14: 0000000000006600 R15: 0000000000000000 [869800.255931] </TASK> [869800.256529] ---[ end trace 0000000000000000 ]--- [869800.260071] page: refcount:2 mapcount:0 mapping:000000007ccfc77f index:0x1d10 pfn:0x608c69 [869800.260075] memcg:ffff8dde00344d40 [869800.260076] aops:btree_aops [btrfs] ino:1 [869800.260151] flags: 0x17fffc00000402a(uptodate|lru|private|writeback|node=0|zone=2|lastcpupid=0x1ffff) [869800.260154] raw: 017fffc00000402a fffff4adc773d4c8 fffff4adc48f3d88 ffff8de36504ba30 [869800.260155] raw: 0000000000001d10 ffff8dde054b8738 00000002ffffffff ffff8dde00344d40 [869800.260156] page dumped because: eb page dump [869800.260157] BTRFS critical (device sdi): corrupt leaf: root=5 block=30474240 slot=5 ino=257, invalid location key type, have 46, expect 132 or 1 [869800.260161] BTRFS info (device sdi): leaf 30474240 gen 9 total ptrs 6 free space 15629 owner 5 [869800.260163] BTRFS info (device sdi): refs 3 lock_owner 0 current 2550294 [869800.260164] item 0 key (256 INODE_ITEM 0) itemoff 16123 itemsize 160 [869800.260165] inode generation 3 transid 0 size 0 nbytes 16384 [869800.260166] block group 0 mode 40755 links 1 uid 0 gid 0 [869800.260167] rdev 0 sequence 0 flags 0x0 [869800.260168] atime 1790340637.0 [869800.260169] ctime 1790340637.0 [869800.260169] mtime 1790340637.0 [869800.260170] otime 1790340637.0 [869800.260170] item 1 key (256 INODE_REF 256) itemoff 16111 itemsize 12 [869800.260172] index 0 name_len 2 [869800.260172] item 2 key (256 DIR_ITEM 982728850) itemoff 16073 itemsize 38 [869800.260173] location key (257 1 0) type 1 [869800.260174] transid 9 data_len 0 name_len 8 [869800.260175] item 3 key (257 INODE_ITEM 0) itemoff 15913 itemsize 160 [869800.260176] inode generation 9 transid 9 size 0 nbytes 0 [869800.260177] block group 0 mode 100664 links 1 uid 0 gid 0 [869800.260177] rdev 0 sequence 0 flags 0x0 [869800.260178] atime 1790340638.38778502 [869800.260179] ctime 1790340638.38778502 [869800.260179] mtime 1790340638.38778502 [869800.260180] otime 1790340638.38778502 [869800.260180] item 4 key (257 INODE_REF 256) itemoff 15895 itemsize 18 [869800.260181] index 2 name_len 8 [869800.260182] item 5 key (257 XATTR_ITEM 751495445) itemoff 15779 itemsize 116 [869800.260183] location key (0 0 0) type 8 [869800.266052] transid 9 data_len 6 name_len 13 [869800.266053] location key (8243121639454149888 46 7229457603934778967) type 0 [869800.266055] transid 113 data_len 26 name_len 0 [869800.266056] location key (8608196880778817904 120 162425) type 9 [869800.266057] transid 8391162079612502016 data_len 26982 name_len 25964 [869800.266058] BTRFS error (device sdi): block=30474240 write time tree block corruption detected [869800.266091] ------------[ cut here ]------------ [869800.266092] WARNING: fs/btrfs/disk-io.c:336 at btree_csum_one_bio+0x20b/0x220 [btrfs], CPU#7: kworker/u50:7/2550294 [869800.268392] Modules linked in: btrfs dm_thin_pool (...) [869800.365851] CPU: 7 UID: 0 PID: 2550294 Comm: kworker/u50:7 Tainted: G W 7.3.0-rc3-btrfs-next-244+ #1 PREEMPT(full) [869800.368937] Tainted: [W]=WARN [869800.369834] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 [869800.372778] Workqueue: writeback wb_workfn (flush-btrfs-3821) [869800.374314] RIP: 0010:btree_csum_one_bio+0x20b/0x220 [btrfs] [869800.375915] Code: 89 44 24 04 (...) [869800.380639] RSP: 0018:ffffce4548e3f7d0 EFLAGS: 00010246 [869800.382008] RAX: 0000000000000000 RBX: ffff8dde054b8738 RCX: 0000000000000000 [869800.383850] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8de091c2ddc0 [869800.385710] RBP: ffff8dde196a2000 R08: 0000000000000000 R09: 3fffffffffefffff [869800.387388] R10: ffffce4548e3f500 R11: 0000000000000003 R12: ffffce4548e3f7d8 [869800.388973] R13: ffff8dde196a2000 R14: ffff8de36504b750 R15: ffff8dde4c497b00 [869800.390414] FS: 0000000000000000(0000) GS:ffff8de5788ad000(0000) knlGS:0000000000000000 [869800.392002] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [869800.393160] CR2: 000055cd6e92ad5c CR3: 00000007cb264001 CR4: 0000000000370ef0 [869800.394595] Call Trace: [869800.395113] <TASK> [869800.395570] btrfs_submit_bbio+0x872/0x890 [btrfs] [869800.397284] write_meta_extent_buffer+0x70/0x80 [btrfs] [869800.398940] btree_writepages+0x141/0x4f0 [btrfs] [869800.400426] ? get_random_u32+0x8a/0xf0 [869800.401417] ? build_slab_freelist+0x47/0x130 [869800.402574] ? preempt_count_add+0x6b/0xa0 [869800.403633] ? _raw_spin_lock_irqsave+0x23/0x50 [869800.404807] ? _raw_spin_unlock_irqrestore+0x22/0x40 [869800.406085] ? alloc_from_new_slab+0x18f/0x330 [869800.407223] do_writepages+0xc6/0x160 [869800.408191] ? refill_objects+0xd8/0x300 [869800.409211] __writeback_single_inode+0x42/0x350 [869800.410408] writeback_sb_inodes+0x231/0x560 [869800.411511] wb_writeback+0x8a/0x300 [869800.412440] wb_workfn+0xbf/0x460 [869800.413291] ? _raw_spin_unlock+0x14/0x30 [869800.414328] ? finish_task_switch.isra.0+0xb9/0x380 [869800.415105] process_one_work+0x1d1/0x3d0 [869800.416633] worker_thread+0x1c4/0x330 [869800.417467] ? __pfx_worker_thread+0x10/0x10 [869800.418452] kthread+0xfc/0x130 [869800.419257] ? __pfx_kthread+0x10/0x10 [869800.420089] ret_from_fork+0x1f7/0x2c0 [869800.420863] ? __pfx_kthread+0x10/0x10 [869800.421654] ret_from_fork_asm+0x1a/0x30 [869800.422484] </TASK> [869800.422953] ---[ end trace 0000000000000000 ]--- [869800.424005] BTRFS error (device sdi state A): Transaction 9 aborted (-EIO) [869800.424010] BTRFS: error (device sdi state A) in __btrfs_run_delayed_items:1162: errno=-5 IO failure [869800.424011] BTRFS info (device sdi state EA): forced readonly [869800.424013] BTRFS warning (device sdi state EA): Skipping commit of aborted transaction. [869800.424014] BTRFS: error (device sdi state EA) in cleanup_transaction:2076: errno=-5 IO failure
Fix this by always setting all fields in the new btrfs_dir_item when we replace an existing xattr.
--- fs/btrfs/xattr.c | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/btrfs/xattr.c +9 -0
@@ -161,6 +161,7 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode,const u16 old_data_len =btrfs_dir_data_len (leaf, di);const u32 item_size =btrfs_item_size (leaf, slot);const u32 data_size =sizeof (*di) + name_len + size; +unsigned long name_ptr;unsigned long data_ptr;char *ptr;
@@ -189,8 +190,16 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, ptr =btrfs_item_ptr (leaf, slot, char); ptr +=btrfs_item_size (leaf, slot) - data_size; di = (struct btrfs_dir_item *)ptr; +memzero_extent_buffer (leaf, (unsigned long )ptr + + offsetof(struct btrfs_dir_item , location), +sizeof (struct btrfs_disk_key )); +btrfs_set_dir_flags (leaf, di,BTRFS_FT_XATTR ); +btrfs_set_dir_transid (leaf, di, trans->transid); +btrfs_set_dir_name_len (leaf, di, name_len);btrfs_set_dir_data_len (leaf, di, size); + name_ptr = (unsigned long )(di +1 ); data_ptr = ((unsigned long )(di +1 )) + name_len; +write_extent_buffer (leaf, name, name_ptr, name_len);write_extent_buffer (leaf, value, data_ptr, size); }else {/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 216/281 btrfs: fix lost error return value in btrfs_listxattr()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 41c8899d59898f6fa51ceebd2b6e55257191a8ee ]
If the input buffer does not have enough space to store the current xattr, we set 'iter_ret' to -ERANGE and then do "break", but that only exits the while loop over the xattrs in the current btrfs_dir_item, and then we continue the btrfs_for_each_slot() iteration, which overwrites the value of 'iter_ret' causing us to lose the error return value and proceed as if the buffer has enough space.
Fix this by returning -ERANGE directly (the path is automatically freed) instead of breaking from the while loop.
--- fs/btrfs/xattr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
fs/btrfs/xattr.c +2 -4
@@ -329,10 +329,8 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)if (!size) goto next; -if (!buffer || (name_len +1 ) > size_left) { - iter_ret = -ERANGE ; -break ; - } +if (!buffer || (name_len +1 ) > size_left) +return -ERANGE ;read_extent_buffer (leaf, buffer, name_ptr, name_len); buffer[name_len] ='\0' ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 217/281 drm/xe/mmio: Fix xe_mmio_wait32() to honor delay/sleep maximums
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alan Previn <[email protected]>
[ Upstream commit e7ed652b1312ffcb9ea2153946615856b5097c78 ]
Check for overflow in udelay use in __xe_mmio_wait32 and avoid growing delays becoming intollerably large by capping the in-loop wait time.
v7: - Change assisted-by to "LLM" (Rodrigo).
- Move the max-wait macro to outside the function (Rodrigo).
v6: - Fix commit message to remove items dropped in v5. Fix the
use of MAX_UDELAY_MS for microsecond unit. (Shasiko review).
v5: - Simplify: don't introduce additional redesign and
use min_t with a backoff to cap wait. (Rodrigo).
v4: - Simplify using poll_timeout_us and poll_timeout_us_atomic and
update all callers to never use a param for atomic (Jani)
v3: - Use div64_s64 kernel API for 32-bit vs 64-bit division.
v2: - Fixed checkpatch failure.
- Added helper for 64-bit DIV_ROUND_UP on 32-bit CPU (Shasiko review). Fixed bug max range in usleep_range(Shasiko review)
--- drivers/gpu/drm/xe/xe_mmio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_mmio.c +3 -2
@@ -288,6 +288,7 @@ u64 xe_mmio_read64_2x32(struct xe_mmio *mmio, struct xe_reg reg)return (u64 )udw <<32 | ldw; } +#define __XE_MMIO_WAIT_MAX_BACKOFF_100MS (100 * USEC_PER_MSEC)static int __xe_mmio_wait32 (struct xe_mmio *mmio,struct xe_reg reg,u32 mask,u32 val,u32 timeout_us,u32 *out_val,bool atomic,bool expect_match) {
@@ -318,10 +319,10 @@ static int __xe_mmio_wait32(struct xe_mmio *mmio, struct xe_reg reg, u32 mask, u wait =ktime_us_delta (end, cur);if (atomic) -udelay (wait); +udelay (min_t (s64, wait, (MAX_UDELAY_MS *1000 )));else usleep_range (wait, wait <<1 ); - wait <<=1 ; + wait =min_t (s64, wait <<1 , __XE_MMIO_WAIT_MAX_BACKOFF_100MS); }if (ret !=0 ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 218/281 drm/xe: Do not emit Wa_16010904313 twice
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tvrtko Ursulin <[email protected]>
[ Upstream commit f7fc61e66041b1f8300cad87005ba84604bfefd9 ]
During development of commit e8372edec948 ("drm/xe/xelp: Implement Wa_16010904313")
one of the interim patch versions contained an incorrect rebase on top of some upstream changes, which caused Wa_16010904313 to be emitted both from the indirect context and from the workaround batch buffer.
As this workaround needs to be emitted from the indirect context for rcs and ccs, but from the workaround batch buffer for bcs, vcs and vecs, we need to pass the location as an input parameter to setup_timestamp_wa() so it can decide whether to emit itself or not.
--- drivers/gpu/drm/xe/xe_lrc.c | 38 ++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-)
drivers/gpu/drm/xe/xe_lrc.c +25 -13
@@ -1099,8 +1099,7 @@ static void xe_lrc_finish(struct xe_lrc *lrc)#define CONTEXT_ACTIVE XE_LRC_CTX_TIMESTAMP_ACTIVEstatic ssize_t setup_utilization_wa (struct xe_lrc *lrc,struct xe_hw_engine *hwe, -u32 *batch, -size_t max_len) +u32 *batch,size_t max_len,bool indirect) {u32 *cmd = batch;
@@ -1131,19 +1130,25 @@ static ssize_t setup_utilization_wa(struct xe_lrc *lrc, }static ssize_t setup_timestamp_wa (struct xe_lrc *lrc,struct xe_hw_engine *hwe, -u32 *batch,size_t max_len) +u32 *batch,size_t max_len,bool indirect) {const u32 ts_addr =__xe_lrc_ctx_timestamp_ggtt_addr (lrc);u32 *cmd = batch; -if (!XE_GT_WA (lrc->gt,16010904313 ) || - !(hwe->class ==XE_ENGINE_CLASS_RENDER || - hwe->class ==XE_ENGINE_CLASS_COMPUTE || - hwe->class ==XE_ENGINE_CLASS_COPY || +if (!XE_GT_WA (lrc->gt,16010904313 )) +return 0 ; + +if (!indirect && + !(hwe->class ==XE_ENGINE_CLASS_COPY || hwe->class ==XE_ENGINE_CLASS_VIDEO_DECODE || hwe->class ==XE_ENGINE_CLASS_VIDEO_ENHANCE ))return 0 ; +if (indirect && + !(hwe->class ==XE_ENGINE_CLASS_RENDER || + hwe->class ==XE_ENGINE_CLASS_COMPUTE )) +return 0 ; +if (xe_gt_WARN_ON (lrc->gt, max_len <12 ))return -ENOSPC ;
@@ -1169,7 +1174,8 @@ static ssize_t setup_timestamp_wa(struct xe_lrc *lrc, struct xe_hw_engine *hwe,static ssize_t setup_configfs_post_ctx_restore_bb (struct xe_lrc *lrc,struct xe_hw_engine *hwe, -u32 *batch,size_t max_len) +u32 *batch,size_t max_len, +bool indirect) {struct xe_device *xe =gt_to_xe (lrc->gt);const u32 *user_batch;
@@ -1198,7 +1204,8 @@ static ssize_t setup_configfs_post_ctx_restore_bb(struct xe_lrc *lrc,static ssize_t setup_configfs_mid_ctx_restore_bb (struct xe_lrc *lrc,struct xe_hw_engine *hwe, -u32 *batch,size_t max_len) +u32 *batch,size_t max_len, +bool indirect) {struct xe_device *xe =gt_to_xe (lrc->gt);const u32 *user_batch;
@@ -1227,7 +1234,8 @@ static ssize_t setup_configfs_mid_ctx_restore_bb(struct xe_lrc *lrc,static ssize_t setup_invalidate_state_cache_wa (struct xe_lrc *lrc,struct xe_hw_engine *hwe, -u32 *batch,size_t max_len) +u32 *batch,size_t max_len, +bool indirect) {u32 *cmd = batch;
@@ -1247,7 +1255,8 @@ static ssize_t setup_invalidate_state_cache_wa(struct xe_lrc *lrc,static ssize_t setup_invalidate_auxccs_wa (struct xe_lrc *lrc,struct xe_hw_engine *hwe, -u32 *batch,size_t max_len) +u32 *batch,size_t max_len, +bool indirect) {struct xe_gt *gt = lrc->gt; u32 *(*emit)(struct xe_gt *gt, u32 *cmd) =
@@ -1264,7 +1273,7 @@ static ssize_t setup_invalidate_auxccs_wa(struct xe_lrc *lrc,struct bo_setup {ssize_t (*setup)(struct xe_lrc *lrc,struct xe_hw_engine *hwe, -u32 *batch,size_t max_size); +u32 *batch,size_t max_size,bool indirect); };struct bo_setup_state {
@@ -1273,6 +1282,7 @@ struct bo_setup_state {struct xe_hw_engine *hwe;size_t max_size;size_t reserve_dw; +bool indirect;unsigned int offset;const struct bo_setup *funcs;unsigned int num_funcs;
@@ -1298,7 +1308,8 @@ static int setup_bo(struct bo_setup_state *state)for (size_t i =0 ; i < state->num_funcs; i++) {ssize_t len = state->funcs[i].setup (state->lrc, state->hwe, - state->ptr, remain); + state->ptr, remain, + state->indirect); remain -= len;
@@ -1404,6 +1415,7 @@ setup_indirect_ctx(struct xe_lrc *lrc, struct xe_hw_engine *hwe)struct bo_setup_state state = { .lrc = lrc, .hwe = hwe, + .indirect = true, .max_size = (63 *64 )/* max 63 cachelines */ , .buffer =NULL , .offset =__xe_lrc_indirect_ctx_offset (lrc),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 219/281 net: make dev_xdp_sb_prog_count() see programs attached through a link
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit d333c8bca6dc3c5e978f6698d39dd799f3b0dc8d ]
ethtool refuses to enable tcp-data-split on a device running a single-buffer XDP program (which can't deal with the payload landing in a separate buffer). dev_xdp_sb_prog_count() only looks at xdp_state[].prog though, ignoring bpf_link integration. Get the prog pointer from dev_xdp_prog(), which will consult both.
bpf_xdp_link_update() needs a bit of a touch-up, too, since ethtool calls dev_xdp_sb_prog_count() with just the instance lock (for ops-locked devices) - we now have to make sure the link updates happen under that lock.
Spotted by AI while reviewing the XDP propagation series, verified and tested on netdevsim with a C test along these lines:
link = bpf_program__attach_xdp(prog, ifindex);
system("ethtool -G eth0 tcp-data-split on")--- net/core/dev.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
net/core/dev.c +7 -4
@@ -10356,13 +10356,15 @@ EXPORT_SYMBOL_GPL(dev_xdp_prog_count);u8 dev_xdp_sb_prog_count (struct net_device *dev) { +struct bpf_prog *prog;u8 count =0 ;int i; -for (i =0 ; i < __MAX_XDP_MODE; i++) -if (dev->xdp_state[i].prog && - !dev->xdp_state[i].prog->aux->xdp_has_frags) +for (i =0 ; i < __MAX_XDP_MODE; i++) { + prog =dev_xdp_prog (dev, i); +if (prog && !prog->aux->xdp_has_frags) count++; + }return count; }
@@ -10742,11 +10744,12 @@ static int bpf_xdp_link_update(struct bpf_link *link, struct bpf_prog *new_prog, bpf_op =dev_xdp_bpf_op (xdp_link->dev, mode); err =dev_xdp_install (xdp_link->dev, mode, bpf_op,NULL , xdp_link->flags, new_prog); +if (!err) + old_prog =xchg (&link->prog, new_prog);netdev_unlock_ops (xdp_link->dev);if (err) goto out_unlock; - old_prog =xchg (&link->prog, new_prog);bpf_prog_put (old_prog); out_unlock:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 220/281 net/mlx5: Lag, split aggregate speed into oper and max helpers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Or Har-Toov <[email protected]>
[ Upstream commit fe4114221364899ab33c3f9574da6b108d3fa79e ]
mlx5_lag_sum_devices_speed computes the LAG aggregate by summing oper speeds across all ports. This has two bugs.
First, it relies on the assumption that a port whose carrier is down will report an oper speed of zero and therefore not contribute to the sum. This assumption does not always hold: when the link partner disconnects the port transitions to DOWN state but firmware may still report a non-zero oper speed, causing the aggregate to include a port that is not actively carrying traffic.
Second, in active-backup mode only one port transmits at a time, so the aggregate should reflect a single port speed rather than the sum of all ports.
Fix this by splitting mlx5_lag_sum_devices_speed into two helpers. mlx5_lag_get_devices_oper_speed reflects the speed currently available:
it queries the vport state of each port and skips any port that is not UP, rather than relying on oper speed being zero. mlx5_lag_get_devices_max_speed is state-independent and returns the maximum achievable speed used as a fallback when speed is 0; for active-backup it takes the maximum single-port speed instead of the sum.
The bugs were discovered during integration and reproduced on a back-to-back ConnectX-8 setup.
The first bug (stale oper speed when link goes down) was observed: after the link partner disconnected, the aggregate TX speed reported to vports remained full speed because firmware still returned the last oper speed, causing vports to be rate-limited to the old aggregate speed.
The second bug (active-backup summing all port speeds instead of the active port's speed) was also triggered: in active-backup mode the computed aggregate was double the expected value since both port speeds were summed even though only one port was active.
--- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 79 +++++++++++++------ 1 file changed, 57 insertions(+), 22 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +57 -22
@@ -1432,30 +1432,40 @@ static bool mlx5_lag_should_disable_lag(struct mlx5_lag *ldev, bool do_bond) }#ifdef CONFIG_MLX5_ESWITCH -static int -mlx5_lag_sum_devices_speed (struct mlx5_lag *ldev,u32 *sum_speed, -int (*get_speed)(struct mlx5_core_dev *,u32 *)) +static int mlx5_lag_get_devices_oper_speed (struct mlx5_lag *ldev, +u32 *sum_speed) { -struct mlx5_core_dev *pf_mdev; -struct lag_func *pf;int pf_idx; -u32 speed; -int ret; *sum_speed =0 ;mlx5_ldev_for_each (pf_idx,0 , ldev) { +u8 opmod =MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT ; +struct mlx5_core_dev *pf_mdev; +struct lag_func *pf; +u32 speed; +u8 state; +int ret; + pf =mlx5_lag_pf (ldev, pf_idx);if (!pf)continue ; pf_mdev = pf->dev;if (!pf_mdev)continue ; + ret =mlx5_query_vport_max_tx_speed (pf_mdev, opmod,0 ,0 , + &speed, &state); +if (ret) { +mlx5_core_dbg (pf_mdev,"State query failed (err=%d)\n" , + ret); +return ret; + } +if (state !=VPORT_STATE_UP ) +continue ; - ret =get_speed (pf_mdev, &speed); + ret =mlx5_port_oper_linkspeed (pf_mdev, &speed);if (ret) {mlx5_core_dbg (pf_mdev, -"Failed to get device speed using %ps. Device %s speed is not available (err=%d)\n" , - get_speed,dev_name (pf_mdev->device), +"Failed to get oper speed (err=%d)\n" , ret);return ret; }
@@ -1466,17 +1476,42 @@ mlx5_lag_sum_devices_speed(struct mlx5_lag *ldev, u32 *sum_speed,return 0 ; } -static int mlx5_lag_sum_devices_max_speed (struct mlx5_lag *ldev,u32 *max_speed) +static int mlx5_lag_get_devices_max_speed (struct mlx5_lag *ldev,u32 *max_speed) { -return mlx5_lag_sum_devices_speed (ldev, max_speed, - mlx5_port_max_linkspeed); -} +bool take_max; +int pf_idx; -static int mlx5_lag_sum_devices_oper_speed (struct mlx5_lag *ldev, -u32 *oper_speed) -{ -return mlx5_lag_sum_devices_speed (ldev, oper_speed, - mlx5_port_oper_linkspeed); + take_max = ldev->tracker.tx_type ==NETDEV_LAG_TX_TYPE_ACTIVEBACKUP ; +if (ldev->mode ==MLX5_LAG_MODE_MPESW ) + take_max = false; + + *max_speed =0 ; +mlx5_ldev_for_each (pf_idx,0 , ldev) { +struct mlx5_core_dev *pf_mdev; +struct lag_func *pf; +u32 speed; +int ret; + + pf =mlx5_lag_pf (ldev, pf_idx); +if (!pf) +continue ; + pf_mdev = pf->dev; +if (!pf_mdev) +continue ; + + ret =mlx5_port_max_linkspeed (pf_mdev, &speed); +if (ret) { +mlx5_core_dbg (pf_mdev, +"Failed to get max speed (err=%d)\n" , + ret); +return ret; + } + + *max_speed = take_max ? +max (*max_speed, speed) : *max_speed + speed; + } + +return 0 ; } static void mlx5_lag_modify_device_vports_speed(struct mlx5_core_dev *mdev,
@@ -1525,7 +1560,7 @@ void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)int pf_idx;if (ldev->mode ==MLX5_LAG_MODE_MPESW ) { -if (mlx5_lag_sum_devices_oper_speed (ldev, &speed)) +if (mlx5_lag_get_devices_oper_speed (ldev, &speed))return ; }else { speed = ldev->tracker.bond_speed_mbps;
@@ -1533,8 +1568,8 @@ void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)return ; } -/* If speed is not set, use the sum of max speeds of all PFs */ -if (!speed &&mlx5_lag_sum_devices_max_speed (ldev, &speed)) +/* If speed is not set, fall back to the max achievable speed */ +if (!speed &&mlx5_lag_get_devices_max_speed (ldev, &speed))return ; speed = speed /MLX5_MAX_TX_SPEED_UNIT ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 221/281 net: stmmac: Do not advertise S-VLAN stripping when it is disabled
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ovidiu Panait <[email protected]>
[ Upstream commit c63ce7b9c7e7b68267899e4a0fc14b35d2c1dfcc ]
C-VLAN and S-VLAN tag stripping are both controlled by the EVLS bit, so disabling rx-vlan-offload also disables S-VLAN tag stripping. However, rx-vlan-stag-hw-parse keeps being advertised as enabled:
root@rzv2h-evk:~# ethtool -K end1 rx-vlan-offload off root@rzv2h-evk:~# ethtool -k end1 | grep -i vlan rx-vlan-offload: off tx-vlan-offload: off [fixed] rx-vlan-filter: on [fixed] vlan-challenged: off [fixed] tx-vlan-stag-hw-insert: off [fixed] rx-vlan-stag-hw-parse: on [fixed] rx-vlan-stag-filter: on [fixed]
Fix this inconsistency by making NETIF_F_HW_VLAN_STAG_RX follow NETIF_F_HW_VLAN_CTAG_RX.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +7 -0
@@ -6264,6 +6264,13 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,if (priv->plat->bugged_jumbo && (dev->mtu >ETH_DATA_LEN )) features &= ~NETIF_F_CSUM_MASK ; +if (dwmac_is_xmac (priv->plat->core_type)) { +if (features &NETIF_F_HW_VLAN_CTAG_RX ) + features |=NETIF_F_HW_VLAN_STAG_RX ; +else + features &= ~NETIF_F_HW_VLAN_STAG_RX ; + } +return features; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 222/281 net: stmmac: Disable S-Tag processing on dwmac4
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ovidiu Panait <[email protected]>
[ Upstream commit da17990421d79f1bfb4142f9d767fd33daecc3d8 ]
Currently, hardware VLAN stripping is broken for 802.1ad tags. vlan_rx_hw() hardcodes ETH_P_8021Q when putting the hardware tag into the skb, rather than using the actual protocol from the packet. Because of this, packets that contain a 802.1ad outer tag are incorrectly passed up the stack as having an 802.1Q tag. This causes QinQ ping between two hosts to fail.
vlan_rx_hw() is shared by dwxgmac2 and dwmac4: on dwxgmac2 the tag type is available in the RDES3 write-back descriptor (the ET_LT field), so the outer tag type can be determined based on that info. However, dwmac4 doesn't seem to provide the tag type. The Length/Type field in RDES3 only indicates whether the packet is single or double-tagged, not which tag type was stripped.
Since dwmac4 cannot report the stripped tag type, it cannot support hardware S-Tag stripping correctly. Therefore, restrict the NETIF_F_HW_VLAN_STAG_RX and NETIF_F_HW_VLAN_STAG_FILTER advertisement to dwxgmac2 only.
With this, 802.1ad tags are left in place and handled by the software VLAN path.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +7 -3
@@ -6264,7 +6264,7 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,if (priv->plat->bugged_jumbo && (dev->mtu >ETH_DATA_LEN )) features &= ~NETIF_F_CSUM_MASK ; -if (dwmac_is_xmac (priv->plat->core_type)) { +if (priv->plat->core_type ==DWMAC_CORE_XGMAC ) {if (features &NETIF_F_HW_VLAN_CTAG_RX ) features |=NETIF_F_HW_VLAN_STAG_RX ;else
@@ -8073,14 +8073,18 @@ static int __stmmac_dvr_probe(struct device *device, ndev->watchdog_timeo =msecs_to_jiffies (watchdog);#ifdef STMMAC_VLAN_TAG_USED /* Both mac100 and gmac support receive VLAN tag detection */ - ndev->features |=NETIF_F_HW_VLAN_CTAG_RX |NETIF_F_HW_VLAN_STAG_RX ; + ndev->features |=NETIF_F_HW_VLAN_CTAG_RX ; +if (priv->plat->core_type ==DWMAC_CORE_XGMAC ) + ndev->features |=NETIF_F_HW_VLAN_STAG_RX ; +if (dwmac_is_xmac (priv->plat->core_type)) { ndev->hw_features |=NETIF_F_HW_VLAN_CTAG_RX ; priv->hw->hw_vlan_en = true; }if (priv->dma_cap.vlhash) { ndev->features |=NETIF_F_HW_VLAN_CTAG_FILTER ; - ndev->features |=NETIF_F_HW_VLAN_STAG_FILTER ; +if (priv->plat->core_type ==DWMAC_CORE_XGMAC ) + ndev->features |=NETIF_F_HW_VLAN_STAG_FILTER ; }if (priv->dma_cap.vlins) ndev->features |=NETIF_F_HW_VLAN_CTAG_TX ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 223/281 net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leto Liu (刘涛) <[email protected]>
[ Upstream commit 7e170da2edec9fb0717543571cc14b0aabee2b46 ]
CPU accesses QDMA via the bus. When multiple modules are using the bus simultaneously, CPU access to QDMA may encounter bus timeouts and fails, resulting in QDMA configuration failures and potentially causing packet transmission issues. In order to mitigate the issue, introduce a retry mechanism to airoha_qdma_set_trtcm_param routine in order to ensure the configuration is correctly applied to the hardware.
It's enough to try a second time for the TRTCM config to be actually applied to make sure we are not in the middle of bucket handling as it does operate on fixed time slot internally.
--- drivers/net/ethernet/airoha/airoha_eth.c | 32 +++++++++++++++++++----- drivers/net/ethernet/airoha/airoha_eth.h | 2 ++ 2 files changed, 28 insertions(+), 6 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +26 -6
@@ -2716,14 +2716,34 @@ static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel,FIELD_PREP (TRTCM_METER_GROUP_MASK , group) |FIELD_PREP (TRTCM_PARAM_INDEX_MASK , idx) |FIELD_PREP (TRTCM_PARAM_RATE_TYPE_MASK , mode); +int i; -airoha_qdma_wr (qdma,REG_TRTCM_DATA_LOW (addr), val); -airoha_qdma_wr (qdma,REG_TRTCM_CFG_PARAM (addr), config); +/* +* HW might be in the middle of bucket handling and might +* require an additional try to actually apply the new TRTCM +* config. +*/ +for (i =0 ; i <TRTCM_CONFIG_RETRY_MAX ; i++) { +u32 data =0 ; -return read_poll_timeout (airoha_qdma_rr, val, - val &TRTCM_PARAM_RW_DONE_MASK , -USEC_PER_MSEC ,10 *USEC_PER_MSEC , true, - qdma,REG_TRTCM_CFG_PARAM (addr)); +airoha_qdma_wr (qdma,REG_TRTCM_DATA_LOW (addr), val); +airoha_qdma_wr (qdma,REG_TRTCM_CFG_PARAM (addr), config); + +if (read_poll_timeout (airoha_qdma_rr, data, + data &TRTCM_PARAM_RW_DONE_MASK , +USEC_PER_MSEC ,10 *USEC_PER_MSEC , + true, qdma,REG_TRTCM_CFG_PARAM (addr))) +continue ; + +if (airoha_qdma_get_trtcm_param (qdma, channel, addr, param, + mode, &data,NULL )) +continue ; + +if (data == val) +return 0 ; + } + +return -EBUSY ; } static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel,
drivers/net/ethernet/airoha/airoha_eth.h +2 -0
@@ -171,6 +171,8 @@ enum trtcm_param {#define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6)#define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0) +#define TRTCM_CONFIG_RETRY_MAX 2 +enum airoha_dma_map_type {AIROHA_DMA_UNMAPPED ,AIROHA_DMA_MAP_SINGLE ,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 224/281 riscv: vector: Fix data pointer constraints in context save/restore
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Troy Mitchell <[email protected]>
[ Upstream commit 572b25f3a6453d8e7fc28cc0cefe41ed15be166c ]
The standard vector save/restore asm advances datap but declares it as input-only. An inlined caller reusing the original pointer may therefore use the advanced address instead.
Declare datap as read-write so the compiler can preserve the original pointer when needed.
--- arch/riscv/include/asm/vector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/include/asm/vector.h +2 -2
@@ -225,7 +225,7 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,"add %1, %1, %0\n\t" "vse8.v v24, (%1)\n\t" ".option pop\n\t" - : "=&r" (vl) : "r" (datap) : "memory"); + : "=&r" (vl), "+r" (datap) : : "memory"); } riscv_v_disable(); }
@@ -261,7 +261,7 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_"add %1, %1, %0\n\t" "vle8.v v24, (%1)\n\t" ".option pop\n\t" - : "=&r" (vl) : "r" (datap) : "memory"); + : "=&r" (vl), "+r" (datap) : : "memory"); } __vstate_csr_restore(restore_from);riscv_v_disable ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/281 riscv: vector: Fix output operands in context save
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aurelien Jarno <[email protected]>
[ Upstream commit 55b1276ef9c1c27e8a2b2c5e8ef0515b5ad0332c ]
The __vstate_csr_save() inline assembly declares vcsr as an output operand while the assembly only writes vstart, vtype, and vl. Remove it as it is later saved by the C code.
--- arch/riscv/include/asm/vector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/include/asm/vector.h +2 -2
@@ -132,8 +132,8 @@ static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest) "csrr %0, " __stringify(CSR_VSTART) "\n\t" "csrr %1, " __stringify(CSR_VTYPE) "\n\t" "csrr %2, " __stringify(CSR_VL) "\n\t" - : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl), - "=r" (dest->vcsr) : :); + : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl) + : :);if (has_xtheadvector ()) {unsigned long status;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/281 Revert "net: stmmac: propagate PTP addend and system time programming errors"
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit d5a007b9b457c915ab1a53227e8939e4018aa97a ]
This reverts commit 232d49dd4b40a666283de9e722899f088ed581b2.
Anirudh reports a regression from this change.
It is not appropriate for net this late in the release cycle in the first place, so let's revert and revisit in net-next.
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 106 +++++------------- .../net/ethernet/stmicro/stmmac/stmmac_ptp.c | 10 +- 2 files changed, 32 insertions(+), 84 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +28 -78
@@ -601,64 +601,31 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, } } -static void stmmac_restore_subsecond_increment (struct stmmac_priv *priv, -u32 default_addend) +static void stmmac_update_subsecond_increment (struct stmmac_priv *priv) {bool xmac =dwmac_is_xmac (priv->plat->core_type);u32 sec_inc =0 ; - -stmmac_config_addend (priv, priv->ptpaddr, default_addend); -stmmac_config_hw_tstamping (priv, priv->ptpaddr, priv->systime_flags); -stmmac_config_sub_second_increment (priv, priv->ptpaddr, - priv->plat->clk_ptp_rate, - xmac, &sec_inc); - priv->default_addend = default_addend; - priv->sub_second_inc = sec_inc; -} - -static int stmmac_update_subsecond_increment (struct stmmac_priv *priv, -u32 systime_flags) -{ -bool xmac =dwmac_is_xmac (priv->plat->core_type); -u32 sec_inc =0 , val;u64 temp =0 ; -int ret; -stmmac_config_hw_tstamping (priv, priv->ptpaddr, systime_flags); +stmmac_config_hw_tstamping (priv, priv->ptpaddr, priv->systime_flags);/* program Sub Second Increment reg */ stmmac_config_sub_second_increment (priv, priv->ptpaddr, priv->plat->clk_ptp_rate, xmac, &sec_inc); -if (!sec_inc) { - ret = -EINVAL ; - goto error; - } + temp =div_u64 (1000000000ULL , sec_inc); + +/* Store sub second increment for later use */ + priv->sub_second_inc = sec_inc;/* calculate default added value: * formula is : * addend = (2^32)/freq_div_ratio; * where, freq_div_ratio = 1e9ns/sec_inc */ - temp =div_u64 (1000000000ULL , sec_inc); temp = (u64)(temp <<32 ); - val =div_u64 (temp, priv->plat->clk_ptp_rate); - - ret =stmmac_config_addend (priv, priv->ptpaddr, val); -if (ret) - goto error; - - priv->sub_second_inc = sec_inc; - priv->default_addend = val; - -return 0 ; -error: -/* Restore previous configuration */ -stmmac_config_hw_tstamping (priv, priv->ptpaddr, priv->systime_flags); -stmmac_config_sub_second_increment (priv, priv->ptpaddr, - priv->plat->clk_ptp_rate, xmac, -NULL ); -return ret; + priv->default_addend =div_u64 (temp, priv->plat->clk_ptp_rate); +stmmac_config_addend (priv, priv->ptpaddr, priv->default_addend); }/**
@@ -887,42 +854,35 @@ static int stmmac_hwtstamp_get(struct net_device *dev,/** * stmmac_init_tstamp_counter - init hardware timestamping counter * @priv: driver private structure +* @systime_flags: timestamping flags * Description: * Initialize hardware counter for packet timestamping. * This is valid as long as the interface is open and not suspended. * Will be rerun after resuming from suspend, case in which the timestamping * flags updated by stmmac_hwtstamp_set() also need to be restored. */ -static int stmmac_init_tstamp_counter (struct stmmac_priv *priv) +static int stmmac_init_tstamp_counter (struct stmmac_priv *priv, +u32 systime_flags) { -u32 default_addend = priv->default_addend;struct timespec64 now; -int ret;if (!priv->plat->clk_ptp_rate) {netdev_err (priv->dev,"Invalid PTP clock rate" );return -EINVAL ; } - ret =stmmac_update_subsecond_increment (priv, priv->systime_flags); -if (ret) -return ret; +stmmac_config_hw_tstamping (priv, priv->ptpaddr, systime_flags); + priv->systime_flags = systime_flags; + +stmmac_update_subsecond_increment (priv);/* initialize system time */ ktime_get_real_ts64 (&now);/* lower 32 bits of tv_sec are safe until y2106 */ - ret =stmmac_init_systime (priv, priv->ptpaddr, (u32 )now.tv_sec, - now.tv_nsec); -if (ret) - goto error; +stmmac_init_systime (priv, priv->ptpaddr, (u32 )now.tv_sec, now.tv_nsec);return 0 ; -error: -/* Restore previous configuration */ -stmmac_restore_subsecond_increment (priv, default_addend); - -return ret; }/**
@@ -945,14 +905,8 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv)return -EOPNOTSUPP ; } -/* Reset hw ts configuration */ -memset (&priv->tstamp_config,0 ,sizeof (priv->tstamp_config)); - priv->systime_flags =STMMAC_HWTS_ACTIVE |PTP_TCR_TSCFUPDT ; - priv->tsfupdt_coarse = false; - priv->hwts_tx_en =0 ; - priv->hwts_rx_en =0 ; - - ret =stmmac_init_tstamp_counter (priv); + ret =stmmac_init_tstamp_counter (priv,STMMAC_HWTS_ACTIVE | +PTP_TCR_TSCFUPDT );if (ret) {netdev_warn (priv->dev,"PTP init failed\n" );return ret;
@@ -973,6 +927,10 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv)netdev_info (priv->dev,"IEEE 1588-2008 Advanced Timestamp supported\n" ); +memset (&priv->tstamp_config,0 ,sizeof (priv->tstamp_config)); + priv->hwts_tx_en =0 ; + priv->hwts_rx_en =0 ; +if (priv->plat->flags &STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY )stmmac_hwtstamp_correct_latency (priv, priv);
@@ -7767,26 +7725,18 @@ static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id, {struct stmmac_devlink_priv *dl_priv =devlink_priv (dl);struct stmmac_priv *priv = dl_priv->stmmac_priv; -u32 systime_flags = priv->systime_flags; -int ret; -if (ctx->val.vbool) - systime_flags &= ~PTP_TCR_TSCFUPDT ; + priv->tsfupdt_coarse = ctx->val.vbool; + +if (priv->tsfupdt_coarse) + priv->systime_flags &= ~PTP_TCR_TSCFUPDT ;else - systime_flags |=PTP_TCR_TSCFUPDT ; + priv->systime_flags |=PTP_TCR_TSCFUPDT ;/* In Coarse mode, we can use a smaller subsecond increment, let's * reconfigure the systime, subsecond increment and addend. */ - ret =stmmac_update_subsecond_increment (priv, systime_flags); -if (ret) { -NL_SET_ERR_MSG_MOD (extack, -"failed to reconfigure PTP adjustment" ); -return ret; - } - - priv->tsfupdt_coarse = ctx->val.vbool; - priv->systime_flags = systime_flags; +stmmac_update_subsecond_increment (priv);return 0 ; }
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +4 -6
@@ -28,15 +28,14 @@ static int stmmac_adjust_freq(struct ptp_clock_info *ptp, long scaled_ppm) container_of(ptp, struct stmmac_priv, ptp_clock_ops);unsigned long flags;u32 addend; -int ret; addend =adjust_by_scaled_ppm (priv->default_addend, scaled_ppm);write_lock_irqsave (&priv->ptp_lock, flags); - ret =stmmac_config_addend (priv, priv->ptpaddr, addend); +stmmac_config_addend (priv, priv->ptpaddr, addend);write_unlock_irqrestore (&priv->ptp_lock, flags); -return ret; +return 0 ; }/**
@@ -154,13 +153,12 @@ static int stmmac_set_time(struct ptp_clock_info *ptp,struct stmmac_priv *priv = container_of(ptp, struct stmmac_priv, ptp_clock_ops);unsigned long flags; -int ret;write_lock_irqsave (&priv->ptp_lock, flags); - ret =stmmac_init_systime (priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec); +stmmac_init_systime (priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec);write_unlock_irqrestore (&priv->ptp_lock, flags); -return ret; +return 0 ; } static int stmmac_enable(struct ptp_clock_info *ptp,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/281 scsi: ufs: mediatek: Handle mPHY power-on failures
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit 78e04810542da416d3bead707b60cec773f1524d ]
The MediaTek helper enables VA09 before powering on the mPHY, but ignores the PHY result. A failure can leave VA09 enabled and publish mphy_powered_on as true, so a later power-on attempt is skipped.
Check phy_power_on(), attempt to unwind VA09 on failure, and leave the state flag unchanged. Propagate the helper result from host initialization as well as its existing resume caller. Keep the original PHY error if the supply rollback also fails, and report that rollback separately.
The issue was found by our static-analysis tool.
--- drivers/ufs/host/ufs-mediatek.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/ufs/host/ufs-mediatek.c +16 -2
@@ -509,6 +509,7 @@ static int ufs_mtk_mphy_power_on(struct ufs_hba *hba, bool on)struct ufs_mtk_host *host =ufshcd_get_variant (hba);struct phy *mphy = host->mphy;struct arm_smccc_res res; +int err;int ret =0 ;if (!mphy || !(on ^ host->mphy_powered_on))
@@ -523,7 +524,18 @@ static int ufs_mtk_mphy_power_on(struct ufs_hba *hba, bool on)usleep_range (200 ,210 );ufs_mtk_va09_pwr_ctrl (res,1 ); } -phy_power_on (mphy); + ret =phy_power_on (mphy); +if (ret) { +if (ufs_mtk_is_va09_supported (hba)) { +ufs_mtk_va09_pwr_ctrl (res,0 ); + err =regulator_disable (host->reg_va09); +if (err) +dev_err (hba->dev, +"failed to unwind va09: %d\n" , err); + } +dev_err (hba->dev,"failed to enable mphy: %d\n" , ret); +return ret; + } }else {phy_power_off (mphy);if (ufs_mtk_is_va09_supported (hba)) {
@@ -1295,7 +1307,9 @@ static int ufs_mtk_init(struct ufs_hba *hba)* * Enable phy clocks specifically here. */ -ufs_mtk_mphy_power_on (hba, true); + err =ufs_mtk_mphy_power_on (hba, true); +if (err) + goto out_variant_clear;if (ufs_mtk_is_rtff_mtcmos (hba)) {/* First Restore here, to avoid backup unexpected value */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/281 wifi: mt76: mt7996: take over connection monitoring
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit 0ec14f8c8c5da1c9c314bce4f0bf53c009fbc998 ]
mt76 runs its own beacon monitor. mt76_beacon_mon_check() counts a miss for each affiliated link of an MLD, and it stops the count while the phy is off channel. mt7996 supplies it through mt76_rx_beacon(), but it does not set CONNECTION_MONITOR. Therefore mac80211 also starts its legacy monitor.
The legacy monitor knows one interface, not a set of links. It also counts while the radio serves a different link. After seven beacon intervals it removes an association that still carries traffic on the other links.
Set the CONNECTION_MONITOR flag to disable the legacy monitor.
--- drivers/net/wireless/mediatek/mt76/mt7996/init.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/wireless/mediatek/mt76/mt7996/init.c +1 -0
@@ -536,6 +536,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)ieee80211_hw_set (hw,NO_VIRTUAL_MONITOR );ieee80211_hw_set (hw,SUPPORTS_MULTI_BSSID );ieee80211_hw_set (hw,CHANCTX_STA_CSA ); +ieee80211_hw_set (hw,CONNECTION_MONITOR ); hw->max_tx_fragments =4 ; wiphy->txq_memory_limit =32 <<20 ;/* 32 MiB */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/281 wifi: mac80211: add AQL support for multicast packets
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit 836c1addd3bf42d47ca5f7a5780a7ec52abf332e ]
Excessive multicast traffic with little competing unicast traffic can easily flood hardware queues, leading to throughput issues. Additionally, filling the hardware queues with too many packets breaks FQ for multicast data. Fix this by enabling AQL for multicast packets.
Stable-dep-of: 752770d7a58c ("wifi: mt76: account non-AQL frames per peer rather than per link")
--- include/net/cfg80211.h | 1 + include/net/mac80211.h | 3 ++- net/mac80211/debugfs.c | 13 ++++++++-- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/main.c | 1 + net/mac80211/sta_info.c | 17 ++++++++++++- net/mac80211/sta_info.h | 3 ++- net/mac80211/status.c | 5 ++-- net/mac80211/tx.c | 52 ++++++++++++++++++++------------------ 9 files changed, 66 insertions(+), 31 deletions(-)
include/net/cfg80211.h +1 -0
@@ -3722,6 +3722,7 @@ enum wiphy_params_flags {/* The per TXQ device queue limit in airtime */ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000 +#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_MC 50000/* The per interface airtime threshold to switch to lower queue limit */ #define IEEE80211_AQL_THRESHOLD 24000
include/net/mac80211.h +2 -1
@@ -1317,6 +1317,7 @@ ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate)* @status_data: internal data for TX status handling, assigned privately, * see also &enum ieee80211_status_data for the internal documentation * @status_data_idr: indicates status data is IDR allocated ID for ack frame +* @tx_time_mc: TX time estimate is for a multicast frame, used internally * @tx_time_est: TX time estimate in units of 4us, used internally * @control: union part for control data * @control.rates: TX rates array to try
@@ -1354,8 +1355,8 @@ struct ieee80211_tx_info { status_data_idr:1, status_data:13, hw_queue:4, + tx_time_mc:1, tx_time_est:10; -/* 1 free bit */ union { struct {
net/mac80211/debugfs.c +11 -2
@@ -210,11 +210,13 @@ static ssize_t aql_pending_read(struct file *file,"VI %u us\n" "BE %u us\n" "BK %u us\n" +"MC %u us\n" "total %u us\n" ,atomic_read (&local->aql_ac_pending_airtime[IEEE80211_AC_VO ]),atomic_read (&local->aql_ac_pending_airtime[IEEE80211_AC_VI ]),atomic_read (&local->aql_ac_pending_airtime[IEEE80211_AC_BE ]),atomic_read (&local->aql_ac_pending_airtime[IEEE80211_AC_BK ]), +atomic_read (&local->aql_mc_pending_airtime), atomic_read(&local->aql_total_pending_airtime));return simple_read_from_buffer (user_buf, count, ppos, buf, len);
@@ -239,7 +241,8 @@ static ssize_t aql_txq_limit_read(struct file *file,"VO %u %u\n" "VI %u %u\n" "BE %u %u\n" -"BK %u %u\n" , +"BK %u %u\n" +"MC %u\n" , local->aql_txq_limit_low[IEEE80211_AC_VO ], local->aql_txq_limit_high[IEEE80211_AC_VO ], local->aql_txq_limit_low[IEEE80211_AC_VI],
@@ -247,7 +250,8 @@ static ssize_t aql_txq_limit_read(struct file *file, local->aql_txq_limit_low[IEEE80211_AC_BE ], local->aql_txq_limit_high[IEEE80211_AC_BE ], local->aql_txq_limit_low[IEEE80211_AC_BK ], - local->aql_txq_limit_high[IEEE80211_AC_BK]); + local->aql_txq_limit_high[IEEE80211_AC_BK ], + local->aql_txq_limit_mc);return simple_read_from_buffer (user_buf, count, ppos, buf, len); }
@@ -273,6 +277,11 @@ static ssize_t aql_txq_limit_write(struct file *file,else buf[count] ='\0' ; +if (sscanf (buf,"mcast %u" , &q_limit_low) ==1 ) { + local->aql_txq_limit_mc = q_limit_low; +return count; + } +if (sscanf (buf,"%u %u %u" , &ac, &q_limit_low, &q_limit_high) !=3 )return -EINVAL ;
net/mac80211/ieee80211_i.h +2 -0
@@ -1464,10 +1464,12 @@ struct ieee80211_local {spinlock_t handle_wake_tx_queue_lock;u16 airtime_flags; +u32 aql_txq_limit_mc;u32 aql_txq_limit_low[IEEE80211_NUM_ACS ];u32 aql_txq_limit_high[IEEE80211_NUM_ACS ];u32 aql_threshold;atomic_t aql_total_pending_airtime; +atomic_t aql_mc_pending_airtime;atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS ];const struct ieee80211_ops *ops;
net/mac80211/main.c +1 -0
@@ -988,6 +988,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,spin_lock_init (&local->rx_path_lock);spin_lock_init (&local->queue_stop_reason_lock); + local->aql_txq_limit_mc =IEEE80211_DEFAULT_AQL_TXQ_LIMIT_MC ;for (i =0 ; i <IEEE80211_NUM_ACS ; i++) {INIT_LIST_HEAD (&local->active_txqs[i]);spin_lock_init (&local->active_txq_lock[i]);
net/mac80211/sta_info.c +16 -1
@@ -2493,13 +2493,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggregates);void ieee80211_sta_update_pending_airtime (struct ieee80211_local *local,struct sta_info *sta,u8 ac, -u16 tx_airtime,bool tx_completed) +u16 tx_airtime,bool tx_completed, +bool mcast) {int tx_pending;if (!wiphy_ext_feature_isset (local->hw.wiphy,NL80211_EXT_FEATURE_AQL ))return ; +if (mcast) { +if (!tx_completed) { +atomic_add (tx_airtime, &local->aql_mc_pending_airtime); +return ; + } + + tx_pending =atomic_sub_return (tx_airtime, + &local->aql_mc_pending_airtime); +if (tx_pending <0 ) +atomic_cmpxchg (&local->aql_mc_pending_airtime, + tx_pending,0 ); +return ; + } +if (!tx_completed) {if (sta) atomic_add(tx_airtime,
net/mac80211/sta_info.h +2 -1
@@ -147,7 +147,8 @@ struct airtime_info {void ieee80211_sta_update_pending_airtime (struct ieee80211_local *local,struct sta_info *sta,u8 ac, -u16 tx_airtime,bool tx_completed); +u16 tx_airtime,bool tx_completed, +bool mcast);struct sta_info ;
net/mac80211/status.c +3 -2
@@ -774,7 +774,7 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,ieee80211_sta_update_pending_airtime (local, sta,skb_get_queue_mapping (skb), tx_time_est, - true); + true, info->tx_time_mc);rcu_read_unlock (); }
@@ -1255,10 +1255,11 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw,/* Do this here to avoid the expensive lookup of the sta * in ieee80211_report_used_skb(). */ +bool mcast =IEEE80211_SKB_CB (skb)->tx_time_mc;ieee80211_sta_update_pending_airtime (local, sta,skb_get_queue_mapping (skb), tx_time_est, - true); + true, mcast);ieee80211_info_set_tx_time_est (IEEE80211_SKB_CB (skb),0 ); }
net/mac80211/tx.c +28 -24
102 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 230/281 wifi: mac80211: add ieee80211_txq_aql_pending()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit 9a197e71eb7b45860e37a9f3bdf61a843781ae12 ]
Add a function to allow drivers to query the pending AQL airtime for a given txq, for both unicast and broadcast.
This will be used for mt76 to limit buffering in AP mode for power-save stations.
Stable-dep-of: 752770d7a58c ("wifi: mt76: account non-AQL frames per peer rather than per link")
--- include/net/mac80211.h | 11 +++++++++++ net/mac80211/tx.c | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+)
include/net/mac80211.h +11 -0
@@ -6877,6 +6877,17 @@ void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,bool ieee80211_txq_airtime_check (struct ieee80211_hw *hw,struct ieee80211_txq *txq); +/** +* ieee80211_txq_aql_pending - get pending AQL airtime for a txq +* +* @hw: pointer obtained from ieee80211_alloc_hw() +* @txq: pointer obtained from station or virtual interface +* +* Return: pending airtime (in usec) for the given txq. +*/ +u32 ieee80211_txq_aql_pending (struct ieee80211_hw *hw, +struct ieee80211_txq *txq); +/** * ieee80211_iter_keys - iterate keys programmed into the device * @hw: pointer obtained from ieee80211_alloc_hw()
net/mac80211/tx.c +18 -0
@@ -4281,6 +4281,24 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, }EXPORT_SYMBOL (ieee80211_txq_airtime_check); +u32 ieee80211_txq_aql_pending (struct ieee80211_hw *hw, +struct ieee80211_txq *txq) +{ +struct ieee80211_local *local =hw_to_local (hw); +struct sta_info *sta; + +if (unlikely (txq->tid ==IEEE80211_NUM_TIDS )) +return 0 ; + +if (!txq->sta) +return atomic_read (&local->aql_mc_pending_airtime); + + sta = container_of(txq->sta, struct sta_info, sta); + +return atomic_read (&sta->airtime[txq->ac].aql_tx_pending); +} +EXPORT_SYMBOL (ieee80211_txq_aql_pending); +static bool ieee80211_txq_schedule_airtime_check (struct ieee80211_local *local,u8 ac) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 231/281 wifi: mt76: add PS buffering support for HW-managed TIM drivers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit a547b414bf7ea9877456b5630573293cc0e2cae7 ]
Add MT_DRV_HW_PS_BUFFERING flag for drivers where firmware controls the TIM bit based on buffered frames. Instead of blocking all TX to PS stations (which starves firmware and prevents TIM from being set), allow limited frame delivery using AQL pending airtime as the throttle. Replenish the firmware buffer on TX completion.
Add mt76_sta_ps_transition() helper for drivers to call from MCU PS sync events.
Stable-dep-of: 752770d7a58c ("wifi: mt76: account non-AQL frames per peer rather than per link")
--- drivers/net/wireless/mediatek/mt76/mt76.h | 3 + .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + drivers/net/wireless/mediatek/mt76/tx.c | 77 +++++++++++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-)
drivers/net/wireless/mediatek/mt76/mt76.h +3 -0
@@ -539,6 +539,7 @@ struct mt76_hw_cap {#define MT_DRV_HW_MGMT_TXQ BIT(4)#define MT_DRV_AMSDU_OFFLOAD BIT(5)#define MT_DRV_IGNORE_TXS_FAILED BIT(6) +#define MT_DRV_HW_PS_BUFFERING BIT(7)struct mt76_driver_ops {u32 drv_flags;
@@ -1541,6 +1542,8 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw, u16 tids, int nframes, enum ieee80211_frame_release_type reason, bool more_data); +void mt76_sta_ps_transition (struct mt76_dev *dev,struct mt76_wcid *wcid, +bool ps);bool mt76_has_tx_pending (struct mt76_phy *phy);int mt76_update_channel (struct mt76_phy *phy);void mt76_update_survey (struct mt76_phy *phy);
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +1 -0
@@ -1068,6 +1068,7 @@ enum {MCU_UNI_EVENT_IE_COUNTDOWN =0x09 ,MCU_UNI_EVENT_COREDUMP =0x0a ,MCU_UNI_EVENT_BSS_BEACON_LOSS =0x0c , +MCU_UNI_EVENT_PS_SYNC =0x0d ,MCU_UNI_EVENT_SCAN_DONE =0x0e ,MCU_UNI_EVENT_RDD_REPORT =0x11 , MCU_UNI_EVENT_ROC = 0x27,
drivers/net/wireless/mediatek/mt76/tx.c +71 -6
119 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 232/281 wifi: mt76: account non-AQL frames per peer rather than per link
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit 752770d7a58c821c17909c3ca67f9f14ab146a40 ]
The driver increments wcid->non_aql_packets when a frame goes to the hardware queue. The completion path decrements the counter again.
For an MLO peer, the hardware reports the completion on the link that it used for the transmission. This link can differ from the link that the driver queued the frame on. In that case, the increment and the decrement go to different link wcids, and the count drifts upwards until it blocks the station.
Keep the count on the wcid of the primary link, like the other peer-wide state.
--- drivers/net/wireless/mediatek/mt76/mt76.h | 7 +++++++ drivers/net/wireless/mediatek/mt76/tx.c | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt76.h +7 -0
@@ -1425,6 +1425,13 @@ mtxq_to_txq(struct mt76_txq *mtxq) return container_of(ptr, struct ieee80211_txq, drv_priv); } +/* peer-wide state uses the wcid of the primary link */ +static inline struct mt76_wcid * +mt76_wcid_primary (struct mt76_wcid *wcid) +{ +return wcid->def_wcid ? wcid->def_wcid : wcid; +} +static inline struct ieee80211_sta *wcid_to_sta (struct mt76_wcid *wcid) {
drivers/net/wireless/mediatek/mt76/tx.c +8 -3
@@ -234,6 +234,8 @@ mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid,if (!wcid || info->tx_time_est)return ; + wcid =mt76_wcid_primary (wcid); + pending =atomic_dec_return (&wcid->non_aql_packets);if (pending <0 )atomic_cmpxchg (&wcid->non_aql_packets, pending,0 );
@@ -344,7 +346,8 @@ __mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb,if (!non_aql)return idx; - pending =atomic_inc_return (&wcid->non_aql_packets); +/* the hardware can report the completion on a different link */ + pending =atomic_inc_return (&mt76_wcid_primary (wcid)->non_aql_packets);if (stop && pending >=MT_MAX_NON_AQL_PKT ) *stop = true;
@@ -498,7 +501,8 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q,return 0 ; } -if (atomic_read (&wcid->non_aql_packets) >=MT_MAX_NON_AQL_PKT ) +if (atomic_read (&mt76_wcid_primary (wcid)->non_aql_packets) >= +MT_MAX_NON_AQL_PKT )return 0 ; skb =mt76_txq_dequeue (phy, mtxq);
@@ -595,7 +599,8 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid)continue ; } -if (atomic_read (&wcid->non_aql_packets) >=MT_MAX_NON_AQL_PKT ) +if (atomic_read (&mt76_wcid_primary (wcid)->non_aql_packets) >= +MT_MAX_NON_AQL_PKT )continue ;if (dev->queue_ops->tx_cleanup && q->queued +2 *MT_TXQ_FREE_THR >= q->ndesc) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/281 wifi: mt76: use ALTX queue for packets to disassociated stations
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit 7c2e6b43c3082c2242ecfa5875b2b4b2cd06140f ]
Running these packets through regular buffering queues could cause them to hang, especially when the packets were for stations that in disassoc state.
--- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt7996/main.c | 1 + drivers/net/wireless/mediatek/mt76/tx.c | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/main.c +2 -0
@@ -805,6 +805,7 @@ __mt7915_drop_sta(void *ptr, u8 *mac, struct ieee80211_vif *vif) msta = (struct mt7915_sta *)sta->drv_priv;mt7915_mcu_add_sta (data->dev, vif, sta,CONN_STATE_DISCONNECT , false); + msta->wcid.tx_info &= ~MT_WCID_TX_INFO_SET ; msta->wcid.sta_disabled =1 ; msta->wcid.sta =0 ; }
@@ -871,6 +872,7 @@ int mt7915_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,mutex_unlock (&dev->mt76.mutex);mt7915_mcu_add_sta (dev, vif, sta,CONN_STATE_DISCONNECT , false); + msta->wcid.tx_info &= ~MT_WCID_TX_INFO_SET ; msta->wcid.sta_disabled =1 ; msta->wcid.sta =0 ;return 0 ;
drivers/net/wireless/mediatek/mt76/mt7996/main.c +1 -0
@@ -1429,6 +1429,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,else if (sta->mlo && links ==BIT (link_id))/* last link */ mt7996_mcu_teardown_mld_sta (dev, link, msta_link); + msta_link->wcid.tx_info &= ~MT_WCID_TX_INFO_SET ; msta_link->wcid.sta_disabled =1 ; msta_link->wcid.sta =0 ; links = links & ~BIT (link_id);
drivers/net/wireless/mediatek/mt76/tx.c +2 -1
@@ -672,7 +672,8 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid,if ((dev->drv->drv_flags &MT_DRV_HW_MGMT_TXQ ) && !(info->flags &IEEE80211_TX_CTL_HW_80211_ENCAP ) && !ieee80211_is_data_present (hdr->frame_control) && - (!ieee80211_is_bufferable_mmpdu (skb) || + (!(wcid->tx_info &MT_WCID_TX_INFO_SET ) || + !ieee80211_is_bufferable_mmpdu (skb) ||ieee80211_is_deauth (hdr->frame_control) ||ieee80211_is_disassoc (hdr->frame_control) || head == &wcid->tx_offchannel))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 234/281 wifi: mt76: mt7925: restore the legacy BSS after MLO teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaron Ma <[email protected]>
[ Upstream commit 8f9e5807671ee97cacf1957f4a09e43f4f1017a9 ]
Removing the last link of an MLD interface leaves the firmware with the MLD BSS and DEV entry: the removal loop skips the deflink, and the hweight16(mvif->valid_links) fallback reads the stale pre-update valid_links, which is still non-zero. The entry keeps the per-link MAC address, so every subsequent legacy association fails authentication until the interface is removed or the module is reloaded.
Remove the deflink BSS and add it back as a legacy BSS on the transition to zero links.
--- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +12 -0
@@ -2220,6 +2220,18 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mvif->valid_links = new_links; +/* Restore the legacy BSS after disabling MLO */ +if (old_links && !new_links) { +mt792x_mac_link_bss_remove (dev, &mvif->bss_conf, + &mvif->sta.deflink); + err =mt7925_mac_link_bss_add (dev, &vif->bss_conf, + &mvif->sta.deflink); +if (err <0 ) { +mt792x_mutex_release (dev); +return err; + } + } +mt792x_mutex_release (dev);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 235/281 wifi: mt76: mt7996: fix struct mt7996_mcu_wed_rro_ba_delete_event layout
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tao Gong <[email protected]>
[ Upstream commit 9c9e4e4c8b45873e53774ab8207825b37b7565ec ]
Correct the definition of struct mt7996_mcu_wed_rro_ba_delete_event to match the 12-byte event record layout observed from firmware.
The event TLV sent by firmware is 12 bytes in length rather than 8 bytes. It contains an additional 4 bytes that appear to be wlan_id and tid. For example, a captured batch deletion of sessions 8, 9, and 10 (TIDs 6, 0, and 1) shows:
00000000: 02 00 0c 00 08 00 01 00 06 00 00 00 02 00 0c 00 00000010: 09 00 01 00 00 00 00 00 02 00 0c 00 0a 00 01 00 00000020: 01 00 00 00
The previous 8-byte structure corrupts the parsing of subsequent TLVs, leading to unhandled delete events and erroneous session deletions. Consequently, clients that create and tear down BA sessions in batches (such as Apple Wi-Fi clients) fail to properly clean up stale sessions, causing HW_RRO to be lost upon subsequent reassociations.
--- drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +3 -0
@@ -284,6 +284,9 @@ struct mt7996_mcu_wed_rro_ba_delete_event {__le16 len;__le16 session_id; +__le16 wlan_id; +u8 tid; +u8 __rsv1;u8 __rsv2[2 ]; } __packed;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 236/281 wifi: mt76: mt7915: disable rx napi when removing device
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Cavallari <[email protected]>
[ Upstream commit 916c484d29d82f45c281da2786fcd6a1284430ce ]
Unloading the mt7915e module with a MT7916 triggers multiples WARN in __netif_napi_del_locked() and in page_pool_disable_direct_recycling() because the driver does not disable the napi before destroying it.
This is troublesome since on MT7916 it is required to unload the module and reinsert it with a different enable_6ghz parameter to change the frequency. The system generally becomes unstable after reinserting the module.
Fix it by disabling rx napi inside mt7915e itself, even through napi rx is enabled in the mt76 generic code. Disabling it in the mt76 generic code was previously attempted and reverted because napi was disabled too late and rx handlers could access freed structures. Note that the tx side is enabled and disabled by the mt76 generic code.
It is disabled in mt7915_stop_hardware() since, unlike mt7915_unregister_device(), is it also called in the probe path if an error happens after rx napi is enabled.
--- drivers/net/wireless/mediatek/mt76/mt7915/init.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7915/init.c +6 -0
@@ -1208,6 +1208,12 @@ static void mt7915_unregister_ext_phy(struct mt7915_dev *dev)static void mt7915_stop_hardware (struct mt7915_dev *dev) { +int i; + +mt76_for_each_q_rx (&dev->mt76, i) { +if (!mt76_queue_is_wed_rro (&dev->mt76.q_rx[i])) +napi_disable (&dev->mt76.napi[i]); + }mt7915_mcu_exit (dev);mt76_connac2_tx_token_put (&dev->mt76);mt7915_dma_cleanup (dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/281 wifi: mt76: mt7925: dont put the band auto marker in TGID
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Devin Wittmayer <[email protected]>
[ Upstream commit 5796b2bf9ff718342773ac760d86eb126c112a4f ]
band_idx starts out as 0xff, which tells the firmware to pick the band on its own. A remain-on-channel grant normally writes a real index over it before anything else looks. Two cases never see that: an MLD station throws the grant away, and an AP has no association to trigger one.
The TX descriptor reads the same field into a two-bit slot, so the marker comes out as 3. Every data frame from those two goes out tagged band 3. Beacons are fine, they reach the firmware separately and never build a descriptor.
Same numbers on an MT7925 and on a Netgear A9000:
stock patched station, one link 0x00000001 0x00000001 station, two links 0x00003001 0x00000001 AP 0x00003013 0x00000013 real band index, STA 0x00001001 0x00001001 real band index, AP 0x00001013 0x00001013
Only the marker cases move.
--- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7925/mac.c +2 -1
@@ -781,7 +781,8 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, val =FIELD_PREP (MT_TXD1_WLAN_IDX , wcid->idx) |FIELD_PREP (MT_TXD1_OWN_MAC , omac_idx); -if (band_idx) +/* 0xff means BAND_AUTO to the firmware, and TGID cannot encode it */ +if (band_idx && band_idx !=0xff ) val |=FIELD_PREP (MT_TXD1_TGID , band_idx); txwi[1 ] =cpu_to_le32 (val);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/281 wifi: mt76: mt792x: treat 0xff ACPI SAR entries as no limit
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junjie Cao <[email protected]>
[ Upstream commit 45f53d98cbaa078907b6a300ff3974dfb5ae8776 ]
Some ASUS firmware carries 0xff in MTGS power slots. The driver narrows the byte to s8, so the slot reads as -1 in its 0.5 dBm units, wins the min() against the regulatory limit and caps every rate the driver programs. On an ASUS Zenbook 14 UM3406HA (MT7922) all six MTGS tables hold 0xff in every power byte, and txpower_sku shows tmac 3 on every supported OFDM/HT/VHT/HE rate against EEPROM entries of 25-39, under both the FR and the world regulatory domain. Huy Nguyen Dinh Quang saw the same clamp on a Vivobook S 14 M5406WA, with 5 GHz TX at ~40 Mbps, and proposed skipping the geo entry.
Skip a geo entry whose power byte is 0xff. The MTDS entry is narrowed the same way on its way into the geo lookup, so give it the same treatment, and clamp the dynamic value plus offset at 127 so a no-limit entry cannot wrap.
--- .../wireless/mediatek/mt76/mt792x_acpi_sar.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c +14 -4
@@ -279,10 +279,18 @@ mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,if (idx >= max)return dyn_power; - geo_power = (band_pwr + idx)->pwr; - dyn_power += (band_pwr + idx)->offset; + band_pwr += idx; -return min (geo_power, dyn_power); +/* Some OEM tables carry 0xff here; narrowed to s8 that is -1 and +* wins the min() below. Take it as no limit. +*/ +if (band_pwr->pwr ==0xff ) +return dyn_power; + + geo_power = band_pwr->pwr; + +return min_t (int, geo_power, +min_t (int, dyn_power + band_pwr->offset,127 )); } static s8
@@ -315,7 +323,9 @@ mt792x_asar_range_pwr(struct mt792x_phy *phy,else band =NL80211_BAND_2GHZ ; -return mt792x_asar_get_geo_pwr (phy, band, limit[idx]); +/* 0xff: no dynamic limit, see mt792x_asar_get_geo_pwr() */ +return mt792x_asar_get_geo_pwr (phy, band, + limit[idx] ==0xff ?127 : limit[idx]); }int mt792x_init_acpi_sar_power (struct mt792x_phy *phy,bool set_default)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 239/281 wifi: mt76: mt792x: pick the SAR table layout from the table itself
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Devin Wittmayer <[email protected]>
[ Upstream commit af97bf5a7e72da9026de184676df8e2fa57e1058 ]
The SAR power tables are parsed with a version byte from a different ACPI table, never their own.
Some firmware declares version 1 and ships version 2 tables. The entries then start a byte early, and the 6 GHz limits the table carries are dropped, because the older layout has no room for them.
ASUS GA503RM, MTCL declares v1:
MTGS 28 bytes v1: (28-6) % 5 = 2, no fit v2: 3 tables MTDS 19 bytes v1: (19-6) % 6 = 1, no fit v2: 1 table
The layouts accept disjoint lengths, so take whichever one fits and keep it per table.
--- .../wireless/mediatek/mt76/mt792x_acpi_sar.c | 110 +++++++++++------- .../wireless/mediatek/mt76/mt792x_acpi_sar.h | 2 + 2 files changed, 73 insertions(+), 39 deletions(-)
drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c +71 -39
@@ -108,68 +108,98 @@ mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)return ret; } +/* A table's layout is decided by the version byte in MTCL, but some firmware +* labels a version 2 table as version 1. Accept whichever layout the table +* actually has: for every accepted entry count the two do not overlap, since +* one is 6 + 5n bytes and the other 7 + 7n. +*/ +static bool +mt792x_asar_fits (u8 *table,int len,int prelen,int sarlen,int nr_off, +int min,int max) +{ +int tblcnt; + +if (len < prelen || (len - prelen) % sarlen) +return false; + + tblcnt = (len - prelen) / sarlen; +if (tblcnt < min || tblcnt > max) +return false; + +return tblcnt == table[nr_off]; +} +/* MTDS : Dynamic SAR Power Table */ static int -mt792x_asar_acpi_read_mtds (struct mt792x_dev *dev,u8 **table,u8 version) +mt792x_asar_acpi_read_mtds (struct mt792x_dev *dev,u8 **table,u8 version, +u8 *used) { -int len, ret, sarlen, prelen, tblcnt; -bool enable; +int len, ret; +u8 *t; ret =mt792x_acpi_read (dev, MT792x_ACPI_MTDS, table, &len);if (ret)return ret; + t = *table; +/* Table content validation */ -switch (version) { -case 1 : - enable = ((struct mt792x_asar_dyn *)*table)->enable; - sarlen =sizeof (struct mt792x_asar_dyn_limit ); - prelen =sizeof (struct mt792x_asar_dyn ); -break ; -case 2 : - enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable; - sarlen =sizeof (struct mt792x_asar_dyn_limit_v2 ); - prelen =sizeof (struct mt792x_asar_dyn_v2 ); -break ; -default : +if (mt792x_asar_fits (t, len,sizeof (struct mt792x_asar_dyn ), +sizeof (struct mt792x_asar_dyn_limit ), + offsetof(struct mt792x_asar_dyn , nr_tbl), + MT792x_ASAR_MIN_DYN, MT792x_ASAR_MAX_DYN)) + *used =1 ; +else if (mt792x_asar_fits (t, len,sizeof (struct mt792x_asar_dyn_v2 ), +sizeof (struct mt792x_asar_dyn_limit_v2 ), + offsetof(struct mt792x_asar_dyn_v2 , nr_tbl), + MT792x_ASAR_MIN_DYN, MT792x_ASAR_MAX_DYN)) + *used =2 ; +else return -EINVAL ; - } - tblcnt = (len - prelen) / sarlen; -if (!enable || - tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN) +if (!t[offsetof(struct mt792x_asar_dyn , enable)])return -EINVAL ; +if (version && *used != version) +dev_info (dev->mt76.dev, +"MTDS is v%u, MTCL says v%u; using v%u\n" , + *used, version, *used); +return 0 ; }/* MTGS : Geo SAR Power Table */ static int -mt792x_asar_acpi_read_mtgs (struct mt792x_dev *dev,u8 **table,u8 version) +mt792x_asar_acpi_read_mtgs (struct mt792x_dev *dev,u8 **table,u8 version, +u8 *used) { -int len, ret, sarlen, prelen, tblcnt; +int len, ret; +u8 *t; ret =mt792x_acpi_read (dev, MT792x_ACPI_MTGS, table, &len);if (ret)return ret; + t = *table; +/* Table content validation */ -switch (version) { -case 1 : - sarlen =sizeof (struct mt792x_asar_geo_limit ); - prelen =sizeof (struct mt792x_asar_geo ); -break ; -case 2 : - sarlen =sizeof (struct mt792x_asar_geo_limit_v2 ); - prelen =sizeof (struct mt792x_asar_geo_v2 ); -break ; -default : +if (mt792x_asar_fits (t, len,sizeof (struct mt792x_asar_geo ), +sizeof (struct mt792x_asar_geo_limit ), + offsetof(struct mt792x_asar_geo , nr_tbl), + MT792x_ASAR_MIN_GEO, MT792x_ASAR_MAX_GEO)) + *used =1 ; +else if (mt792x_asar_fits (t, len,sizeof (struct mt792x_asar_geo_v2 ), +sizeof (struct mt792x_asar_geo_limit_v2 ), + offsetof(struct mt792x_asar_geo_v2 , nr_tbl), + MT792x_ASAR_MIN_GEO, MT792x_ASAR_MAX_GEO)) + *used =2 ; +else return -EINVAL ; - } - tblcnt = (len - prelen) / sarlen; -if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO) -return -EINVAL ; +if (version && *used != version) +dev_info (dev->mt76.dev, +"MTGS is v%u, MTCL says v%u; using v%u\n" , + *used, version, *used);return 0 ; }
@@ -205,14 +235,16 @@ int mt792x_init_acpi_sar(struct mt792x_dev *dev) asar->countrylist =NULL ; } - ret =mt792x_asar_acpi_read_mtds (dev, (u8 **)&asar->dyn, asar->ver); + ret =mt792x_asar_acpi_read_mtds (dev, (u8 **)&asar->dyn, asar->ver, + &asar->dyn_ver);if (ret) {devm_kfree (dev->mt76.dev, asar->dyn); asar->dyn =NULL ; }/* MTGS is optional */ - ret =mt792x_asar_acpi_read_mtgs (dev, (u8 **)&asar->geo, asar->ver); + ret =mt792x_asar_acpi_read_mtgs (dev, (u8 **)&asar->geo, asar->ver, + &asar->geo_ver);if (ret) {devm_kfree (dev->mt76.dev, asar->geo); asar->geo =NULL ;
@@ -254,7 +286,7 @@ mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,break ; } -if (asar->ver ==1 ) { +if (asar->geo_ver ==1 ) { band_pwr = &asar->geo->tbl[idx].band[0 ]; max =ARRAY_SIZE (asar->geo->tbl[idx].band); }else {
@@ -305,7 +337,7 @@ mt792x_asar_range_pwr(struct mt792x_phy *phy,if (!capa)return 127 ; -if (asar->ver ==1 ) { +if (asar->dyn_ver ==1 ) { limit = &asar->dyn->tbl[0 ].frp[0 ]; max =ARRAY_SIZE (asar->dyn->tbl[0 ].frp); }else {
drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h +2 -0
@@ -105,6 +105,8 @@ struct mt792x_asar_fg {struct mt792x_acpi_sar {u8 ver; +u8 dyn_ver; +u8 geo_ver;union {struct mt792x_asar_dyn *dyn;struct mt792x_asar_dyn_v2 *dyn_v2;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 240/281 drm/xe/sysctrl: Fix mailbox header handling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mallesh Koujalagi <[email protected]>
[ Upstream commit f0a4447789ef28ba1aded2db19543d69c640b5e5 ]
The mailbox header was defined as __le32, which required cpu_to_le32() and le32_to_cpu() conversions whenever it was write or read.
These conversions are unnecessary because the header is always handled as a 32-bit value, not as raw bytes. It is written with xe_mmio_write32() and read with xe_mmio_read32(), which already handle any required byte-order conversion through the underlying writel()/readl() accessors.
As a result, the header value is already in CPU byte order when used by the driver. Keeping the extra conversions had no effect on little-endian systems and could cause an incorrect on big-endian systems.
So use u32 for the mailbox header and remove the unnecessary endianness conversions.
--- drivers/gpu/drm/xe/xe_sysctrl_mailbox.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/gpu/drm/xe/xe_sysctrl_mailbox.c +8 -8
@@ -19,20 +19,20 @@#include "xe_sysctrl_mailbox_types.h" struct xe_sysctrl_mailbox_msg_hdr { -__le32 data; -} __packed; +u32 data; +};#define XE_SYSCTRL_HDR_GROUP_ID (hdr) \ - FIELD_GET(SYSCTRL_HDR_GROUP_ID_MASK, le32_to_cpu((hdr)->data)) + FIELD_GET(SYSCTRL_HDR_GROUP_ID_MASK, (hdr)->data)#define XE_SYSCTRL_HDR_COMMAND (hdr) \ - FIELD_GET(SYSCTRL_HDR_COMMAND_MASK, le32_to_cpu((hdr)->data)) + FIELD_GET(SYSCTRL_HDR_COMMAND_MASK, (hdr)->data)#define XE_SYSCTRL_HDR_IS_RESPONSE (hdr) \ - FIELD_GET(SYSCTRL_HDR_IS_RESPONSE, le32_to_cpu((hdr)->data)) + FIELD_GET(SYSCTRL_HDR_IS_RESPONSE, (hdr)->data)#define XE_SYSCTRL_HDR_RESULT (hdr) \ - FIELD_GET(SYSCTRL_HDR_RESULT_MASK, le32_to_cpu((hdr)->data)) + FIELD_GET(SYSCTRL_HDR_RESULT_MASK, (hdr)->data)static bool sysctrl_wait_bit_clear (struct xe_sysctrl *sc,u32 bit_mask,unsigned int timeout_ms)
@@ -126,8 +126,8 @@ static int sysctrl_prepare_command(struct xe_device *xe,return -ENOMEM ; hdr = (struct xe_sysctrl_mailbox_msg_hdr *)buffer; - hdr->data =cpu_to_le32 (FIELD_PREP (SYSCTRL_HDR_GROUP_ID_MASK , group_id) | -FIELD_PREP (SYSCTRL_HDR_COMMAND_MASK , command)); + hdr->data =FIELD_PREP (SYSCTRL_HDR_GROUP_ID_MASK , group_id) | +FIELD_PREP (SYSCTRL_HDR_COMMAND_MASK , command);if (data_in && data_in_len)memcpy (buffer +sizeof (*hdr), data_in, data_in_len);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 241/281 Revert "wifi: ath12k: add panic handler"
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yingying Tang <[email protected]>
[ Upstream commit 05165f7b3b9ab6817fe216c24f90755b7c5e15f3 ]
ath12k_core_panic_handler() is invoked via atomic_notifier_call_chain(), which runs inside an RCU read-side critical section. The current code calls ath12k_pci_sw_reset() synchronously from this context, which eventually reaches mhi_device_get_sync() and schedule_timeout(), triggering a voluntary context switch within RCU.
Call trace: rcu_note_context_switch+0x4c4/0x508 (P) __schedule+0xbc/0x1204 schedule+0x34/0x110 schedule_timeout+0x84/0x11c __mhi_device_get_sync+0x164/0x228 [mhi] mhi_device_get_sync+0x1c/0x3c [mhi] ath12k_wifi7_pci_bus_wake_up+0x20/0x2c [ath12k_wifi7] ath12k_pci_read32+0x58/0x350 [ath12k] ath12k_pci_clear_dbg_registers+0x28/0xb8 [ath12k] ath12k_pci_panic_handler+0x20/0x44 [ath12k] ath12k_core_panic_handler+0x28/0x3c [ath12k] notifier_call_chain+0x78/0x1c0 atomic_notifier_call_chain+0x3c/0x5c
Revert change "wifi: ath12k: add panic handler" to avoid this issue.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
--- drivers/net/wireless/ath/ath12k/core.c | 33 +------------------------- drivers/net/wireless/ath/ath12k/core.h | 3 --- drivers/net/wireless/ath/ath12k/hif.h | 9 ------- drivers/net/wireless/ath/ath12k/pci.c | 8 ------- 4 files changed, 1 insertion(+), 52 deletions(-)
drivers/net/wireless/ath/ath12k/core.c +1 -32
@@ -1770,29 +1770,6 @@ int ath12k_core_pre_init(struct ath12k_base *ab)return 0 ; } -static int ath12k_core_panic_handler (struct notifier_block *nb, -unsigned long action,void *data) -{ - struct ath12k_base *ab = container_of(nb, struct ath12k_base, - panic_nb); - -return ath12k_hif_panic_handler (ab); -} - -static int ath12k_core_panic_notifier_register (struct ath12k_base *ab) -{ - ab->panic_nb.notifier_call = ath12k_core_panic_handler; - -return atomic_notifier_chain_register (&panic_notifier_list, - &ab->panic_nb); -} - -static void ath12k_core_panic_notifier_unregister (struct ath12k_base *ab) -{ -atomic_notifier_chain_unregister (&panic_notifier_list, - &ab->panic_nb); -} -static inline bool ath12k_core_hw_group_create_ready (struct ath12k_hw_group *ag) {
@@ -2208,18 +2185,13 @@ int ath12k_core_init(struct ath12k_base *ab)struct ath12k_hw_group *ag;int ret; - ret =ath12k_core_panic_notifier_register (ab); -if (ret) -ath12k_warn (ab,"failed to register panic handler: %d\n" , ret); -mutex_lock (&ath12k_hw_group_mutex); ag =ath12k_core_hw_group_assign (ab);if (!ag) {mutex_unlock (&ath12k_hw_group_mutex);ath12k_warn (ab,"unable to get hw group\n" ); - ret = -ENODEV ; - goto err_unregister_notifier; +return -ENODEV ; }mutex_unlock (&ath12k_hw_group_mutex);
@@ -2244,8 +2216,6 @@ int ath12k_core_init(struct ath12k_base *ab) err_unassign_hw_group:ath12k_core_hw_group_unassign (ab); -err_unregister_notifier: -ath12k_core_panic_notifier_unregister (ab);return ret; }
@@ -2254,7 +2224,6 @@ void ath12k_core_deinit(struct ath12k_base *ab) {ath12k_core_hw_group_destroy (ab->ag);ath12k_core_hw_group_unassign (ab); -ath12k_core_panic_notifier_unregister (ab); }void ath12k_core_free (struct ath12k_base *ab)
drivers/net/wireless/ath/ath12k/core.h +0 -3
@@ -15,7 +15,6 @@#include <linux/ctype.h> #include <linux/firmware.h> #include <linux/of_reserved_mem.h> -#include <linux/panic_notifier.h> #include <linux/average.h> #include <linux/of.h> #include <linux/rhashtable.h>
@@ -1146,8 +1145,6 @@ struct ath12k_base {#endif /* CONFIG_ACPI */ -struct notifier_block panic_nb; -struct ath12k_hw_group *ag;struct ath12k_wsi_info wsi_info;enum ath12k_firmware_mode fw_mode;
drivers/net/wireless/ath/ath12k/hif.h +0 -9
@@ -30,7 +30,6 @@ struct ath12k_hif_ops {void (*ce_irq_enable)(struct ath12k_base *ab);void (*ce_irq_disable)(struct ath12k_base *ab);void (*get_ce_msi_idx)(struct ath12k_base *ab,u32 ce_id,u32 *msi_idx); -int (*panic_handler)(struct ath12k_base *ab);void (*coredump_download)(struct ath12k_base *ab); };
@@ -149,14 +148,6 @@ static inline void ath12k_hif_power_down(struct ath12k_base *ab, bool is_suspend ab->hif.ops->power_down (ab, is_suspend); } -static inline int ath12k_hif_panic_handler (struct ath12k_base *ab) -{ -if (!ab->hif.ops->panic_handler) -return NOTIFY_DONE ; - -return ab->hif.ops->panic_handler (ab); -} -static inline void ath12k_hif_coredump_download (struct ath12k_base *ab) {if (ab->hif.ops->coredump_download)
drivers/net/wireless/ath/ath12k/pci.c +0 -8
@@ -1473,13 +1473,6 @@ void ath12k_pci_power_down(struct ath12k_base *ab, bool is_suspend)ath12k_pci_sw_reset (ab_pci->ab, false); } -static int ath12k_pci_panic_handler (struct ath12k_base *ab) -{ -ath12k_pci_sw_reset (ab, false); - -return NOTIFY_OK ; -} -static const struct ath12k_hif_ops ath12k_pci_hif_ops = { .start = ath12k_pci_start, .stop = ath12k_pci_stop,
@@ -1497,7 +1490,6 @@ static const struct ath12k_hif_ops ath12k_pci_hif_ops = { .ce_irq_enable = ath12k_pci_hif_ce_irq_enable, .ce_irq_disable = ath12k_pci_hif_ce_irq_disable, .get_ce_msi_idx = ath12k_pci_get_ce_msi_idx, - .panic_handler = ath12k_pci_panic_handler,#ifdef CONFIG_ATH12K_COREDUMP .coredump_download = ath12k_pci_coredump_download,#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 242/281 ASoC: adau1372: allow sleepy powerdown GPIOs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alvin Šipraga <[email protected]>
[ Upstream commit 64dd172bbffe6135d79a06ddad53dd2da4d8bd62 ]
gpiod_set_value() emits a WARN when the GPIO provider indicates that it can sleep, as is typical for I2C controlled GPIO expanders. Since the powerdown GPIO is only manipulated in process context, use the _cansleep variant instead. This lets the driver use sleepy GPIOs to power off the chip.
--- sound/soc/codecs/adau1372.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
sound/soc/codecs/adau1372.c +3 -4
@@ -798,7 +798,7 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable)if (ret)return ret;if (adau1372->pd_gpio) -gpiod_set_value (adau1372->pd_gpio,0 ); +gpiod_set_value_cansleep (adau1372->pd_gpio,0 );if (adau1372->switch_mode) adau1372->switch_mode (adau1372->dev);
@@ -818,8 +818,7 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable)ADAU1372_CLK_CTRL_PLL_EN , 0);regcache_cache_only (adau1372->regmap, true); -if (adau1372->pd_gpio) -gpiod_set_value (adau1372->pd_gpio,1 ); +gpiod_set_value_cansleep (adau1372->pd_gpio,1 );clk_disable_unprepare (adau1372->mclk);return ret; }
@@ -836,7 +835,7 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable)* map. No need to do any register writes to manually * turn things off. */ -gpiod_set_value (adau1372->pd_gpio,1 ); +gpiod_set_value_cansleep (adau1372->pd_gpio,1 );regcache_mark_dirty (adau1372->regmap); }else { regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 243/281 ASoC: adau1372: fix invalid default state of output ASRC and DAC muxes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alvin Šipraga <[email protected]>
[ Upstream commit 36d0e05815ce84266c8b138cd4f84c3feeaf15f4 ]
The chip has strange power-on reset values for the registers that control the output ASRC and DAC source muxes - the values are actually marked "Reserved" in the datasheet [1]. This means that until the underlying kcontrols are modified, the codec will not function as expected.
The registers in question are:
register / field reset value valid values
ASRCO_SOURCE_0_1 0x10 ASRC_OUT_SOURCE0 0 4 .. 16 ASRC_OUT_SOURCE1 1 4 .. 16 ASRCO_SOURCE_2_3 0x10 ASRC_OUT_SOURCE2 2 4 .. 16 ASRC_OUT_SOURCE3 3 4 .. 16 DAC_SOURCE_0_1 0x10 DAC_SOURCE_0 0 12 .. 13 DAC_SOURCE_1 1 12 .. 13
Set some sane defaults during the driver probe:
ADC Decimator0 -> Output ASRC0
ADC Decimator1 -> Output ASRC1
ADC Decimator2 -> Output ASRC2
ADC Decimator3 -> Output ASRC3
Input ASRC0 -> DAC0
Input ASRC1 -> DAC1
With this, the chip will behave as advertised by the default kcontrol state exposed by the driver.
[1] https://www.analog.com/media/en/technical-documentation/data-sheets/ADAU1372.pdf
--- sound/soc/codecs/adau1372.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
sound/soc/codecs/adau1372.c +11 -0
@@ -1002,6 +1002,17 @@ int adau1372_probe(struct device *dev, struct regmap *regmap,regmap_write (regmap,ADAU1372_REG_OP_STAGE_MUTE ,0x0 ); +/* +* Set sane default values for the Output ASRC and DAC input muxes, +* since the power-on reset defaults are invalid "Reserved" states. +*/ +regmap_write (regmap,ADAU1372_REG_ASRCO_SOURCE_0_1 , +0x54 );/* Decimator0, Decimator1 */ +regmap_write (regmap,ADAU1372_REG_ASRCO_SOURCE_2_3 , +0x76 );/* Decimator2, Decimator3 */ +regmap_write (regmap,ADAU1372_REG_DAC_SOURCE , +0xdc );/* Input ASRC0, Input ASRC1 */ +regmap_write (regmap,0x7 ,0x01 );/* CLOCK OUT */ return devm_snd_soc_register_component (dev, &adau1372_driver, &adau1372_dai_driver,1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 244/281 ASoC: samsung: i2s: enable op_clk in probe to balance runtime PM
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Szyprowski <[email protected]>
[ Upstream commit 7529254014764ae0b8c33d5c61a58f36c65b1a1c ]
The probe function puts the device into the runtime PM active state with only the "iis" bus clock enabled, but it also sets priv->op_clk to the parent of the RCLK_SRC mux without enabling it. The runtime PM suspend callback disables both clocks, so the first runtime suspend after probe disables op_clk once more than it was enabled.
On Exynos5250 Arndale both clocks resolve to the same EXYNOS_I2S_BUS gate, so this triggers the following warning during boot:
WARNING: drivers/clk/clk.c:1270 at clk_core_disable+0xe4/0x23c, CPU#0: kworker/u8:3/45 i2s_bus already disabled ... Workqueue: pm pm_runtime_work Call trace: ... clk_core_disable from clk_disable+0x28/0x34 clk_disable from i2s_runtime_suspend+0x58/0x68 [snd_soc_i2s] i2s_runtime_suspend [snd_soc_i2s] from genpd_runtime_suspend+0xc4/0x298 genpd_runtime_suspend from __rpm_callback+0x48/0x18c __rpm_callback from rpm_callback+0x4c/0x50 rpm_callback from rpm_suspend+0xf4/0x718 rpm_suspend from pm_runtime_work+0x9c/0xa0 ...
Fix this by enabling op_clk in probe right after getting it, so the clock state matches the runtime PM active state, and by disabling it in remove.
--- sound/soc/samsung/i2s.c | 8 ++++++++ 1 file changed, 8 insertions(+)
sound/soc/samsung/i2s.c +8 -0
@@ -1518,9 +1518,16 @@ static int samsung_i2s_probe(struct platform_device *pdev) goto err_disable_pm; priv->op_clk =clk_get_parent (priv->clk_table[CLK_I2S_RCLK_SRC ]); + ret =clk_prepare_enable (priv->op_clk); +if (ret <0 ) { + priv->op_clk =NULL ; + goto err_unregister_clk; + }return 0 ; +err_unregister_clk: +i2s_unregister_clock_provider (priv); err_disable_pm:pm_runtime_disable (&pdev->dev); err_del_sec:
@@ -1543,6 +1550,7 @@ static void samsung_i2s_remove(struct platform_device *pdev)i2s_unregister_clock_provider (priv);i2s_delete_secondary_device (priv); +clk_disable_unprepare (priv->op_clk);clk_disable_unprepare (priv->clk);pm_runtime_put_noidle (&pdev->dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 245/281 spi: sg2044-nor: Return transfer errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Han Gao <[email protected]>
[ Upstream commit e130c54fbb603f9aeacf5029789b4a2c8c48f416 ]
The transfer helpers return errors, but both dispatchers discard them. Return these errors to the SPI memory layer instead of reporting success.
--- drivers/spi/spi-sg2044-nor.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/spi/spi-sg2044-nor.c +9 -8
@@ -307,15 +307,15 @@ static ssize_t sg2044_spifmc_tran_cmd(struct sg2044_spifmc *spifmc,return 0 ; } -static void sg2044_spifmc_trans (struct sg2044_spifmc *spifmc, -const struct spi_mem_op *op) +static int sg2044_spifmc_trans (struct sg2044_spifmc *spifmc, +const struct spi_mem_op *op) {if (op->data.dir ==SPI_MEM_DATA_IN ) -sg2044_spifmc_read (spifmc, op); +return sg2044_spifmc_read (spifmc, op);else if (op->data.dir ==SPI_MEM_DATA_OUT ) -sg2044_spifmc_write (spifmc, op); +return sg2044_spifmc_write (spifmc, op);else -sg2044_spifmc_tran_cmd (spifmc, op); +return sg2044_spifmc_tran_cmd (spifmc, op); } static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc,
@@ -389,19 +389,20 @@ static int sg2044_spifmc_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) {struct sg2044_spifmc *spifmc; +int ret; spifmc =spi_controller_get_devdata (mem->spi->controller);mutex_lock (&spifmc->lock);if (op->addr.nbytes ==0 ) -sg2044_spifmc_trans_reg (spifmc, op); + ret =sg2044_spifmc_trans_reg (spifmc, op);else -sg2044_spifmc_trans (spifmc, op); + ret =sg2044_spifmc_trans (spifmc, op);mutex_unlock (&spifmc->lock); -return 0 ; +return ret; }static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 246/281 spi: sg2044-nor: Honor SPI clock limits
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Han Gao <[email protected]>
[ Upstream commit 61d44a2c07b2ad3f7db167f17ff2461ffe78441c ]
The fixed divider ignores device and operation clock limits. Calculate the divider before each transfer so SCK does not exceed either limit.
Advertise the controller maximum speed so the SPI core supplies a default when the device omits its maximum speed. Use the fastest divider when no speed limit is supplied.
--- drivers/spi/spi-sg2044-nor.c | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
drivers/spi/spi-sg2044-nor.c +39 -0
@@ -385,6 +385,29 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc,return 0 ; } +static int sg2044_spifmc_set_speed (struct sg2044_spifmc *spifmc, +unsigned int speed_hz) +{ +unsigned long rate =clk_get_rate (spifmc->clk); +u64 div; +u32 reg; + +if (!rate) +return -EINVAL ; + +/* SCK = HCLK / (2 * (SckDiv + 1)); round down the resulting SCK. */ + div = speed_hz ?DIV_ROUND_UP_ULL (rate,2ULL * speed_hz) :1 ; +if (div >SPIFMC_CTRL_SCK_DIV_MASK +1 ) +return -EINVAL ; + + reg =readl (spifmc->io_base +SPIFMC_CTRL ); + reg &= ~SPIFMC_CTRL_SCK_DIV_MASK ; + reg |= div -1 ; +writel (reg, spifmc->io_base +SPIFMC_CTRL ); + +return 0 ; +} +static int sg2044_spifmc_exec_op (struct spi_mem *mem,const struct spi_mem_op *op) {
@@ -395,11 +418,18 @@ static int sg2044_spifmc_exec_op(struct spi_mem *mem,mutex_lock (&spifmc->lock); + ret =sg2044_spifmc_set_speed (spifmc, op->max_freq ? +min (op->max_freq, mem->spi->max_speed_hz) : + mem->spi->max_speed_hz); +if (ret) + goto out_unlock; +if (op->addr.nbytes ==0 ) ret =sg2044_spifmc_trans_reg (spifmc, op);else ret =sg2044_spifmc_trans (spifmc, op); +out_unlock:mutex_unlock (&spifmc->lock);return ret;
@@ -409,6 +439,10 @@ static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = { .exec_op = sg2044_spifmc_exec_op, }; +static const struct spi_controller_mem_caps sg2044_spifmc_mem_caps = { + .per_op_freq = true, +}; +static void sg2044_spifmc_init (struct sg2044_spifmc *spifmc) {u32 tran_csr;
@@ -448,6 +482,10 @@ static int sg2044_spifmc_probe(struct platform_device *pdev)if (IS_ERR (spifmc->clk))return dev_err_probe (dev,PTR_ERR (spifmc->clk),"Cannot get and enable AHB clock\n" ); + ctrl->max_speed_hz =clk_get_rate (spifmc->clk) /2 ; +if (!ctrl->max_speed_hz) +return dev_err_probe (dev, -EINVAL ,"Invalid AHB clock rate\n" ); + spifmc->dev = &pdev->dev; spifmc->ctrl = ctrl;
@@ -459,6 +497,7 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) ctrl->bits_per_word_mask =SPI_BPW_MASK (8 ); ctrl->auto_runtime_pm = false; ctrl->mem_ops = &sg2044_spifmc_mem_ops; + ctrl->mem_caps = &sg2044_spifmc_mem_caps; ctrl->mode_bits =SPI_RX_DUAL |SPI_TX_DUAL |SPI_RX_QUAD |SPI_TX_QUAD ; ret =devm_mutex_init (dev, &spifmc->lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 247/281 cgroup/cpuset: Call is_in_v2_mode() after acquiring cpuset_mutex in cpuset_handle_hotplug()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waiman Long <[email protected]>
[ Upstream commit b8eb5fd5bdb4c03758b056c70e5e1d962ca89757 ]
It is reported by sashiko that calling is_in_v2_mode() outside of cpuset_mutex critical section in cpuset_handle_hotplug() can introduce a TOCTOU race where cgroup hierarchy may have changed from v1 to v2 or vice versa after is_in_v2_mode() is called leading to erroneously skip the allocation of tmpmasks or incorrectly modify cpus_allowed masks, resulting in cpuset state corruption. Fix that by calling is_in_v2_mode() after acquiring the cpuset_mutex.
--- kernel/cgroup/cpuset.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/cgroup/cpuset.c +5 -4
@@ -3925,15 +3925,16 @@ static void cpuset_handle_hotplug(void)static cpumask_t new_cpus;static nodemask_t new_mems;bool cpus_updated, mems_updated; -bool on_dfl =is_in_v2_mode (); +bool on_dfl;struct tmpmasks tmp, *ptmp =NULL ; -if (on_dfl && !alloc_tmpmasks (&tmp)) - ptmp = &tmp; -lockdep_assert_cpus_held ();mutex_lock (&cpuset_mutex); + on_dfl =is_in_v2_mode (); +if (on_dfl && !alloc_tmpmasks (&tmp)) + ptmp = &tmp; +/* fetch the available cpus/mems and find out which changed how */ cpumask_copy (&new_cpus, cpu_active_mask); new_mems = node_states[N_MEMORY ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 248/281 flow_dissector: avoid u16 truncation of skb->len when computing thoff
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 99bda1ecbd56905f8bd934b9cdccd3bd251192b1 ]
__skb_flow_dissect() computes key_control->thoff using:
key_control->thoff = min_t(u16, nhoff, skb ? skb->len : hlen);
min_t(u16, ...) casts both arguments to u16 before comparing them. Whenever skb->len (or hlen) modulo 65536 is smaller than nhoff, the truncated length wins and thoff is set to a bogus small value, even when the dissection succeeded. For example, an IPv4/TCP frame with skb->len == 65540 and nhoff == 34 gets thoff == 4, so callers such as skb_probe_transport_header() point the transport header inside the Ethernet header.
Such skbs are not exotic:
- At the time of commit d0c081b49137 ("flow_dissector: properly cap thoff field"), AF_PACKET with PACKET_VNET_HDR could already build GSO skbs larger than 64KB (MTU checks are skipped for GSO, and alloc_skb_with_frags() accepted up to MAX_SKB_FRAGS (17) order-0 pages on top of the linear part).
- BIG TCP now makes skbs larger than 64KB common.
- The following patch makes tun_get_user() dissect IFF_TAP frames before eth_type_trans() pulls the Ethernet header, so a GSO frame carrying a 65522..65535 byte L3 packet will be dissected with skb->len in [65536, 65549].
Compare as u32 instead. If the resulting offset cannot be represented in the u16 key_control->thoff, cap it to U16_MAX and report the dissection as failed rather than silently returning a wrong transport offset. thoff is still set on failure, as some callers (such as eth_get_headlen()) use it regardless of the return value.
--- net/core/flow_dissector.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
net/core/flow_dissector.c +8 -1
@@ -1071,6 +1071,7 @@ bool __skb_flow_dissect(const struct net *net,int mpls_lse =0 ;int num_hdrs =0 ;u8 ip_proto =0 ; +u32 thoff;bool ret;if (!data) {
@@ -1692,7 +1693,13 @@ bool __skb_flow_dissect(const struct net *net, ret = true; out: - key_control->thoff =min_t (u16, nhoff, skb ? skb->len : hlen); + thoff =min_t (u32, nhoff, skb ? skb->len : hlen); +if (unlikely (thoff >U16_MAX )) { +/* Cannot be represented in key_control->thoff. */ + thoff =U16_MAX ; + ret = false; + } + key_control->thoff = thoff; key_basic->n_proto = proto; key_basic->ip_proto = ip_proto;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 249/281 net: always dissect GSO packets in __virtio_net_hdr_to_skb()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 44378d02c5aaae24b60a75fbbb539b4aa4db503d ]
Commit 9e8db5913264 ("net: avoid false positives in untrusted gso validation") added a '&& skb->network_header' check before flow-dissecting GSO packets without VIRTIO_NET_HDR_F_NEEDS_CSUM in __virtio_net_hdr_to_skb(), because some callers (such as tun_get_user(), tun_xdp_one(), virtnet_receive_done(), and raw_verify_header()) called virtio_net_hdr_*_to_skb() before initializing skb->network_header and skb->dev.
Because __alloc_skb() and __build_skb_around() zero-initialize skb->network_header to 0 (unlike mac_header and transport_header which are initialized to ~0U), those four callers always had skb->network_header == 0 and bypassed flow dissection in __virtio_net_hdr_to_skb(). More generally, skb->network_header is an offset from skb->head (where 0 is also a valid offset whenever skb_headroom(skb) is 0), not a boolean flag.
Whenever the 'if (gso_type && skb->network_header)' branch was skipped, the fallback 'else if (gso_type)' only pulled nh_min_len + thlen (40 bytes for TCPv4) without dissecting the packet, without validating ip_proto or n_proto, and without setting skb->transport_header.
If the packet has a malformed network header, it is not rejected and a subsequent skb_probe_transport_header() also fails, leaving skb->transport_header at ~0U (0xffff). Similarly, if an IPv4 packet carries IP options (ihl > 5) or an IPv6 packet carries extension headers, pulling only nh_min_len + thlen can leave the TCP header outside skb->head. In both cases, tcp_hdrlen(skb) in skb_gso_transport_seglen() reads out-of-bounds:
BUG: KASAN: slab-out-of-bounds in skb_gso_transport_seglen Read of size 2 by task poc/133 skb_gso_transport_seglen (net/core/gso.c:155) skb_gso_validate_mac_len (net/core/gso.c:270) tbf_enqueue (net/sched/sch_tbf.c:260) dev_qdisc_enqueue (net/core/dev.c:4227) __dev_queue_xmit (net/core/dev.c:4884)
In addition, checking virtio_net_hdr_match_proto() only inside 'if (!skb->protocol)' before flow dissection both skipped validation when skb->protocol was pre-set by the caller and rejected VLAN-tagged frames whose outer L2 protocol is ETH_P_8021Q or ETH_P_8021AD.
Fix this by:
1. Initializing skb->dev and skb->network_header (plus skb->protocol for IFF_TUN) before virtio_net_hdr_*_to_skb() in tun_get_user(), tun_xdp_one(), virtnet_receive_done(), and raw_verify_header(). In tun_get_user(), drop the redundant skb_reset_mac_header(skb) in the IFF_TUN case since __virtio_net_hdr_to_skb() unconditionally resets mac_header.
2. Removing '&& skb->network_header' and the unvalidated 'else if (gso_type)' fallback in __virtio_net_hdr_to_skb() so all GSO packets without VIRTIO_NET_HDR_F_NEEDS_CSUM are flow-dissected, have their transport header pulled into linear data, and have skb->transport_header set.
3. Moving the virtio_net_hdr_match_proto() check to after skb_flow_dissect_flow_keys_basic(), validating keys.basic.n_proto against hdr_gso_type.
--- arch/um/drivers/vector_transports.c | 1 + drivers/net/tun.c | 23 ++++++----- drivers/net/virtio_net.c | 2 + include/linux/virtio_net.h | 60 ++++++++++++----------------- 4 files changed, 42 insertions(+), 44 deletions(-)
arch/um/drivers/vector_transports.c +1 -0
@@ -209,6 +209,7 @@ static int raw_verify_header(if ((vheader->flags &VIRTIO_NET_HDR_F_DATA_VALID ) >0 )return 1 ; +skb_set_network_header (skb,ETH_HLEN );virtio_net_hdr_to_skb (skb, vheader,virtio_legacy_is_little_endian ());return 0 ; }
drivers/net/tun.c +14 -9
@@ -1818,12 +1818,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } } -if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, &hdr)) { -atomic_long_inc (&tun->rx_frame_errors); - err = -EINVAL ; - goto free_skb; - } - + skb->dev = tun->dev;switch (tun->flags &TUN_TYPE_MASK ) {case IFF_TUN :if (tun->flags &IFF_NO_PI ) {
@@ -1848,9 +1843,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } } -skb_reset_mac_header (skb); +skb_reset_network_header (skb); skb->protocol = pi.proto; - skb->dev = tun->dev;break ;case IFF_TAP :if (!pskb_may_pull (skb,ETH_HLEN )) {
@@ -1858,10 +1852,19 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, drop_reason =SKB_DROP_REASON_HDR_TRUNC ; goto drop; } - skb->protocol =eth_type_trans (skb, tun->dev); +skb_set_network_header (skb,ETH_HLEN );break ; } +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, &hdr)) { +atomic_long_inc (&tun->rx_frame_errors); + err = -EINVAL ; + goto free_skb; + } + +if ((tun->flags &TUN_TYPE_MASK ) ==IFF_TAP ) + skb->protocol =eth_type_trans (skb, tun->dev); +/* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) {skb_zcopy_init (skb, msg_control);
@@ -2469,6 +2472,8 @@ static int tun_xdp_one(struct tun_struct *tun, features =tun_vnet_hdr_guest_features (READ_ONCE (tun->vnet_hdr_sz)); tnl_hdr = (struct virtio_net_hdr_v1_hash_tunnel *)gso; + skb->dev = tun->dev; +skb_set_network_header (skb,ETH_HLEN );if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, tnl_hdr)) {atomic_long_inc (&tun->rx_frame_errors);kfree_skb (skb);
drivers/net/virtio_net.c +2 -0
@@ -2515,6 +2515,8 @@ static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue * goto frame_err; } + skb->dev = dev; +skb_set_network_header (skb,ETH_HLEN );if (virtio_net_hdr_tnl_to_skb (skb, &hdr->tnl_hdr, vi->rx_tnl, vi->rx_tnl_csum,virtio_is_little_endian (vi->vdev))) {
include/linux/virtio_net.h +25 -35
73 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 250/281 net: sparx5: start the TOD counters on non-PTP lan969x variants
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quentin Freimanis <[email protected]>
[ Upstream commit ca5012757bf027df83d51593a07656cb8c80caee ]
Currently the base non-PTP-capable lan969x variants don't forward any Ethernet frames:
$ ip link set eth10 up
$ ip addr add 10.0.0.54/24 dev eth10
$ ping 10.0.0.1 PING 10.0.0.1 (10.0.0.1): 56 data bytes
--- 10.0.0.1 ping statistics ---
7 packets transmitted, 0 packets received, 100% packet loss
$ cat /proc/interrupts | grep fdma
20: 0 GIC-0 120 Level sparx5-fdma $ ip -s link show dev eth10
RX: bytes packets errors dropped missed mcast
20828 102 0 0 0 72
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0Testing showed that starting the domain 0 TOD counter gets networking working again, but start all three for consistency with the PTP variants.
--- .../ethernet/microchip/sparx5/sparx5_ptp.c | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-)
drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +16 -12
@@ -622,18 +622,9 @@ int sparx5_ptp_init(struct sparx5 *sparx5) sparx5->ptp =1 ; } -if (!sparx5->ptp) -return 0 ; - -for (i =0 ; i <SPARX5_PHC_COUNT ; ++i) { - err =sparx5_ptp_phc_init (sparx5, i, &sparx5_ptp_clock_info); -if (err) -return err; - } - -spin_lock_init (&sparx5->ptp_clock_lock); -spin_lock_init (&sparx5->ptp_ts_id_lock); -mutex_init (&sparx5->ptp_lock); +/* The base, non-PTP-capable lan969x variants need the +* first TOD counter running to forward frames. +*/ /* Disable master counters */ spx5_wr (PTP_PTP_DOM_CFG_PTP_ENA_SET (0 ), sparx5,PTP_PTP_DOM_CFG );
@@ -657,6 +648,19 @@ int sparx5_ptp_init(struct sparx5 *sparx5)/* Enable master counters */ spx5_wr (PTP_PTP_DOM_CFG_PTP_ENA_SET (0x7 ), sparx5,PTP_PTP_DOM_CFG ); +if (!sparx5->ptp) +return 0 ; + +for (i =0 ; i <SPARX5_PHC_COUNT ; ++i) { + err =sparx5_ptp_phc_init (sparx5, i, &sparx5_ptp_clock_info); +if (err) +return err; + } + +spin_lock_init (&sparx5->ptp_clock_lock); +spin_lock_init (&sparx5->ptp_ts_id_lock); +mutex_init (&sparx5->ptp_lock); +for (i =0 ; i < sparx5->data->consts->n_ports; i++) { port = sparx5->ports[i];if (!port)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 251/281 devlink: fix devlink_rel reference leak when notify work is pending
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haishuang Yan <[email protected]>
[ Upstream commit c5381ae1cd1481cc431c2bd79460472c6f1d3ce3 ]
devlink_rel_nested_in_notify_work_schedule() takes a reference on the devlink_rel for the notify work and then queues the work, ignoring the return value of schedule_delayed_work(). If the work is already pending, nothing new is queued, the work runs only once and drops only one reference, so the extra one is leaked together with the devlink_rel and its index in devlink_rels.
This is easy to hit. devl_register() of a nested instance queues the work, and if the instance is unregistered before the work has run, devlink_rel_put() queues it again while it is still pending. The work also keeps rescheduling itself for as long as the parent devlink lock cannot be taken, which widens the window. Registering and unregistering a nested devlink instance 100 times while holding the parent lock leaks all 100 devlink_rel objects.
The reschedule path in devlink_rel_nested_in_notify_work() has the same
problem: if the work was queued again while it was running, the reference it holds is never dropped.
Drop the reference in both places when the work was already pending. The pending work holds its own reference, so this can not be the last one.
--- net/devlink/core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
net/devlink/core.c +8 -2
@@ -96,13 +96,19 @@ static void devlink_rel_nested_in_notify_work(struct work_struct *work)return ; reschedule_work: -schedule_delayed_work (&rel->nested_in.notify_work,1 ); +/* The work may have been queued again meanwhile, which took its own +* reference. Drop ours in that case. +*/ +if (!schedule_delayed_work (&rel->nested_in.notify_work,1 )) +__devlink_rel_put (rel); }static void devlink_rel_nested_in_notify_work_schedule (struct devlink_rel *rel) {__devlink_rel_get (rel); -schedule_delayed_work (&rel->nested_in.notify_work,0 ); +/* The pending work holds a reference already, drop the new one. */ +if (!schedule_delayed_work (&rel->nested_in.notify_work,0 )) +__devlink_rel_put (rel); }static struct devlink_rel *devlink_rel_alloc (void )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 252/281 net/sched: cls_route: reject change with no routing attribute
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit b72e025ac556984766392001e74b7587e145bcd0 ]
route4_change on a filter change that supplies no routing attribute at all (no to/from/iif) reaches route4_set_parms with fold set, so the handle-mismatch check -- which only fires when creating -- is skipped. nhandle is built as the all-wildcard bucket (0x8000 | 0xFFFF << 16) and the filter is rekeyed to 0xFFFF8000, so the handle userspace already stored no longer addresses the filter. (The netlink reply carries the new handle; what goes stale is the handle the caller holds.)
The rekey actually misclassifies the packets. The new handle's from_hash is the wildcard chain (route4_hash_wild), whose filters route4_classify applies without any id/iif comparison, and its bucket (256) is reached by every packet through the h < 256 restart. A filter that matched only a specific realm therefore becomes an unconditional catch-all that applies its classid and actions to all otherwise-unmatched traffic.
Reject a change that would rekey the filter this way, with the routing-attributes-required error. A change that names at least one of to/from/iif, and an in-place replace that keeps the routing identity via a supplied attribute, are unaffected. A change of a filter that already sits at the wildcard handle is a metadata-only update and remains valid, so only a change that actually moves the handle is rejected.
This is a follow-up to commit 41e85e54e564 ("net/sched: cls_route: Fix in-place replace"); this patch closes the no-routing-attribute variant that the parent series did not address.
Conditions to recreate the bug:
tc qdisc add dev lo clsact
tc filter add dev lo ingress protocol ip pref 100 route from 1 to 1 tc filter change dev lo ingress protocol ip pref 100 handle 0x10001 \
route classid 1:2
tc filter show dev lo ingress # handle became 0xffff8000; iproute2's
# stored 0x10001 no longer addresses it
A change of a filter already at the wildcard handle, e.g. one created by "tc filter add dev lo ingress protocol ip pref 100 route classid 1:1" (iproute2 defaults the handle to 0xffff8000), stays a valid metadata-only update.
--- net/sched/cls_route.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/sched/cls_route.c +6 -0
@@ -434,6 +434,12 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,if (handle && (!fold || nhandle == (handle & ~0x7F00 ))) nhandle |= handle &0x7F00 ; +if (fold && !tb[TCA_ROUTE4_TO ] && !tb[TCA_ROUTE4_FROM ] && + !tb[TCA_ROUTE4_IIF ] && nhandle != fold->handle) { +NL_SET_ERR_MSG (extack,"Routing attributes required" ); +return -EINVAL ; + } +if (handle && !fold && nhandle != handle) { NL_SET_ERR_MSG_FMT(extack, "Handle mismatch constructed: %x (expected: %x)",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 253/281 Revert "net/mlx5: E-Switch, preserve max tx speed on vport state modification"
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Or Har-Toov <[email protected]>
[ Upstream commit 86b785b18f8f4a6b4a7392d1e2b29d6bb095011a ]
This reverts commit ad0ae7aefa7a ("net/mlx5: E-Switch, preserve max tx speed on vport state modification").
mlx5_modify_vport_admin_state() and mlx5_esw_adj_vport_modify() query the vport's current max_tx_speed before modifying its state, and write that value back, to avoid resetting it to 0 as a side effect of an unrelated admin-state change.
That's unnecessary: max_tx_speed is optional in MODIFY_VPORT_STATE - FW skips writing it whenever it's 0, treating that as "not provided" rather than "reset to zero". Leaving it unset already preserves FW's current value, with no query needed.
--- .../mellanox/mlx5/core/esw/adj_vport.c | 20 ---------------- .../net/ethernet/mellanox/mlx5/core/vport.c | 24 ------------------- 2 files changed, 44 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c +0 -20
@@ -11,26 +11,6 @@ int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev, u16 vport,lockdep_assert_held (&dev->priv.eswitch->state_lock); -if (MLX5_CAP_ESW (dev, esw_vport_state_max_tx_speed)) { -u8 op_mod =MLX5_VPORT_STATE_OP_MOD_ESW_VPORT ; -struct mlx5_vport *esw_vport; -u32 speed =0 ; -int err; - - err =mlx5_query_vport_max_tx_speed (dev, op_mod, vport, - true, &speed,NULL ); -if (err) { - esw_vport =mlx5_eswitch_get_vport (dev->priv.eswitch, - vport); - speed =IS_ERR (esw_vport) ?0 : - esw_vport->agg_max_tx_speed; -mlx5_core_dbg (dev, -"Failed to query vport %d max tx speed, err=%d, using cached %u\n" , - vport, err, speed); - } -MLX5_SET (modify_vport_state_in, in, max_tx_speed, speed); - } -MLX5_SET (modify_vport_state_in, in, opcode,MLX5_CMD_OP_MODIFY_VPORT_STATE ); MLX5_SET(modify_vport_state_in, in, op_mod,
drivers/net/ethernet/mellanox/mlx5/core/vport.c +0 -24
@@ -93,30 +93,6 @@ int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,lockdep_assert_held (&mdev->priv.eswitch->state_lock);#endif -if (MLX5_CAP_ESW (mdev, esw_vport_state_max_tx_speed) && - opmod ==MLX5_VPORT_STATE_OP_MOD_ESW_VPORT && - vport !=MLX5_VPORT_UPLINK ) { -u32 speed =0 ; -int err; - - err =mlx5_query_vport_max_tx_speed (mdev, opmod, vport, - other_vport, &speed,NULL ); -if (err) { -#ifdef CONFIG_MLX5_ESWITCH -struct mlx5_vport *esw_vport; - - esw_vport =mlx5_eswitch_get_vport (mdev->priv.eswitch, - vport); - speed =IS_ERR (esw_vport) ?0 : - esw_vport->agg_max_tx_speed; -#endif -mlx5_core_dbg (mdev, -"Failed to query vport %d max tx speed, err=%d, using cached %u\n" , - vport, err, speed); - } -MLX5_SET (modify_vport_state_in, in, max_tx_speed, speed); - } -MLX5_SET (modify_vport_state_in, in, opcode,MLX5_CMD_OP_MODIFY_VPORT_STATE );MLX5_SET (modify_vport_state_in, in, op_mod, opmod);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 254/281 net: bcmgenet: if UMAC was suspended in SW_RESET, restore it to SW_RESET
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Chen <[email protected]>
[ Upstream commit 5857e5a196b069766a39ceb754736326a43bc6bc ]
When the revised suspend/resume sequence was introduced this led to an edge case where the TX is left disabled in the following sequence.
1. phy link is down, so UMAC is held in reset and then network interface is WoL enabled
2. Enter suspend, bcmgenet_wol_power_down_cfg() enables UMAC_RX since MAC is in SW_RESET
4. Enter resume, UMAC_RX is left enabled. Since we only enable UMAC_TX and UMAC_RX in SW_RESET. The UMAC_TX is never enabled again on link up.
The issue was hit in production and I verified the sequence keeps the TX disabled in my standalone test. I tested on an internal development board with bcm77122, but should be reproducible on any genet HW that supports power management.
--- drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c +11 -0
@@ -253,6 +253,17 @@ int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, reg =bcmgenet_umac_readl (priv,UMAC_CMD ); reg &= ~CMD_CRC_FWD ;bcmgenet_umac_writel (priv, reg,UMAC_CMD ); + +/* +* Mirror wol_power_down_cfg(). If only UMAC_RX +* is enabled, then we must place the UMAC back +* into SW_RESET. +*/ + reg =bcmgenet_umac_readl (priv,UMAC_CMD ); +if ((reg &CMD_RX_EN ) && !(reg &CMD_TX_EN )) { + reg |=CMD_SW_RESET ; +bcmgenet_umac_writel (priv, reg,UMAC_CMD ); + }spin_unlock_bh (&priv->reg_lock);/* Resume link status tracking */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 255/281 pfcp: make sure the SEID is linear before reading it
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haishuang Yan <[email protected]>
[ Upstream commit 23609bce9e1de525d1d0e73fc68c6e7971d0b49e ]
pfcp_encap_recv() only makes sure that the UDP header and the 4 byte PFCP header are in the linear area. When the S flag is set, pfcp_session_recv() then reads the 8 byte SEID that follows, which is not covered by the pskb_may_pull() check.
A short PFCP packet with the S flag set, or one whose session header lies in a fragment, therefore makes pfcp_session_recv() read beyond the end of the packet data, and whatever it finds there is stored in the tunnel metadata that flower later classifies on.
Pull up to the end of the SEID before reading it, and drop packets that are too short to contain it. Reload the header pointer afterwards since pskb_may_pull() may reallocate the skb head.
Use offsetofend() rather than sizeof(struct pfcphdr_session): the structure is not packed, so its size is 16 bytes because of the alignment of the __be64 member, while the header on the wire is only 12 bytes, and valid session messages would be dropped.
--- drivers/net/pfcp.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/net/pfcp.c +7 -0
@@ -64,6 +64,13 @@ static int pfcp_encap_recv(struct sock *sk, struct sk_buff *skb) goto drop; unparsed =pfcp_hdr (skb); +if (unparsed->flags &PFCP_SEID_FLAG ) { +if (unlikely (!pskb_may_pull (skb,PFCP_HLEN + + offsetofend(struct pfcphdr_session, + seid)))) + goto drop; + unparsed =pfcp_hdr (skb); + }ip_tunnel_flags_zero (flags); tun_dst = udp_tun_rx_dst(skb, sk->sk_family, flags, 0,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 256/281 ASoC: sdw_utils: Add codec info for CS35L62
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Binding <[email protected]>
[ Upstream commit 0306d211558196e051b83e067ee4c30e1b6f94a8 ]
CS35L62 is very similar to CS35L63, and uses the same driver, so we can use the same configuration.
Stable-dep-of: 4b7f393a01f8 ("ASoC: sdw_utils: Switch CS35L56/57/62/63 to use OT25 DAI")
--- sound/soc/sdw_utils/soc_sdw_utils.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
sound/soc/sdw_utils/soc_sdw_utils.c +29 -0
@@ -834,6 +834,35 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, .dai_num = 2, }, + { + .vendor_id = 0x01fa, + .part_id = 0x3562, + .name_prefix = "AMP", + .is_amp = true, + .dais = { + { + .direction = {true, false}, + .dai_name = "cs35l56-sdw1", + .component_name = "cs35l56", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .init = asoc_sdw_cs_amp_init, + .rtd_init = asoc_sdw_cs_spk_rtd_init, + .controls = generic_spk_controls, + .num_controls = ARRAY_SIZE(generic_spk_controls), + .widgets = generic_spk_widgets, + .num_widgets = ARRAY_SIZE(generic_spk_widgets), + }, + { + .direction = {false, true}, + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, + .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, + }, { .vendor_id = 0x01fa, .part_id = 0x3563,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 257/281 ASoC: sdw_utils: Switch CS35L56/57/62/63 to use OT25 DAI
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit 4b7f393a01f88e8d03cba94cf4948652de14789b ]
Change the CS35L56/57/62/63 entries in codec_info_list[] to use the OT25 DAI for amp feedback.
The AMP feedback was incorrectly using a DAI connected to DP3, but that is reserved for SDCA companion amp. The correct amp output is DP4, which is the SDCA OT25 feedback output.
--- sound/soc/sdw_utils/soc_sdw_utils.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
sound/soc/sdw_utils/soc_sdw_utils.c +4 -4
@@ -797,7 +797,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, { .direction = {false, true}, - .dai_name = "cs35l56-sdw1c", + .dai_name = "cs35l56-ot25", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
@@ -826,7 +826,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, { .direction = {false, true}, - .dai_name = "cs35l56-sdw1c", + .dai_name = "cs35l56-ot25", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
@@ -855,7 +855,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, { .direction = {false, true}, - .dai_name = "cs35l56-sdw1c", + .dai_name = "cs35l56-ot25", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
@@ -884,7 +884,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, { .direction = {false, true}, - .dai_name = "cs35l56-sdw1c", + .dai_name = "cs35l56-ot25", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 258/281 riscv: fix NR_CPUS range leaking 64-bit default onto 32-bit builds
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leo Yu-Chi Liang <[email protected]>
[ Upstream commit 07e23d0b6f58eec351e52058b9a0a8af7b019c09 ]
rv32_defconfig is built in two stages:
- the generic (64-bit-oriented) "defconfig" target runs first and resolves a concrete NR_CPUS value using the 64BIT default.
- then 32-bit.config is merged on top via merge_config.sh, which does not touch NR_CPUS.
The final "make olddefconfig" pass sees NR_CPUS=256 is still within the "2 512" range therefore never recalculates it against the 32BIT default of 32, silently keeping the leaked 64-bit-stage value.
The NR_CPUS-scaled regions (percpu areas, ...etc) of the resulting CONFIG_NR_CPUS=256 on a 32-bit build bloats and does not fit in the percpu/stack layout for rv32 and hangs early boot on QEMU's virt machine.
Fix this by explicitly specify the range for 32/64 and SBI_V01/!SBI_V01.
--- arch/riscv/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
arch/riscv/Kconfig +3 -2
@@ -450,9 +450,10 @@ config SMP config NR_CPUS int "Maximum number of CPUs (2-512)" depends on SMP - range 2 512 if !RISCV_SBI_V01 - range 2 32 if RISCV_SBI_V01 && 32BIT + range 2 512 if !RISCV_SBI_V01 && 64BIT + range 2 128 if !RISCV_SBI_V01 && 32BIT range 2 64 if RISCV_SBI_V01 && 64BIT + range 2 32 if RISCV_SBI_V01 && 32BIT default "32" if 32BIT default "64" if RISCV_SBI_V01 && 64BIT default "256" if !RISCV_SBI_V01 && 64BIT
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 259/281 riscv: ptrace: Zero-initialize regset buffers before copyin
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zongmin Zhou <[email protected]>
[ Upstream commit 59052a88ba5fe4a7bb34b3a0a42aa5d453d3bb97 ]
user_regset_copyin() only copies count bytes, so a PTRACE_SETREGSET request with a short iov_len leaves the rest of the stack buffer uninitialized. In riscv_vr_set() that garbage is validated and copied into the target's vstate, and in riscv_cfi_set() it can be stored as the target's shadow stack pointer. Zero both buffers, as the get path already does.
--- arch/riscv/kernel/ptrace.c | 2 ++ 1 file changed, 2 insertions(+)
arch/riscv/kernel/ptrace.c +2 -0
@@ -230,6 +230,7 @@ static int riscv_vr_set(struct task_struct *target,return -ENODATA ;/* Copy rest of the vstate except datap */ +memset (&ptrace_vstate,0 ,sizeof (ptrace_vstate)); ret =user_regset_copyin (&pos, &count, &kbuf, &ubuf, &ptrace_vstate,0 ,sizeof (struct __riscv_v_regset_state ));if (unlikely (ret))
@@ -345,6 +346,7 @@ static int riscv_cfi_set(struct task_struct *target, regs =task_pt_regs (target); +memset (&user_cfi,0 ,sizeof (user_cfi)); ret =user_regset_copyin (&pos, &count, &kbuf, &ubuf, &user_cfi,0 ,-1 );if (ret)return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 260/281 bnxt_en: Add bnxt_clear_bars() helper
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Chan <[email protected]>
[ Upstream commit 8f5d59b9cfa69beab3b52cca621bb301c5af98aa ]
In bnxt_io_slot_reset(), we clear the 6 BAR registers. Add a helper function to do that. The helper will be used again in the next patch.
Stable-dep-of: 386f887c8290 ("bnxt_en: Fix driver init in kdump kernel")
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +10 -6
@@ -17066,6 +17066,14 @@ void bnxt_print_device_info(struct bnxt *bp)pcie_print_link_status (bp->pdev); } +static void bnxt_clear_bars (struct pci_dev *pdev) +{ +int off; + +for (off =PCI_BASE_ADDRESS_0 ; off <=PCI_BASE_ADDRESS_5 ; off +=4 ) +pci_write_config_dword (pdev, off,0 ); +} +static int bnxt_init_one (struct pci_dev *pdev,const struct pci_device_id *ent) {struct bnxt_hw_resc *hw_resc;
@@ -17547,7 +17555,6 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)struct bnxt *bp =netdev_priv (netdev);int retry =0 ;int err =0 ; -int off;netdev_info (bp->dev,"PCI Slot Reset\n" );
@@ -17576,11 +17583,8 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)* write the BARs to 0 to force restore, in case of fatal error. */ if (test_and_clear_bit (BNXT_STATE_PCI_CHANNEL_IO_FROZEN , - &bp->state)) { -for (off =PCI_BASE_ADDRESS_0 ; - off <=PCI_BASE_ADDRESS_5 ; off +=4 ) -pci_write_config_dword (bp->pdev, off,0 ); - } + &bp->state)) +bnxt_clear_bars (pdev);pci_restore_state (pdev);bnxt_inv_fw_health_reg (bp);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 261/281 bnxt_en: Fix driver init in kdump kernel
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Chan <[email protected]>
[ Upstream commit 386f887c8290f0747dd0180e98ec7a82811bc506 ]
Fix and strengthen the FLR sequence when initializing in the kdump kernel. If the NIC is behind a PCIe switch in synthetic (smart) mode, the switch may need to see that the BARs have been initialized before it will pass mem read/write TLPs to the NIC.
On a Dell system with a PEX89144 PCIe switch, echo c > /proc/sysrq-trigger will trigger fatal AER without this patch:
bnxt_en 0000:67:00.0: enabling device (0000 -> 0002)
[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 5 [Hardware Error]: event severity: recoverable
[Hardware Error]: Error 0, type: fatal
[Hardware Error]: section_type: PCIe error
[Hardware Error]: port_type: 5, upstream switch port
...
Add a new bnxt_kdump_reset() to do the expanded FLR sequence in the kdump kernel. We now disable bus master and memory, save the PCI state, do the FLR, clear the BARs, and restore the PCI state. The BARs have to be cleared to ensure that they get re-initialized and visible to the PCIe switch.
Since it is the kdump kernel, we make every effort to continue in the best possible way even if pci_save_state() or pcie_flr() returns error. After FLR, we poll for an additional 5 seconds before aborting in case the device is not properly returning CRS. This is similar to the 5-second wait in bnxt_io_slot_reset().
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 45 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +40 -5
@@ -17074,6 +17074,43 @@ static void bnxt_clear_bars(struct pci_dev *pdev)pci_write_config_dword (pdev, off,0 ); } +/* Clear any pending DMA transactions from crash kernel while loading driver in +* capture kernel. +*/ +static int bnxt_kdump_reset (struct pci_dev *pdev) +{ +int rc, i; +u16 cmd; + +pci_read_config_word (pdev,PCI_COMMAND , &cmd); + cmd &= ~(PCI_COMMAND_MASTER |PCI_COMMAND_MEMORY ); +pci_write_config_word (pdev,PCI_COMMAND , cmd); + +if (pci_save_state (pdev)) +dev_warn (&pdev->dev,"Failed to save PCI state, PCI restore may be incomplete\n" ); + + rc =pcie_flr (pdev); +if (rc) +dev_warn (&pdev->dev,"pcie_flr() failed (rc: %d), trying to continue\n" , + rc); + +/* In case device is not returning CRS, wait 5 seconds longer */ +for (i =0 ; i <50 ; i++) { +pci_read_config_word (pdev,PCI_COMMAND , &cmd); +if (!PCI_POSSIBLE_ERROR (cmd)) +break ; +msleep (100 ); + } +if (PCI_POSSIBLE_ERROR (cmd)) { +dev_err (&pdev->dev,"PCI config space inaccessible after FLR, aborting\n" ); +return -ENODEV ; + } + +bnxt_clear_bars (pdev); +pci_restore_state (pdev); +return 0 ; +} +static int bnxt_init_one (struct pci_dev *pdev,const struct pci_device_id *ent) {struct bnxt_hw_resc *hw_resc;
@@ -17089,12 +17126,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)return -ENODEV ; } -/* Clear any pending DMA transactions from crash kernel -* while loading driver in capture kernel. -*/ if (is_kdump_kernel ()) { -pci_clear_master (pdev); -pcie_flr (pdev); + rc =bnxt_kdump_reset (pdev); +if (rc) +return rc; } max_irqs =bnxt_get_max_irq (pdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 262/281 bnxt_en: Re-write the BARs following any type of PCIe errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavan Chebbi <[email protected]>
[ Upstream commit 2e52da27096d9dbc487f303bbc10d44ad837a8c4 ]
Currently the driver zeroes the BARs only when fatal PCIe errors are reported so that pci_restore_state() restores it. However firmware handles both fatal and non-fatal errors the same way when it sees the slot reset resulting from the PCI_ERS_RESULT_NEED_RESET return code from the driver. This means that we must re-write the BARs post recovery even during non-fatal errors. Otherwise we will see that every MMIO access returns all-ones and the firmware appears dead.
Zero-out the BARs during PCIe error recovery regardless of type of PCIe error, and make the wait after the hot reset unconditional.
Disable memory decode and bus mastering before rewriting the BARs so the device doesn't decode a half-updated address, bailing out if config space is still inaccessible. Defer pci_enable_device() until after the BAR rewrite and restore, so the device isn't re-enabled while its BARs are still being rewritten, then re-enable the device and re-assert bus mastering. Guard the same Command register cleanup on the re-enable failure path against an inaccessible device.
Skip re-enabling the device in bnxt_io_slot_reset() if it is already enabled, so enable_cnt does not go unbalanced. A concurrent bnxt_fw_reset_task() can also be re-enabling the same device in its ENABLE_DEV state, so guard that call the same way.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 107 ++++++++++++---------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 - 2 files changed, 60 insertions(+), 48 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +60 -47
@@ -15505,7 +15505,7 @@ static void bnxt_fw_reset_task(struct work_struct *work)if (test_and_clear_bit (BNXT_STATE_FW_ACTIVATE_RESET , &bp->state) && !test_bit (BNXT_STATE_FW_ACTIVATE , &bp->state))bnxt_dl_remote_reload (bp); -if (pci_enable_device (bp->pdev)) { +if (!pci_is_enabled (bp->pdev) &&pci_enable_device (bp->pdev)) {netdev_err (bp->dev,"Cannot re-enable PCI device\n" ); rc = -ENODEV ; goto fw_reset_abort;
@@ -17557,10 +17557,8 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev,* so we disable bus master to prevent any potential bad DMAs before * freeing kernel memory. */ -if (state == pci_channel_io_frozen) { -set_bit (BNXT_STATE_PCI_CHANNEL_IO_FROZEN , &bp->state); +if (state == pci_channel_io_frozen)bnxt_fw_fatal_close (bp); - }if (netif_running (netdev))__bnxt_close_nic (bp, true, true);
@@ -17590,65 +17588,80 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)struct bnxt *bp =netdev_priv (netdev);int retry =0 ;int err =0 ; +u16 cmd;netdev_info (bp->dev,"PCI Slot Reset\n" ); -if (test_bit (BNXT_STATE_PCI_CHANNEL_IO_FROZEN , &bp->state)) { -/* After DPC, the chip should return CRS when the vendor ID -* config register is read until it is ready. On all chips, -* this is not happening reliably so add a 5-second delay as a -* workaround. -*/ -msleep (5000 ); - } +/* After a PCIe hot reset, the chip should return CRS when the +* vendor ID config register is read until it is ready. On all +* chips, this is not happening reliably so add a 5-second delay +* as a workaround. +*/ +msleep (5000 );netdev_lock (netdev); -if (pci_enable_device (pdev)) { +pci_read_config_word (pdev,PCI_COMMAND , &cmd); +if (PCI_POSSIBLE_ERROR (cmd)) {dev_err (&pdev->dev, -"Cannot re-enable PCI device after reset.\n" ); - }else { -pci_set_master (pdev); -/* Upon fatal error, our device internal logic that latches to -* BAR value is getting reset and will restore only upon -* rewriting the BARs. -* -* As pci_restore_state() does not re-write the BARs if the -* value is same as saved value earlier, driver needs to -* write the BARs to 0 to force restore, in case of fatal error. -*/ -if (test_and_clear_bit (BNXT_STATE_PCI_CHANNEL_IO_FROZEN , - &bp->state)) -bnxt_clear_bars (pdev); -pci_restore_state (pdev); +"PCI config space inaccessible after reset\n" ); + goto reset_exit; + } -bnxt_inv_fw_health_reg (bp); -bnxt_try_map_fw_health_reg (bp); +/* Upon PCIe error, our device internal logic that latches to +* BAR value is getting reset and will restore only upon +* rewriting the BARs. +* +* As pci_restore_state() does not re-write the BARs if the +* value is same as saved value earlier, driver needs to +* write the BARs to 0 to force restore. +*/ +pci_clear_master (pdev); +pci_read_config_word (pdev,PCI_COMMAND , &cmd); + cmd &= ~PCI_COMMAND_MEMORY ; +pci_write_config_word (pdev,PCI_COMMAND , cmd); -/* In some PCIe AER scenarios, firmware may take up to -* 10 seconds to become ready in the worst case. -*/ -do { - err =bnxt_try_recover_fw (bp); -if (!err) -break ; - retry++; - }while (retry <BNXT_FW_SLOT_RESET_RETRY ); +bnxt_clear_bars (pdev); +pci_restore_state (pdev); -if (err) { -dev_err (&pdev->dev,"Firmware not ready\n" ); - goto reset_exit; +if (!pci_is_enabled (pdev) &&pci_enable_device (pdev)) { +dev_err (&pdev->dev, +"Cannot re-enable PCI device after reset.\n" ); +pci_read_config_word (pdev,PCI_COMMAND , &cmd); +if (!PCI_POSSIBLE_ERROR (cmd)) { + cmd &= ~(PCI_COMMAND_MASTER |PCI_COMMAND_MEMORY ); +pci_write_config_word (pdev,PCI_COMMAND , cmd); } + goto reset_exit; + } +pci_set_master (pdev); - err =bnxt_hwrm_func_reset (bp); +bnxt_inv_fw_health_reg (bp); +bnxt_try_map_fw_health_reg (bp); + +/* In some PCIe AER scenarios, firmware may take up to +* 10 seconds to become ready in the worst case. +*/ +do { + err =bnxt_try_recover_fw (bp);if (!err) - result =PCI_ERS_RESULT_RECOVERED ; +break ; + retry++; + }while (retry <BNXT_FW_SLOT_RESET_RETRY ); -/* IRQ will be initialized later in bnxt_io_resume */ -bnxt_ulp_irq_stop (bp); -bnxt_clear_int_mode (bp); +if (err) { +dev_err (&pdev->dev,"Firmware not ready\n" ); + goto reset_exit; } + err =bnxt_hwrm_func_reset (bp); +if (!err) + result =PCI_ERS_RESULT_RECOVERED ; + +/* IRQ will be initialized later in bnxt_io_resume */ +bnxt_ulp_irq_stop (bp); +bnxt_clear_int_mode (bp); + reset_exit:clear_bit (BNXT_STATE_IN_FW_RESET , &bp->state);bnxt_clear_reservations (bp, true);
drivers/net/ethernet/broadcom/bnxt/bnxt.h +0 -1
@@ -2470,7 +2470,6 @@ struct bnxt {#define BNXT_STATE_ABORT_ERR 5#define BNXT_STATE_FW_FATAL_COND 6#define BNXT_STATE_DRV_REGISTERED 7 -#define BNXT_STATE_PCI_CHANNEL_IO_FROZEN 8#define BNXT_STATE_NAPI_DISABLED 9#define BNXT_STATE_FW_ACTIVATE 11#define BNXT_STATE_RECOVER 12
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 263/281 flow_dissector: avoid u16 truncation of hlen in bpf_flow_dissect()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit f202578efc738b883cfa04889eb97328914265a7 ]
bpf_flow_dissect() sanitizes the offsets returned by the BPF program with clamp_t(u16, ..., hlen).
If hlen is bigger than 65535, the upper bound is truncated before the comparison. For instance, with hlen == 65536, both nhoff and thoff are forced to zero, even if the program returned sane values.
This is the same class of bug as the one fixed in commit 99bda1ecbd56 ("flow_dissector: avoid u16 truncation of skb->len when computing thoff"). That fix does not cover the BPF path, because __skb_flow_dissect() returns early when a BPF flow dissector handles the packet.
struct bpf_flow_keys stores nhoff and thoff as u16, so cap hlen to U16_MAX before clamping.
Found by sashiko while reviewing the commit above.
--- net/core/flow_dissector.c | 2 ++ 1 file changed, 2 insertions(+)
net/core/flow_dissector.c +2 -0
@@ -1023,6 +1023,8 @@ u32 bpf_flow_dissect(struct bpf_prog *prog, struct bpf_flow_dissector *ctx, result =bpf_prog_run_pin_on_cpu (prog, ctx); +/* bpf_flow_keys offsets are u16: do not let @hlen be truncated. */ + hlen =min_t (int, hlen,U16_MAX ); flow_keys->nhoff =clamp_t (u16, flow_keys->nhoff, nhoff, hlen); flow_keys->thoff =clamp_t (u16, flow_keys->thoff, flow_keys->nhoff, hlen);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 264/281 bareudp: set the inner protocol to the protocol of the packet
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haishuang Yan <[email protected]>
[ Upstream commit c57b2e60d4bf29707c51b39ed3bacbcce2f3ba2e ]
bareudp_xmit_skb() sets the inner protocol of the skb to the configured ethertype, and bareudp6_xmit_skb() does not set it at all. The inner protocol is used by skb_udp_tunnel_segment() to segment the inner packet when GSO has to be done in software, for instance when the lower device does not offload the checksum.
With multiproto, an IPv4 device also carries IPv6, so the IPv6 header is parsed as an IPv4 one. With IPv6 underlay, the inner protocol is whatever the skb had before. In both cases segmentation fails and the packets are dropped. With veth tx checksum offload disabled, iperf3 TCP over bareudp gets about 10 Mbit/s with thousands of retransmits instead of about 700 Mbit/s for IPv6 over IPv4 and for both families over IPv6, while IPv4 over IPv4 works.
At this point skb->protocol is the protocol of the inner packet, which bareudp_xmit() has already checked against the configuration, so use it on both paths.
--- drivers/net/bareudp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/bareudp.c +2 -1
@@ -355,7 +355,7 @@ static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev,if (err) goto free_dst; -skb_set_inner_protocol (skb, bareudp->ethertype); +skb_set_inner_protocol (skb, skb->protocol); udp_tunnel_xmit_skb(rt, sk, skb, saddr, info->key.u.ipv4.dst, tos, ttl, df, sport, bareudp->port, !net_eq(bareudp->net, dev_net(bareudp->dev)),
@@ -423,6 +423,7 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,if (err) goto free_dst; +skb_set_inner_protocol (skb, skb->protocol); daddr = info->key.u.ipv6.dst; udp_tunnel6_xmit_skb(dst, sk, skb, dev, &saddr, &daddr, prio, ttl,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 265/281 drm/imagination: Fix reference and vm_bo handling in remap()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gyeyoung Baek <[email protected]>
[ Upstream commit 196d4d10c1b33ae176685ff42a9994a5952b1d0f ]
When a map overlaps part of an existing mapping, pvr_vm_gpuva_remap() splits the mapping into prev/next parts covering what the request did not take, instead of creating something new. It gets two things wrong.
- A GEM reference is taken for each part but it's never needed and never dropped, so it leaks one reference per split (remap-next-in-2m in tests/imagination/pvr_vm_map.c):
CRITICAL: 33558528 bytes of shmem still held after close
- The parts still belong to the original object being split, but pvr_vm_gpuva_remap() links them to ctx->gpuvm_bo, the new object's vm_bo:
prev_va --obj--> BO_A BO_B <--obj-- vm_bo (ctx->gpuvm_bo)
\___________link___________/ (mismatch: BO_A != BO_B) drm_gpuva_link() catches the mismatch:
WARNING: drivers/gpu/drm/drm_gpuvm.c:2108 at drm_gpuva_link+0x2ec/0x310
drm_WARN_ON(obj != vm_bo->obj)
Call trace:
drm_gpuva_link
pvr_vm_gpuva_remap
__drm_gpuvm_sm_map
pvr_vm_map
pvr_ioctl_vm_map Link them to op->remap.unmap->va->vm_bo instead.
The locking of the GPUVA lists of the other objects touched by a split is not addressed here; it is handled by switching the GPUVM to immediate mode.
--- drivers/gpu/drm/imagination/pvr_vm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpu/drm/imagination/pvr_vm.c +4 -4
@@ -418,6 +418,8 @@ pvr_vm_gpuva_unmap(struct drm_gpuva_op *op, void *op_ctx)static int pvr_vm_gpuva_remap (struct drm_gpuva_op *op,void *op_ctx) { +/* The split parts belong to the object of the mapping being split. */ +struct drm_gpuvm_bo *vm_bo = op->remap.unmap->va->vm_bo;struct pvr_vm_bind_op *ctx = op_ctx;u64 va_start =0 , va_range =0 ;int err;
@@ -433,14 +435,12 @@ pvr_vm_gpuva_remap(struct drm_gpuva_op *op, void *op_ctx)drm_gpuva_remap (&ctx->prev_va->base, &ctx->next_va->base, &op->remap);if (op->remap.prev) { -pvr_gem_object_get (gem_to_pvr_gem (ctx->prev_va->base.gem.obj)); -drm_gpuva_link (&ctx->prev_va->base, ctx->gpuvm_bo); +drm_gpuva_link (&ctx->prev_va->base, vm_bo); ctx->prev_va =NULL ; }if (op->remap.next) { -pvr_gem_object_get (gem_to_pvr_gem (ctx->next_va->base.gem.obj)); -drm_gpuva_link (&ctx->next_va->base, ctx->gpuvm_bo); +drm_gpuva_link (&ctx->next_va->base, vm_bo); ctx->next_va =NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 266/281 veth: fix peer NETDEV_XDP_ACT_NDO_XMIT after GRO is toggled while down
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tianyi Gao <[email protected]>
[ Upstream commit 3f090039995f609a8e236f37980dee5712dded61 ]
A veth device advertises NETDEV_XDP_ACT_NDO_XMIT only if its peer has an XDP program attached or GRO enabled, that is, only if the peer will have NAPI to receive the frames.
veth_set_features() updates the peer's flag when GRO is toggled, but returns early if the device is down, and veth_open() only refreshes the flags of the device being opened. Toggling GRO while the device is down therefore leaves the peer's flag stale after the device comes up.
If GRO was enabled while down, the device comes up with NAPI but the peer does not advertise NDO_XMIT, and devmap rejects redirects to the peer with -EOPNOTSUPP. If GRO was disabled while down, the device comes up without NAPI but the peer still advertises NDO_XMIT, so redirects are accepted and then dropped in veth_xdp_xmit() with -ENXIO.
Commit 7a6102aa6df0 ("veth: Update XDP feature set when bringing up device") made veth_open() refresh the device's own flags. Refresh the peer's flags there too. The peer's flag depends on this device's XDP program and GRO setting, not on whether the peer is up, so it is correct to set it even if the peer is down.
--- drivers/net/veth.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/veth.c +1 -0
@@ -1414,6 +1414,7 @@ static int veth_open(struct net_device *dev) }veth_set_xdp_features (dev); +veth_set_xdp_features (peer);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 267/281 net/mlx5e: Order ICOSQ cc update after CQ doorbell
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li RongQing <[email protected]>
[ Upstream commit b056ca4d3742d0769f91137bb1ce3215428398f7 ]
mlx5e_poll_ico_cq() requires sq->cc to be updated only after mlx5_cqwq_update_db_record(), otherwise a CQ overrun may occur.
The current implementation updates sq->cc before the CQ doorbell record, violating this ordering requirement.
Update the CQ doorbell record first and use dma_wmb() before updating sq->cc. This ensures that the CQ space is released to the device before the corresponding ICOSQ consumer index is updated by software.
--- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +4 -2
@@ -908,10 +908,12 @@ int mlx5e_poll_ico_cq(struct mlx5e_cq *cq) }while (!last_wqe); }while ((++i <MLX5E_TX_CQ_POLL_BUDGET ) && (cqe =mlx5_cqwq_get_cqe (&cq->wq))); - sq->cc = sqcc; -mlx5_cqwq_update_db_record (&cq->wq); +/* ensure cq space is freed before enabling more cqes */ +dma_wmb (); + + sq->cc = sqcc;return i; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 268/281 mlxsw: spectrum_flower: Fix port range register leak in tmplt_create()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Machata <[email protected]>
[ Upstream commit 7ea07afb230f1342ab0f9365edd0aae137f425d3 ]
mlxsw_sp_flower_tmplt_create() parses a flow_cls_offload template into a stack-local struct mlxsw_sp_acl_rule_info purely to compute rulei.values.elusage. Parsing can acquire port range registers via mlxsw_sp_flower_parse_ports_range(), but since this rulei never goes through mlxsw_sp_acl_rulei_destroy(), those registers were never released, including through chain template deletion.
Factor out of mlxsw_sp_acl_rulei_destroy() the code to actually release the necessary resources and call from mlxsw_sp_flower_tmplt_create() to plug the leak.
The issue was found during a review of Wentao Liang's patch referenced below.
--- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 10 ++++++++-- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 9 +++++++-- 3 files changed, 17 insertions(+), 4 deletions(-)
drivers/net/ethernet/mellanox/mlxsw/spectrum.h +2 -0
@@ -989,6 +989,8 @@ void mlxsw_sp_acl_ruleset_prio_get(struct mlxsw_sp_acl_ruleset *ruleset,struct mlxsw_sp_acl_rule_info *mlxsw_sp_acl_rulei_create (struct mlxsw_sp_acl *acl,struct mlxsw_afa_block *afa_block); +void mlxsw_sp_acl_rulei_free (struct mlxsw_sp *mlxsw_sp, +const struct mlxsw_sp_acl_rule_info *rulei);void mlxsw_sp_acl_rulei_destroy (struct mlxsw_sp *mlxsw_sp,struct mlxsw_sp_acl_rule_info *rulei);int mlxsw_sp_acl_rulei_commit (struct mlxsw_sp_acl_rule_info *rulei);
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +8 -2
@@ -340,8 +340,8 @@ mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl,return ERR_PTR (err); } -void mlxsw_sp_acl_rulei_destroy (struct mlxsw_sp *mlxsw_sp, -struct mlxsw_sp_acl_rule_info *rulei) +void mlxsw_sp_acl_rulei_free (struct mlxsw_sp *mlxsw_sp, +const struct mlxsw_sp_acl_rule_info *rulei) {if (rulei->action_created)mlxsw_afa_block_destroy (rulei->act_block);
@@ -351,6 +351,12 @@ void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp *mlxsw_sp,if (rulei->dst_port_range_reg_valid)mlxsw_sp_port_range_reg_put (mlxsw_sp, rulei->dst_port_range_reg_index); +} + +void mlxsw_sp_acl_rulei_destroy (struct mlxsw_sp *mlxsw_sp, +struct mlxsw_sp_acl_rule_info *rulei) +{ +mlxsw_sp_acl_rulei_free (mlxsw_sp, rulei);kfree (rulei); }
drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +7 -2
@@ -862,14 +862,19 @@ int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,memset (&rulei,0 ,sizeof (rulei)); err =mlxsw_sp_flower_parse (mlxsw_sp, block, &rulei, f);if (err) -return err; + goto out; + ruleset =mlxsw_sp_acl_ruleset_get (mlxsw_sp, block, f->common.chain_index,MLXSW_SP_ACL_PROFILE_FLOWER , &rulei.values.elusage); + err =PTR_ERR_OR_ZERO (ruleset); + +out: +mlxsw_sp_acl_rulei_free (mlxsw_sp, &rulei);/* keep the reference to the ruleset */ -return PTR_ERR_OR_ZERO (ruleset); +return err; } void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 269/281 net/packet: call packet_parse_headers after virtio_net_hdr_to_skb
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Willem de Bruijn <[email protected]>
[ Upstream commit 513e23857a3a64fa73a1deb36a385eecd4ad7690 ]
SOCK_RAW packet sockets incorrectly drop VLAN-tagged GSO packets without VIRTIO_NET_HDR_F_NEEDS_CSUM.
packet_parse_headers() sets skb->protocol to VLAN but advances skb->network_header past the VLAN tags. virtio_net_hdr_to_skb() then flow dissects these packets, which parses the network header as a VLAN tag, and fails.
Call packet_parse_headers() after virtio_net_hdr_to_skb() instead.
This matches tun_get_user() and tap_get_user() and thus simplifies overall complexity.
Commit 01fdecc0480d ("net: packet: fix wrong transport_header when sending VLAN-tagged frame") fixed the same issue for the transport header probe in packet_parse_headers(). That probe is now skipped if virtio_net_hdr_to_skb() already set the transport header, avoiding a redundant dissection.
Implementation details:
- Do not set skb->protocol to the RX wildcard ETH_P_ALL. SOCK_RAW then derives it from the link layer header, as before. SOCK_DGRAM now leaves it 0, or derives it from gso_type for GSO with NEEDS_CSUM.
- Keep virtio_net_hdr_set_proto() last, so that the link layer protocol takes priority over gso_type.
--- net/packet/af_packet.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
net/packet/af_packet.c +15 -10
@@ -1943,8 +1943,7 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)/* On TX skb->data is the L2 header; anchor it for all socket types. */ skb_reset_mac_header (skb); -if ((!skb->protocol || skb->protocol ==htons (ETH_P_ALL )) && - sock->type ==SOCK_RAW ) +if (!skb->protocol && sock->type ==SOCK_RAW ) skb->protocol =dev_parse_header_protocol (skb);skb_probe_transport_header (skb);
@@ -2614,7 +2613,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,struct page *page;int err; - skb->protocol = proto; +if (proto !=htons (ETH_P_ALL )) + skb->protocol = proto; skb->dev = dev; skb->priority = sockc->priority; skb->mark = sockc->mark;
@@ -2678,8 +2678,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,if (unlikely (!skb->len))return -EINVAL ; -packet_parse_headers (skb, sock); -return tp_len; }
@@ -2919,9 +2917,13 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) tp_len = -EINVAL ; goto tpacket_error; } -virtio_net_hdr_set_proto (skb, &vnet_hdr); } +packet_parse_headers (skb, po->sk.sk_socket); + +if (has_vnet_hdr) +virtio_net_hdr_set_proto (skb, &vnet_hdr); + uarg =kmalloc (sizeof (*uarg),GFP_KERNEL );if (unlikely (!uarg)) {if (likely (len_sum >0 ))
@@ -3122,7 +3124,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) goto out_free; } - skb->protocol = proto; +if (proto !=htons (ETH_P_ALL )) + skb->protocol = proto; skb->dev = dev; skb->priority = sockc.priority; skb->mark = sockc.mark;
@@ -3131,16 +3134,18 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)if (unlikely (extra_len ==4 )) skb->no_fcs =1 ; -packet_parse_headers (skb, sock); -if (vnet_hdr_sz) { err =virtio_net_hdr_to_skb (skb, &vnet_hdr,vio_le ());if (err) goto out_free; len += vnet_hdr_sz; -virtio_net_hdr_set_proto (skb, &vnet_hdr); } +packet_parse_headers (skb, sock); + +if (vnet_hdr_sz) +virtio_net_hdr_set_proto (skb, &vnet_hdr); + err =packet_xmit (po, skb);if (unlikely (err !=0 )) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 270/281 ptp: ocp: fix PCIe delay estimation calculation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vadim Fedorenko <[email protected]>
[ Upstream commit f8c8bd159a9bf626dd4921b7746ff481fce8c758 ]
The commit in fixes introduced a high cap for delayas U64_MAX value while ktime_t is actually s64. This is wrong cap as it becomes negative value and any comparison to a real delay will fail to update delay value. Use KTIME_MAX constant as correct max cap for PCIe delay.
The issue was hit in production (a negative value is observed):
# cat /sys/class/timecard/ocp0/ts_window_adjust -3
--- drivers/ptp/ptp_ocp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ptp/ptp_ocp.c +1 -1
@@ -1825,7 +1825,7 @@ ptp_ocp_watchdog(struct timer_list *t)static void ptp_ocp_estimate_pci_timing (struct ptp_ocp *bp) { -ktime_t start, end, delay =U64_MAX ; +ktime_t start, end, delay =KTIME_MAX ;u32 ctrl;int i;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 271/281 net: openvswitch: validate transport header presence in set_ipv6_addr
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fernando Fernandez Mancera <[email protected]>
[ Upstream commit 3c6a4b11330fe424557538a98fca9f0a4c5bf313 ]
When executing IPv6 address rewrite actions on IPv6 fragments, set_ipv6_addr() calls update_ipv6_checksum(). If parse_ipv6hdr() processes a non-first IPv6 fragment, it sets key->ip.proto to NEXTHDR_FRAGMENT and returns early without calling skb_set_transport_header().
update_ipv6_checksum() unconditionally evaluates skb_transport_offset() on entry before checking l4_proto. Because skb->transport_header is uninitialized, this triggers a warning under CONFIG_DEBUG_NET=y although it is completely harmless.
Fix this by returning early in update_ipv6_checksum() if l4_proto is NEXTHDR_FRAGMENT. This avoids reading the uninitialized transport offset for fragments while preserving the debug warning for any other protocol where the transport header is unexpectedly missing.
See the syzbot trace:
!skb_transport_header_was_set(skb)
WARNING: ./include/linux/skbuff.h:3075 at skb_transport_header include/linux/skbuff.h:3075 [inline], CPU#1: syz-executor463/5635 WARNING: ./include/linux/skbuff.h:3075 at skb_transport_offset include/linux/skbuff.h:3250 [inline], CPU#1: syz-executor463/5635 WARNING: ./include/linux/skbuff.h:3075 at update_ipv6_checksum net/openvswitch/actions.c:361 [inline], CPU#1: syz-executor463/5635 WARNING: ./include/linux/skbuff.h:3075 at set_ipv6_addr+0x462/0x660 net/openvswitch/actions.c:399, CPU#1: syz-executor463/5635 [...] RIP: 0010:skb_transport_header include/linux/skbuff.h:3075 [inline] RIP: 0010:skb_transport_offset include/linux/skbuff.h:3250 [inline] RIP: 0010:update_ipv6_checksum net/openvswitch/actions.c:361 [inline] RIP: 0010:set_ipv6_addr+0x462/0x660 net/openvswitch/actions.c:399 [...] Call Trace: <TASK> set_ipv6 net/openvswitch/actions.c:531 [inline] do_execute_actions+0x557e/0x8600 net/openvswitch/actions.c:1366 ovs_execute_actions+0xde/0x520 net/openvswitch/actions.c:1592 ovs_packet_cmd_execute+0xb4f/0xf10 net/openvswitch/datapath.c:705 genl_family_rcv_msg_doit+0x233/0x340 net/netlink/genetlink.c:1114 genl_rcv_msg+0x614/0x7a0 net/netlink/genetlink.c:1209 netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2572 genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218 netlink_unicast+0x7bd/0x940 net/netlink/af_netlink.c:1361 netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1916 sock_sendmsg_nosec+0x14e/0x190 net/socket.c:800
--- net/openvswitch/actions.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
net/openvswitch/actions.c +6 -1
@@ -358,7 +358,12 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,static void update_ipv6_checksum (struct sk_buff *skb,u8 l4_proto,__be32 addr[4 ],const __be32 new_addr[4 ]) { -int transport_len = skb->len -skb_transport_offset (skb); +int transport_len; + +if (l4_proto ==NEXTHDR_FRAGMENT ) +return ; + + transport_len = skb->len -skb_transport_offset (skb);if (l4_proto ==NEXTHDR_TCP ) {if (likely (transport_len >=sizeof (struct tcphdr )))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 272/281 vsock: Fix memory leak in vmci_transport_recv_dgram_cb()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilia Gavrilov <[email protected]>
[ Upstream commit 7c24a4e87e219072e5e106f6791146cbf3b056b3 ]
During the closure of a datagram socket, the vmci_transport_recv_dgram_cb() function may be called, which will add the packets
to the socket's backlog; then, after the receive queue is cleared, the __release_sock() function will move the packet back from the socket's backlog to the receive queue, which will lead to a memory leak.
sock_close
__sock_release
__vsock_release
// take ownership by user-space
lock_sock_nested
sock_set_flag(sk, SOCK_DEAD)
vmci_transport_release
vmci_dispatch_dgs
vmci_datagram_invoke_guest_handler
vmci_transport_recv_dgram_cb
sk_receive_skb
if (!sock_owned_by_user(sk))
[...]
(!) else if sk_add_backlog()
[...] (!) skb_queue_purge(&sk->sk_receive_queue)
release_sock
__release_sock
// move all packets from the backlog
// to the receive queue
(!) sk_backlog_rcv
vsock_queue_rcv_skb
[...]
__sock_queue_rcv_skb
if (!sock_flag(sk, SOCK_DEAD))
// Since the socket is "dead", the function will not
// be called
sk->sk_data_ready(sk)
sock_release_ownership(sk);
sock_putAdd a receive queue cleanup in the socket destructor to fix this.
syzkaller report:
unreferenced object 0xffff8880117bfb80 (size 240):
comm "irq/56-vmw_vmci", pid 205, jiffies 4295064448 (age 73.992s) hex dump (first 32 bytes):
78 dc 26 1e 80 88 ff ff 78 dc 26 1e 80 88 ff ff x.&.....x.&.....
00 00 00 00 00 00 00 00 c0 da 26 1e 80 88 ff ff ..........&.....
backtrace:
[<ffffffff82be8d97>] __alloc_skb+0x287/0x330 net/core/skbuff.c:505
[<ffffffffa114bcbd>] vmci_transport_recv_dgram_cb+0xbd/0x210 [vmw_vsock_vmci_transport]
[<ffffffffa05072c6>] vmci_datagram_invoke_guest_handler+0x366/0x450 [vmw_vmci]
[<ffffffffa050ad75>] vmci_dispatch_dgs+0x235/0x490 [vmw_vmci]
[<ffffffffa050b025>] vmci_interrupt+0x55/0x290 [vmw_vmci]
[<ffffffff8137ad2b>] irq_thread_fn+0x8b/0x1a0 kernel/irq/manage.c:1205
[<ffffffff8137c7ae>] irq_thread+0x28e/0x530 kernel/irq/manage.c:1314
[<ffffffff81253dd6>] kthread+0x2e6/0x3a0 kernel/kthread.c:376
[<ffffffff81004392>] ret_from_fork+0x22/0x30 arch/x86/entry/entry_64.S:295Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with Syzkaller.
--- net/vmw_vsock/af_vsock.c | 2 ++ 1 file changed, 2 insertions(+)
net/vmw_vsock/af_vsock.c +2 -0
@@ -1021,6 +1021,8 @@ static void vsock_sk_destruct(struct sock *sk) {struct vsock_sock *vsk =vsock_sk (sk); +/* Socket buffers may remain in the queue in VMCI datagram mode */ +__skb_queue_purge (&sk->sk_receive_queue);/* Flush MSG_ZEROCOPY leftovers. */ __skb_queue_purge (&sk->sk_error_queue);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 273/281 drm/vc4: Disable the V3D interrupt across runtime suspend
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
[ Upstream commit 6abc8e4dcfbc1d26a94c0ac037dfb3716f686b1f ]
vc4_irq_disable() masks the V3D interrupt sources and then calls synchronize_irq() before the V3D is powered down. However, by itself, this is not enough to quiesce the interrupt handler.
synchronize_irq() waits for handlers that have already set IRQD_IRQ_INPROGRESS, and for irqchips reporting IRQCHIP_STATE_ACTIVE. A GIC interrupt chip is able to mark an interrupt active as soon as a CPU acknowledges it, so there these checks cover the whole dispatch path. On RPi 0-3, however, the interrupt controller is ARMCTRL, which has no active state. A CPU that has read the hwirq out of the pending register but has not yet reached handle_level_irq() stays invisible to synchronize_irq().
During a power transition, vc4_irq() may therefore run after the power domain is off, where every V3D register read will return 0xdeadbeef. 0xdeadbeef has FLDONE, FRDONE and OUTOMEM set. This problem doesn't trigger NULL pointer dereference issues only because the functions vc4_irq_finish_bin_job() and vc4_irq_finish_render_job() return early when there is no job pointer, but OUTOMEM is still able to schedule vc4_overflow_mem_work() after vc4_irq_disable() has already cancelled it.
Therefore, fix the spurious interrupts by disabling the interrupt line across the PM transition. To preserve the enable/disable balance, move vc4_irq_install() and request the IRQ line disabled before the first runtime resume.
--- drivers/gpu/drm/vc4/vc4_irq.c | 18 +++--------------- drivers/gpu/drm/vc4/vc4_v3d.c | 13 +++++++------ 2 files changed, 10 insertions(+), 21 deletions(-)
drivers/gpu/drm/vc4/vc4_irq.c +3 -15
@@ -276,7 +276,7 @@ vc4_irq_disable(struct drm_device *dev)V3D_WRITE (V3D_INTCTL ,V3D_DRIVER_IRQS );/* Finish any interrupt handler still in flight. */ -synchronize_irq (vc4->irq); +disable_irq (vc4->irq);cancel_work_sync (&vc4->overflow_mem_work); }
@@ -284,7 +284,6 @@ vc4_irq_disable(struct drm_device *dev)int vc4_irq_install (struct drm_device *dev,int irq) {struct vc4_dev *vc4 =to_vc4_dev (dev); -int ret;if (WARN_ON_ONCE (vc4->gen >VC4_GEN_4 ))return -ENODEV ;
@@ -298,19 +297,8 @@ int vc4_irq_install(struct drm_device *dev, int irq)init_waitqueue_head (&vc4->job_wait_queue);INIT_WORK (&vc4->overflow_mem_work, vc4_overflow_mem_work); -/* Clear any pending interrupts someone might have left around -* for us. -*/ -V3D_WRITE (V3D_INTCTL ,V3D_DRIVER_IRQS ); - - ret =devm_request_irq (dev->dev, irq, vc4_irq,0 , -dev_name (dev->dev), dev); -if (ret) -return ret; - -vc4_irq_enable (dev); - -return 0 ; +return devm_request_irq (dev->dev, irq, vc4_irq,IRQF_NO_AUTOEN , +dev_name (dev->dev), dev); }void vc4_irq_uninstall (struct drm_device *dev)
drivers/gpu/drm/vc4/vc4_v3d.c +7 -6
@@ -397,6 +397,7 @@ static int vc4_v3d_runtime_resume(struct device *dev)vc4_v3d_init_hw (&vc4->base);vc4_irq_enable (&vc4->base); +enable_irq (vc4->irq);return 0 ; }
@@ -453,6 +454,12 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)return ret; vc4->irq = ret; + ret =vc4_irq_install (drm, vc4->irq); +if (ret) { +drm_err (drm,"Failed to install IRQ handler\n" ); +return ret; + } + ret =devm_pm_runtime_enable (dev);if (ret)return ret;
@@ -474,12 +481,6 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)V3D_WRITE (V3D_BPOA ,0 );V3D_WRITE (V3D_BPOS ,0 ); - ret =vc4_irq_install (drm, vc4->irq); -if (ret) { -drm_err (drm,"Failed to install IRQ handler\n" ); - goto err_put_runtime_pm; - } -pm_runtime_use_autosuspend (dev);pm_runtime_set_autosuspend_delay (dev,40 );/* a little over 2 frames. */ pm_runtime_put_autosuspend (dev);
Signature · 1 line
2.53.0