Compare commits

..

99 Commits

Author SHA1 Message Date
Linus Torvalds
89a312991d Merge tag 'cifs-fixes-7.3-rc2' of https://git.manguebit.org/linux
Pull smb client fixes from Paulo Alcantara:

 - Fixes for fallocate range operations (insert, collapse, zero, punch
   hole)

   The insert range implementation copied overlapping chunks in the
   wrong direction, corrupting file data on every server except Windows.

   Several related issues in the same area are also addressed — stale
   page cache and FS-Cache readback, an integer truncation on large
   files, missing RLIMIT_FSIZE validation and missing sparse file
   marking.

 - Data corruption fixes in the O_TRUNC open path: one where i_size was
   zeroed before the server confirmed the truncate and another where the
   lack of locking allowed concurrent buffered writes to be silently
   discarded

 - Heap overflow fixes in legacy SMB1 paths: one in extended attribute
   writes and one in POSIX ACL handling, both exploitable via
   unprivileged setxattr(2)

 - Fix for multiuser mount with krb5 failing because the username option
   was not propagated to new per-user connections

 - Fix for split debug message in __release_mid() after a printk
   conversion

* tag 'cifs-fixes-7.3-rc2' of https://git.manguebit.org/linux:
  smb: client: reject SetEA requests that do not fit the request buffer
  smb: client: fix data corruption with concurrent writes and O_TRUNC
  cifs: don't update i_size in cifs_do_truncate without a cached handle
  smb: client: fix heap overflow in cifs_do_set_acl()
  smb: client: fix multiuser mount with krb5
  smb: client: transport: Fix debug printing in __release_mid()
  smb/client: invalidate fscache for fallocate range operations
  smb/client: fix stale page cache in insert/collapse range
  smb/client: fix integer truncation in collapse range
  smb/client: fix data corruption in emulated insert range
  smb/client: mark file sparse before emulating insert range
  smb/client: validate new EOF for zero range
  smb/client: validate new EOF for insert range
  cifs: add revalidation on FSCTL failure in smb2_duplicate_extents()
2026-09-01 13:37:14 -07:00
Linus Torvalds
9a58da8005 Merge tag 'ksmbd-for-7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/smb
Pull smb server fixes from Namjae Jeon:

 - Prevent unintended data exposure by clearing pipe compound padding
   and the response buffer

 - Initialize missing fields in FS_OBJECT_ID_INFORMATION,
   FS_CONTROL_INFORMATION, and FS_POSIX_INFORMATION

 - Propagate DACL parsing and allocation failures so malformed security
   descriptors are rejected

 - Rate-limit errors for unmapped SIDs to prevent kernel log flooding

 - Drain multichannel sessions during LOGOFF, wake deferred locks and
   cancellable requests, and ensure cancellation callbacks run only once

 - Fix listener kthread reference handling and teardown ordering during
   netdevice events

 - Validate normalized-name and IPC share configuration response lengths

 - Update the KSMBD MAINTAINERS entry and add Paulo Alcantara as an
   SMBDIRECT co-maintainer

* tag 'ksmbd-for-7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/smb:
  ksmbd: validate normalized name response length
  ksmbd: fix listener task lifetime on netdev events
  ksmbd: prevent out-of-bounds reads in share config responses
  ksmbd: rate limit unmapped SID errors
  ksmbd: propagate DACL parsing errors
  ksmbd: zero pipe read compound padding
  ksmbd: safely drain sessions during logoff
  MAINTAINERS: Update the KSMBD entry
  MAINTAINERS: Add Paulo Alcantara as an SMBDIRECT co-maintainer
  ksmbd: fill in FileSysIdentifier in FS_POSIX_INFORMATION
  ksmbd: initialize FileSystemControlFlags in FS_CONTROL_INFORMATION
  ksmbd: zero the FS_OBJECT_ID_INFORMATION buffer before filling it in
2026-09-01 08:17:01 -07:00
Linus Torvalds
786262be60 Merge tag 'edac_updates_for_v7.3_rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras
Pull forgotten EDAC updates from Borislav Petkov:
 "Somewhat belated (and forgotten :-\) EDAC updates lineup for v7.3:

   - Mark the mpc85xx and ThunderX EDAC drivers as orphaned due to lack
     of access to hardware

   - Remove the unused fake error injection interface from the EDAC
     debugfs code due to potential races between logging a fake and a
     real hw error

   - edac_mc_sysfs: Use sysfs_emit_at() for proper bounds checking

   - Remove Mark Gross from maintainer entries and move him to CREDITS

   - Load the AMD address translation library only on systems which can
     actually make use of it (have ECC memory) instead of on every AMD
     Zen system out there

   - In edac_altera, detect the SoC variant using the ECC manager's
     compatible string instead of the build architecture to select the
     correct interrupt layout, and remove leftover architecture-specific
     ifdeffery from the double-bit error handling path

   - Add a new reviewer for the Xilinx EDAC drivers

   - Unify address translation logic in Intel client EDAC drivers igen6
     and ie31200 along with detecting memory controller counts at boot
     time instead of relying on hardcoded, platform specific numbers.
     Also, fix a bunch of issues in them; work by Qiuxu Zhuo

   - Add support for a new Intel processor platform Starfire which is a
     derivative of Panther Lake SoCs

   - The usual cleanups and fixlets all over"

* tag 'edac_updates_for_v7.3_rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras: (24 commits)
  EDAC/thunderx: Orphan it
  EDAC/device_sysfs: Cleanup around edac_device_ctl_poll_msec_store()
  EDAC/device_sysfs: Use kstrtouint() for poll_msec to prevent truncation
  EDAC/igen6: Add Intel Starfire SoCs support
  EDAC/igen6: Refactor address translation logic
  EDAC/igen6: Remove redundant resource configuration tables
  EDAC/igen6: Detect present memory controllers at runtime
  EDAC/igen6: Simplify compute die ID comments
  EDAC/igen6: Remove unnecessary XOR on the zero-valued interleave bit
  EDAC/igen6: Fix Raptor Lake-P logged error address
  EDAC/igen6: Fix channel address decode for non-hash mode
  EDAC/igen6: Fix channel selection hash
  EDAC/igen6: Fix interleave boundary condition
  EDAC/ie31200: Decouple DIMM width decoding from enum order
  RAS/AMD/ATL: Remove conditional return with no effect
  EDAC: Remove redundant dev_err()
  MAINTAINERS: Add Radhey Shyam Pandey as Xilinx EDAC reviewer
  EDAC/altera: Remove remaining CONFIG_64BIT ifdefs in the DB-error path
  EDAC/altera: Use ECC manager compatible to select A10/S10 IRQ layout
  RAS/AMD/ATL, EDAC/amd64: Only load ATL when needed
  ...
2026-08-31 20:10:39 -07:00
Borislav Petkov (AMD)
68c90773f4 Merge remote-tracking branches 'ras/edac-misc', 'ras/edac-drivers' and 'ras/edac-amd-atl' into edac-updates
* ras/edac-misc:
  EDAC/thunderx: Orphan it
  EDAC/device_sysfs: Cleanup around edac_device_ctl_poll_msec_store()
  EDAC/device_sysfs: Use kstrtouint() for poll_msec to prevent truncation
  MAINTAINERS: Add Radhey Shyam Pandey as Xilinx EDAC reviewer
  MAINTAINERS: Remove Mark Gross from relevant entries
  EDAC/sysfs: Use sysfs_emit_at() in dimmdev_location_show()
  EDAC/mpc85xx: Orphan it

* ras/edac-drivers:
  EDAC/igen6: Add Intel Starfire SoCs support
  EDAC/igen6: Refactor address translation logic
  EDAC/igen6: Remove redundant resource configuration tables
  EDAC/igen6: Detect present memory controllers at runtime
  EDAC/igen6: Simplify compute die ID comments
  EDAC/igen6: Remove unnecessary XOR on the zero-valued interleave bit
  EDAC/igen6: Fix Raptor Lake-P logged error address
  EDAC/igen6: Fix channel address decode for non-hash mode
  EDAC/igen6: Fix channel selection hash
  EDAC/igen6: Fix interleave boundary condition
  EDAC/ie31200: Decouple DIMM width decoding from enum order
  EDAC: Remove redundant dev_err()
  EDAC/altera: Remove remaining CONFIG_64BIT ifdefs in the DB-error path
  EDAC/altera: Use ECC manager compatible to select A10/S10 IRQ layout

* ras/edac-amd-atl:
  RAS/AMD/ATL: Remove conditional return with no effect
  RAS/AMD/ATL, EDAC/amd64: Only load ATL when needed
  EDAC/debugfs: Remove the fake_inject debugfs interface

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
2026-08-31 19:29:47 -07:00
Linus Torvalds
abdf623ddb Merge tag 'wq-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
Pull workqueue fixes from Tejun Heo:

 - An unbound worker pool could be freed while still reachable through
   the pending-activation list, leading to a use-after-free. Unlink
   before dropping the reference

 - On PREEMPT_RT, the BH workqueue kick raised softirqs from preemptible
   context, tripping a lockdep assertion and possibly losing
   concurrently raised softirq bits

 - Draining BH work off a dead CPU nests two pools' callback locks,
   which lockdep misreported as recursive locking. The nesting cannot
   deadlock. Annotate it

 - Reject watchdog thresholds that overflow the conversion to jiffies

 - Make the drgn workqueue dump script work again on kernels and vmcores
   from before the workqueue attrs field rename

* tag 'wq-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq:
  tools/workqueue/wq_dump.py: Support backward compatibility for wq->attrs rename
  workqueue: reject watchdog thresholds that overflow jiffies
  workqueue: Fix unbound pool lifetime for pending pwqs
  workqueue: Use raise_softirq() to trigger softirq in irq_work handler
  workqueue: Annotate cb_lock nesting when draining a dead BH pool
2026-08-31 14:38:40 -07:00
Linus Torvalds
c3b510de42 Merge tag 'cgroup-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
Pull cgroup fixes from Tejun Heo:

 - After cgroup.kill was written to a cgroup, every child cloned into it
   with CLONE_INTO_CGROUP was spuriously killed because the fork path
   snapshotted the kill counter before resolving the target cgroup

 - Releasing an isolated cpuset partition dropped the isolation of CPUs
   isolated on the kernel command line

 - Selftest and documentation fixes

* tag 'cgroup-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup:
  selftests/cgroup: test clone3() into a previously killed cgroup
  cgroup: fix spurious SIGKILL of CLONE_INTO_CGROUP children
  selftests/cgroup: Add test for preserving boot-isolated CPUs
  cgroup/cpuset: Preserve boot-isolated CPUs on partition release
  selftests/cgroup: Drop invalid boot isolation comparison
  docs: cgroup-v2: fix misc.events key format description
  selftests/cgroup: Fix cg_run_in_subcgroups ignoring arg parameter
  selftests/cgroup: set the test plan after the setup checks
2026-08-31 14:20:32 -07:00
Aaron Tomlin
93e257938a tools/workqueue/wq_dump.py: Support backward compatibility for wq->attrs rename
Commit 464e454e1c ("workqueue: rename wq->unbound_attrs to wq->attrs")
renamed wq->unbound_attrs to wq->attrs. When running wq_dump.py against
older running kernels or vmcores where struct workqueue_struct still
contains unbound_attrs, drgn raises an AttributeError.

Add a wq_attrs() helper to allow wq_dump.py to inspect both older and newer
kernel versions seamlessly.

Fixes: 464e454e1c ("workqueue: rename wq->unbound_attrs to wq->attrs")
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 10:17:08 -10:00
Linus Torvalds
bf1079577a Merge tag 'sched_ext-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext
Pull sched_ext fixes from Tejun Heo:

 - The task ownership check in the dispatch queue move operation raced
   against the task exiting or moving to a different sub-scheduler,
   spuriously triggering scheduler aborts. Fix by moving the check under
   the queue lock

 - The cgroup bandwidth change callback runs in a sleepable context but
   sleepable implementations were rejected at load time. Allow them and
   add a marker so userspace can detect the capability

 - Sync tooling headers with the scx repo for accumulated compatibility
   improvements

 - Example scheduler fixes: ignored timer re-arm failures and vtime
   credit loss on cgroup migration

 - Documentation and comment fixes

* tag 'sched_ext-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext:
  sched_ext: Fix missing @slice and @vtime descriptions in finish_dispatch() kernel-doc
  sched_ext: Fix several comment issues
  sched_ext: Check bpf_timer_start return values in scx_qmap
  sched_ext: Fix vtime delta loss in scx_flatcg cgroup migration
  sched_ext: Fix timer pinning and return value in scx_central
  docs/sched_ext: document that cgroup CPU knobs are scheduler-dependent
  sched_ext: Fix spurious aborts in scx_bpf_dsq_move() on ownership change races
  sched_ext: Sync common and compat headers from the scx repo
  sched_ext: Sync tools autogen enum headers from the scx repo
  Docs/admin-guide/cgroup-v2: document BPF scheduler callbacks for cpu.max and cpu.idle
  sched_ext: Fix nonexistent field in sched-ext.rst example
  sched_ext: Allow ops.cgroup_set_bandwidth() to be sleepable
2026-08-31 13:13:35 -07:00
Linus Torvalds
a7f25dc23f Merge tag 'xfs-fixes-7.3-rc2' of gitolite.kernel.org:/pub/scm/fs/xfs/xfs-linux
Pull xfs fixes from Carlos Maiolino:
 "This contains a few fixes for the zoned storage support, a possible
  deadlock vector fix, some code refactoring patches and a quota evasion
  fix on XFS while exporting it via NFS.

  Please note that for the quota evasion fix, a couple patches for the
  capability subsystem are included in the pull request. Those have been
  ack'ed by the respective maintainer which also agreed to have them
  going through the xfs tree.

  This also includes a patch for the quota subsystem to stop issuing
  audit messages during quota enforcing. Quota maintainer also ack'ed
  and agreed with this going through xfs tree"

* tag 'xfs-fixes-7.3-rc2' of gitolite.kernel.org:/pub/scm/fs/xfs/xfs-linux:
  capability: unexport has_capability_noaudit
  xfs: replace ns_capable_noaudit
  quota: Don't issue audit messages on quota enforcing
  capability: Add new capable_noaudit
  xfs: fix capability check in xfs
  xfs: restore bi_bdev in xfs_zone_gc_write_chunk
  xfs: split ioend handling into a separate source file
  xfs: factor out a xfs_iomap_set_anon_write helper
  xfs: fix zoned write iomap flags assignments
  xfs: fix racy open zone caching
  xfs: handle NULL open_zone for merged ioends in xfs_ioend_put_open_zones
  xfs: use inode_init_always_gfp with __GFP_NOFAIL in xfs_inode_alloc
  xfs: remove kmem_to_page()
  xfs: don't flush and invalidate internal RT device twice in xfs_shutdown_devices
  xfs: split an assert in xfs_trans_log_buf
  xfs: don't hold buffer locks across sync transaction commit in xfs_sync_sb_buf
2026-08-31 10:45:40 -07:00
Jiacheng Xu
068c35b5d0 workqueue: reject watchdog thresholds that overflow jiffies
The watchdog threshold is supplied in seconds but is multiplied by HZ
before being used as a jiffies interval. Reject values that exceed
MAX_JIFFY_OFFSET / HZ so the multiplication cannot wrap and the
time_after() comparisons remain within their supported range.

The check is performed before changing the threshold or watchdog timer.
Zero remains the value used to disable the watchdog.

Fixes: 82607adcf9 ("workqueue: implement lockup detector")
Signed-off-by: Jiacheng Xu <stitch@zju.edu.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 07:31:46 -10:00
Liang Luo
068e5a0bc5 sched_ext: Fix missing @slice and @vtime descriptions in finish_dispatch() kernel-doc
Commit 13f1eae3b6 ("sched_ext: Synchronize slice and dsq_vtime
writes") added the slice and vtime parameters to finish_dispatch() but
did not update its kernel-doc, which produces warnings:

  Warning: function parameter 'slice' not described in 'finish_dispatch'
  Warning: function parameter 'vtime' not described in 'finish_dispatch'

Describe both parameters using the same wording as
dispatch_to_local_dsq(), which receives the same values.

Signed-off-by: Liang Luo <luoliang@kylinos.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 07:28:04 -10:00
Wanwu Li
4881a13521 sched_ext: Fix several comment issues
Fix several comment issues found during review:
__setschduler_prio() -> __setscheduler_class()
scx_iter_scx_dsq_new() -> bpf_iter_scx_dsq_new()
scx_next_task_scx() -> set_next_task_scx()

Signed-off-by: Wanwu Li <liwanwu@kylinos.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 06:48:16 -10:00
Wanwu Li
84590dbb9f sched_ext: Check bpf_timer_start return values in scx_qmap
monitor_timerfn(), lowpri_timerfn() and round_robin_timerfn() ignore
bpf_timer_start()'s return value: a failed re-arm silently stops the
periodic heartbeat, starving every task parked in LOWPRI_DSQ (lowpri)
or freezing cid rotation (round-robin).  Check the returns and raise
scx_bpf_error(), matching the init paths.

Signed-off-by: Wanwu Li <liwanwu@kylinos.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 06:47:39 -10:00
Wanwu Li
b6ee92d7f7 sched_ext: Fix vtime delta loss in scx_flatcg cgroup migration
fcg_cgroup_move() lost the signed vtime offset across cgroup
migration in the mechanical conversion to time helpers:
time_delta() clamps negative deltas to 0, so a queued task (whose
dsq_vtime is normally behind the source frontier) loses its
accumulated vtime credit and lands exactly at the destination
frontier instead of keeping its relative position.  Restore the
wrapping signed subtraction.

Fixes: 62addc6dbf ("sched_ext: Use time helpers in BPF schedulers")
Signed-off-by: Wanwu Li <liwanwu@kylinos.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 06:47:33 -10:00
Wanwu Li
2376135986 sched_ext: Fix timer pinning and return value in scx_central
central_timerfn() re-arms the timer with a hardcoded
BPF_F_TIMER_CPU_PIN flag and ignores the return value, defeating
start_central_timer()'s -EINVAL fallback for kernels without the flag
(<6.7): on such kernels the first tick kills the timer permanently
with no diagnostic.  Honor timer_pinned and check the return like
the initial arm does.

Fixes: 22a920209a ("sched_ext: Implement tickless support")
Signed-off-by: Wanwu Li <liwanwu@kylinos.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 06:47:26 -10:00
Etienne Perot
3f4b7d1a49 selftests/cgroup: test clone3() into a previously killed cgroup
Once cgroup.kill had been written to a cgroup, a stale kill_seq
snapshot (taken in cgroup_css_set_fork() before the target cgroup was
resolved) caused every child subsequently cloned into that cgroup with
clone3(CLONE_INTO_CGROUP) to be SIGKILLed on the spot.

Add a regression test: create a cgroup, kill it while it is empty,
then clone a child into it and check that the child runs and exits
cleanly. On a kernel without the fix, the test fails:

  not ok 4 test_cgkill_clone_into_killed

The test is skipped on kernels without clone3() or without
CLONE_INTO_CGROUP.

Cc: Shakeel Butt <shakeel.butt@linux.dev>
Assisted-by: LLM
Signed-off-by: Etienne Perot <eperot@google.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 06:19:53 -10:00
Etienne Perot
8e35992021 cgroup: fix spurious SIGKILL of CLONE_INTO_CGROUP children
Since commit b69bb476de ("cgroup: fix race between fork and
cgroup.kill"), the fork path snapshots the kill_seq of the child's
future cgroup into kargs->kill_seq, and cgroup_post_fork() SIGKILLs
the child if that cgroup's kill_seq has changed in the meantime, to
catch forks racing with a cgroup.kill sweep.

For CLONE_INTO_CGROUP, however, the snapshot in cgroup_css_set_fork()
is taken before the target cgroup has been resolved: kargs->cgrp is
always NULL at this point (it is only set at the end of the function).
So the "if (kargs->cgrp)" branch is dead code and the snapshot always
records the kill_seq of the parent's cgroup. cgroup_post_fork() then
compares it with the kill_seq of the target cgroup, so the child gets
SIGKILLed whenever the two cgroups have been killed a different number
of times.

As a result, once cgroup.kill has been written to a cgroup, every
child subsequently cloned into it with clone3(CLONE_INTO_CGROUP) is
killed on the spot, for as long as the cgroup exists: kill_seq is not
exposed to userspace and never resets.

Re-snapshot kill_seq from the target cgroup once it has been resolved,
and drop the dead branch at the early snapshot site.

This does not reopen the race fixed by b69bb476de. For
CLONE_INTO_CGROUP, everything from the snapshot to the check in
cgroup_post_fork() runs with cgroup_mutex held, and kill_seq is
only ever incremented under cgroup_mutex.

tj: Updated the comment above kill_seq to reflect the new serialization
rules as suggested by Shakeel Butt.

Fixes: b69bb476de ("cgroup: fix race between fork and cgroup.kill")
Cc: stable@vger.kernel.org
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Assisted-by: LLM
Signed-off-by: Etienne Perot <eperot@google.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31 06:19:38 -10:00
Yunpeng Tian
4aa2c106ae smb: client: reject SetEA requests that do not fit the request buffer
CIFSSMBSetEA() copies the caller's extended attribute value into the
SMB request buffer without checking that it fits.  The requirement is
stated in the source but was never implemented:

	/*BB add length check to see if it would fit in
	     negotiated SMB buffer size BB */
	/* if (ea_value_len > buffer_size - 512 (enough for header)) */
	if (ea_value_len)
		memcpy(parm_data->list.name + name_len + 1,
		       ea_value, ea_value_len);

The only bound applied on the way in is in cifs_xattr_set():

	#define MAX_EA_VALUE_SIZE CIFSMaxBufSize
	...
	if (size > MAX_EA_VALUE_SIZE)

CIFSMaxBufSize is the full payload capacity of the buffer, so a value
of exactly that size leaves no room for the SMB header, the TRANS2
parameter block, the fealist header and the EA name that are written
ahead of it in the same object.

SendReceive() already enforces the correct limit on this very length:

	if (in_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)

but it is called after the copy has taken place.  An unprivileged
setxattr(2) on an SMB1 mount with a 250-byte name and a 16384-byte
value writes 16384 bytes starting 345 bytes into a 16588-byte
cifs_request object, ending 141 bytes past it:

  BUG: KASAN: slab-out-of-bounds in CIFSSMBSetEA+0xabc/0xde0
  Write of size 16384 at addr ffff888003aa0159 by task init/68
   __asan_memcpy+0x3c/0x60
   CIFSSMBSetEA+0xabc/0xde0
   cifs_xattr_set+0xd3a/0xff0
   __vfs_setxattr+0x13e/0x1a0
  The buggy address is located 345 bytes inside of
   allocated 16588-byte region

Apply SendReceive()'s limit to the assembled request before the copy
rather than after it, and widen the byte counters so the sum cannot
wrap before it is tested.

byte_count is also tested against U16_MAX, because it is stored in the
16-bit pSMB->ByteCount.  That becomes reachable when CIFSMaxBufSize is
raised at module load, where it may be set as high as 1024*127: with a
5-byte EA name and a 65521-byte value, count is exactly U16_MAX while
byte_count is 65556, and cpu_to_le16() would truncate it to 20 and
transmit a frame whose ByteCount does not match its length.  Testing
byte_count covers count as well, since byte_count is the larger of the
two and count's only 16-bit consumer is written after this point.

check_add_overflow() is evaluated first so that total_len is assigned
before it is reported.

Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Reported-by: Yunpeng Tian <shionthanatos@gmail.com>
Reported-by: Mingda Zhang <npczmd@qq.com>
Reported-by: Gongming Wang <gmwgg05@gmail.com>
Reported-by: Qinrun Dai <jupmouse@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Yunpeng Tian <shionthanatos@gmail.com>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-31 12:01:07 -03:00
Paulo Alcantara
a8603b52b3 smb: client: fix data corruption with concurrent writes and O_TRUNC
cifs_do_truncate() flushes dirty pages with filemap_write_and_wait()
and truncates the file on the server, but in the old code both
operations ran without holding i_rwsem or invalidate_lock.  A
concurrent buffered write via netfs_perform_write() -- which only
needs i_rwsem shared -- could dirty new pages after the flush but
before the local truncation, and those pages would be silently
discarded by cifs_setsize() -> truncate_pagecache().

Fix by acquiring inode_lock (exclusive i_rwsem) and
filemap_invalidate_lock at the top of cifs_do_truncate(), so the
entire flush-truncate-resize sequence is atomic with respect to:

  - buffered writes (blocked by exclusive i_rwsem, since
    netfs_start_io_write takes i_rwsem shared),
  - read page faults (blocked by exclusive invalidate_lock, since
    filemap_fault takes it shared),
  - writeback collection (blocked by netfs_wb_begin/netfs_wb_end
    around the server truncate and local resize, since
    netfs_writepages also acquires the wb lock).

Fixes: 110fee6b9b ("smb: client: fix missing timestamp updates with O_TRUNC")
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Shyam Prasad N <sprasad@microsoft.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Bharath SM <bharathsm@microsoft.com>
Cc: stable@vger.kernel.org
2026-08-31 11:49:50 -03:00
Alon Shakevsky
ba9572bc43 ksmbd: validate normalized name response length
FILE_NORMALIZED_NAME_INFORMATION converts the open file path to UTF-16.
smb2_allocate_rsp_buf() leaves these responses in the 448-byte small
buffer, and get_file_normalized_name_info() converts the path without
checking the remaining space.

An authenticated client can query a long path and make
smbConvertToUTF16() write beyond work->response_buf.

Use the large response buffer for normalized-name queries. Before
conversion, verify that the response has room for the worst-case UTF-16
output and its terminator.

Fixes: 10aeff72ab ("ksmbd: support normalized name information")
Assisted-by: Antiproof:GPT-5.6-Sol
Signed-off-by: Alon Shakevsky <shakevsky@berkeley.edu>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:27:36 +09:00
Namjae Jeon
a506290f59 ksmbd: fix listener task lifetime on netdev events
The listener thread exits when its listening socket is shutdown. The
netdevice notifier shuts down the socket before calling kthread_stop(), so
the task_struct can be freed before kthread_stop() gets its reference.

Create the listener in a stopped state and hold an extra task_struct
reference until kthread_stop_put() completes. Also stop and release
listeners before freeing their interface records during TCP teardown.

Fixes: 3316a8fc84 ("ksmbd: server: avoid busy polling in accept loop")
Reported-by: Farhad Alemi <farhad.alemi@berkeley.edu>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:27:11 +09:00
Namjae Jeon
f25e93768f ksmbd: prevent out-of-bounds reads in share config responses
Validate IPC share configuration payload sizes before consuming
variable-length fields. Bound veto list parsing and account for
the separator byte when deriving the path length.

Fixes: a677ebd8ca ("ksmbd: validate payload size in ipc response")
Reported-by: Kanishka De Silva <kpskanna1915@gmail.com>
Reported-by: Farhad Alemi <farhad.alemi@berkeley.edu>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:27:06 +09:00
Namjae Jeon
feca5e70fc ksmbd: rate limit unmapped SID errors
A client can include many structurally valid but unmapped SIDs in a DACL.
Logging every mapping failure lets one request generate hundreds of kernel
error messages.

Rate limit the message to prevent an authenticated client from flooding
the kernel log.

Fixes: e2f34481b2 ("cifsd: add server-side procedures for SMB3")
Reported-by: Cheryl Babcock <cheryl@renat.io>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:45 +09:00
Namjae Jeon
c61dc7b1b4 ksmbd: propagate DACL parsing errors
parse_dacl() silently accepts truncated ACEs and allocation failures,
allowing set_info_sec() to continue with an incomplete ACL conversion.

Return parsing and allocation errors to parse_sec_desc() so malformed
security descriptors are rejected before inode attributes or ACL xattrs
are updated.

Fixes: e2f34481b2 ("cifsd: add server-side procedures for SMB3")
Reported-by: Cheryl Babcock <cheryl@renat.io>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:38 +09:00
Namjae Jeon
73f860489e ksmbd: zero pipe read compound padding
Compound response handling extends the last response iov to an eight-byte
boundary.

smb2_read_pipe() allocates only the payload size, so the alignment padding
can expose up to seven bytes of uninitialized kernel heap memory.

Allocate the aligned size and clear the unused tail before pinning the
response buffer.

Fixes: e2b76ab8b5 ("ksmbd: add support for read compound")
Reported-by: Cheryl Babcock <cheryl@renat.io>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:34 +09:00
Namjae Jeon
d12168084c ksmbd: safely drain sessions during logoff
SMB3 multichannel allows requests for one session to run on multiple
connections. Wait for all channels bound to a session before freeing
shared session objects.

A deferred byte-range lock remains counted as a running request and only
wakes when its file closes. Wake blocked locks during the drain without
unpublishing or modifying their file objects. Synchronous CANCEL requests
must invoke their cancellation callback to wake pending operations, while
CHANGE_NOTIFY completion remains specific to the asynchronous path.

Serialize session teardown with channel registration and previous-session
cleanup, and use atomic work-state transitions so LOGOFF, CANCEL, and
connection teardown invoke cancellation callbacks only once.

Fixes: 76e98a158b ("ksmbd: fix race condition between destroy_previous_session() and smb2 operations()")
Reported-by: Cheryl Babcock <cheryl@renat.io>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:30 +09:00
Namjae Jeon
5c944895a9 MAINTAINERS: Update the KSMBD entry
Steve French passed away recently. He was a long-time maintainer
of Linux's SMB support and will be greatly missed.

Update the KSMBD entry to no longer list Steve French as a
maintainer.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:26 +09:00
Namjae Jeon
edcd92df5e MAINTAINERS: Add Paulo Alcantara as an SMBDIRECT co-maintainer
Steve French passed away recently. He was a long-time maintainer
of Linux's SMB support and will be greatly missed.

Add Paulo Alcantara as a co-maintainer of SMBDIRECT.

Acked-by: Paulo Alcantara <pc@manguebit.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
Acked-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:21 +09:00
Aleksandr Khromov
db2267b27c ksmbd: fill in FileSysIdentifier in FS_POSIX_INFORMATION
smb2_get_info_filesystem() reports 56 bytes for FS_POSIX_INFORMATION,
that is the whole of FILE_SYSTEM_POSIX_INFO, but never assigns
FileSysIdentifier.  Those eight bytes go to the client as they are found
in the response buffer.

The buffer is zeroed on allocation, so a standalone request leaks
nothing.  A compound request can leak: the offset of the next response
is advanced by the length pinned for the previous one, so a reply that
was written into the buffer and then dropped in favour of the short
error response of smb2_set_err_rsp() stays there, and the next reply is
laid over it with only the header cleared.

Report the file system id statfs() returned, which is what the field is
for.  FileSysIdentifier is __le64 and f_fsid is a pair of ints, so
assemble the value first, val[0] as the low half, and convert it on the
way out.

Fixes: e2f34481b2 ("cifsd: add server-side procedures for SMB3")
Cc: stable@vger.kernel.org
Signed-off-by: Aleksandr Khromov <haa@amicon.ru>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:17 +09:00
Aleksandr Khromov
c0cd3fc682 ksmbd: initialize FileSystemControlFlags in FS_CONTROL_INFORMATION
smb2_get_info_filesystem() reports 48 bytes for FS_CONTROL_INFORMATION,
that is the whole of struct smb2_fs_control_info, but never assigns
FileSystemControlFlags.  Those four bytes go to the client as they are
found in the response buffer.

The buffer is zeroed on allocation, so a standalone request leaks
nothing.  A compound request can leak: the offset of the next response
is advanced by the length pinned for the previous one, so a reply that
was written into the buffer and then dropped in favour of the short
error response of smb2_set_err_rsp() stays there, and the next reply is
laid over it with only the header cleared.

ksmbd does not implement quota tracking, so report no control flags.

Fixes: e2f34481b2 ("cifsd: add server-side procedures for SMB3")
Cc: stable@vger.kernel.org
Signed-off-by: Aleksandr Khromov <haa@amicon.ru>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:12 +09:00
Aleksandr Khromov
399aa12450 ksmbd: zero the FS_OBJECT_ID_INFORMATION buffer before filling it in
smb2_get_info_filesystem() reports 64 bytes for FS_OBJECT_ID_INFORMATION,
that is the whole of struct object_id_info, but writes only 46 of them:

 - objid[] is 16 bytes, and when the volume UUID is not available only
   sizeof(stfs.f_fsid) (8) bytes are copied into it;
 - extended_info.version_string[] is STRING_LENGTH (28) bytes, and only
   strlen("1.1.0") (5) bytes are copied into it.

The response buffer is zeroed on allocation (kvzalloc() in
smb2_allocate_rsp_buf()), so for a standalone request the remaining 31
bytes are zero.  In a compound request they need not be.  The offset of
the next response is advanced by the length pinned for the previous one,
so if a preceding command wrote its reply into the buffer and then
failed, smb2_set_err_rsp() pins only the short error response and the
next reply lands inside the area that has already been written.  Only
the header is cleared there:

	memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);

The client then receives up to 31 bytes of a response it was not meant
to see, including one that failed with an access denied error.

Clear the structure before filling it in.  As a side effect
version_string is now NUL terminated.

Fixes: e2f34481b2 ("cifsd: add server-side procedures for SMB3")
Suggested-by: ChenXiaoSong <chenxiaosong@chenxiaosong.com>
Cc: stable@vger.kernel.org
Signed-off-by: Aleksandr Khromov <haa@amicon.ru>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-31 19:26:07 +09:00
Frank Sorenson
fe39cd9d48 cifs: don't update i_size in cifs_do_truncate without a cached handle
If find_writable_file() returns null, cifs_file_flush will return
0 without issuing set_file_size, and the outer 'if (!rc)' block
will set i_size to 0 before telling the server to truncate.  If
the cifs_open() then fails, the inode will have size 0, while
the server file is unchanged.

Move the netfs_resize_file() and cifs_setsize() into the 'if
(cfile)', so they only run after a successful set_file_size.

In the no-handle else branch, evict stale pages with
truncate_inode_pages before the O_TRUNC open to dispose of old
cache pages, and let the open response set the i_size.

Fixes: 110fee6b9b ("smb: client: fix missing timestamp updates with O_TRUNC")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:05:38 -03:00
Frank Sorenson
1dac61e2c2 smb: client: fix heap overflow in cifs_do_set_acl()
cifs_set_acl() validates ACL size using posix_acl_xattr_size():

        4 + (count * 8)  // 4-byte header + 8 bytes per ACE

cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS
wire format into the same buffer:

        6 + (count * 10)  // 6-byte header + 10 bytes per ACE

An ACL that passes the xattr-based check in cifs_set_acl() can
overflow the heap when posix_acl_to_cifs() writes the larger CIFS
format.

Validate the CIFS format size against the remaining buffer space and
USHRT_MAX before converting--data_count is __u16, so sizes above
USHRT_MAX truncate the on-wire packet length, causing the server to
apply a partial ACL.  Replace MaxDataCount = 1000 with
min(CIFSMaxBufSize, USHRT_MAX).

Fixes: dc1af4c4b4 ("cifs: implement set acl method")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:05:26 -03:00
Paulo Alcantara
6949939586 smb: client: fix multiuser mount with krb5
Customer reported that they could no longer mount their SMB shares
with multiuser mount option and krb5.  Turned out that the client
wasn't duplicating username option when creating multiuser
connections, therefore failing to retrieve credentials as
cifs.upcall(8) couldn't find them in keytab.

Fix this by duplicating username option (if set) from original fs
context before creating multiuser connections with krb5.

Reproducer:

  ```
  $ ktutil
  ktutil:  add_entry -password -p testuser -k 1 -e aes256-cts
  Password for testuser@ZELDA.TEST:
  ktutil:  write_kt /etc/krb5.keytab
  ktutil:  quit
  $ klist -ke
  Keytab name: FILE:/etc/krb5.keytab
  KVNO Principal
   ---- ----------------------------------------------------------------
     1 testuser@ZELDA.TEST (aes256-cts-hmac-sha1-96)
  $ mount.cifs //w22-root2/scratch /mnt/1 -o \
      	uid=1000,sec=krb5,username=testuser@ZELDA.TEST,multiuser
  mount error(13): Permission denied
  Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and
  kernel log messages (dmesg)
  ```

Reported-by: Jacob Shivers <jshivers@redhat.com>
Fixes: 12b4c5d98c ("smb: client: fix krb5 mount with username option")
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Shyam Prasad N <sprasad@microsoft.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Bharath SM <bharathsm@microsoft.com>
Cc: Namjae Jeon <linkinjeon@kernel.org>
Cc: stable@vger.kernel.org
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:05:16 -03:00
Andy Shevchenko
d83a21bb26 smb: client: transport: Fix debug printing in __release_mid()
Long time ago during upgrading printk():s to the respective pr_<level>()
calls one misconversion happened and nobody has noticed that. So,
previously printk(KERN_DEBUG) + printk() worked as one long debug print
since the trailing '\n' is only present in the followup printk() format
string. The culprit change missed that and split the message to two on
the different levels. Restore the original behaviour to make users be
less confused in the most likely never happen cases of partially getting
that message.

Fixes: 0b456f04bc ("cifs: convert printk(LEVEL...) to pr_<level>")
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:05:05 -03:00
Huiwen He
448ba0ae65 smb/client: invalidate fscache for fallocate range operations
smb3_zero_range(), smb3_punch_hole(), smb3_insert_range(), and
smb3_collapse_range() modify file contents through server-side range
operations. These operations discard the affected page cache, but leave
the FS-Cache cookie valid, so a later read may return data cached before
the range operation.

Fix this by invalidating FS-Cache after outstanding I/O has completed
and before modifying the file on the server.

Run the following as root on a CIFS mount with fsc enabled and an active
CacheFiles backend:

        bash -c '
                MNT=/mnt/cifs
                FILE="$MNT/repro"

                # Generate four 1 MiB random blocks: [A][B][C][D].
                dd if=/dev/urandom of=/tmp/src bs=1M count=4 status=none

                # Expected contents after zeroing B: [A][zero][C][D].
                cp /tmp/src /tmp/expected
                dd if=/dev/zero of=/tmp/expected bs=1M seek=1 count=1 \
                        conv=notrunc status=none
                cp /tmp/src "$FILE"

                # Populate FS-Cache, then discard the page cache.
                sync
                echo 1 > /proc/sys/vm/drop_caches
                cat "$FILE" > /dev/null
                sync
                echo 1 > /proc/sys/vm/drop_caches

                fallocate --zero-range -o 1M -l 1M "$FILE"

                if cmp -s /tmp/expected "$FILE"; then
                        echo "readback: OK"
                else
                        echo "readback: STALE DATA"
                fi
        '

Before this change, the readback differs from /tmp/expected:

        readback: STALE DATA

After this change, it matches:

        readback: OK

Fixes: 30175628bf ("[SMB3] Enable fallocate -z support for SMB3 mounts")
Fixes: 31742c5a33 ("enable fallocate punch hole ("fallocate -p") for SMB3")
Fixes: 5476b5dd82 ("cifs: add support for FALLOC_FL_COLLAPSE_RANGE")
Fixes: 7fe6fe95b9 ("cifs: add FALLOC_FL_INSERT_RANGE support")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Suggested-by: Namjae Jeon <linkinjeon@kernel.org>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:04:21 -03:00
Huiwen He
01261a6fa4 smb/client: fix stale page cache in insert/collapse range
smb3_insert_range() and smb3_collapse_range() use
truncate_pagecache_range() to invalidate the affected page cache.
However, if off or old_eof is not page-aligned, the boundary pages are
only partially zeroed and remain uptodate. As a result, the client may
return stale data after a successful insert/collapse range operation.

For example, with 4K pages:

    page 0          page 1          page 2
    0------4K       4K------8K      8K------12K
       ^                                ^
    off=2K                       old_eof=10K

Page 1 is removed from the page cache, while the boundary pages are
only partially zeroed. After COPYCHUNK moves the data on the server,
these cached pages may still return stale data.

This can be reproduced on a CIFS mount:

    bash -c '
            FILE=/mnt/scratch/repro

            # Use a 6 KiB file so EOF is not page-aligned.
            dd if=/dev/urandom of=/tmp/src bs=1K count=6 status=none

            # Expected: a 4 KiB hole followed by the original data.
            rm -f /tmp/expected
            truncate -s 4K /tmp/expected
            cat /tmp/src >> /tmp/expected

            cp /tmp/src "$FILE"

            # Prime the page cache before moving data on the server.
            cat "$FILE" > /dev/null

            fallocate --insert-range -o 0 -l 4K "$FILE"

            if cmp -s /tmp/expected "$FILE"; then
                    echo "readback: OK"
            else
                    echo "readback: STALE DATA"
            fi
    '

Fix this by writing back dirty data and discarding the page cache from
the start of the page containing off to EOF before moving data on the
server.

Fixes: 9c8b7a293f ("smb3: fix temporary data corruption in insert range")
Fixes: fa30a81f25 ("smb3: fix temporary data corruption in collapse range")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:04:21 -03:00
Huiwen He
7811701d6a smb/client: fix integer truncation in collapse range
smb3_collapse_range() stores the ssize_t return value of
smb2_copychunk_range() in an int. A successful copy larger than
INT_MAX is truncated to a negative value and treated as an error.

Reproducer:

	MNT=/mnt/scratch

	truncate -s 2056M "$MNT/file"
	fallocate --collapse-range -o 1M -l 1M "$MNT/file"

Fix this by using __smb2_copychunk_range(), which reports success as
zero instead of returning the copied byte count.

Before this change, the reproducer fails with:

	fallocate: fallocate failed: Success

and the file size remains unchanged at 2056 MiB. After this change, the
reproducer succeeds and the file size becomes the expected 2055 MiB.

Fixes: 5476b5dd82 ("cifs: add support for FALLOC_FL_COLLAPSE_RANGE")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:04:20 -03:00
Huiwen He
0923ae9f23 smb/client: fix data corruption in emulated insert range
smb3_insert_range() shifts [off, EOF) right with COPYCHUNK, copying from
low to high offsets. When the ranges overlap, the copy can overwrite
source data that has not yet been copied. For a 1 MiB insert at offset 0:

  offset:    0       1M      2M      3M      4M      5M
  before:   |   A   |   B   |   C   |   D   |
  expected: | hole  |   A   |   B   |   C   |   D   |
  current:  | hole  |   A   |   A   |   A   |   A   | (corrupted)

Let x be the insertion offset, L the total length to move, delta the
insert length, and C the normal chunk size allowed by the server.
Insert range maps

  [x, x + L) -> [x + delta, x + delta + L).

When delta >= L, the complete source and target ranges are disjoint, so
the normal copy order and chunk size are safe:

  offset: 0       4       8      12      16      20      24      28      32
  source: [--S0--][--S1--][--S2--][--S3--]
  target:                                 [--T0--][--T1--][--T2--][--T3--]

When delta < L, the complete source and target ranges overlap, so the
copy must proceed from EOF backwards. There are two subcases.

If delta >= C, each corresponding source and target chunk is disjoint.
The 1 MiB example has L = 4 MiB and delta = C = 1 MiB:

  offset: 0       1M      2M      3M      4M      5M
  source: [--S0--][--S1--][--S2--][--S3--]
  target:         [--T0--][--T1--][--T2--][--T3--]

Copying S0 from [0, 1M) to [1M, 2M) overwrites S1 before it is copied.
Processing chunks from EOF backwards prevents this inter-chunk
overwrite.

If delta < C, the source and target ranges of a normal chunk also
overlap. For example, with L = 16, delta = 2 and C = 4:

  offset: 0   2   4   6   8  10  12  14  16  18
  source: [--S0--][--S1--][--S2--][--S3--]
  target:     [--T0--][--T1--][--T2--][--T3--]

Here S0 and T0 overlap over [2,4), S1 and T1 over [6,8), and so on.
Backward ordering cannot control how the server copies bytes inside one
descriptor, so the chunk size must be limited to delta.

Fix this by copying overlapping right shifts from EOF backwards. Limit
the chunk size to delta when delta < C so that each chunk's source and
target ranges do not overlap. Using larger chunks would require a way to
identify servers that safely handle overlapping COPYCHUNK descriptors.

Therefore:

  delta >= L:
    keep the normal copy order and chunk size

  delta < L:
    delta >= C: copy backwards and keep the normal chunk size
    delta <  C: copy backwards and limit the chunk size to delta

Only the delta < C subcase requires reducing the chunk size for data
integrity.

Reproducer:

  bash -c '
          MNT=/mnt/scratch

          # Generate four 1 MiB random blocks: [A][B][C][D].
          dd if=/dev/urandom of=/tmp/src bs=1M count=4 status=none

          # With C = 1 MiB, test delta = C and delta < C.
          for delta in 1M 1K; do
                  truncate -s 0 /tmp/expected
                  truncate -s "$delta" /tmp/expected
                  cat /tmp/src >> /tmp/expected

                  cp /tmp/src "$MNT/file"
                  fallocate --insert-range -o 0 -l "$delta" "$MNT/file"

                  if cmp -s /tmp/expected "$MNT/file"; then
                          echo "delta=$delta: OK"
                  else
                          echo "delta=$delta: CORRUPTED"
                  fi
          done
  '

The corruption reproduces with Samba and ksmbd, while Windows handles
the overlapping COPYCHUNK ranges safely.

The 1 MiB case tests delta >= C, while the 1 KiB case tests delta < C.
Before this change, the reproducer reports:

  delta=1M: CORRUPTED
  delta=1K: CORRUPTED

After this change, it passes against both ksmbd and Samba:

  delta=1M: OK
  delta=1K: OK

Fixes: 7fe6fe95b9 ("cifs: add FALLOC_FL_INSERT_RANGE support")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:04:20 -03:00
Huiwen He
cd03ce4950 smb/client: mark file sparse before emulating insert range
The SMB client emulates FALLOC_FL_INSERT_RANGE with SET_EOF, COPYCHUNK
and SET_ZERO_DATA.

SET_ZERO_DATA creates a hole only when the file is sparse. On a
non-sparse file, it clears the inserted range but leaves its blocks
allocated, causing the extent count check in xfstests generic/064 to
fail.

Fix this by marking the file sparse before modifying it.

This patch produces the expected sparse extents in xfstests generic/064
only when the server-reported block size is compatible with the server's
deallocation granularity.

For ksmbd, the reported block size follows the backing filesystem,
and the test passes. For Samba, the test passes with a block size
matching the backend granularity, for example, 4 KiB on Btrfs, but not
with the default 1 KiB value. For Windows Server 2022, 4 KiB inserts do
not generate holes, while aligned inserts of 64 KiB or larger do.

Fixes: 7fe6fe95b9 ("cifs: add FALLOC_FL_INSERT_RANGE support")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:04:20 -03:00
Huiwen He
88972e3575 smb/client: validate new EOF for zero range
When FALLOC_FL_ZERO_RANGE is used without FALLOC_FL_KEEP_SIZE,
smb3_zero_range() may extend EOF without checking RLIMIT_FSIZE, allowing
the file to grow beyond the caller's file-size limit.

Fix this by calling inode_newsize_ok() before sending the zero-range
request when the operation would extend EOF.

Reproducer, using a file on a CIFS mount:

	bash -c '
	        FILE=/mnt/cifs/repro

	        trap "" SIGXFSZ
	        ulimit -f 3072

	        truncate -s 2M "$FILE"
	        fallocate --zero-range -o 0 -l 4M "$FILE"
	        echo "fallocate rc=$?"
	        stat -c "file size=%s" "$FILE"
	'

Before this change, the operation succeeds despite the 3 MiB limit:

	fallocate rc=0
	file size=4194304

After this change, fallocate fails and leaves the file at 2 MiB.

Fixes: 72c419d9b0 ("cifs: fix smb3_zero_range so it can expand the file-size when required")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:04:20 -03:00
Huiwen He
1519dc88c8 smb/client: validate new EOF for insert range
smb3_insert_range() does not check if the new file size
(i_size + len) is valid. This allows FALLOC_FL_INSERT_RANGE to bypass
RLIMIT_FSIZE, exceed s_maxbytes, or produce a size outside the loff_t
range.

Use check_add_overflow() to calculate the new EOF. Validate it with
inode_newsize_ok() before modifying the file.

Reproducer, using a file on a CIFS mount:

	bash -c '
		FILE=/mnt/cifs/repro

		trap "" SIGXFSZ
		ulimit -f 3072		# RLIMIT_FSIZE = 3 MiB

		# A regular write is stopped at 3 MiB.
		dd if=/dev/zero of="$FILE" bs=1M count=4 status=none
		stat -c "size after write: %s" "$FILE"

		# Insert 2 MiB into a 2 MiB file.
		truncate -s 2M "$FILE"
		fallocate -i -o 0 -l 2M "$FILE"
		stat -c "size after insert: %s" "$FILE"
	'

Before this change, the regular write stops at the 3 MiB limit, but
insert range grows the file to 4 MiB:

	dd: error writing '/mnt/cifs/repro': File too large
	size after write: 3145728
	size after insert: 4194304

After this change, insert range also fails at the limit and leaves the
2 MiB file unchanged:

	dd: error writing '/mnt/cifs/repro': File too large
	size after write: 3145728
	fallocate: fallocate failed: File too large
	size after insert: 2097152

Fixes: 7fe6fe95b9 ("cifs: add FALLOC_FL_INSERT_RANGE support")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-30 14:04:20 -03:00
Frank Sorenson
53676a5e28 cifs: add revalidation on FSCTL failure in smb2_duplicate_extents()
smb2_duplicate_extents() has no handling for
FSCTL_DUPLICATE_EXTENTS_TO_FILE failure: when the FSCTL fails, local
inode metadata may be stale from the pre-extension or from concurrent
remote writes, but is never refreshed.

Force revalidation on FSCTL failure and use i_size_read() for the
pre-extension check.

Fixes: cfc63fc812 ("smb3: fix cached file size problems in duplicate extents (reflink)")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
2026-08-26 19:32:05 -03:00
Yao Kai
cf9c8aaea0 workqueue: Fix unbound pool lifetime for pending pwqs
KASAN reports a use-after-free of an unbound worker_pool in
node_activate_pending_pwq():

  BUG: KASAN: slab-use-after-free in _raw_spin_trylock+0x6d/0x120
  Read of size 4 at addr ffff8880089ce000 by task kworker/u22:0/318

  CPU: 1 UID: 0 PID: 318 Comm: kworker/u22:0 Not tainted 7.2.0 #1 PREEMPT(lazy)
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
  Workqueue:  0x0 (flush-8:0)
  Call Trace:
   <TASK>
   dump_stack_lvl+0x53/0x70
   print_report+0xce/0x610
   kasan_report+0xce/0x100
   _raw_spin_trylock+0x6d/0x120
   pwq_dec_nr_in_flight+0x4b4/0xcb0
   process_one_work+0x921/0x11a0
   worker_thread+0x4d0/0xd20
   kthread+0x2de/0x3c0
   ret_from_fork+0x3aa/0x620
   ret_from_fork_asm+0x1a/0x30
   </TASK>

  Allocated by task 311:
   alloc_pwq+0x439/0xca0
   apply_wqattrs_prepare+0x75e/0xd10
   apply_workqueue_attrs_locked+0x44/0xa0
   wq_nice_store+0x350/0x450

  Freed by task 0:
   kfree+0x127/0x3b0
   rcu_core+0x523/0x1780
   handle_softirqs+0x1b3/0x610

  Last potentially related work creation:
   put_unbound_pool+0x3f3/0x7d0
   pwq_release_workfn+0x494/0x8e0
   kthread_worker_fn+0x1ff/0x790

Canceling the last inactive work skips pwq_dec_nr_active(), so an empty
pwq can remain on pending_pwqs when its refcnt reaches zero.
pwq_release_workfn() currently puts the pool before removing that pwq.
If this drops the last pool reference, the pool can be RCU-freed while
the pwq remains reachable, and node_activate_pending_pwq() may trylock
the freed pool->lock.

Remove the pwq from pending_pwqs before putting the pool.

Fixes: 5797b1c189 ("workqueue: Implement system-wide nr_active enforcement for unbound workqueues")
Cc: stable@vger.kernel.org
Signed-off-by: Yao Kai <yaokai34@huawei.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-24 07:42:40 -10:00
Tao Cui
6586705bc2 docs/sched_ext: document that cgroup CPU knobs are scheduler-dependent
The scheduler core communicates the initial cpu controller settings
to the BPF scheduler through ops.cgroup_init() and reports subsequent
changes through the corresponding ops.cgroup_set_*() callbacks.
Whether and how a knob takes effect is up to the loaded scheduler:
it may implement the corresponding callback partially or not at all,
so cpu.max, cpu.weight and friends can silently have no effect.

Document this in the basics section of sched-ext.rst.

Signed-off-by: Tao Cui <cuitao@kylinos.cn>
Reviewed-by: Andrea Righi <arighi@nvidia.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-24 07:05:33 -10:00
Guopeng Zhang
87d347a8c8 selftests/cgroup: Add test for preserving boot-isolated CPUs
Put a CPU isolated at boot into an isolated partition, change the
partition back to member and check that the CPU remains isolated.

Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Reviewed-by: Waiman Long <longman@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-24 07:01:44 -10:00
Guopeng Zhang
6c37d7e074 cgroup/cpuset: Preserve boot-isolated CPUs on partition release
isolated_cpus tracks CPUs isolated with isolcpus= as well as CPUs in
isolated cpuset partitions. When an isolated partition is released,
isolated_cpus_update() removes its whole CPU mask. This also clears CPUs
which were already isolated at boot.

This can be reproduced on a cgroup v2 system booted with
isolcpus=domain,15:

    cd /sys/fs/cgroup
    echo +cpuset > cgroup.subtree_control
    mkdir cpuset-repro
    echo 15 > cpuset-repro/cpuset.cpus
    echo isolated > cpuset-repro/cpuset.cpus.partition
    echo member > cpuset-repro/cpuset.cpus.partition
    cat cpuset.cpus.isolated

CPU 15 is absent before the change. It must remain in
cpuset.cpus.isolated after the partition is released.

Update isolated_cpus one CPU at a time and keep CPUs outside the
boot-time domain housekeeping mask isolated.

Fixes: c188f33c86 ("cgroup/cpuset: Account for boot time isolated CPUs")
Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Acked-by: Waiman Long <longman@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-24 07:01:30 -10:00
Guopeng Zhang
2bf404b1bd selftests/cgroup: Drop invalid boot isolation comparison
check_isolcpus() clears ISOLCPUS before rebuilding it from sched domain
data. Comparing that empty value with
/sys/devices/system/cpu/isolated makes the test fail whenever
isolcpus=domain is present.

That sysfs file is generated from HK_TYPE_DOMAIN_BOOT and does not change
when cpuset updates HK_TYPE_DOMAIN. Re-reading it cannot validate dynamic
housekeeping updates. The cpuset.cpus.isolated and sched domain checks
already cover the two dynamic interfaces, so remove the invalid comparison.

This can be reproduced on a kernel booted with isolcpus=domain,15:

    # tools/testing/selftests/cgroup/test_cpuset_prs.sh

The test fails its first state-matrix isolation check before the change and
continues past that check afterward.

Fixes: 6df415aa46 ("cgroup/cpuset: Defer housekeeping_update() calls from CPU hotplug to workqueue")
Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Reviewed-by: Waiman Long <longman@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-24 07:01:15 -10:00
Cheng Lingfei
909a3f0e9d docs: cgroup-v2: fix misc.events key format description
In misc cgroup, misc.events does not output a simple "max" key. Instead,
each registered misc resource outputs a separate key suffixed with ".max"
(i.e., "<res>.max").
Update the documentation to clarify that the entry key is "<res>.max".

Suggested-by: Michal Koutný <mkoutny@suse.com>
Signed-off-by: Cheng Lingfei <chenglingfei@foxmail.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-24 06:55:59 -10:00
Tejun Heo
cca061dccf sched_ext: Fix spurious aborts in scx_bpf_dsq_move() on ownership change races
scx_dsq_move() verifies that the task belongs to the calling scheduler
before taking any locks and aborts the scheduler on mismatch. The task can
lose the sched association at any point: It can run and fully exit, which
clears the association, or get rehomed to a different sub-sched. Both are
benign races, but the early ownership check escalates them into scheduler
aborts.

Move the ownership check below the cursor-lost check. Every ownership change
dequeues the task first, so a task that is still on the iterated DSQ under
the lock while owned elsewhere indicates a genuine violation and should
abort.

Also fix two stale comments still referencing sched_ext_free(), which has
been renamed to sched_ext_dead().

Fixes: bb4d9fd551 ("sched_ext: scx_dsq_move() should validate the task belongs to the right scheduler")
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-22 06:50:10 -10:00
Tejun Heo
9e8581a090 sched_ext: Sync common and compat headers from the scx repo
Sync common.bpf.h, compat.bpf.h and compat.h with the scx repo, which
accumulated the following:

- __COMPAT_read_enum() can now recover 64-bit scx enum values from kernel
  BTF generated without BTF_KIND_ENUM64 support (pahole < 1.24 or
  --skip_encoding_btf_enum64, e.g. COS/GKE kernels), substituting values
  from the build-time vmlinux.h cross-checked against the low 32 bits the
  kernel does provide.

- is_migration_disabled() no longer assumes the BPF prolog always disables
  migration. Since 8e4f0b1ebc ("bpf: use rcu_read_lock_dont_migrate() for
  trampoline.c") the prolog only does so under CONFIG_PREEMPT_RCU, so the
  old current-task test under-reported on v6.18+ !PREEMPT_RCU kernels. A
  runtime probe on bpf_scx_reg() handles older kernels with backported
  trampoline behavior.

- __COMPAT_scx_bpf_dsq_peek() is gated behind kernel v7.1 where 2f2ea77092
  ("sched_ext: Use dsq->first_task instead of list_empty() in
  dispatch_enqueue() FIFO-tail") fixed the kfunc spuriously returning NULL
  on non-empty FIFO DSQs, and the new
  scx_bpf_reenqueue_local_from_anywhere() provides a callable-from-anywhere
  reenqueue which prefers the generic scx_bpf_dsq_reenq(). Both were first
  posted by Gavin Guo and Changwoo Min and are picked up here with the
  review feedback folded in.

- __COMPAT_scx_bpf_cpu_curr() and the scx_bpf_cpu_rq() declaration are
  restored. Schedulers built from these headers still run on pre-v6.18
  kernels where scx_bpf_cpu_curr() does not resolve and the scx_bpf_cpu_rq()
  fallback still exists.

- scx_clock_task() and scx_clock_pelt() document their stale-read behavior
  for remote idle CPUs under NO_HZ_IDLE.

Link: https://lore.kernel.org/all/20260817143126.562923-1-changwoo@igalia.com
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-21 21:19:26 -10:00
Tejun Heo
e10b8b4931 sched_ext: Sync tools autogen enum headers from the scx repo
Regenerate enum_defs.autogen.h against the current tree, picking up the
dispatch verdict enums and dropping the marker for the removed
SCX_RQ_IN_BALANCE. Add enums_abi.autogen.h, a table of 64-bit scx enumerator
values generated from vmlinux.h, used as the substitution source when the
running kernel's BTF truncates 64-bit enum values to 32 bits.

Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-21 21:19:26 -10:00
Carlos Maiolino
412f89fb39 capability: unexport has_capability_noaudit
This has been originally exported to be used in xfs. Giving we are not
using it anymore, unexport for consistency.

Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Serge Hallyn <sergeh@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:55 +02:00
Carlos Maiolino
be9c45bdb1 xfs: replace ns_capable_noaudit
Now that capable_noaudit() is available, we don't need to keep
using ns_capable_noaudit() and specifying the usernamespace every single
time.

Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Carlos Maiolino
4642259374 quota: Don't issue audit messages on quota enforcing
Calling capable() to determine if we can bypass quota enforcement or not
can trigger spurious audit messages. We don't really require it here so
just use the capable_noaudit() version.

Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Carlos Maiolino
1b91724d0b capability: Add new capable_noaudit
In some situations (quota enforcement bypass in this case) we'd like to
check for a specific capability without triggering spurious audit
messages from security modules like selinux.

Add a new helper so we don't need to use ns_capable_noaudit() directly.

Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Serge Hallyn <sergeh@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Carlos Maiolino
e2f62a9744 xfs: fix capability check in xfs
An user reported a bug where he managed to evade group's quota
by changing a file's gid to a different group id the same user
belonged to, even though quotas were enforced on both gids and the
file's size was big enough to exceed the quota's hardlimit.

Commit eba0549bc7 replaced a capable() call by a
has_capability_noaudit() to prevent unnecessary selinux audit messages.
Turns out that both calls have slightly different semantics even though
their documentation seems similar. Where in a nutshell:

capable() - Tests the task's effective credentials
has_ns_capability_noaudit() - Tests the task's real credentials

This most of the time has no practical difference but in some cases like
changing attrs (specifically group id in this case) through a NFS client
this will allow the quota code to use XFS_QMOPT_FORCE_RES, effectively
bypassing quota accounting checks.

Using instead ns_capable_noaudit() should fix this issue and prevent
selinux audit messages.

This also fix the remaining calls to has_capability_noaudit()

Fixes: eba0549bc7 ("xfs: don't generate selinux audit messages for capability testing")
Cc: stable@vger.kernel.org # v5.18
Reported-by: Dr. Thomas Orgis <thomas.orgis@uni-hamburg.de>
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Serge Hallyn <sergeh@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
885435535b xfs: restore bi_bdev in xfs_zone_gc_write_chunk
xfs_zone_gc_write_chunk relies on bi_bdev to still be valid, which is not
true when XFS is used on top of a stacked block device.  This can lead to
misdirected GC writes, writing of plain text when using dm-crypt, or
miscalculated I/O limits in xfs_zone_gc_split_write.

Fix this by reassigning bi_bdev.

Fixes: 080d01c41d ("xfs: implement zoned garbage collection")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
6b855256eb xfs: split ioend handling into a separate source file
The ioend handling used to be only for buffered writeback, but has been
extended to direct I/O and reads.  Split it into a new source file.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
0510346e8e xfs: factor out a xfs_iomap_set_anon_write helper
De-duplicate the iomap setup for zoned writes.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
4bc67fc800 xfs: fix zoned write iomap flags assignments
Don't overwrite IOMAP_F_DIRTY with IOMAP_F_ANON_WRITE, but ensure both
flags are set instead.

Note that in practice this is harmless as all zoned writes force a metadata
transaction anyway, but incorrectly assigned flags are still a landmine
that will cause problems at some point.

Fixes: 058dd70c65 ("xfs: implement buffered writes to zoned RT devices")
Fixes: 2e23834058 ("xfs: implement direct writes to zoned RT devices")
Cc: stable@vger.kernel.org # v6.15
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
2d829cc767 xfs: fix racy open zone caching
When testing on very fast storage devices, I've observed writers using
io_uring creating many open zones with just a few kiB written to it,
which then don't get used.  I tracked this down to multiple io_uring
helper threads finding a full zone in i_private, and then going on to
select a one, with the final one winning the race and leaving it in
i_private.

Fix this by dropping full zones from i_private as soon we find them,
checking cached for a cached zoned when a single writes needs a new zone,
and by keeping an existing cached zone in xfs_set_cached_zone when it
still has space available, dropping the newly found/allocated one
instead.  This uses i_flags_lock as a low-level spinlock for short
hold times to avoid interactions with the ilock, which is used for
completions.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
ae28561189 xfs: handle NULL open_zone for merged ioends in xfs_ioend_put_open_zones
In theory we could fail multiple ioends before an open zone was assigned
to them, and the iomap code could merge them.  Check for NULL not only
for the main ioend but also all merged ones on ->io_list to handle this
case.

Fixes: 058dd70c65 ("xfs: implement buffered writes to zoned RT devices")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
4e07cd78e1 xfs: use inode_init_always_gfp with __GFP_NOFAIL in xfs_inode_alloc
Just like the inode allocation itself, allocation of the security data
inside of inode_init_always(_gfp) must not fail here as we can be inside
an already dirty transaction context.  Note that we do not have to pass
GFP_NOFS explicitly as we are already in a nofs context when in a
transaction, as seen by the call to alloc_inode_sb.

Also update the comment about this a bit to be more clear.

Fixes: bf904248a2 ("[XFS] Combine the XFS and Linux inodes")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Tal Zussman
750a361bfc xfs: remove kmem_to_page()
kmem_to_page() has been unused since commit 5ced480d48 ("xfs: simplify
building the bio in xlog_write_iclog"), so remove it. This also removes
the last instance of 'struct page' in fs/xfs/.

Signed-off-by: Tal Zussman <tz2294@columbia.edu>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
7fc296b379 xfs: don't flush and invalidate internal RT device twice in xfs_shutdown_devices
Check for an internal RT device to remove a bit of extra work.

Fixes: bdc03eb5f9 ("xfs: allow internal RT devices for zoned mode")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Christoph Hellwig
b9b541e70d xfs: split an assert in xfs_trans_log_buf
Split the "irst <= last && last < BBTOB(bp->b_length)" assert into two to
make it clear which condition fired.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:54 +02:00
Yun Zhou
41a28c865d xfs: don't hold buffer locks across sync transaction commit in xfs_sync_sb_buf
xfs_sync_sb_buf() holds sb/rtsb buffer locks across a synchronous
xfs_trans_commit(), which flushes the CIL push workqueue internally.
If shutdown occurs during the CIL push, xfs_buf_item_unpin() needs to
lock these buffers to fail them, causing a deadlock:

  setlabel:          holds buf lock -> flush_workqueue(xfs-cil)
  CIL push worker:   xfs_buf_item_unpin -> xfs_buf_lock(same buf)

Remove the xfs_trans_bhold() calls so that commit releases the buffer
locks normally.  After the sync commit, re-acquire the buffers via
mp->m_sb_bp / mp->m_rtsb_bp for the on-disk writeback.

Fixes: f7664b3197 ("xfs: implement online get/set fs label")
Reported-by: syzbot+837bcd54843dd6262f2f@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=837bcd54843dd6262f2f
Cc: stable@vger.kernel.org
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2026-08-21 09:09:53 +02:00
Hongfu Li
a8c6daab4b selftests/cgroup: Fix cg_run_in_subcgroups ignoring arg parameter
cg_run_in_subcgroups() discards its arg and always passes NULL to cg_run(),
turning the (void *)100 from test_kmem_dead_cgroups() into NULL so no
allocation occurs.

This makes test_kmem_dead_cgroups() falsely pass without exercising the
"dying cgroup with charged slab" scenario it intends to test.

Pass the arg through to cg_run() to fix this.

Fixes: 933dc80ec2 ("kselftests: cgroup: add kernel memory accounting tests")
Signed-off-by: Hongfu Li <lihongfu@kylinos.cn>
Reviewed-by: Michal Koutný <mkoutny@suse.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-20 07:48:01 -10:00
Liang Luo
72c5ae18eb Docs/admin-guide/cgroup-v2: document BPF scheduler callbacks for cpu.max and cpu.idle
The cpu.weight and cpu.weight.nice entries already state that the files
also affect a BPF scheduler through the cgroup_set_weight callback.
However, cpu.max, cpu.max.burst and cpu.idle only mention the fair-class
scheduler, even though sched_ext implements the cgroup_set_bandwidth
(notified with the period/quota from cpu.max and the burst from
cpu.max.burst) and cgroup_set_idle callbacks from these interfaces.

Mirror the cpu.weight wording for the three entries and generalize the
category preamble to refer to the corresponding cgroup_set_* callback so
it keeps covering the entries below.

Suggested-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Liang Luo <luoliang@kylinos.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-20 07:45:22 -10:00
Zqiang
7e2f2a377a workqueue: Use raise_softirq() to trigger softirq in irq_work handler
bh_pool_kick_normal() and bh_pool_kick_highpri() are registered via
init_irq_work() without the IRQ_WORK_HARD_IRQ flag. On PREEMPT_RT, such
irq_work items are processed by the per-CPU irq_workd kthread in preemptible
task context with IRQs enabled. However, raise_softirq_irqoff() requires
IRQs to be disabled. Calling it from irq_workd trips the lockdep assertion
in __raise_softirq_irqoff() and the non-atomic update of the softirq pending
mask can lose bits raised by an interrupt on the same CPU. Replace
raise_softirq_irqoff() with raise_softirq() in the irq_work handlers.

Fixes: 2f34d7337d ("workqueue: Fix queue_work_on() with BH workqueues")
Cc: stable@vger.kernel.org # v6.9+
Signed-off-by: Zqiang <qiang.zhang@linux.dev>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-19 10:13:47 -10:00
Hemanth Selam
0c893d170f selftests/cgroup: set the test plan after the setup checks
The cgroup tests announce their plan before checking whether cgroup v2 is
available, so on a host without it they promise a number of results and
then skip out after the first one:

	TAP version 13
	1..3
	ok 1 # SKIP cgroup v2 isn't mounted
	# Planned tests != run tests (3 != 1)
	# Totals: pass:0 fail:0 xfail:0 xpass:0 skip:1 error:0

ksft_exit_skip() can only emit a well formed "1..0 # SKIP" line while no
plan has been printed, as the comment above it in kselftest.h points out.

Move ksft_set_plan() below the setup checks that can skip, so that a
skipped run reports:

	TAP version 13
	1..0 # SKIP cgroup v2 isn't mounted

Several of the tests skip more than once while setting up, for a missing
or unwritable controller as well, so the plan goes after the last of
them.  test_core joins its two setup paths at the post_v2_setup label and
sets the plan there.

Reporting each planned test as skipped instead would keep the plan where
it is, but the setup failures here mean the whole test cannot run rather
than its individual cases being skipped, which is what "1..0 # SKIP" is
for.

Fixes: 1dc830ee4c ("selftests/cgroup: conform test to KTAP format output")
Signed-off-by: Hemanth Selam <hemanth.selam@gmail.com>
Reviewed-by: Sarthak Sharma <sarthak.sharma@arm.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-19 10:04:43 -10:00
Liang Luo
4fb8d6379d sched_ext: Fix nonexistent field in sched-ext.rst example
The ops.exit() example in sched-ext.rst reads ei->type, but
struct scx_exit_info has never had a type field - the exit reason is
exposed as ei->kind since the struct was introduced. A scheduler
written following the example fails to compile with

    error: no member named 'type' in 'struct scx_exit_info'

Use ei->kind.

Fixes: fa48e8d2c7 ("sched_ext: Documentation: scheduler: Document extensible scheduler class")
Signed-off-by: Liang Luo <luoliang@kylinos.cn>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-19 10:04:11 -10:00
Tejun Heo
6eca8f94d8 workqueue: Annotate cb_lock nesting when draining a dead BH pool
On PREEMPT_RT, bh_worker() wraps work item execution in pool->cb_lock to
provide a handshake for canceling BH work items. When a CPU goes down,
drain_dead_softirq_workfn() runs the dead pool's bh_worker() nested inside
the local pool's bh_worker(), acquiring the cb_locks of two different pools
without a nesting annotation. lockdep reports possible recursive locking:

  ============================================
  WARNING: possible recursive locking detected
  --------------------------------------------
  ktimers/0/16 is trying to acquire lock:
  ffff8880b873a990 (&pool->cb_lock){+...}-{3:3}, at: bh_worker+0x7d/0x880

  but task is already holding lock:
  ffff8880b863a990 (&pool->cb_lock){+...}-{3:3}, at: bh_worker+0x7d/0x880

  Call Trace:
   bh_worker+0x7d/0x880 kernel/workqueue.c:3688
   drain_dead_softirq_workfn+0x95/0x220 kernel/workqueue.c:3763
   process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405
   bh_worker+0x46a/0x880 kernel/workqueue.c:3708
   tasklet_action+0xc/0x70 kernel/softirq.c:965

The nesting can't deadlock. A pool's bh_worker() runs nested only while the
pool's CPU is dead, entered from a live pool's bh_worker() on the draining
CPU, so the ordering is always live to dead. CPU hotplug operations are
serialized and the drain is synchronous, so the nesting depth never exceeds
two. Annotate the inner acquisition with SINGLE_DEPTH_NESTING.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: syzbot+1bd20115328f8254ed62@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1bd20115328f8254ed62
Fixes: ad7c7f4b9c ("workqueue: Provide a handshake for canceling BH workers")
Cc: stable@vger.kernel.org # v6.18+
2026-08-18 08:53:17 -10:00
Changwoo Min
5f01293930 sched_ext: Allow ops.cgroup_set_bandwidth() to be sleepable
ops.cgroup_set_bandwidth() is delivered from scx_group_set_bandwidth(),
which runs from the cpu.max cgroup interface write path (tg_set_bandwidth())
in process context. scx_group_set_bandwidth() holds
percpu_down_read(&scx_cgroup_ops_rwsem), whose read side may sleep.
The call site is therefore sleepable, like ops.cgroup_init().

bpf_scx_check_member() rejects a sleepable program on any member not on its
allow-list, so a BPF scheduler cannot allocate -- which is sleepable -- when
a cgroup gains a cpu.max limit at runtime; it must instead pre-reserve memory
for a callback that cannot allocate. Add cgroup_set_bandwidth() to the
allow-list so the callback can allocate on demand, and document that it may
block.

A scheduler must decide at load time whether to mark the callback sleepable,
but the allow-list entry is a verifier property with no symbol to probe. Add
a compatibility marker whose presence in the kernel's BTF lets userspace detect
this support: DEFINE_SCX_COMPAT_MARKER() emits an empty, callerless function,
here scx_compat_marker_cgroup_set_bandwidth_may_sleep(). It is __used
__retain so neither the compiler nor the linker (under
CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) drops it. The markers share the
scx_compat_marker_ prefix and are collected near the end of ext.c so more
can be added as further capabilities appear.

Signed-off-by: Changwoo Min <changwoo@igalia.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-18 07:59:38 -10:00
Borislav Petkov (AMD)
d6eac38681 EDAC/thunderx: Orphan it
Robert doesn't have hardware to test patches anymore and no one else has
shown interest in maintaining this driver, so orphan it, for now at
least.

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Robert Richter <rric@kernel.org>
Link: https://lore.kernel.org/r/annRsN6UBDPsFLr2@rric.localdomain
2026-08-10 17:57:02 -07:00
Borislav Petkov (AMD)
9987979189 EDAC/device_sysfs: Cleanup around edac_device_ctl_poll_msec_store()
- Align function args
- Fix comment style
- Fixup formatting around edac_device_reset_delay_period() too

The not-too-trivial change is converting the
edac_device_reset_delay_period() msec argument to unsigned int as that
is what the rest of the code expects.

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
2026-08-07 16:30:52 -07:00
Jad Keskes
66cc9dec91 EDAC/device_sysfs: Use kstrtouint() for poll_msec to prevent truncation
The poll_msec sysfs store file uses simple_strtoul() which accepts an unsigned
long, but the target field (poll_msec) is unsigned int. On 64-bit systems,
a value > UINT_MAX is silently truncated when stored.

Fix the mismatch by using kstrtouint() instead. This rejects values larger
than UINT_MAX at parse time, making truncation impossible. Also add a check
for value < 1 to reject the 0-delay case, which would cause the poll work to
spin without delay and consume 100% CPU.

Fixes: e27e3dac65 ("drivers/edac: add edac_device class")
Signed-off-by: Jad Keskes <inasj268@gmail.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://patch.msgid.link/20260730145549.148229-1-inasj268@gmail.com
2026-08-07 16:11:52 -07:00
Qiuxu Zhuo
1713cc6b0e EDAC/igen6: Add Intel Starfire SoCs support
Starfire is a derivative of Panther Lake SoC and shares a similar memory
subsystem architecture. Add Starfire compute die ID and reuse Panther
Lake's configuration data for EDAC support.

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Tested-by: Jie Wang <jie.wang@intel.com>
Link: https://patch.msgid.link/20260730025454.4099934-1-qiuxu.zhuo@intel.com
2026-07-30 11:34:12 -07:00
Qiuxu Zhuo
377c68b988 EDAC/igen6: Refactor address translation logic
The igen6 EDAC driver implements similar interleave and hash translation
logic at multiple levels of the memory hierarchy.

The separate implementations duplicate decoding logic, making future
changes harder and increasing the risk of behavior diverging.

Consolidate the common address translation operations into shared
helpers so all decoding paths use a single implementation.

No functional changes intended.

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-11-qiuxu.zhuo@intel.com
2026-07-30 11:33:47 -07:00
Qiuxu Zhuo
1f43c17ce5 EDAC/igen6: Remove redundant resource configuration tables
Several resource configuration tables differ only in their num_imc
value, while all other fields are identical. Their only purpose is to
describe the number of memory controllers supported by a platform.

Since IMC count is now detected at runtime, these duplicate tables no
longer carry any unique platform information. Reuse the shared
configurations and remove the redundant tables.

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-10-qiuxu.zhuo@intel.com
2026-07-30 11:33:24 -07:00
Qiuxu Zhuo
e492449e39 EDAC/igen6: Detect present memory controllers at runtime
The igen6_edac currently relies on res_config::num_imc to describe the
number of memory controllers supported by each SoC. As a result, adding
support for a new platform requires updating this configuration even
though the hardware can be discovered at runtime.

Instead, detect the number of present memory controllers at runtime and
size the driver state accordingly. This eliminates the need to update
res_config whenever a new SoC variant is added.

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-9-qiuxu.zhuo@intel.com
2026-07-30 11:33:06 -07:00
Qiuxu Zhuo
8ac9136d79 EDAC/igen6: Simplify compute die ID comments
The existing comments repeat information already implied by the code
structure. Shorten them to SoC names only to reduce clutter and
improve readability.

No functional changes intended.

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-8-qiuxu.zhuo@intel.com
2026-07-30 11:32:43 -07:00
Qiuxu Zhuo
a118a5e2f1 EDAC/igen6: Remove unnecessary XOR on the zero-valued interleave bit
When reconstructing the removed interleave bit from an inflated memory
slice address, where a zero was inserted at the interleave bit position,
it's unnecessary to XOR this zero-valued interleave bit.

Remove this unnecessary XOR operation. No functional changes intended.

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-7-qiuxu.zhuo@intel.com
2026-07-30 11:32:20 -07:00
Qiuxu Zhuo
0361f576ec EDAC/igen6: Fix Raptor Lake-P logged error address
Raptor Lake-P was treated as using a different IBECC (In-Band ECC) error
address format and therefore had a dedicated extraction path that shifted
the logged address.

However, Raptor Lake-P uses the same cache-line-granularity error address
format as other IBECC platforms. The special handling causes the logged
address to be decoded incorrectly.

Fix the issue by removing Raptor Lake-P specific extraction logic and using
the common path instead. This also allows reusing Alder Lake resource
configuration data.

Fixes: d23627a768 ("EDAC/igen6: Add Intel Raptor Lake-P SoCs support")
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-6-qiuxu.zhuo@intel.com
2026-07-30 11:31:57 -07:00
Qiuxu Zhuo
7b348d0d40 EDAC/igen6: Fix channel address decode for non-hash mode
In non-hash mode, decode_channel_addr() and channel index extraction
used a hardcoded interleave bit position 6 instead of the actual
intlv_bit parameter, causing incorrect channel address decoding.

Fix this by using intlv_bit consistently in both hash and non-hash modes.

Fixes: 10590a9d4f ("EDAC/igen6: Add EDAC driver for Intel client SoCs using IBECC")
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-5-qiuxu.zhuo@intel.com
2026-07-30 11:31:27 -07:00
Qiuxu Zhuo
540b79536f EDAC/igen6: Fix channel selection hash
In channel selection hash mode, the hardware decoding logic always
includes the channel interleave bit in XOR operations. However, the
hash mask may or may not include this channel interleave bit. When
the mask does include this bit, the current igen6_edac code performs
XOR on the interleave bit twice, effectively ignoring it - which is
incorrect.

Fix this issue by ensuring the hash mask always includes the interleave
bit, so XOR is performed on the interleave bit exactly once.

Fixes: 10590a9d4f ("EDAC/igen6: Add EDAC driver for Intel client SoCs using IBECC")
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-4-qiuxu.zhuo@intel.com
2026-07-30 11:30:58 -07:00
Qiuxu Zhuo
f4008169bd EDAC/igen6: Fix interleave boundary condition
The address translation logic splits the memory space into interleaved
and non-interleaved regions using a boundary at 2 * s_size.

The current check uses '>' and incorrectly classifies the boundary
address (2 * s_size) as part of the interleaved region. This leads to
incorrect channel/sub-channel selection at the region boundary.

Fix the classification by using '>=' so that the boundary address is
handled in the non-interleaved region, matching the hardware layout.

Fixes: 10590a9d4f ("EDAC/igen6: Add EDAC driver for Intel client SoCs using IBECC")
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-3-qiuxu.zhuo@intel.com
2026-07-30 11:30:08 -07:00
Qiuxu Zhuo
141556543c EDAC/ie31200: Decouple DIMM width decoding from enum order
The current method to get DIMM width relied on DEV_* enum ordering via a
linear offset (+ DEV_X8), tightly coupling hardware encoding to enum layout.

Replace it with explicit decoding to remove this dependency, as the
enum is expected to grow with additional device widths.

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Link: https://patch.msgid.link/20260730024238.4096623-2-qiuxu.zhuo@intel.com
2026-07-30 11:29:24 -07:00
Sang-Heon Jeon
0c4775d3a7 RAS/AMD/ATL: Remove conditional return with no effect
Both branches of the check return the same value, so the check has no effect.
Remove it and return the value directly.

This is the result of running the Coccinelle script from
scripts/coccinelle/misc/cond_return_no_effect.cocci.

Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://patch.msgid.link/20260723184538.3888637-28-ekffu200098@gmail.com
2026-07-23 12:23:05 -07:00
Pan Chuang
36a6518e74 EDAC: Remove redundant dev_err()
Since

  55b48e23f5 ("genirq/devres: Add error handling in devm_request_*_irq()"),

devm_request_irq() and devm_request_threaded_irq() automatically log detailed
error messages on failure. Remove the now-redundant driver-specific dev_err()
calls.

Signed-off-by: Pan Chuang <panchuang@vivo.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> # aspeed
Link: https://patch.msgid.link/20260713131510.332386-1-panchuang@vivo.com
2026-07-22 08:11:29 -07:00
Radhey Shyam Pandey
e09afa69e3 MAINTAINERS: Add Radhey Shyam Pandey as Xilinx EDAC reviewer
I have volunteered to review Xilinx EDAC related changes. Add myself as
a reviewer to stay aligned with ongoing patch activity and actively contribute
to this subsystem.

Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://patch.msgid.link/20260625103749.1416083-1-radhey.shyam.pandey@amd.com
2026-07-11 17:59:02 -07:00
Rounak Das
11f5fd3607 EDAC/altera: Remove remaining CONFIG_64BIT ifdefs in the DB-error path
Replace the remaining two CONFIG_64BIT ifdefs with the is_s10 flag, so driver
behavior is fully determined by the ECC manager's compatible string rather
than the build architecture.

These two ifdefs guard the double-bit-error path, where SError handling and
the arm_smccc_smc() reboot call are arm64-specific. Switching to is_s10 means
s10_edac_dberr_handler() now compiles on 32-bit as well — this is safe because
all the symbols it depends on (arm_smccc_smc, INTEL_SIP_SMC_ECC_DBE, and the
S10 sysmgr defines) are already available on 32-bit socfpga. Since the
function only executes when is_s10 is true, Arria10 behavior is unaffected.

This is handled separately from the IRQ-index selection change, as the
double-bit-error path is a distinct concern.

Signed-off-by: Rounak Das <rounakdas2025@gmail.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Dinh Nguyen <dinguyen@kernel.org>
Assisted-by: Claude:claude-sonnet-5
Link: https://patch.msgid.link/20260708091135.94114-3-rounakdas2025@gmail.com
2026-07-10 18:49:17 -07:00
Rounak Das
d4486fc309 EDAC/altera: Use ECC manager compatible to select A10/S10 IRQ layout
The SDMMC ECC IRQ layout selection uses CONFIG_64BIT to distinguish between
Arria10 and Stratix10 paths.

Detect the SoC once at probe via the device match table (.data) store it in
struct altr_arria10_edac, and use it instead of CONFIG_64BIT.

This keeps the decision correct for every ECC child device (OCRAM, SD/MMC,
etc.) and avoids any runtime compatible lookup.

Signed-off-by: Rounak Das <rounakdas2025@gmail.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Dinh Nguyen <dinguyen@kernel.org>
Link: https://patch.msgid.link/20260708091135.94114-2-rounakdas2025@gmail.com
2026-07-10 15:50:55 -07:00
Yazen Ghannam
4c3da04827 RAS/AMD/ATL, EDAC/amd64: Only load ATL when needed
The AMD Address Translation Library (ATL) will attempt to load on all AMD
Zen/SMCA systems.

However, only systems with DRAM ECC enabled will use the library. Other
systems will fail to load the library and produce an unnecessary message to
the user. More importantly, that thing is dead code loaded and unused.

Remove the ATL module dependency table to prevent autoloading. Request
ATL to load from EDAC once all system checks are complete.

  [ bp: Massage commit message. ]

Fixes: 3f3174996b ("RAS: Introduce AMD Address Translation Library")
Closes: https://lore.kernel.org/20260305154528.1171999-1-mario.limonciello@amd.com
Reported-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Tested-by: Deskhmukh Shrirang <Shrirang.Deskhmukh@amd.com>
Link: https://lore.kernel.org/all/20260307144910.GA113343@yaz-khff2.amd.com
2026-07-07 19:45:05 -07:00
Uwe Kleine-König
97dfcb871b MAINTAINERS: Remove Mark Gross from relevant entries
Sending mail to Mark's Intel address results in the Intel mail server
rejecting the mail. Dave Hansen confirmed he left Intel.  The kernel.org
address seems to work, but there was no reply from Mark on the discussion
about broken email settings and his maintainer entries.

So drop him from all maintainer entries and move him to credits.

Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
Link: https://patch.msgid.link/20260703173803.3589003-2-ukleinek@kernel.org
2026-07-05 07:51:09 -07:00
Abhinav Ananthu
07897bdf7a EDAC/sysfs: Use sysfs_emit_at() in dimmdev_location_show()
Replace the use of scnprintf() with sysfs_emit_at() in dimmdev_location_show()
to format the output into the sysfs buffer and thus improve clarity and ensure
proper bounds checking in line with the preferred sysfs_emit() API usage for
sysfs 'show' functions.

No functional change intended.

  [ bp: Massage commit message. ]

Signed-off-by: Abhinav Ananthu <abhinav.ogl@gmail.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Link: https://patch.msgid.link/20250619202133.11843-1-abhinav.ogl@gmail.com
2026-06-30 12:39:17 -07:00
Yazen Ghannam
90cfd27df4 EDAC/debugfs: Remove the fake_inject debugfs interface
The interface has a potential race condition between a real and fake
error when updating the memory controller's error descriptor.

There doesn't seem to be an active user base for this interface, so
remove it.

Closes: https://sashiko.dev/#/patchset/20260518160716.171578-1-yazen.ghannam%40amd.com
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Suggested-by: Borislav Petkov <bp@alien8.de>
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/linux-edac/20260611012336.GHaioOGB0NBxv5BZXS@fat_crate.local
2026-06-29 18:18:51 -07:00
Borislav Petkov (AMD)
749c510248 EDAC/mpc85xx: Orphan it
Johannes doesn't have the hardware to test patches on it anymore and
TTBOMK, no one else has shown interest so orphan the driver, for now at
least.

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Johannes Thumshirn <jth@kernel.org>
Link: https://patch.msgid.link/20260612153839.GCaiwn_7qOic4KLF8P@fat_crate.local
2026-06-28 16:14:43 -07:00
84 changed files with 1951 additions and 884 deletions

View File

@@ -1512,6 +1512,10 @@ N: Andy Gross
E: agross@kernel.org
D: Qualcomm SoC subsystem and drivers
N: Mark Gross
E: markgross@kernel.org
D: x86/mellanox platform maintenance and various x86 specific drivers
N: Grant Grundler
E: grantgrundler@gmail.com
W: http://obmouse.sourceforge.net/

View File

@@ -1130,9 +1130,9 @@ policy and the underlying scheduler. From the point of view of the cpu controlle
processes can be categorized as follows:
* Processes under the fair-class scheduler
* Processes under a BPF scheduler with the ``cgroup_set_weight`` callback
* Processes under a BPF scheduler with the corresponding ``cgroup_set_*`` callback
* Everything else: ``SCHED_{FIFO,RR,DEADLINE}`` and processes under a BPF scheduler
without the ``cgroup_set_weight`` callback
without the corresponding ``cgroup_set_*`` callback
For details on when a process is under the fair-class scheduler or a BPF scheduler,
check out :ref:`Documentation/scheduler/sched-ext.rst <sched-ext>`.
@@ -1223,7 +1223,9 @@ will be referred to. All time durations are in microseconds.
$PERIOD duration. "max" for $MAX indicates no limit. If only
one number is written, $MAX is updated.
This file affects only processes under the fair-class scheduler.
This file affects only processes under the fair-class scheduler and a BPF
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
the callback actually does.
cpu.max.burst
A read-write single value file which exists on non-root
@@ -1231,7 +1233,9 @@ will be referred to. All time durations are in microseconds.
The burst in the range [0, $MAX].
This file affects only processes under the fair-class scheduler.
This file affects only processes under the fair-class scheduler and a BPF
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
the callback actually does.
cpu.pressure
A read-write nested-keyed file.
@@ -1283,7 +1287,9 @@ will be referred to. All time durations are in microseconds.
own relative priorities, but the cgroup itself will be treated as
very low priority relative to its peers.
This file affects only processes under the fair-class scheduler.
This file affects only processes under the fair-class scheduler and a BPF
scheduler with the ``cgroup_set_idle`` callback depending on what the
callback actually does.
Memory
------
@@ -3064,7 +3070,7 @@ resources (res_a and res_b) are registered then:
change in this file generates a file modified event. All fields in
this file are hierarchical.
max
<res>.max
The number of times the cgroup's resource usage was
about to go over the max boundary.

View File

@@ -230,7 +230,7 @@ optional. The following modified excerpt is from
void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei)
{
exit_type = ei->type;
exit_type = ei->kind;
}
SEC(".struct_ops")
@@ -242,6 +242,21 @@ optional. The following modified excerpt is from
.name = "simple",
};
Scheduler-Dependent Knobs
-------------------------
The fair-class scheduler enforces CPU controller settings such as
``cpu.max``, ``cpu.weight`` and ``cpu.idle``. For sched_ext tasks, the
scheduler core communicates these settings to the BPF scheduler
through ``ops.cgroup_init()`` and reports subsequent changes through
the corresponding ``ops.cgroup_set_*()`` callbacks. Similarly, per-task
nice changes are converted to weights and reported through
``ops.set_weight()``.
Each BPF scheduler is responsible for implementing the scheduling
semantics of these settings and may choose to ignore them. Consult the
loaded scheduler's documentation before relying on these controls.
Dispatch Queues
---------------

View File

@@ -9387,9 +9387,8 @@ S: Maintained
F: drivers/edac/octeon_edac*
EDAC-CAVIUM THUNDERX
M: Robert Richter <rric@kernel.org>
L: linux-edac@vger.kernel.org
S: Odd Fixes
S: Orphan
F: drivers/edac/thunderx_edac*
EDAC-CORE
@@ -9416,9 +9415,8 @@ S: Supported
F: drivers/edac/dmc520_edac.c
EDAC-E752X
M: Mark Gross <markgross@kernel.org>
L: linux-edac@vger.kernel.org
S: Maintained
S: Orphan
F: drivers/edac/e752x_edac.c
EDAC-E7XXX
@@ -9493,9 +9491,8 @@ S: Maintained
F: drivers/edac/igen6_edac.c
EDAC-MPC85XX
M: Johannes Thumshirn <morbidrsa@gmail.com>
L: linux-edac@vger.kernel.org
S: Maintained
S: Orphan
F: drivers/edac/mpc85xx_edac.[ch]
EDAC-NPCM
@@ -13283,7 +13280,6 @@ F: drivers/crypto/intel/keembay/ocs-aes.h
INTEL KEEM BAY OCS ECC CRYPTO DRIVER
M: Prabhjot Khurana <prabhjot.khurana@intel.com>
M: Mark Gross <mgross@linux.intel.com>
S: Maintained
F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-ecc.yaml
F: drivers/crypto/intel/keembay/Kconfig
@@ -14240,8 +14236,6 @@ F: tools/testing/selftests/
KERNEL SMB3 SERVER (KSMBD)
M: Namjae Jeon <linkinjeon@kernel.org>
M: Namjae Jeon <linkinjeon@samba.org>
M: Steve French <smfrench@gmail.com>
M: Steve French <sfrench@samba.org>
R: Sergey Senozhatsky <senozhatsky@chromium.org>
R: Tom Talpey <tom@talpey.com>
R: ChenXiaoSong <chenxiaosong@chenxiaosong.com>
@@ -25236,10 +25230,9 @@ F: Documentation/admin-guide/LSM/Smack.rst
F: security/smack/
SMBDIRECT (RDMA Stream Transport with Read/Write-Offload, MS-SMBD)
M: Steve French <smfrench@gmail.com>
M: Steve French <sfrench@samba.org>
M: Namjae Jeon <linkinjeon@kernel.org>
M: Namjae Jeon <linkinjeon@samba.org>
M: Paulo Alcantara <pc@manguebit.org>
R: Stefan Metzmacher <metze@samba.org>
R: Tom Talpey <tom@talpey.com>
L: linux-cifs@vger.kernel.org
@@ -26954,8 +26947,7 @@ S: Maintained
F: drivers/net/ethernet/tehuti/tn40*
TELECOM CLOCK DRIVER FOR MCPL0010
M: Mark Gross <markgross@kernel.org>
S: Supported
S: Orphan
F: drivers/char/tlclk.c
TEMPO SEMICONDUCTOR DRIVERS
@@ -29937,12 +29929,14 @@ F: include/uapi/linux/xilinx-v4l2-controls.h
XILINX VERSAL EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
F: drivers/edac/versal_edac.c
XILINX VERSALNET EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.yaml
F: drivers/edac/versalnet_edac.c
@@ -29988,6 +29982,7 @@ F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
XILINX ZYNQMP OCM EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml
F: drivers/edac/zynqmp_edac.c

View File

@@ -302,12 +302,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
IRQF_SHARED,
pdev->name,
pdev);
if (ret != 0) {
dev_err(&pdev->dev,
"failed to request UE IRQ %d (%d)\n",
al_mc->irq_ue, ret);
if (ret != 0)
return ret;
}
}
if (al_mc->irq_ce > 0) {
@@ -317,12 +313,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
IRQF_SHARED,
pdev->name,
pdev);
if (ret != 0) {
dev_err(&pdev->dev,
"failed to request CE IRQ %d (%d)\n",
al_mc->irq_ce, ret);
if (ret != 0)
return ret;
}
}
return 0;

View File

@@ -1507,6 +1507,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
int edac_idx, rc;
struct device_node *np;
const struct edac_device_prv_data *prv = &a10_sdmmceccb_data;
bool is_s10 = device->edac->is_s10;
rc = altr_check_ecc_deps(device);
if (rc)
@@ -1548,15 +1549,14 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
/*
* Update the PortB IRQs - A10 has 4, S10 has 2, Index accordingly
*
* FIXME: Instead of ifdefs with different architectures the driver
* should properly use compatibles.
*/
#ifdef CONFIG_64BIT
altdev->sb_irq = irq_of_parse_and_map(np, 1);
#else
altdev->sb_irq = irq_of_parse_and_map(np, 2);
#endif
/* Using compatibles to determine the IRQ Index */
if (is_s10)
altdev->sb_irq = irq_of_parse_and_map(np, 1);
else
altdev->sb_irq = irq_of_parse_and_map(np, 2);
if (!altdev->sb_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n");
rc = -ENODEV;
@@ -1570,29 +1570,28 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
goto err_release_group_1;
}
#ifdef CONFIG_64BIT
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Error PortB DBIRQ alloc\n");
goto err_release_group_1;
if (is_s10) {
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
goto err_release_group_1;
}
} else {
altdev->db_irq = irq_of_parse_and_map(np, 3);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
rc = -ENODEV;
goto err_release_group_1;
}
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
goto err_release_group_1;
}
}
#else
altdev->db_irq = irq_of_parse_and_map(np, 3);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
rc = -ENODEV;
goto err_release_group_1;
}
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
goto err_release_group_1;
}
#endif
rc = edac_device_add_device(dci);
if (rc) {
@@ -1974,29 +1973,29 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
goto err_release_group1;
}
#ifdef CONFIG_64BIT
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Unable to parse DB IRQ index\n");
goto err_release_group1;
if (edac->is_s10) {
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Unable to parse DB IRQ index\n");
goto err_release_group1;
}
} else {
altdev->db_irq = irq_of_parse_and_map(np, 1);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
rc = -ENODEV;
goto err_release_group1;
}
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
goto err_release_group1;
}
}
#else
altdev->db_irq = irq_of_parse_and_map(np, 1);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
rc = -ENODEV;
goto err_release_group1;
}
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
goto err_release_group1;
}
#endif
rc = edac_device_add_device(dci);
if (rc) {
@@ -2059,7 +2058,6 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = {
/************** Stratix 10 EDAC Double Bit Error Handler ************/
#define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m)
#ifdef CONFIG_64BIT
/* panic routine issues reboot on non-zero panic_timeout */
extern int panic_timeout;
@@ -2106,7 +2104,6 @@ static int s10_edac_dberr_handler(struct notifier_block *this,
return NOTIFY_DONE;
}
#endif
/****************** Arria 10 EDAC Probe Function *********************/
static int altr_edac_a10_probe(struct platform_device *pdev)
@@ -2122,6 +2119,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, edac);
INIT_LIST_HEAD(&edac->a10_ecc_devices);
edac->is_s10 = !!device_get_match_data(&pdev->dev);
edac->ecc_mgr_map =
altr_sysmgr_regmap_lookup_by_phandle(pdev->dev.of_node,
"altr,sysmgr-syscon");
@@ -2153,8 +2152,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
irq_set_chained_handler_and_data(edac->sb_irq,
altr_edac_a10_irq_handler,
edac);
#ifdef CONFIG_64BIT
if (edac->is_s10)
{
int dberror, err_addr;
@@ -2177,15 +2175,14 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
regmap_write(edac->ecc_mgr_map,
S10_SYSMGR_UE_ADDR_OFST, 0);
}
}
#else
edac->db_irq = platform_get_irq(pdev, 1);
if (edac->db_irq < 0)
return edac->db_irq;
} else {
edac->db_irq = platform_get_irq(pdev, 1);
if (edac->db_irq < 0)
return edac->db_irq;
irq_set_chained_handler_and_data(edac->db_irq,
altr_edac_a10_irq_handler, edac);
#endif
irq_set_chained_handler_and_data(edac->db_irq,
altr_edac_a10_irq_handler, edac);
}
for_each_child_of_node(pdev->dev.of_node, child) {
if (!of_device_is_available(child))
@@ -2207,7 +2204,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
static const struct of_device_id altr_edac_a10_of_match[] = {
{ .compatible = "altr,socfpga-a10-ecc-manager" },
{ .compatible = "altr,socfpga-s10-ecc-manager" },
{ .compatible = "altr,socfpga-s10-ecc-manager", .data = (void *)1 },
{},
};
MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);

View File

@@ -394,6 +394,7 @@ struct altr_arria10_edac {
struct irq_chip irq_chip;
struct list_head a10_ecc_devices;
struct notifier_block panic_notifier;
bool is_s10;
};
#endif /* #ifndef _ALTERA_EDAC_H */

View File

@@ -4173,6 +4173,8 @@ static int __init amd64_edac_init(void)
goto err_pci;
}
request_module_nowait("amd_atl");
/* register stuff with EDAC MCE */
if (boot_cpu_data.x86 >= 0x17) {
amd_register_ecc_decoder(decode_umc_error);

View File

@@ -214,10 +214,8 @@ static int config_irq(void *ctx, struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH,
DRV_NAME, ctx);
if (rc) {
dev_err(&pdev->dev, "unable to request irq %d\n", irq);
if (rc)
return rc;
}
/* enable interrupts */
regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,

View File

@@ -1,50 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/string_choices.h>
#include "edac_module.h"
static struct dentry *edac_debugfs;
static ssize_t edac_fake_inject_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct device *dev = file->private_data;
struct mem_ctl_info *mci = to_mci(dev);
static enum hw_event_mc_err_type type;
u16 errcount = mci->fake_inject_count;
if (!errcount)
errcount = 1;
type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
: HW_EVENT_ERR_CORRECTED;
printk(KERN_DEBUG
"Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
errcount,
(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
str_plural(errcount),
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2]
);
edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2],
"FAKE ERROR", "for EDAC testing only");
return count;
}
static const struct file_operations debug_fake_inject_fops = {
.open = simple_open,
.write = edac_fake_inject_write,
.llseek = generic_file_llseek,
};
void __init edac_debugfs_init(void)
{
edac_debugfs = debugfs_create_dir("edac", NULL);
@@ -57,29 +16,7 @@ void edac_debugfs_exit(void)
void edac_create_debugfs_nodes(struct mem_ctl_info *mci)
{
struct dentry *parent;
char name[80];
int i;
parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
for (i = 0; i < mci->n_layers; i++) {
sprintf(name, "fake_inject_%s",
edac_layer_name[mci->layers[i].type]);
debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_layer[i]);
}
debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_ue);
debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_count);
debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev,
&debug_fake_inject_fops);
mci->debugfs = parent;
mci->debugfs = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
}
/* Create a toplevel dir under EDAC's debugfs hierarchy */

View File

@@ -342,14 +342,10 @@ static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
}
/*
* edac_device_reset_delay_period
*
* need to stop any outstanding workq queued up at this time
* because we will be resetting the sleep time.
* Then restart the workq on the new delay
* Stop any outstanding workq queued up at this time because sleep time will
* be reset. Then restart the workq on the new delay.
*/
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
unsigned long msec)
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec)
{
edac_dev->poll_msec = msec;
edac_dev->delay = msecs_to_jiffies(msec);

View File

@@ -84,18 +84,23 @@ static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info
return sprintf(data, "%u\n", ctl_info->poll_msec);
}
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info
*ctl_info, const char *data,
size_t count)
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info *ctl_info,
const char *data, size_t count)
{
unsigned long value;
unsigned int value;
int ret;
/* get the value and enforce that it is non-zero, must be at least
* one millisecond for the delay period, between scans
* Then cancel last outstanding delay for the work request
* and set a new one.
/*
* Get the value, make sure it is non-zero, must be at least one millisecond
* for the delay period between scans.
*/
value = simple_strtoul(data, NULL, 0);
ret = kstrtouint(data, 0, &value);
if (ret < 0)
return ret;
if (value < 1)
return -EINVAL;
edac_device_reset_delay_period(ctl_info, value);
return count;

View File

@@ -129,7 +129,7 @@ static ssize_t dimmdev_location_show(struct device *dev,
ssize_t count;
count = edac_dimm_info_location(dimm, data, PAGE_SIZE);
count += scnprintf(data + count, PAGE_SIZE - count, "\n");
count += sysfs_emit_at(data, count, "\n");
return count;
}

View File

@@ -52,8 +52,7 @@ bool edac_queue_work(struct delayed_work *work, unsigned long delay);
bool edac_stop_work(struct delayed_work *work);
bool edac_mod_work(struct delayed_work *work, unsigned long delay);
extern void edac_device_reset_delay_period(struct edac_device_ctl_info
*edac_dev, unsigned long msec);
extern void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec);
extern void edac_mc_reset_delay_period(unsigned long value);
/*

View File

@@ -235,10 +235,8 @@ static int highbank_mc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
0, dev_name(&pdev->dev), mci);
if (res < 0) {
dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
if (res < 0)
goto err2;
}
devres_close_group(&pdev->dev, NULL);
return 0;

View File

@@ -416,7 +416,23 @@ static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int dimm,
{
dd->size = field_get(cfg->reg_mad_dimm_size_mask[dimm], addr_decode) * cfg->reg_mad_dimm_size_granularity;
dd->ranks = field_get(cfg->reg_mad_dimm_rank_mask[dimm], addr_decode) + 1;
dd->dtype = field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode) + DEV_X8;
switch (field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode)) {
case 0:
dd->dtype = DEV_X8;
break;
case 1:
dd->dtype = DEV_X16;
break;
case 2:
dd->dtype = DEV_X32;
break;
case 3:
dd->dtype = DEV_X64;
break;
default:
dd->dtype = DEV_UNKNOWN;
}
}
static void ie31200_get_dimm_config(struct mem_ctl_info *mci, void __iomem *window,

View File

@@ -42,7 +42,8 @@
#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo))
#define NUM_IMC 2 /* Max memory controllers */
/* Probing upper bound, not a hardware capability limit. */
#define MAX_IMC_TO_PROBE 8
#define NUM_CHANNELS 2 /* Max channels */
#define NUM_DIMMS 2 /* Max DIMMs per channel */
@@ -122,6 +123,43 @@
#define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6)
#define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26)
/*
* A slice represents a portion of memory space participating in an
* interleave relationship within the memory hierarchy.
*
* It can represent in different levels such as:
*
* - a pair of memory controllers
* - a memory controller
* - a memory channel
* - a memory sub-channel / DIMM
*
* +--------+
* | |
* | Zone 1 |
* | |
* +--------+ +--------+
* | | | |
* | | | |
* | Zone 0 | | Zone 0 |
* | | | |
* | | | |
* +--------+ +--------+
*
* Slice L Slice S
*
* Memory space is divided into:
*
* - Zone 0 : Interleaved region
* - Zone 1 : Non-interleaved region (upper part of the large slice).
*/
struct slice {
/* Slice address. */
u64 addr;
/* Slice that @addr belongs to. */
int id;
};
struct igen6_imc {
int mc;
struct mem_ctl_info *mci;
@@ -175,8 +213,6 @@ static struct res_config {
/* Set imc->dimm_{l_size,s_size,l_map}[chan]. */
void (*set_dimm_params)(struct igen6_imc *imc, int chan);
bool (*ibecc_available)(struct pci_dev *pdev);
/* Extract error address logged in IBECC */
u64 (*err_addr)(u64 ecclog);
/* Convert error address logged in IBECC to system physical address */
u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc);
/* Convert error address logged in IBECC to integrated memory controller address */
@@ -184,11 +220,11 @@ static struct res_config {
} *res_cfg;
static struct igen6_pvt {
struct igen6_imc imc[NUM_IMC];
void __iomem *memss_pma_cr;
u64 ms_hash;
u64 ms_s_size;
int ms_l_map;
struct igen6_imc imc[];
} *igen6_pvt;
/* The top of low usable DRAM */
@@ -225,7 +261,8 @@ static char ecclog_buf[ECCLOG_POOL_SIZE];
static struct irq_work ecclog_irq_work;
static struct work_struct ecclog_work;
/* Compute die IDs for Elkhart Lake with IBECC */
/* SoC compute die IDs with IBECC capability. */
/* Elkhart Lake */
#define DID_EHL_SKU5 0x4514
#define DID_EHL_SKU6 0x4528
#define DID_EHL_SKU7 0x452a
@@ -238,22 +275,22 @@ static struct work_struct ecclog_work;
#define DID_EHL_SKU14 0x4534
#define DID_EHL_SKU15 0x4536
/* Compute die IDs for ICL-NNPI with IBECC */
/* ICL-NNPI */
#define DID_ICL_SKU8 0x4581
#define DID_ICL_SKU10 0x4585
#define DID_ICL_SKU11 0x4589
#define DID_ICL_SKU12 0x458d
/* Compute die IDs for Tiger Lake with IBECC */
/* Tiger Lake */
#define DID_TGL_SKU 0x9a14
/* Compute die IDs for Alder Lake with IBECC */
/* Alder Lake */
#define DID_ADL_SKU1 0x4601
#define DID_ADL_SKU2 0x4602
#define DID_ADL_SKU3 0x4621
#define DID_ADL_SKU4 0x4641
/* Compute die IDs for Alder Lake-N with IBECC */
/* Alder Lake-N */
#define DID_ADL_N_SKU1 0x4614
#define DID_ADL_N_SKU2 0x4617
#define DID_ADL_N_SKU3 0x461b
@@ -267,38 +304,38 @@ static struct work_struct ecclog_work;
#define DID_ADL_N_SKU11 0x467c
#define DID_ADL_N_SKU12 0x4632
/* Compute die IDs for Arizona Beach with IBECC */
/* Arizona Beach */
#define DID_AZB_SKU1 0x4676
/* Compute did IDs for Amston Lake with IBECC */
/* Amston Lake */
#define DID_ASL_SKU1 0x464a
#define DID_ASL_SKU2 0x4646
#define DID_ASL_SKU3 0x4652
/* Compute die IDs for Raptor Lake-P with IBECC */
/* Raptor Lake-P */
#define DID_RPL_P_SKU1 0xa706
#define DID_RPL_P_SKU2 0xa707
#define DID_RPL_P_SKU3 0xa708
#define DID_RPL_P_SKU4 0xa716
#define DID_RPL_P_SKU5 0xa718
/* Compute die IDs for Meteor Lake-PS with IBECC */
/* Meteor Lake-PS */
#define DID_MTL_PS_SKU1 0x7d21
#define DID_MTL_PS_SKU2 0x7d22
#define DID_MTL_PS_SKU3 0x7d23
#define DID_MTL_PS_SKU4 0x7d24
/* Compute die IDs for Meteor Lake-P with IBECC */
/* Meteor Lake-P */
#define DID_MTL_P_SKU1 0x7d01
#define DID_MTL_P_SKU2 0x7d02
#define DID_MTL_P_SKU3 0x7d14
/* Compute die IDs for Arrow Lake-UH with IBECC */
/* Arrow Lake-UH */
#define DID_ARL_UH_SKU1 0x7d06
#define DID_ARL_UH_SKU2 0x7d20
#define DID_ARL_UH_SKU3 0x7d30
/* Compute die IDs for Panther Lake-H with IBECC */
/* Panther Lake-H */
#define DID_PTL_H_SKU1 0xb000
#define DID_PTL_H_SKU2 0xb001
#define DID_PTL_H_SKU3 0xb002
@@ -314,15 +351,114 @@ static struct work_struct ecclog_work;
#define DID_PTL_H_SKU13 0xb02a
#define DID_PTL_H_SKU14 0xb00a
/* Compute die IDs for Wildcat Lake with IBECC */
/* Starfire */
#define DID_STF_SKU1 0xb02b
/* Wildcat Lake */
#define DID_WCL_SKU1 0xfd00
/* Compute die IDs for Nova Lake-H/HX with IBECC */
/* Nova Lake-H/HX */
#define DID_NVL_H_SKU1 0xd701
#define DID_NVL_H_SKU2 0xd702
#define DID_NVL_H_SKU3 0xd704
#define DID_NVL_H_SKU4 0xd705
/* Remove the interleave bit and shift upper part down to fill gap. */
static u64 squeeze_addr(u64 addr, int intlv_bit)
{
u64 slice_addr;
slice_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
slice_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
return slice_addr;
}
/* Shift the upper bits up and insert a zero at the @intlv_bit bit position. */
static u64 inflate_addr(u64 addr, int intlv_bit)
{
u64 inflated_addr;
/* Insert a zero at @intlv_bit position. */
inflated_addr = GET_BITFIELD(addr, intlv_bit, 63) << (intlv_bit + 1);
inflated_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
return inflated_addr;
}
static u64 compute_hash(u64 addr, u64 hash_mask, u64 hash_base, int intlv_bit)
{
u64 hash_addr;
int i;
/*
* In hash mode, @intlv_bit is the lowest selected bit of @addr
* to be XORed. While @mask may or may not include this @intlv_bit,
* we enforce that @mask includes @intlv_bit to ensure @intlv_bit is
* XORed exactly once.
*/
hash_mask |= BIT_ULL(intlv_bit);
hash_addr = addr & hash_mask;
for (i = 6; i < 20; i++)
hash_base ^= (hash_addr >> i) & 1;
return hash_base;
}
/*
* Converts a higher-level address (system / IMC / channel) into a lower-level
* slice address and identifier.
*/
static void translate_to_lower_level(u64 addr, u64 hash_mask, u64 hash_base,
int intlv_bit, u64 s_size, int l_map,
struct slice *slice)
{
/* In non-interleave zone. */
if (addr >= 2 * s_size) {
slice->addr = addr - s_size;
slice->id = l_map;
return;
}
/* In interleave zone. */
slice->addr = squeeze_addr(addr, intlv_bit);
/* Non-hash mode. */
if (!hash_mask) {
slice->id = GET_BITFIELD(addr, intlv_bit, intlv_bit);
return;
}
/* Hash mode. */
slice->id = compute_hash(addr, hash_mask, hash_base, intlv_bit);
}
/* Reconstruct address for upper memory hierarchy level. */
static u64 translate_to_upper_level(u64 addr, u64 hash_mask, u64 hash_base,
int intlv_bit, u64 s_size)
{
u64 inflated_addr, hash_val;
/* In non-interleave zone. */
if (addr >= s_size)
return addr + s_size;
/*
* In interleave zone.
*
* Insert a zero at @intlv_bit position.
*/
inflated_addr = inflate_addr(addr, intlv_bit);
/*
* Reconstruct the removed interleave bit and use it to replace
* the zero at @intlv_bit position.
*/
hash_val = compute_hash(inflated_addr, hash_mask, hash_base, intlv_bit);
return inflated_addr | (hash_val << intlv_bit);
}
static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
{
union {
@@ -354,6 +490,46 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
return 0;
}
/* Check whether the memory controller is absent. */
static bool imc_absent(void __iomem *window)
{
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
}
/* Return MMIO base address of the memory controller if it's present, otherwise return NULL. */
static void __iomem *map_imc_window(u64 mchbar, int pmc)
{
void __iomem *window;
window = ioremap(mchbar + pmc * MCHBAR_SIZE, MCHBAR_SIZE);
if (!window)
return NULL;
if (imc_absent(window)) {
iounmap(window);
return NULL;
}
return window;
}
/* Return the number of present memory controllers. */
static int get_imc_num(u64 mchbar)
{
void __iomem *window;
int lmc, pmc;
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
window = map_imc_window(mchbar, pmc);
if (window) {
iounmap(window);
lmc++;
}
}
return lmc;
}
static bool ehl_ibecc_available(struct pci_dev *pdev)
{
u32 v;
@@ -450,21 +626,9 @@ static u64 mem_addr_to_sys_addr(u64 maddr)
return maddr;
}
static u64 mem_slice_hash(u64 addr, u64 mask, u64 hash_init, int intlv_bit)
{
u64 hash_addr = addr & mask, hash = hash_init;
u64 intlv = (addr >> intlv_bit) & 1;
int i;
for (i = 6; i < 20; i++)
hash ^= (hash_addr >> i) & 1;
return hash ^ intlv;
}
static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
{
u64 maddr, hash, mask, ms_s_size;
u64 mask, ms_s_size;
int intlv_bit;
u32 ms_hash;
@@ -477,12 +641,7 @@ static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
mask = MEM_SLICE_HASH_MASK(ms_hash);
intlv_bit = MEM_SLICE_HASH_LSB_MASK_BIT(ms_hash) + 6;
maddr = GET_BITFIELD(eaddr, intlv_bit, 63) << (intlv_bit + 1) |
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
hash = mem_slice_hash(maddr, mask, mc, intlv_bit);
return maddr | (hash << intlv_bit);
return translate_to_upper_level(eaddr, mask, mc, intlv_bit, ms_s_size);
}
static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc)
@@ -504,8 +663,9 @@ static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc)
static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
{
u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size;
u64 ms_s_size = igen6_pvt->ms_s_size;
struct igen6_imc *imc = &igen6_pvt->imc[mc];
struct slice slice;
int intlv_bit;
u32 mc_hash;
@@ -516,15 +676,8 @@ static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6;
imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit |
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
return imc_addr;
}
static u64 rpl_p_err_addr(u64 ecclog)
{
return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog);
translate_to_lower_level(eaddr, 0, 0, intlv_bit, ms_s_size, 0, &slice);
return slice.addr;
}
static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc)
@@ -686,6 +839,7 @@ static struct res_config tgl_cfg = {
.err_addr_to_imc_addr = tgl_err_addr_to_imc_addr,
};
/* Shared by Alder Lake, Alder Lake-N, Arizona Beach, Amston Lake, and Raptor Lake-P */
static struct res_config adl_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -701,37 +855,6 @@ static struct res_config adl_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config adl_n_cfg = {
.machine_check = true,
.num_imc = 1,
.reg_mchbar_mask = GENMASK_ULL(41, 17),
.reg_tom_mask = GENMASK_ULL(41, 20),
.reg_touud_mask = GENMASK_ULL(41, 20),
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
.imc_base = 0xd800,
.ibecc_base = 0xd400,
.ibecc_error_log_offset = 0x68,
.ibecc_available = tgl_ibecc_available,
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config rpl_p_cfg = {
.machine_check = true,
.num_imc = 2,
.reg_mchbar_mask = GENMASK_ULL(41, 17),
.reg_tom_mask = GENMASK_ULL(41, 20),
.reg_touud_mask = GENMASK_ULL(41, 20),
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
.imc_base = 0xd800,
.ibecc_base = 0xd400,
.ibecc_error_log_offset = 0x68,
.ibecc_available = tgl_ibecc_available,
.err_addr = rpl_p_err_addr,
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config mtl_ps_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -749,6 +872,7 @@ static struct res_config mtl_ps_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
/* Shared by Meteor Lake-P, Arrow Lake-UH, and Wildcat Lake */
static struct res_config mtl_p_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -764,6 +888,7 @@ static struct res_config mtl_p_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
/* Shared by Panther Lake-H and Starfire */
static struct res_config ptl_h_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -794,21 +919,6 @@ static struct res_config ptl_h_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config wcl_cfg = {
.machine_check = true,
.num_imc = 1,
.reg_mchbar_mask = GENMASK_ULL(41, 17),
.reg_tom_mask = GENMASK_ULL(41, 20),
.reg_touud_mask = GENMASK_ULL(41, 20),
.reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5),
.imc_base = 0xd800,
.ibecc_base = 0xd400,
.ibecc_error_log_offset = 0x170,
.ibecc_available = mtl_p_ibecc_available,
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config nvl_h_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -861,27 +971,27 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_ADL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
@@ -892,6 +1002,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
@@ -906,7 +1017,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU14), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&wcl_cfg },
{ PCI_VDEVICE(INTEL, DID_STF_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU1), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU2), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU3), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
@@ -1007,55 +1118,13 @@ static void set_dimm_params(struct igen6_imc *imc, int chan)
imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val);
}
static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit)
{
u64 hash_addr = addr & mask, hash = 0;
u64 intlv = (addr >> intlv_bit) & 1;
int i;
for (i = 6; i < 20; i++)
hash ^= (hash_addr >> i) & 1;
return (int)hash ^ intlv;
}
static u64 decode_channel_addr(u64 addr, int intlv_bit)
{
u64 channel_addr;
/* Remove the interleave bit and shift upper part down to fill gap */
channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
return channel_addr;
}
static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map,
int *idx, u64 *sub_addr)
{
int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
if (addr > 2 * s_size) {
*sub_addr = addr - s_size;
*idx = l_map;
return;
}
if (CHANNEL_HASH_MODE(hash)) {
*sub_addr = decode_channel_addr(addr, intlv_bit);
*idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit);
} else {
*sub_addr = decode_channel_addr(addr, 6);
*idx = GET_BITFIELD(addr, 6, 6);
}
}
static int igen6_decode(struct decoded_addr *res)
{
struct igen6_imc *imc = &igen6_pvt->imc[res->mc];
u64 addr = res->imc_addr, sub_addr, s_size;
int idx, l_map;
u32 hash;
u64 addr = res->imc_addr, s_size;
int intlv_bit, l_map;
u32 hash, hash_mask;
struct slice slice;
if (addr >= igen6_tom) {
edac_dbg(0, "Address 0x%llx out of range\n", addr);
@@ -1066,17 +1135,25 @@ static int igen6_decode(struct decoded_addr *res)
hash = readl(imc->window + CHANNEL_HASH_OFFSET);
s_size = imc->ch_s_size;
l_map = imc->ch_l_map;
decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr);
res->channel_idx = idx;
res->channel_addr = sub_addr;
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
translate_to_lower_level(addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
res->channel_idx = slice.id;
res->channel_addr = slice.addr;
/* Decode sub-channel/DIMM */
hash = readl(imc->window + CHANNEL_EHASH_OFFSET);
s_size = imc->dimm_s_size[idx];
l_map = imc->dimm_l_map[idx];
decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr);
res->sub_channel_idx = idx;
res->sub_channel_addr = sub_addr;
s_size = imc->dimm_s_size[res->channel_idx];
l_map = imc->dimm_l_map[res->channel_idx];
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
translate_to_lower_level(res->channel_addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
res->sub_channel_idx = slice.id;
res->sub_channel_addr = slice.addr;
return 0;
}
@@ -1230,11 +1307,7 @@ static void ecclog_work_cb(struct work_struct *work)
llist_for_each_entry_safe(node, tmp, head, llnode) {
memset(&res, 0, sizeof(res));
if (res_cfg->err_addr)
eaddr = res_cfg->err_addr(node->ecclog);
else
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
res.mc = node->mc;
res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc);
res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc);
@@ -1476,18 +1549,27 @@ static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev)
{
void __iomem *memss_pma_cr;
struct igen6_pvt *pvt;
int imc_num, rc;
u64 mchbar;
int rc;
pvt = kzalloc_obj(*igen6_pvt);
if (!pvt)
return NULL;
rc = get_mchbar(pdev, &mchbar);
if (rc) {
kfree(pvt);
if (rc)
return NULL;
imc_num = get_imc_num(mchbar);
if (!imc_num) {
igen6_printk(KERN_ERR, "No mc found.\n");
return NULL;
}
edac_dbg(2, "%d mcs found.\n", imc_num);
/* Use the runtime detected IMC count. */
if (res_cfg->num_imc != imc_num)
res_cfg->num_imc = imc_num;
pvt = kzalloc_flex(*pvt, imc, imc_num);
if (!pvt)
return NULL;
memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2);
if (!memss_pma_cr) {
@@ -1572,12 +1654,6 @@ static void igen6_check(struct mem_ctl_info *mci)
irq_work_queue(&ecclog_irq_work);
}
/* Check whether the memory controller is absent. */
static bool igen6_imc_absent(void __iomem *window)
{
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
}
static void imc_release(struct device *dev)
{
/* Nothing to do, the 'imc' owns the 'dev' and will also release it. */
@@ -1689,26 +1765,15 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
{
void __iomem *window;
int lmc, pmc, rc;
u64 base;
for (lmc = 0, pmc = 0; pmc < NUM_IMC; pmc++) {
base = mchbar + pmc * MCHBAR_SIZE;
window = ioremap(base, MCHBAR_SIZE);
if (!window) {
igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx for mc%d\n", base, pmc);
rc = -ENOMEM;
goto out_unregister_mcis;
}
if (igen6_imc_absent(window)) {
iounmap(window);
edac_dbg(2, "Skip absent mc%d\n", pmc);
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
window = map_imc_window(mchbar, pmc);
if (!window)
continue;
}
rc = igen6_register_mci(lmc, window, pdev);
if (rc)
goto out_iounmap;
goto err_unregister;
/* Done, if all present MCs are detected and registered. */
if (++lmc >= res_cfg->num_imc)
@@ -1728,10 +1793,8 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
return 0;
out_iounmap:
err_unregister:
iounmap(window);
out_unregister_mcis:
igen6_unregister_mcis();
return rc;

View File

@@ -729,10 +729,8 @@ static int thunderx_lmc_probe(struct pci_dev *pdev,
thunderx_lmc_err_isr,
thunderx_lmc_threaded_isr, 0,
"[EDAC] ThunderX LMC", mci);
if (ret) {
dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
if (ret)
goto err_free;
}
lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));

View File

@@ -1924,11 +1924,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, irq,
xgene_edac_isr, IRQF_SHARED,
dev_name(&pdev->dev), edac);
if (rc) {
dev_err(&pdev->dev,
"Could not request IRQ %d\n", irq);
if (rc)
goto out_err;
}
}
}

View File

@@ -190,7 +190,6 @@ static const struct x86_cpu_id amd_atl_cpuids[] = {
X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),
{ }
};
MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
static int __init amd_atl_init(void)
{

View File

@@ -771,9 +771,5 @@ int get_address_map(struct addr_ctx *ctx)
dump_address_map(&ctx->map);
ret = validate_address_map(ctx);
if (ret)
return ret;
return ret;
return validate_address_map(ctx);
}

View File

@@ -1240,7 +1240,7 @@ static int ignore_hardlimit(struct dquot *dquot)
{
struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type];
return capable(CAP_SYS_RESOURCE) &&
return capable_noaudit(CAP_SYS_RESOURCE) &&
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
!(info->dqi_flags & DQF_ROOT_SQUASH));
}

View File

@@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count, data_count, param_offset, offset;
size_t cifs_acl_size, bytes_available;
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
setAclRetry:
@@ -3574,8 +3575,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
}
params = 6 + name_len;
pSMB->MaxParameterCount = cpu_to_le16(2);
/* BB find max SMB size from sess */
pSMB->MaxDataCount = cpu_to_le16(1000);
pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -3587,6 +3587,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
parm_data = ((char *)pSMB) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
/* make sure we can fit the larger cifs_posix_aces in the buffer */
cifs_acl_size = sizeof(struct cifs_posix_acl) +
(acl->a_count * sizeof(struct cifs_posix_ace));
bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset;
if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
rc = -E2BIG;
goto setACLerrorExit;
}
/* convert to on the wire format for POSIX ACL */
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
@@ -6325,8 +6334,10 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
int name_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, param_offset, byte_count, offset, count;
__u16 params, param_offset;
unsigned int byte_count, offset, count;
int remap = cifs_remap(cifs_sb);
unsigned int total_len;
cifs_dbg(FYI, "In SetEA\n");
SetEARetry:
@@ -6378,6 +6389,13 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
byte_count = 3 /* pad */ + params + count;
if (check_add_overflow(in_len, byte_count, &total_len) ||
byte_count > U16_MAX ||
total_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
cifs_dbg(VFS, "EA request too large: %u bytes\n", total_len);
cifs_buf_release(pSMB);
return -E2BIG;
}
pSMB->DataCount = cpu_to_le16(count);
parm_data->list_len = cpu_to_le32(count);
parm_data->list.EA_flags = 0;

View File

@@ -4189,14 +4189,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
return rc;
}
static int
cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
static int set_fs_context_auth(struct smb3_fs_context *ctx,
struct cifs_ses *ses)
{
ctx->sectype = ses->sectype;
/* krb5 is special, since we don't need username or pw */
if (ctx->sectype == Kerberos)
/*
* krb5 is special as we might need to pass username (passwordless) down
* to cifs.upcall(8) for keytab.
*/
if (ctx->sectype == Kerberos) {
if (ses->user_name && ses->user_name[0]) {
ctx->username = kstrndup(ses->user_name,
CIFS_MAX_USERNAME_LEN,
GFP_KERNEL);
if (!ctx->username)
return -ENOMEM;
}
return 0;
}
return cifs_set_cifscreds(ctx, ses);
}
@@ -4236,7 +4247,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
ctx->unicode = master_tcon->ses->unicode;
rc = cifs_set_vol_auth(ctx, master_tcon->ses);
rc = set_fs_context_auth(ctx, master_tcon->ses);
if (rc) {
tcon = ERR_PTR(rc);
goto out;

View File

@@ -999,26 +999,50 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
struct cifs_tcon *tcon;
int rc;
rc = filemap_write_and_wait(inode->i_mapping);
if (is_interrupt_error(rc))
rc = inode_lock_killable(inode);
if (rc)
return -ERESTARTSYS;
filemap_invalidate_lock(inode->i_mapping);
rc = filemap_write_and_wait(inode->i_mapping);
if (is_interrupt_error(rc)) {
rc = -ERESTARTSYS;
goto out;
}
mapping_set_error(inode->i_mapping, rc);
cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
rc = cifs_file_flush(xid, inode, cfile);
if (!rc) {
if (cfile) {
struct netfs_inode *ictx = netfs_inode(inode);
tcon = tlink_tcon(cfile->tlink);
server = tcon->ses->server;
netfs_wb_begin(ictx, false);
rc = server->ops->set_file_size(xid, tcon,
cfile, 0, false);
}
if (!rc) {
netfs_resize_file(&cinode->netfs, 0, true);
cifs_setsize(inode, 0);
if (!rc) {
netfs_resize_file(&cinode->netfs, 0, true);
cifs_setsize(inode, 0);
cifs_invalidate_cache(inode, 0);
}
netfs_wb_end(ictx);
} else {
/*
* No cached handle; evict stale pages so they can't
* be served after the file is later extended; let
* the server's O_TRUNC open response set the i_size
*/
truncate_inode_pages(inode->i_mapping, 0);
cifs_invalidate_cache(inode, 0);
}
}
out:
filemap_invalidate_unlock(inode->i_mapping);
inode_unlock(inode);
if (cfile)
cifsFileInfo_put(cfile);
return rc;

View File

@@ -1839,31 +1839,31 @@ smb2_ioctl_query_info(const unsigned int xid,
*
* @tcon: destination file tcon
* @bytes_left: how many bytes are left to copy
* @chunk_size: maximum size of a single chunk
*
* Return: maximum number of chunks with which Chunks[] can be filled.
*/
static inline u32
calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left, u32 chunk_size)
{
u32 max_chunks = READ_ONCE(tcon->max_chunks);
u32 max_bytes_copy = READ_ONCE(tcon->max_bytes_copy);
u32 max_bytes_chunk = READ_ONCE(tcon->max_bytes_chunk);
u64 need;
u32 allowed;
if (!max_bytes_chunk || !max_bytes_copy || !max_chunks)
if (!chunk_size || !max_bytes_copy || !max_chunks)
return 0;
/* chunks needed for the remaining bytes */
need = DIV_ROUND_UP_ULL(bytes_left, max_bytes_chunk);
need = DIV_ROUND_UP_ULL(bytes_left, chunk_size);
/* chunks allowed per cc request */
allowed = DIV_ROUND_UP(max_bytes_copy, max_bytes_chunk);
allowed = DIV_ROUND_UP(max_bytes_copy, chunk_size);
return (u32)umin(need, umin(max_chunks, allowed));
}
/**
* smb2_copychunk_range - server-side copy of data range
* __smb2_copychunk_range - server-side copy of data range
*
* @xid: transaction id
* @src_file: source file
@@ -1875,15 +1875,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE
* IOCTLs, splitting the request into chunks limited by tcon->max_*.
*
* Return: @len on success; negative errno on failure.
* Return: 0 on success; negative errno on failure.
*/
static ssize_t
smb2_copychunk_range(const unsigned int xid,
struct cifsFileInfo *src_file,
struct cifsFileInfo *dst_file,
u64 src_off,
u64 len,
u64 dst_off)
static int
__smb2_copychunk_range(const unsigned int xid,
struct cifsFileInfo *src_file,
struct cifsFileInfo *dst_file,
u64 src_off,
u64 len,
u64 dst_off)
{
int rc = 0;
unsigned int ret_data_len = 0;
@@ -1891,12 +1891,14 @@ smb2_copychunk_range(const unsigned int xid,
struct copychunk_ioctl_rsp *cc_rsp = NULL;
struct cifs_tcon *tcon;
struct srv_copychunk *chunk;
u32 chunks, chunk_count, chunk_bytes;
u32 chunks, chunk_count, chunk_bytes, chunk_size;
u32 copy_bytes, copy_bytes_left;
u32 chunks_written, bytes_written;
u64 total_bytes_left = len;
u64 src_off_prev, dst_off_prev;
u64 max_chunk = 0;
u32 retries = 0;
bool reverse = false;
tcon = tlink_tcon(dst_file->tlink);
@@ -1904,8 +1906,50 @@ smb2_copychunk_range(const unsigned int xid,
dst_file->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dst_off, len);
/*
* Same-file left shifts are safe in forward order. For a right shift,
* let L be the copy length, delta the distance between the source and
* destination, and C the normal chunk size:
*
* delta >= L: copy forwards using C
* delta < L:
* delta >= C: copy backwards using C
* delta < C: copy backwards with chunks limited to delta
*
* Copying backwards prevents one chunk from overwriting data needed by
* a later chunk. Limiting the chunk size to delta prevents an individual
* chunk from overlapping itself.
* This limit can be removed once all supported servers handle overlapping
* descriptors safely.
*
* A small right shift over a large range may therefore require many
* chunks.
*/
if (src_file == dst_file && dst_off > src_off) {
u64 delta = dst_off - src_off;
if (delta < len) {
reverse = true;
max_chunk = delta;
}
}
/*
* A backward copy walks the offsets down from the end of the range.
* Do this once, outside the retry loop, so a retry does not move the
* offsets again.
*/
if (reverse) {
src_off += len;
dst_off += len;
}
retry:
chunk_count = calc_chunk_count(tcon, total_bytes_left);
chunk_size = READ_ONCE(tcon->max_bytes_chunk);
if (max_chunk && max_chunk < chunk_size)
chunk_size = (u32)max_chunk;
chunk_count = calc_chunk_count(tcon, total_bytes_left, chunk_size);
if (!chunk_count) {
rc = -EOPNOTSUPP;
goto out;
@@ -1946,16 +1990,21 @@ smb2_copychunk_range(const unsigned int xid,
while (copy_bytes_left > 0 && chunks < chunk_count) {
chunk = &cc_req->Chunks[chunks++];
chunk_bytes = umin(copy_bytes_left, chunk_size);
if (reverse) {
src_off -= chunk_bytes;
dst_off -= chunk_bytes;
}
chunk->SourceOffset = cpu_to_le64(src_off);
chunk->TargetOffset = cpu_to_le64(dst_off);
chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk);
chunk->Length = cpu_to_le32(chunk_bytes);
/* Buffer is zeroed, no need to set chunk->Reserved = 0 */
src_off += chunk_bytes;
dst_off += chunk_bytes;
if (!reverse) {
src_off += chunk_bytes;
dst_off += chunk_bytes;
}
copy_bytes_left -= chunk_bytes;
copy_bytes += chunk_bytes;
@@ -2003,6 +2052,18 @@ smb2_copychunk_range(const unsigned int xid,
goto out;
}
/*
* A successful COPYCHUNK should copy every descriptor (MS-SMB2
* 3.3.5.15.6). Reject a short backward copy because the rewind
* below only supports forward copying.
*/
if (unlikely(reverse && bytes_written < copy_bytes)) {
cifs_tcon_dbg(VFS, "Copychunk short write %u/%u (reverse)\n",
bytes_written, copy_bytes);
rc = -EIO;
goto out;
}
/* Partial write: rewind */
if (bytes_written < copy_bytes) {
u32 delta = copy_bytes - bytes_written;
@@ -2064,10 +2125,27 @@ smb2_copychunk_range(const unsigned int xid,
trace_smb3_copychunk_done(xid, src_file->fid.volatile_fid,
dst_file->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dst_off, len);
return len;
return 0;
}
}
static ssize_t
smb2_copychunk_range(const unsigned int xid,
struct cifsFileInfo *src_file,
struct cifsFileInfo *dst_file,
u64 src_off,
u64 len,
u64 dst_off)
{
int rc;
rc = __smb2_copychunk_range(xid, src_file, dst_file, src_off, len,
dst_off);
if (rc)
return rc;
return len;
}
static int
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *fid)
@@ -2218,7 +2296,7 @@ smb2_duplicate_extents(const unsigned int xid,
trgtfile->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dest_off, len);
inode = d_inode(trgtfile->dentry);
if (inode->i_size < dest_off + len) {
if (i_size_read(inode) < dest_off + len) {
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
@@ -2235,7 +2313,10 @@ smb2_duplicate_extents(const unsigned int xid,
if (ret_data_len > 0)
cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
if (rc == 0) {
if (rc) {
CIFS_I(inode)->time = 0; /* force reval */
cifs_invalidate_cache(inode, 0);
} else {
qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid, &file_inf);
spin_lock(&inode->i_lock);
@@ -3441,6 +3522,13 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
ses->Suid, offset, len);
new_size = offset + len;
if (!keep_size && i_size_read(inode) < new_size) {
rc = inode_newsize_ok(inode, new_size);
if (rc)
goto out;
}
filemap_invalidate_lock(inode->i_mapping);
netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
@@ -3464,6 +3552,9 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
goto zero_range_exit;
fscache_invalidate(cifs_inode_cookie(inode), NULL,
i_size_read(inode), 0);
rc = smb3_zero_data(file, tcon, offset, len, xid);
if (rc < 0)
goto zero_range_exit;
@@ -3471,7 +3562,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
/*
* do we also need to change the size of the file?
*/
new_size = offset + len;
if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_size);
@@ -3488,6 +3578,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
zero_range_exit:
filemap_invalidate_unlock(inode->i_mapping);
out:
free_xid(xid);
if (rc)
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
@@ -3533,6 +3624,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
*/
truncate_pagecache_range(inode, offset, offset + len - 1);
netfs_wait_for_outstanding_io(inode);
fscache_invalidate(cifs_inode_cookie(inode), NULL,
i_size_read(inode), 0);
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
@@ -3938,18 +4031,26 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
}
filemap_invalidate_lock(inode->i_mapping);
rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1);
rc = filemap_write_and_wait_range(inode->i_mapping,
round_down(off, PAGE_SIZE),
old_eof - 1);
if (rc < 0)
goto out_2;
truncate_pagecache_range(inode, off, old_eof);
netfs_wait_for_outstanding_io(inode);
/*
* Invalidate cached folios from the page containing off to EOF before
* moving data on the server, so subsequent reads do not see stale data.
*/
truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
spin_lock(&inode->i_lock);
netfs_write_zero_point(inode, old_eof);
spin_unlock(&inode->i_lock);
netfs_wait_for_outstanding_io(inode);
rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
old_eof - off - len, off);
rc = __smb2_copychunk_range(xid, cfile, cfile, off + len,
old_eof - off - len, off);
if (rc < 0)
goto out_2;
@@ -3982,7 +4083,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data;
struct inode *inode = file_inode(file);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
__u64 count, old_eof, new_eof;
loff_t old_eof, new_eof;
xid = get_xid();
@@ -3992,15 +4093,32 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
goto out;
}
count = old_eof - off;
new_eof = old_eof + len;
if (check_add_overflow(old_eof, len, &new_eof)) {
rc = -EFBIG;
goto out;
}
rc = inode_newsize_ok(inode, new_eof);
if (rc)
goto out;
/* SET_ZERO_DATA creates a hole only in a sparse file. */
rc = smb2_set_sparse(xid, tcon, cfile, inode, true);
if (rc)
goto out;
filemap_invalidate_lock(inode->i_mapping);
rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1);
rc = filemap_write_and_wait_range(inode->i_mapping,
round_down(off, PAGE_SIZE),
old_eof - 1);
if (rc < 0)
goto out_2;
truncate_pagecache_range(inode, off, old_eof);
netfs_wait_for_outstanding_io(inode);
/*
* Invalidate cached folios from the page containing off to EOF before
* moving data on the server, so subsequent reads do not see stale data.
*/
truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_eof);
@@ -4013,7 +4131,12 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
spin_unlock(&inode->i_lock);
fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
/*
* Move [off, old_eof) right by len. The helper copies backwards if the
* source and destination ranges overlap.
*/
rc = __smb2_copychunk_range(xid, cfile, cfile, off, old_eof - off,
off + len);
if (rc < 0)
goto out_2;
spin_lock(&inode->i_lock);

View File

@@ -101,12 +101,11 @@ void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *midEntry)
trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
midEntry->when_sent, midEntry->when_received);
if (cifsFYI & CIFS_TIMER) {
pr_debug("slow rsp: cmd %d mid %llu",
midEntry->command, midEntry->mid);
cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n",
now - midEntry->when_alloc,
now - midEntry->when_sent,
now - midEntry->when_received);
pr_debug("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n",
midEntry->command, midEntry->mid,
now - midEntry->when_alloc,
now - midEntry->when_sent,
now - midEntry->when_received);
}
}
#endif

View File

@@ -13,6 +13,7 @@
#include "mgmt/ksmbd_ida.h"
#include "mgmt/user_session.h"
#include "connection.h"
#include "vfs_cache.h"
#include "compress.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
@@ -384,12 +385,12 @@ static void ksmbd_conn_cancel_async_requests(struct ksmbd_conn *conn)
spin_lock(&conn->request_lock);
list_for_each_entry_safe(work, tmp, &conn->async_requests,
async_request_entry) {
if (work->state != KSMBD_WORK_ACTIVE)
if (cmpxchg(&work->state, KSMBD_WORK_ACTIVE,
KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
continue;
ksmbd_debug(CONN, "Cancel async request id %d\n",
work->async_id);
work->state = KSMBD_WORK_CANCELLED;
if (work->cancel_fn)
work->cancel_fn(work->cancel_argv);
}
@@ -473,6 +474,9 @@ int ksmbd_conn_wait_idle_sess(struct ksmbd_conn *curr_conn,
if (retry_count >= max_timeout)
return -EIO;
/* A blocked byte-range lock cannot drain until teardown wakes it. */
ksmbd_wake_session_blocked_works(sess);
down_read(&conn_list_lock);
hash_for_each(conn_list, bkt, conn, hlist) {
if (ksmbd_session_is_bound_to_conn(sess, conn)) {

View File

@@ -146,9 +146,9 @@ static struct ksmbd_share_config *__share_lookup(const char *name)
static int parse_veto_list(struct ksmbd_share_config *share,
char *veto_list,
int veto_list_sz)
size_t veto_list_sz)
{
int sz = 0;
size_t sz;
if (!veto_list_sz)
return 0;
@@ -156,7 +156,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
while (veto_list_sz > 0) {
struct ksmbd_veto_pattern *p;
sz = strlen(veto_list);
sz = strnlen(veto_list, veto_list_sz);
if (!sz)
break;
@@ -164,7 +164,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
if (!p)
return -ENOMEM;
p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP);
p->pattern = kstrndup(veto_list, sz, KSMBD_DEFAULT_GFP);
if (!p->pattern) {
kfree(p);
return -ENOMEM;
@@ -172,6 +172,9 @@ static int parse_veto_list(struct ksmbd_share_config *share,
list_add(&p->list, &share->veto_list);
if (sz == veto_list_sz)
break;
veto_list += sz + 1;
veto_list_sz -= (sz + 1);
}
@@ -224,17 +227,28 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
}
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
int path_len = PATH_MAX;
size_t path_len;
if (resp->payload_sz)
path_len = resp->payload_sz - resp->veto_list_sz;
share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
KSMBD_DEFAULT_GFP);
if (!share->path) {
ret = -ENOMEM;
if (resp->payload_sz <= resp->veto_list_sz) {
ret = -EINVAL;
} else {
ret = 0;
path_len = resp->payload_sz - resp->veto_list_sz;
if (resp->veto_list_sz)
path_len--;
if (!path_len) {
ret = -EINVAL;
} else {
share->path = kstrndup(
ksmbd_share_config_path(resp),
path_len, KSMBD_DEFAULT_GFP);
if (!share->path)
ret = -ENOMEM;
else
ret = 0;
}
}
if (share->path) {
share->path_sz = strlen(share->path);
while (share->path_sz > 1 &&
share->path[share->path_sz - 1] == '/')

View File

@@ -666,10 +666,21 @@ void destroy_previous_session(struct ksmbd_conn *conn,
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
goto out;
down_write(&prev_sess->chann_lock);
if (prev_sess->tearing_down) {
up_write(&prev_sess->chann_lock);
goto out;
}
prev_sess->tearing_down = true;
up_write(&prev_sess->chann_lock);
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_RECONNECT);
err = ksmbd_conn_wait_idle_sess(conn, prev_sess);
if (err) {
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP);
down_write(&prev_sess->chann_lock);
prev_sess->tearing_down = false;
up_write(&prev_sess->chann_lock);
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_GOOD);
goto out;
}

View File

@@ -42,6 +42,7 @@ struct ksmbd_session {
bool sign;
bool enc;
bool tearing_down;
int state;
__u8 *Preauth_HashValue;

View File

@@ -97,6 +97,11 @@ static int register_session_channel(struct ksmbd_session *sess,
int rc = 0;
down_write(&sess->chann_lock);
if (sess->tearing_down) {
rc = -ESHUTDOWN;
goto out;
}
if (xa_load(&sess->ksmbd_chann_list, (long)conn))
goto out;
@@ -873,7 +878,8 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
req = smb_get_msg(work->request_buf);
if ((req->InfoType == SMB2_O_INFO_FILE &&
(req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
req->FileInfoClass == FILE_ALL_INFORMATION)) ||
req->FileInfoClass == FILE_ALL_INFORMATION ||
req->FileInfoClass == FILE_NORMALIZED_NAME_INFORMATION)) ||
req->InfoType == SMB2_O_INFO_SECURITY)
sz = large_sz;
}
@@ -3086,17 +3092,41 @@ int smb2_session_logoff(struct ksmbd_work *work)
smb2_set_err_rsp(work);
return -ENOENT;
}
down_write(&sess->chann_lock);
if (sess->tearing_down) {
up_write(&sess->chann_lock);
ksmbd_conn_unlock(conn);
rsp->hdr.Status = STATUS_USER_SESSION_DELETED;
smb2_set_err_rsp(work);
return -ENOENT;
}
sess->tearing_down = true;
up_write(&sess->chann_lock);
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_RECONNECT);
ksmbd_conn_unlock(conn);
err = ksmbd_conn_wait_idle_sess(conn, sess);
if (err) {
down_write(&sess->chann_lock);
sess->tearing_down = false;
up_write(&sess->chann_lock);
ksmbd_all_conn_set_status(sess, KSMBD_SESS_GOOD);
rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
smb2_set_err_rsp(work);
return err;
}
ksmbd_close_session_fds(work);
ksmbd_conn_wait_idle(conn);
if (ksmbd_tree_conn_session_logoff(sess)) {
ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
smb2_set_err_rsp(work);
return -ENOENT;
err = -ENOENT;
} else {
err = 0;
}
down_write(&conn->session_lock);
@@ -3106,6 +3136,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_SETUP);
if (err)
return err;
rsp->StructureSize = cpu_to_le16(4);
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
if (err) {
@@ -6757,7 +6790,7 @@ static int get_file_normalized_name_info(struct ksmbd_work *work,
{
struct smb2_file_alt_name_info *file_info;
char *filename, *normalized, *stream_name;
int conv_len, filename_len;
int buf_free_len, conv_len, filename_len;
if (work->conn->dialect < SMB311_PROT_ID) {
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
@@ -6781,6 +6814,14 @@ static int get_file_normalized_name_info(struct ksmbd_work *work,
return -ENOMEM;
filename_len = strlen(normalized);
buf_free_len = smb2_resp_buf_len(work, sizeof(*rsp) +
sizeof(*file_info));
if (buf_free_len < 0 ||
(size_t)buf_free_len < (filename_len + 1) * sizeof(__le16)) {
kfree(normalized);
return -EINVAL;
}
file_info = (struct smb2_file_alt_name_info *)rsp->Buffer;
conv_len = smbConvertToUTF16((__le16 *)file_info->FileName,
normalized, filename_len,
@@ -7444,6 +7485,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
struct object_id_info *info;
info = (struct object_id_info *)(rsp->Buffer);
memset(info, 0, sizeof(*info));
if (path.mnt->mnt_sb->s_uuid_len == 16)
memcpy(info->objid, path.mnt->mnt_sb->s_uuid.b,
@@ -7499,6 +7541,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
info->FreeSpaceStopFiltering = 0;
info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID);
info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID);
info->FileSystemControlFlags = 0;
info->Padding = 0;
rsp->OutputBufferLength = cpu_to_le32(48);
fixed_len = 48;
@@ -7521,6 +7564,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail);
info->TotalFileNodes = cpu_to_le64(stfs.f_files);
info->FreeFileNodes = cpu_to_le64(stfs.f_ffree);
info->FileSysIdentifier =
cpu_to_le64((u64)(u32)stfs.f_fsid.val[1] << 32 |
(u32)stfs.f_fsid.val[0]);
rsp->OutputBufferLength = cpu_to_le32(56);
fixed_len = 56;
}
@@ -8620,13 +8666,18 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
}
aux_payload_buf =
kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP);
kvmalloc(ALIGN(rpc_resp->payload_sz, 8),
KSMBD_DEFAULT_GFP);
if (!aux_payload_buf) {
err = -ENOMEM;
goto out;
}
memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz);
if (rpc_resp->payload_sz & 7)
memset(aux_payload_buf + rpc_resp->payload_sz, 0,
ALIGN(rpc_resp->payload_sz, 8) -
rpc_resp->payload_sz);
nbytes = rpc_resp->payload_sz;
err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
@@ -9680,14 +9731,14 @@ int smb2_cancel(struct ksmbd_work *work)
* still on conn->async_requests with a live cancel_fn
* pointing at the freed file_lock.
*/
if (iter->state != KSMBD_WORK_ACTIVE)
if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE,
KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
break;
ksmbd_debug(SMB,
"smb2 with AsyncId %llu cancelled command = 0x%x\n",
le64_to_cpu(hdr->Id.AsyncId),
le16_to_cpu(chdr->Command));
iter->state = KSMBD_WORK_CANCELLED;
if (iter->cancel_fn == smb2_notify_cancel_fn)
cancelled_notify =
smb2_notify_cancel_claim(iter->cancel_argv);
@@ -9716,11 +9767,16 @@ int smb2_cancel(struct ksmbd_work *work)
iter == work)
continue;
if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE,
KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
break;
ksmbd_debug(SMB,
"smb2 with mid %llu cancelled command = 0x%x\n",
le64_to_cpu(hdr->MessageId),
le16_to_cpu(chdr->Command));
iter->state = KSMBD_WORK_CANCELLED;
if (iter->cancel_fn)
iter->cancel_fn(iter->cancel_argv);
break;
}
spin_unlock(&conn->request_lock);

View File

@@ -383,10 +383,10 @@ void free_acl_state(struct posix_acl_state *state)
kfree(state->groups);
}
static void parse_dacl(struct mnt_idmap *idmap,
struct smb_acl *pdacl, char *end_of_acl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
struct smb_fattr *fattr)
static int parse_dacl(struct mnt_idmap *idmap,
struct smb_acl *pdacl, char *end_of_acl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
struct smb_fattr *fattr)
{
int i, ret;
u16 num_aces = 0;
@@ -400,13 +400,13 @@ static void parse_dacl(struct mnt_idmap *idmap,
bool owner_found = false, group_found = false, others_found = false;
if (!pdacl)
return;
return 0;
/* validate that we do not go past end of acl */
if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
pr_err("ACL too small to parse DACL\n");
return;
return -EINVAL;
}
ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
@@ -418,31 +418,31 @@ static void parse_dacl(struct mnt_idmap *idmap,
num_aces = le16_to_cpu(pdacl->num_aces);
if (num_aces <= 0)
return;
return 0;
dacl_size = le16_to_cpu(pdacl->size);
if (dacl_size < sizeof(struct smb_acl))
return;
return -EINVAL;
if (num_aces > (dacl_size - sizeof(struct smb_acl)) /
(offsetof(struct smb_ace, sid) +
offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
return;
return -EINVAL;
ret = init_acl_state(&acl_state, num_aces);
if (ret)
return;
return ret;
ret = init_acl_state(&default_acl_state, num_aces);
if (ret) {
free_acl_state(&acl_state);
return;
return ret;
}
ppace = kmalloc_objs(struct smb_ace *, num_aces, KSMBD_DEFAULT_GFP);
if (!ppace) {
free_acl_state(&default_acl_state);
free_acl_state(&acl_state);
return;
return -ENOMEM;
}
/*
@@ -451,8 +451,10 @@ static void parse_dacl(struct mnt_idmap *idmap,
* user/group/other have no permissions
*/
for (i = 0; i < num_aces; ++i) {
if (end_of_acl - acl_base < acl_size)
break;
if (end_of_acl - acl_base < acl_size) {
ret = -EINVAL;
goto out;
}
ppace[i] = (struct smb_ace *)(acl_base + acl_size);
acl_base = (char *)ppace[i];
@@ -465,8 +467,10 @@ static void parse_dacl(struct mnt_idmap *idmap,
(end_of_acl - acl_base <
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
(le16_to_cpu(ppace[i]->size) <
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
break;
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) {
ret = -EINVAL;
goto out;
}
acl_size = le16_to_cpu(ppace[i]->size);
ppace[i]->access_req =
@@ -524,8 +528,8 @@ static void parse_dacl(struct mnt_idmap *idmap,
temp_fattr.cf_uid = INVALID_UID;
ret = sid_to_id(idmap, &ppace[i]->sid, SIDOWNER, &temp_fattr);
if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) {
pr_err("%s: Error %d mapping Owner SID to uid\n",
__func__, ret);
pr_err_ratelimited("%s: Error %d mapping Owner SID to uid\n",
__func__, ret);
continue;
}
@@ -541,7 +545,6 @@ static void parse_dacl(struct mnt_idmap *idmap,
((acl_mode & 0700) >> 6) | 0004;
}
}
kfree(ppace);
if (owner_found) {
/* The owner must be set to at least read-only. */
@@ -584,10 +587,12 @@ static void parse_dacl(struct mnt_idmap *idmap,
fattr->cf_acls =
posix_acl_alloc(acl_state.users->n +
acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
if (fattr->cf_acls) {
cf_pace = fattr->cf_acls->a_entries;
posix_state_to_acl(&acl_state, cf_pace);
if (!fattr->cf_acls) {
ret = -ENOMEM;
goto out;
}
cf_pace = fattr->cf_acls->a_entries;
posix_state_to_acl(&acl_state, cf_pace);
}
}
@@ -598,14 +603,20 @@ static void parse_dacl(struct mnt_idmap *idmap,
fattr->cf_dacls =
posix_acl_alloc(default_acl_state.users->n +
default_acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
if (fattr->cf_dacls) {
cf_pdace = fattr->cf_dacls->a_entries;
posix_state_to_acl(&default_acl_state, cf_pdace);
if (!fattr->cf_dacls) {
ret = -ENOMEM;
goto out;
}
cf_pdace = fattr->cf_dacls->a_entries;
posix_state_to_acl(&default_acl_state, cf_pdace);
}
}
ret = 0;
out:
kfree(ppace);
free_acl_state(&acl_state);
free_acl_state(&default_acl_state);
return ret;
}
static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
@@ -966,8 +977,10 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
if (dacloffset < sizeof(struct smb_ntsd))
return -EINVAL;
parse_dacl(idmap, dacl_ptr, end_of_acl,
owner_sid_ptr, group_sid_ptr, fattr);
rc = parse_dacl(idmap, dacl_ptr, end_of_acl,
owner_sid_ptr, group_sid_ptr, fattr);
if (rc)
return rc;
}
return 0;

View File

@@ -532,14 +532,21 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
if (entry->msg_sz < sizeof(struct ksmbd_share_config_response))
return -EINVAL;
if (resp->payload_sz) {
if (resp->payload_sz < resp->veto_list_sz)
return -EINVAL;
if (strnlen(resp->share_name, sizeof(resp->share_name)) ==
sizeof(resp->share_name))
return -EINVAL;
if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
resp->payload_sz, &msg_sz))
return -EINVAL;
}
if (resp->veto_list_sz > resp->payload_sz)
return -EINVAL;
if (resp->flags != KSMBD_SHARE_FLAG_INVALID &&
!(resp->flags & KSMBD_SHARE_FLAG_PIPE) &&
resp->payload_sz <= resp->veto_list_sz)
return -EINVAL;
if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
resp->payload_sz, &msg_sz))
return -EINVAL;
break;
}
case KSMBD_EVENT_LOGIN_REQUEST_EXT:

View File

@@ -39,6 +39,7 @@ struct tcp_transport {
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
static void tcp_stop_kthread(struct task_struct *kthread);
static void ksmbd_tcp_stop_listener(struct interface *iface);
static struct interface *alloc_iface(char *ifname);
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
@@ -321,13 +322,20 @@ static int ksmbd_tcp_run_kthread(struct interface *iface)
int rc;
struct task_struct *kthread;
kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
iface->name);
kthread = kthread_create(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
iface->name);
if (IS_ERR(kthread)) {
rc = PTR_ERR(kthread);
return rc;
}
/*
* The listener can exit after its socket is shutdown, so keep the
* task_struct alive until the caller has stopped it.
*/
get_task_struct(kthread);
iface->ksmbd_kthread = kthread;
wake_up_process(kthread);
return 0;
}
@@ -598,12 +606,7 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
if (iface && iface->state == IFACE_STATE_CONFIGURED) {
ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
iface->name);
kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
tcp_stop_kthread(iface->ksmbd_kthread);
iface->ksmbd_kthread = NULL;
sock_release(iface->ksmbd_socket);
iface->ksmbd_socket = NULL;
ksmbd_tcp_stop_listener(iface);
iface->state = IFACE_STATE_DOWN;
break;
}
@@ -631,11 +634,25 @@ static void tcp_stop_kthread(struct task_struct *kthread)
if (!kthread)
return;
ret = kthread_stop(kthread);
ret = kthread_stop_put(kthread);
if (ret)
pr_err("failed to stop forker thread\n");
}
static void ksmbd_tcp_stop_listener(struct interface *iface)
{
if (iface->ksmbd_socket)
kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
tcp_stop_kthread(iface->ksmbd_kthread);
iface->ksmbd_kthread = NULL;
if (iface->ksmbd_socket) {
sock_release(iface->ksmbd_socket);
iface->ksmbd_socket = NULL;
}
}
void ksmbd_tcp_destroy(void)
{
struct interface *iface, *tmp;
@@ -643,6 +660,7 @@ void ksmbd_tcp_destroy(void)
unregister_netdevice_notifier(&ksmbd_netdev_notifier);
list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
ksmbd_tcp_stop_listener(iface);
list_del(&iface->entry);
kfree(iface->name);
kfree(iface);

View File

@@ -846,12 +846,25 @@ static void set_close_state_blocked_works(struct ksmbd_file *fp)
spin_lock(&fp->f_lock);
list_for_each_entry(cancel_work, &fp->blocked_works,
fp_entry) {
cancel_work->state = KSMBD_WORK_CLOSED;
cancel_work->cancel_fn(cancel_work->cancel_argv);
if (xchg(&cancel_work->state, KSMBD_WORK_CLOSED) ==
KSMBD_WORK_ACTIVE)
cancel_work->cancel_fn(cancel_work->cancel_argv);
}
spin_unlock(&fp->f_lock);
}
void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess)
{
struct ksmbd_file_table *ft = &sess->file_table;
struct ksmbd_file *fp;
unsigned int id;
read_lock(&ft->lock);
idr_for_each_entry(ft->idr, fp, id)
set_close_state_blocked_works(fp);
read_unlock(&ft->lock);
}
int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
{
struct ksmbd_file *fp;

View File

@@ -226,6 +226,7 @@ void ksmbd_stop_durable_scavenger(void);
bool ksmbd_durable_scavenger_active(void);
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work);
void ksmbd_close_session_fds(struct ksmbd_work *work);
void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess);
int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);
int ksmbd_init_global_file_table(void);
void ksmbd_free_global_file_table(void);

View File

@@ -91,6 +91,7 @@ xfs-y += xfs_aops.o \
xfs_healthmon.o \
xfs_icache.o \
xfs_ioctl.o \
xfs_ioend.o \
xfs_iomap.o \
xfs_iops.o \
xfs_inode.o \

View File

@@ -359,7 +359,11 @@ static inline int xfs_initialize_rtgroups(struct xfs_mount *mp,
# define xfs_rtgroup_unlock(rtg, gf) ((void)0)
# define xfs_rtgroup_trans_join(tp, rtg, gf) ((void)0)
# define xfs_update_rtsb(bp, sb_bp) ((void)0)
# define xfs_log_rtsb(tp, sb_bp) (NULL)
static inline struct xfs_buf *xfs_log_rtsb(struct xfs_trans *tp,
const struct xfs_buf *sb_bp)
{
return NULL;
}
# define xfs_rtgroup_get_geometry(rtg, rgeo) (-EOPNOTSUPP)
#endif /* CONFIG_XFS_RT */

View File

@@ -1470,36 +1470,33 @@ xfs_sync_sb_buf(
bool update_rtsb)
{
struct xfs_trans *tp;
struct xfs_buf *bp;
struct xfs_buf *rtsb_bp = NULL;
int error;
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
if (error)
return error;
bp = xfs_trans_getsb(tp);
xfs_log_sb(tp);
xfs_trans_bhold(tp, bp);
if (update_rtsb) {
rtsb_bp = xfs_log_rtsb(tp, bp);
if (rtsb_bp)
xfs_trans_bhold(tp, rtsb_bp);
}
if (update_rtsb)
xfs_log_rtsb(tp, xfs_trans_getsb(tp));
xfs_trans_set_sync(tp);
error = xfs_trans_commit(tp);
if (error)
goto out;
/*
* write out the sb buffer to get the changes to disk
*/
error = xfs_bwrite(bp);
if (!error && rtsb_bp)
error = xfs_bwrite(rtsb_bp);
out:
if (rtsb_bp)
xfs_buf_relse(rtsb_bp);
xfs_buf_relse(bp);
return error;
/* Re-acquire and write the sb and rtsb to disk. */
xfs_buf_lock(mp->m_sb_bp);
error = xfs_bwrite(mp->m_sb_bp);
xfs_buf_unlock(mp->m_sb_bp);
if (error)
return error;
if (update_rtsb && mp->m_rtsb_bp) {
xfs_buf_lock(mp->m_rtsb_bp);
error = xfs_bwrite(mp->m_rtsb_bp);
xfs_buf_unlock(mp->m_rtsb_bp);
}
return error;
}

View File

@@ -20,6 +20,7 @@
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_icache.h"
#include "xfs_ioend.h"
#include "xfs_zone_alloc.h"
#include "xfs_rtgroup.h"
#include <linux/bio-integrity.h>
@@ -36,15 +37,6 @@ XFS_WPC(struct iomap_writepage_ctx *ctx)
return container_of(ctx, struct xfs_writepage_ctx, ctx);
}
/*
* Fast and loose check if this write could update the on-disk inode size.
*/
static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
{
return ioend->io_offset + ioend->io_size >
XFS_I(ioend->io_inode)->i_disk_size;
}
/*
* Update on-disk file size now that data has been written to disk.
*/
@@ -80,175 +72,6 @@ xfs_setfilesize(
return xfs_trans_commit(tp);
}
static void
xfs_ioend_put_open_zones(
struct iomap_ioend *ioend)
{
struct iomap_ioend *tmp;
/*
* Put the open zone for all ioends merged into this one (if any).
*/
list_for_each_entry(tmp, &ioend->io_list, io_list)
xfs_open_zone_put(tmp->io_private);
/*
* The main ioend might not have an open zone if the submission failed
* before xfs_zone_alloc_and_submit got called.
*/
if (ioend->io_private)
xfs_open_zone_put(ioend->io_private);
}
/*
* IO write completion.
*/
STATIC void
xfs_end_ioend_write(
struct iomap_ioend *ioend)
{
struct xfs_inode *ip = XFS_I(ioend->io_inode);
struct xfs_mount *mp = ip->i_mount;
bool is_zoned = xfs_is_zoned_inode(ip);
xfs_off_t offset = ioend->io_offset;
size_t size = ioend->io_size;
unsigned int nofs_flag;
int error;
/*
* We can allocate memory here while doing writeback on behalf of
* memory reclaim. To avoid memory allocation deadlocks set the
* task-wide nofs context for the following operations.
*/
nofs_flag = memalloc_nofs_save();
/*
* Just clean up the in-memory structures if the fs has been shut down.
*/
if (xfs_is_shutdown(mp)) {
error = -EIO;
goto done;
}
/*
* Clean up all COW blocks and underlying data fork delalloc blocks on
* I/O error. The delalloc punch is required because this ioend was
* mapped to blocks in the COW fork and the associated pages are no
* longer dirty. If we don't remove delalloc blocks here, they become
* stale and can corrupt free space accounting on unmount.
*/
error = blk_status_to_errno(ioend->io_bio.bi_status);
if (unlikely(error)) {
/*
* Zoned writes update the in-core open zone accounting before
* I/O submission. A failed write leaves that state
* inconsistent, so shut down the filesystem instead of letting
* later writers wait forever for open zone space to become
* available.
*/
if (is_zoned) {
xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
goto done;
}
if (ioend->io_flags & IOMAP_IOEND_SHARED) {
ASSERT(!is_zoned);
xfs_reflink_cancel_cow_range(ip, offset, size, true);
xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
offset + size, NULL);
}
goto done;
}
/*
* Success: commit the COW or unwritten blocks if needed.
*/
if (is_zoned)
error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
ioend->io_private, NULLFSBLOCK);
else if (ioend->io_flags & IOMAP_IOEND_SHARED)
error = xfs_reflink_end_cow(ip, offset, size);
else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
error = xfs_iomap_write_unwritten(ip, offset, size, false);
if (!error &&
!(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
xfs_ioend_is_append(ioend))
error = xfs_setfilesize(ip, offset, size);
done:
if (is_zoned)
xfs_ioend_put_open_zones(ioend);
iomap_finish_ioends(ioend, error);
memalloc_nofs_restore(nofs_flag);
}
/*
* Finish all pending IO completions that require transactional modifications.
*
* We try to merge physical and logically contiguous ioends before completion to
* minimise the number of transactions we need to perform during IO completion.
* Both unwritten extent conversion and COW remapping need to iterate and modify
* one physical extent at a time, so we gain nothing by merging physically
* discontiguous extents here.
*
* The ioend chain length that we can be processing here is largely unbound in
* length and we may have to perform significant amounts of work on each ioend
* to complete it. Hence we have to be careful about holding the CPU for too
* long in this loop.
*/
void
xfs_end_io(
struct work_struct *work)
{
struct xfs_inode *ip =
container_of(work, struct xfs_inode, i_ioend_work);
struct iomap_ioend *ioend;
struct list_head tmp;
unsigned long flags;
spin_lock_irqsave(&ip->i_ioend_lock, flags);
list_replace_init(&ip->i_ioend_list, &tmp);
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
iomap_sort_ioends(&tmp);
while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
io_list))) {
list_del_init(&ioend->io_list);
iomap_ioend_try_merge(ioend, &tmp);
if (bio_op(&ioend->io_bio) == REQ_OP_READ)
iomap_finish_ioends(ioend,
blk_status_to_errno(ioend->io_bio.bi_status));
else
xfs_end_ioend_write(ioend);
cond_resched();
}
}
void
xfs_end_bio(
struct bio *bio)
{
struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
struct xfs_inode *ip = XFS_I(ioend->io_inode);
struct xfs_mount *mp = ip->i_mount;
unsigned long flags;
/*
* For Appends record the actually written block number and set the
* boundary flag if needed.
*/
if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
ioend->io_sector = bio->bi_iter.bi_sector;
xfs_mark_rtg_boundary(ioend);
}
spin_lock_irqsave(&ip->i_ioend_lock, flags);
if (list_empty(&ip->i_ioend_list))
WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
&ip->i_ioend_work));
list_add_tail(&ioend->io_list, &ip->i_ioend_list);
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
}
/*
* We cannot cancel the ioend directly on error. We may have already set other
* pages under writeback and hence we have to run I/O completion to mark the
@@ -631,13 +454,8 @@ xfs_zoned_map_blocks(
XFS_BMAPI_REMAP);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
wpc->iomap.type = IOMAP_MAPPED;
wpc->iomap.flags = IOMAP_F_DIRTY;
wpc->iomap.bdev = mp->m_rtdev_targp->bt_bdev;
wpc->iomap.offset = offset;
wpc->iomap.length = XFS_FSB_TO_B(mp, count_fsb);
wpc->iomap.flags = IOMAP_F_ANON_WRITE;
xfs_iomap_set_anon_write(ip, &wpc->iomap, offset,
XFS_FSB_TO_B(mp, count_fsb));
trace_xfs_zoned_map_blocks(ip, offset, wpc->iomap.length);
return 0;
}

View File

@@ -10,6 +10,5 @@ extern const struct address_space_operations xfs_address_space_operations;
extern const struct address_space_operations xfs_dax_aops;
int xfs_setfilesize(struct xfs_inode *ip, xfs_off_t offset, size_t size);
void xfs_end_bio(struct bio *bio);
#endif /* __XFS_AOPS_H__ */

View File

@@ -25,7 +25,7 @@
#include "xfs_iomap.h"
#include "xfs_reflink.h"
#include "xfs_file.h"
#include "xfs_aops.h"
#include "xfs_ioend.h"
#include "xfs_zone_alloc.h"
#include "xfs_error.h"
#include "xfs_errortag.h"

View File

@@ -1174,8 +1174,7 @@ xfs_getfsmap(
if (!xfs_getfsmap_check_keys(&head->fmh_keys[0], &head->fmh_keys[1]))
return -EINVAL;
use_rmap = xfs_has_rmapbt(mp) &&
has_capability_noaudit(current, CAP_SYS_ADMIN);
use_rmap = xfs_has_rmapbt(mp) && capable_noaudit(CAP_SYS_ADMIN);
head->fmh_entries = 0;
/* Set up our device handlers. */

View File

@@ -82,24 +82,20 @@ static inline xa_mark_t ici_tag_to_mark(unsigned int tag)
/*
* Allocate and initialise an xfs_inode.
*
* This can happen in context of already dirtied transactions, so the memory
* allocations must not fail.
*/
struct xfs_inode *
xfs_inode_alloc(
struct xfs_mount *mp,
xfs_ino_t ino)
{
gfp_t gfp = GFP_KERNEL | __GFP_NOFAIL;
struct xfs_inode *ip;
/*
* XXX: If this didn't occur in transactions, we could drop GFP_NOFAIL
* and return NULL here on ENOMEM.
*/
ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, GFP_KERNEL | __GFP_NOFAIL);
if (inode_init_always(mp->m_super, VFS_I(ip))) {
kmem_cache_free(xfs_inode_cache, ip);
return NULL;
}
ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, gfp);
inode_init_always_gfp(mp->m_super, VFS_I(ip), gfp);
VFS_I(ip)->i_ino = ino;
/* VFS doesn't initialise i_mode! */

View File

@@ -647,7 +647,7 @@ xfs_ioctl_setattr_get_trans(
goto out_error;
error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
has_capability_noaudit(current, CAP_FOWNER), &tp);
capable_noaudit(CAP_FOWNER), &tp);
if (error)
goto out_error;

184
fs/xfs/xfs_ioend.c Normal file
View File

@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016-2025 Christoph Hellwig.
* All Rights Reserved.
*/
#include "xfs_platform.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_inode.h"
#include "xfs_iomap.h"
#include "xfs_trace.h"
#include "xfs_bmap_util.h"
#include "xfs_reflink.h"
#include "xfs_zone_alloc.h"
#include "xfs_ioend.h"
static void
xfs_ioend_put_open_zones(
struct iomap_ioend *ioend)
{
struct iomap_ioend *tmp;
/*
* Put the open zone for all ioends merged into this one (if any).
*/
list_for_each_entry(tmp, &ioend->io_list, io_list)
xfs_open_zone_put(tmp->io_private);
/*
* The main ioend might not have an open zone if the submission failed
* before xfs_zone_alloc_and_submit got called.
*/
if (ioend->io_private)
xfs_open_zone_put(ioend->io_private);
}
static void
xfs_end_ioend_write(
struct iomap_ioend *ioend)
{
struct xfs_inode *ip = XFS_I(ioend->io_inode);
struct xfs_mount *mp = ip->i_mount;
bool is_zoned = xfs_is_zoned_inode(ip);
xfs_off_t offset = ioend->io_offset;
size_t size = ioend->io_size;
unsigned int nofs_flag;
int error;
/*
* We can allocate memory here while doing writeback on behalf of
* memory reclaim. To avoid memory allocation deadlocks set the
* task-wide nofs context for the following operations.
*/
nofs_flag = memalloc_nofs_save();
/*
* Just clean up the in-memory structures if the fs has been shut down.
*/
if (xfs_is_shutdown(mp)) {
error = -EIO;
goto done;
}
/*
* Clean up all COW blocks and underlying data fork delalloc blocks on
* I/O error. The delalloc punch is required because this ioend was
* mapped to blocks in the COW fork and the associated pages are no
* longer dirty. If we don't remove delalloc blocks here, they become
* stale and can corrupt free space accounting on unmount.
*/
error = blk_status_to_errno(ioend->io_bio.bi_status);
if (unlikely(error)) {
/*
* Zoned writes update the in-core open zone accounting before
* I/O submission. A failed write leaves that state
* inconsistent, so shut down the filesystem instead of letting
* later writers wait forever for open zone space to become
* available.
*/
if (is_zoned) {
xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
goto done;
}
if (ioend->io_flags & IOMAP_IOEND_SHARED) {
ASSERT(!is_zoned);
xfs_reflink_cancel_cow_range(ip, offset, size, true);
xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
offset + size, NULL);
}
goto done;
}
/*
* Success: commit the COW or unwritten blocks if needed.
*/
if (is_zoned)
error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
ioend->io_private, NULLFSBLOCK);
else if (ioend->io_flags & IOMAP_IOEND_SHARED)
error = xfs_reflink_end_cow(ip, offset, size);
else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
error = xfs_iomap_write_unwritten(ip, offset, size, false);
if (!error &&
!(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
xfs_ioend_is_append(ioend))
error = xfs_setfilesize(ip, offset, size);
done:
if (is_zoned)
xfs_ioend_put_open_zones(ioend);
iomap_finish_ioends(ioend, error);
memalloc_nofs_restore(nofs_flag);
}
/*
* Finish all pending IO completions that require transactional modifications.
*
* We try to merge physical and logically contiguous ioends before completion to
* minimise the number of transactions we need to perform during IO completion.
* Both unwritten extent conversion and COW remapping need to iterate and modify
* one physical extent at a time, so we gain nothing by merging physically
* discontiguous extents here.
*
* The ioend chain length that we can be processing here is largely unbound in
* length and we may have to perform significant amounts of work on each ioend
* to complete it. Hence we have to be careful about holding the CPU for too
* long in this loop.
*/
void
xfs_end_io(
struct work_struct *work)
{
struct xfs_inode *ip =
container_of(work, struct xfs_inode, i_ioend_work);
struct iomap_ioend *ioend;
struct list_head tmp;
unsigned long flags;
spin_lock_irqsave(&ip->i_ioend_lock, flags);
list_replace_init(&ip->i_ioend_list, &tmp);
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
iomap_sort_ioends(&tmp);
while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
io_list))) {
list_del_init(&ioend->io_list);
iomap_ioend_try_merge(ioend, &tmp);
if (bio_op(&ioend->io_bio) == REQ_OP_READ)
iomap_finish_ioends(ioend,
blk_status_to_errno(ioend->io_bio.bi_status));
else
xfs_end_ioend_write(ioend);
cond_resched();
}
}
void
xfs_end_bio(
struct bio *bio)
{
struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
struct xfs_inode *ip = XFS_I(ioend->io_inode);
struct xfs_mount *mp = ip->i_mount;
unsigned long flags;
/*
* For Appends record the actually written block number and set the
* boundary flag if needed.
*/
if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
ioend->io_sector = bio->bi_iter.bi_sector;
xfs_mark_rtg_boundary(ioend);
}
spin_lock_irqsave(&ip->i_ioend_lock, flags);
if (list_empty(&ip->i_ioend_list))
WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
&ip->i_ioend_work));
list_add_tail(&ioend->io_list, &ip->i_ioend_list);
spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
}

16
fs/xfs/xfs_ioend.h Normal file
View File

@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __XFS_IOEND_H
#define __XFS_IOEND_H
/*
* Fast and loose check if this write could update the on-disk inode size.
*/
static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
{
return ioend->io_offset + ioend->io_size >
XFS_I(ioend->io_inode)->i_disk_size;
}
void xfs_end_bio(struct bio *bio);
#endif /* __XFS_IOEND_H */

View File

@@ -1083,12 +1083,7 @@ xfs_zoned_direct_write_iomap_begin(
return error;
}
iomap->type = IOMAP_MAPPED;
iomap->flags = IOMAP_F_DIRTY;
iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
iomap->offset = offset;
iomap->length = length;
iomap->flags = IOMAP_F_ANON_WRITE;
xfs_iomap_set_anon_write(ip, iomap, offset, length);
return 0;
}

View File

@@ -29,6 +29,20 @@ int xfs_zero_range(struct xfs_inode *ip, loff_t pos, loff_t len,
int xfs_truncate_page(struct xfs_inode *ip, loff_t pos,
struct xfs_zone_alloc_ctx *ac, bool *did_zero);
static inline void
xfs_iomap_set_anon_write(
struct xfs_inode *ip,
struct iomap *iomap,
loff_t offset,
loff_t length)
{
iomap->type = IOMAP_MAPPED;
iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
iomap->offset = offset;
iomap->length = length;
iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
}
static inline xfs_filblks_t
xfs_aligned_fsb_count(
xfs_fileoff_t offset_fsb,

View File

@@ -834,7 +834,7 @@ xfs_setattr_nonsize(
}
error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
has_capability_noaudit(current, CAP_FOWNER), &tp);
capable_noaudit(CAP_FOWNER), &tp);
if (error)
goto out_dqrele;

View File

@@ -289,15 +289,4 @@ int xfs_rw_bdev(struct block_device *bdev, sector_t sector, unsigned int count,
# define PTR_FMT "%p"
#endif
/*
* Helper for IO routines to grab backing pages from allocated kernel memory.
*/
static inline struct page *
kmem_to_page(void *addr)
{
if (is_vmalloc_addr(addr))
return vmalloc_to_page(addr);
return virt_to_page(addr);
}
#endif /* _XFS_PLATFORM_H */

View File

@@ -445,7 +445,7 @@ xfs_shutdown_devices(
blkdev_issue_flush(mp->m_logdev_targp->bt_bdev);
invalidate_bdev(mp->m_logdev_targp->bt_bdev);
}
if (mp->m_rtdev_targp) {
if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) {
blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev);
invalidate_bdev(mp->m_rtdev_targp->bt_bdev);
}

View File

@@ -521,7 +521,8 @@ xfs_trans_log_buf(
{
struct xfs_buf_log_item *bip = bp->b_log_item;
ASSERT(first <= last && last < BBTOB(bp->b_length));
ASSERT(first <= last);
ASSERT(last < BBTOB(bp->b_length));
ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED));
xfs_trans_dirty_buf(tp, bp);

View File

@@ -793,17 +793,35 @@ xfs_get_cached_zone(
rcu_read_lock();
oz = VFS_I(ip)->i_private;
if (oz) {
/*
* GC only steals open zones at mount time, so no GC zones
* should end up in the cache.
*/
ASSERT(!oz->oz_is_gc);
if (!atomic_inc_not_zero(&oz->oz_ref))
oz = NULL;
}
rcu_read_unlock();
if (!oz)
goto out_unlock;
/*
* GC only steals open zones at mount time, so no GC zones should end up
* in the cache.
*/
ASSERT(!oz->oz_is_gc);
/*
* Drop the old cached open zone if it is full.
*/
if (oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
spin_lock(&ip->i_flags_lock);
oz = VFS_I(ip)->i_private;
if (oz && oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
VFS_I(ip)->i_private = NULL;
spin_unlock(&ip->i_flags_lock);
xfs_open_zone_put(oz);
oz = NULL;
goto out_unlock;
}
spin_unlock(&ip->i_flags_lock);
}
if (!atomic_inc_not_zero(&oz->oz_ref))
oz = NULL;
out_unlock:
rcu_read_unlock();
return oz;
}
@@ -818,18 +836,41 @@ xfs_get_cached_zone(
* that were every written to, but significantly simplifies the cached zone
* lookup. Because the open_zone is clearly marked as full when all data
* in the underlying RTG was written, the caching is always safe.
*
* Called with a reference on @oz held. And returns two references on the
* returned zone: one for the caller and one for pinning the zone in
* inode->i_private.
*/
static void
static struct xfs_open_zone *
xfs_set_cached_zone(
struct xfs_inode *ip,
struct xfs_open_zone *oz)
{
struct xfs_open_zone *old_oz;
/*
* If the open zone cached in the inode still has free space, use that
* instead of the new open zone just selected. This can happen when
* multiple threads race to perform zone selection for an inode.
* io_uring worker threads seem to be good way to trigger this.
*
* We need to grab an extra reference to this open zone as the caller
* owns a reference in addition to the i_private pointer.
*/
spin_lock(&ip->i_flags_lock);
old_oz = VFS_I(ip)->i_private;
if (old_oz && old_oz->oz_allocated < rtg_blocks(old_oz->oz_rtg) &&
atomic_inc_not_zero(&old_oz->oz_ref)) {
spin_unlock(&ip->i_flags_lock);
xfs_open_zone_put(oz);
return old_oz;
}
VFS_I(ip)->i_private = oz;
atomic_inc(&oz->oz_ref);
old_oz = xchg(&VFS_I(ip)->i_private, oz);
spin_unlock(&ip->i_flags_lock);
if (old_oz)
xfs_open_zone_put(old_oz);
return oz;
}
static void
@@ -873,14 +914,13 @@ xfs_zone_alloc_and_submit(
* the inode is still associated with a zone and use that if so.
*/
if (!*oz)
*oz = xfs_get_cached_zone(ip);
if (!*oz) {
select_zone:
*oz = xfs_get_cached_zone(ip);
if (!*oz) {
*oz = xfs_select_zone(mp, write_hint, pack_tight);
if (!*oz)
goto out_error;
xfs_set_cached_zone(ip, *oz);
*oz = xfs_set_cached_zone(ip, *oz);
}
alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size),

View File

@@ -869,6 +869,11 @@ xfs_zone_gc_write_chunk(
WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW);
list_move_tail(&chunk->entry, &data->writing);
/*
* If we run on top of stacked block device, the read I/O might have
* reset bi_bdev, restore it to the one we want.
*/
bio_set_dev(&chunk->bio, mp->m_rtdev_targp->bt_bdev);
bio_reuse(&chunk->bio, REQ_OP_WRITE);
while ((split_chunk = xfs_zone_gc_split_write(data, chunk)))
xfs_zone_gc_submit_write(data, split_chunk);

View File

@@ -145,6 +145,7 @@ extern bool has_capability_noaudit(struct task_struct *t, int cap);
extern bool has_ns_capability_noaudit(struct task_struct *t,
struct user_namespace *ns, int cap);
extern bool capable(int cap);
bool capable_noaudit(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
@@ -167,6 +168,10 @@ static inline bool capable(int cap)
{
return true;
}
static inline bool capable_noaudit(int cap)
{
return true;
}
static inline bool ns_capable(struct user_namespace *ns, int cap)
{
return true;

View File

@@ -527,7 +527,10 @@ struct cgroup {
int nr_threaded_children; /* # of live threaded child cgroups */
/* sequence number for cgroup.kill, serialized by css_set_lock. */
/*
* Sequence number for cgroup.kill. Incremented with both cgroup_mutex
* and css_set_lock held. Readers hold either one.
*/
unsigned int kill_seq;
struct kernfs_node *kn; /* cgroup kernfs entry */

View File

@@ -598,9 +598,6 @@ struct mem_ctl_info {
int op_state;
struct dentry *debugfs;
u8 fake_inject_layer[EDAC_MAX_LAYERS];
bool fake_inject_ue;
u16 fake_inject_count;
/*
* Memory Controller hierarchy

View File

@@ -326,7 +326,6 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
{
return has_ns_capability_noaudit(t, &init_user_ns, cap);
}
EXPORT_SYMBOL(has_capability_noaudit);
static bool ns_capable_common(struct user_namespace *ns,
int cap,
@@ -416,6 +415,24 @@ bool capable(int cap)
return ns_capable(&init_user_ns, cap);
}
EXPORT_SYMBOL(capable);
/**
* capable_noaudit - Determine if the current task has a superior
* capability in effect by checking the process's effective
* capabilities (unaudited).
* @cap: The capability to be tested for
*
* This is the same as capable(), except it uses CAP_OPT_NOAUDIT as to prevent
* issuing spurious audit messages.
*
* This sets PF_SUPERPRIV on the task if the capability is available on the
* assumption that it's about to be used.
*/
bool capable_noaudit(int cap)
{
return ns_capable_noaudit(&init_user_ns, cap);
}
EXPORT_SYMBOL(capable_noaudit);
#endif /* CONFIG_MULTIUSER */
/**

View File

@@ -6873,10 +6873,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs)
spin_lock_irq(&css_set_lock);
cset = task_css_set(current);
get_css_set(cset);
if (kargs->cgrp)
kargs->kill_seq = kargs->cgrp->kill_seq;
else
kargs->kill_seq = cset->dfl_cgrp->kill_seq;
kargs->kill_seq = cset->dfl_cgrp->kill_seq;
spin_unlock_irq(&css_set_lock);
if (!(kargs->flags & CLONE_INTO_CGROUP)) {
@@ -6940,6 +6937,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs)
put_css_set(cset);
kargs->cgrp = dst_cgrp;
kargs->kill_seq = dst_cgrp->kill_seq;
return ret;
err:

View File

@@ -1259,6 +1259,28 @@ static void reset_partition_data(struct cpuset *cs)
cpumask_copy(cs->effective_cpus, parent->effective_cpus);
}
/* Return true if isolated_cpus changes. */
static bool isolated_cpu_update(int new_prs, int cpu)
{
lockdep_assert_held(&callback_lock);
lockdep_assert_held(&cpuset_mutex);
if (new_prs == PRS_ISOLATED) {
if (cpumask_test_cpu(cpu, isolated_cpus))
return false;
cpumask_set_cpu(cpu, isolated_cpus);
return true;
}
/* CPUs isolated at boot must remain isolated. */
if (!cpumask_test_cpu(cpu,
housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)) ||
!cpumask_test_cpu(cpu, isolated_cpus))
return false;
cpumask_clear_cpu(cpu, isolated_cpus);
return true;
}
/*
* isolated_cpus_update - Update the isolated_cpus mask
* @old_prs: old partition_root_state
@@ -1267,19 +1289,16 @@ static void reset_partition_data(struct cpuset *cs)
*/
static void isolated_cpus_update(int old_prs, int new_prs, struct cpumask *xcpus)
{
bool updated = false;
int cpu;
WARN_ON_ONCE(old_prs == new_prs);
lockdep_assert_held(&callback_lock);
lockdep_assert_held(&cpuset_mutex);
if (new_prs == PRS_ISOLATED) {
if (cpumask_subset(xcpus, isolated_cpus))
return;
cpumask_or(isolated_cpus, isolated_cpus, xcpus);
} else {
if (!cpumask_intersects(xcpus, isolated_cpus))
return;
cpumask_andnot(isolated_cpus, isolated_cpus, xcpus);
}
update_housekeeping = true;
for_each_cpu(cpu, xcpus)
updated |= isolated_cpu_update(new_prs, cpu);
if (updated)
update_housekeeping = true;
}
/*

View File

@@ -876,9 +876,9 @@ struct task_struct *scx_task_iter_next_locked(struct scx_task_iter *iter)
* unloading. The init_tasks ("swappers") should be excluded
* from the iteration because:
*
* - It's unsafe to use __setschduler_prio() on an init_task to
* determine the sched_class to use as it won't preserve its
* idle_sched_class.
* - It's unsafe to use __setscheduler_class() on an init_task
* to determine the sched_class to use as it won't preserve
* its idle_sched_class.
*
* - ops.init/exit_task() can easily be confused if called with
* init_tasks as they, e.g., share PID 0.
@@ -2806,6 +2806,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
* @p: task to finish dispatching
* @qseq_at_dispatch: qseq when @p started getting dispatched
* @dsq_id: destination DSQ ID
* @slice: slice carried by the insert verdict, 0 keeps the current value
* @vtime: vtime carried by the insert verdict, committed on PRIQ inserts
* @enq_flags: %SCX_ENQ_*
*
* Dispatching to local DSQs may need to wait for queueing to complete or
@@ -5514,7 +5516,7 @@ static const struct kset_uevent_ops scx_uevent_ops = {
};
/*
* Used by sched_fork() and __setscheduler_prio() to pick the matching
* Used by sched_fork() and __setscheduler_class() to pick the matching
* sched_class. dl/rt are already handled.
*/
bool task_should_scx(int policy)
@@ -7694,7 +7696,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
/*
* Enable ops for every task. Fork is excluded by scx_fork_rwsem
* preventing new tasks from being added. No need to exclude tasks
* leaving as sched_ext_free() can handle both prepped and enabled
* leaving as sched_ext_dead() can handle both prepped and enabled
* tasks. Prep all tasks first and then enable them with preemption
* disabled.
*
@@ -7786,7 +7788,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
/*
* We're fully committed and can't fail. The task READY -> ENABLED
* transitions here are synchronized against sched_ext_free() through
* transitions here are synchronized against sched_ext_dead() through
* scx_tasks_lock.
*/
percpu_down_write(&scx_fork_rwsem);
@@ -8079,6 +8081,7 @@ static int bpf_scx_check_member(const struct btf_type *t,
case offsetof(struct sched_ext_ops, cgroup_init):
case offsetof(struct sched_ext_ops, cgroup_exit):
case offsetof(struct sched_ext_ops, cgroup_prep_move):
case offsetof(struct sched_ext_ops, cgroup_set_bandwidth):
#endif
case offsetof(struct sched_ext_ops, cpu_online):
case offsetof(struct sched_ext_ops, cpu_offline):
@@ -9003,12 +9006,6 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
if (unlikely(READ_ONCE(sch->aborting)))
return false;
if (unlikely(!scx_task_on_sched(sch, p))) {
scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
p->comm, p->pid);
return false;
}
/*
* Can be called from either ops.dispatch() holding the dispatched rq's
* lock or any context where no rq lock is held. If latter, lock @p's
@@ -9040,6 +9037,17 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
goto out;
}
/*
* @p has been on $src_dsq and can't move anymore. If @p is not on @sch,
* the caller didn't have authority over @p at the time of the call.
*/
if (unlikely(!scx_task_on_sched(sch, p))) {
scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
p->comm, p->pid);
raw_spin_unlock(&src_dsq->lock);
goto out;
}
/* @p is still on $src_dsq and stable, determine the destination */
dst_dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, task_cpu(p));
@@ -9765,7 +9773,7 @@ __bpf_kfunc struct task_struct *bpf_iter_scx_dsq_next(struct bpf_iter_scx_dsq *i
* bpf_iter_scx_dsq_destroy - Destroy a DSQ iterator
* @it: iterator to destroy
*
* Undo scx_iter_scx_dsq_new().
* Undo bpf_iter_scx_dsq_new().
*/
__bpf_kfunc void bpf_iter_scx_dsq_destroy(struct bpf_iter_scx_dsq *it)
{
@@ -11041,3 +11049,16 @@ static int __init scx_init(void)
return 0;
}
__initcall(scx_init);
/*
* Compatibility markers for userspace. Existence of a marker function
* represents that the kernel supports that sched-ext feature.
*/
/*
* scx_compat_marker_cgroup_set_bandwidth_may_sleep: advertises that
* ops.cgroup_set_bandwidth() may be implemented as a sleepable callback.
*/
#ifdef CONFIG_EXT_GROUP_SCHED
DEFINE_SCX_COMPAT_MARKER(cgroup_set_bandwidth_may_sleep);
#endif /* CONFIG_EXT_GROUP_SCHED */

View File

@@ -442,7 +442,7 @@ struct sched_ext_ops {
*
* Note that this callback may be called from a CPU other than the
* one the task is going to run on. This can happen when a task
* property is changed (i.e., affinity), since scx_next_task_scx(),
* property is changed (i.e., affinity), since set_next_task_scx(),
* which triggers this callback, may run on a CPU different from
* the task's assigned CPU.
*
@@ -753,7 +753,7 @@ struct sched_ext_ops {
* @burst_us: bandwidth control burst
*
* Update @cgrp's bandwidth control parameters. This is from the cpu.max
* cgroup interface.
* cgroup interface. This operation may block.
*
* @quota_us / @period_us determines the CPU bandwidth @cgrp is entitled
* to. For example, if @period_us is 1_000_000 and @quota_us is
@@ -2001,6 +2001,27 @@ struct scx_bstr_buf {
char line[SCX_EXIT_MSG_LEN];
};
/* Internal helper for DEFINE_SCX_COMPAT_MARKER(). */
#define DECLARE_SCX_COMPAT_MARKER(func) \
extern void scx_compat_marker_##func(void)
/**
* DEFINE_SCX_COMPAT_MARKER() - define a userspace capability marker
* @func: marker suffix; the defined symbol is scx_compat_marker_@func
*
* Emit an empty, callerless function that is retained in the kernel's BTF.
* Its presence is part of the kernel<->userspace contract: userspace probes
* scx_compat_marker_@func (e.g. via BTF) to detect that this kernel supports
* the corresponding feature.
*
* The leading declaration suppresses the missing-prototype warning; the
* trailing declaration consumes the semicolon at the use site.
*/
#define DEFINE_SCX_COMPAT_MARKER(func) \
DECLARE_SCX_COMPAT_MARKER(func); \
__used __retain void scx_compat_marker_##func(void) {} \
DECLARE_SCX_COMPAT_MARKER(func)
extern struct scx_sched __rcu *scx_root;
DECLARE_PER_CPU(struct rq *, scx_locked_rq_state);

View File

@@ -3197,7 +3197,16 @@ __acquires(&pool->lock)
#ifdef CONFIG_PREEMPT_RT
static void worker_lock_callback(struct worker_pool *pool)
{
spin_lock(&pool->cb_lock);
/*
* SINGLE_DEPTH_NESTING is for a dead pool's bh_worker() running from
* drain_dead_softirq_workfn() inside a live pool's bh_worker(). The
* unlocked read is stable: the flag is only set while @pool's CPU is
* dead, inside a serialized hotplug operation. data_race() as the value
* only affects the lockdep annotation and the read can be elided when
* lockdep is disabled.
*/
spin_lock_nested(&pool->cb_lock,
data_race(pool->flags) & POOL_BH_DRAINING ? SINGLE_DEPTH_NESTING : 0);
}
static void worker_unlock_callback(struct worker_pool *pool)
@@ -5285,12 +5294,6 @@ static void pwq_release_workfn(struct kthread_work *work)
mutex_unlock(&wq->mutex);
}
if (!is_percpu_pool(pool)) {
mutex_lock(&wq_pool_mutex);
put_unbound_pool(pool);
mutex_unlock(&wq_pool_mutex);
}
if (!list_empty(&pwq->pending_node)) {
struct wq_node_nr_active *nna =
wq_node_nr_active(pwq->wq, pwq->pool->node);
@@ -5300,6 +5303,12 @@ static void pwq_release_workfn(struct kthread_work *work)
raw_spin_unlock_irq(&nna->lock);
}
if (!is_percpu_pool(pool)) {
mutex_lock(&wq_pool_mutex);
put_unbound_pool(pool);
mutex_unlock(&wq_pool_mutex);
}
kfree_rcu(pwq, rcu);
/*
@@ -8050,6 +8059,9 @@ static int wq_watchdog_param_set_thresh(const char *val,
if (ret)
return ret;
if (thresh > MAX_JIFFY_OFFSET / HZ)
return -ERANGE;
if (system_percpu_wq)
wq_watchdog_set_thresh(thresh);
else
@@ -8080,12 +8092,12 @@ static inline void wq_watchdog_init(void) { }
static void bh_pool_kick_normal(struct irq_work *irq_work)
{
raise_softirq_irqoff(TASKLET_SOFTIRQ);
raise_softirq(TASKLET_SOFTIRQ);
}
static void bh_pool_kick_highpri(struct irq_work *irq_work)
{
raise_softirq_irqoff(HI_SOFTIRQ);
raise_softirq(HI_SOFTIRQ);
}
static void __init restrict_unbound_cpumask(const char *name, const struct cpumask *mask)

View File

@@ -48,6 +48,7 @@
extern int LINUX_KERNEL_VERSION __kconfig;
extern const char CONFIG_CC_VERSION_TEXT[64] __kconfig __weak;
extern const char CONFIG_LOCALVERSION[64] __kconfig __weak;
extern bool CONFIG_PREEMPT_RCU __kconfig __weak;
/*
* Earlier versions of clang/pahole lost upper 32bits in 64bit enums which can
@@ -97,6 +98,7 @@ s32 scx_bpf_pick_any_cpu_node(const cpumask_t *cpus_allowed, int node, u64 flags
s32 scx_bpf_pick_any_cpu(const cpumask_t *cpus_allowed, u64 flags) __ksym;
bool scx_bpf_task_running(const struct task_struct *p) __ksym;
s32 scx_bpf_task_cpu(const struct task_struct *p) __ksym;
struct rq *scx_bpf_cpu_rq(s32 cpu) __ksym __weak;
struct rq *scx_bpf_locked_rq(void) __ksym;
struct task_struct *scx_bpf_cpu_curr(s32 cpu) __ksym __weak;
struct task_struct *scx_bpf_tid_to_task(u64 tid) __ksym __weak;
@@ -527,32 +529,103 @@ static __always_inline const struct cpumask *cast_mask(struct bpf_cpumask *mask)
return (const struct cpumask *)mask;
}
/*
* True if the non-sleepable BPF trampoline prolog (__bpf_prog_enter) calls
* migrate_disable() for the current task. Recorded once by
* scx_lib_init_probe, an fentry program on bpf_scx_reg() that fires during
* the natural scheduler-attach call chain (auto-attached by scx_ops_attach!).
*
* Defaults to true (conservative). Over-reporting in is_migration_disabled()
* causes local-only dispatch, which is safe. Under-reporting can crash the
* scheduler, so we err high if the probe somehow fails to run.
*/
bool __scx_prolog_disables_migration __weak = true;
/*
* scx_lib_init_probe - non-sleepable prolog probe.
*
* Attached to bpf_scx_reg(), the .reg callback in bpf_sched_ext_ops
* (kernel/sched/ext.c). The kernel's struct_ops machinery invokes
* bpf_scx_reg when userspace creates the scheduler link, before
* ops.init() fires. Its address is taken in the vtable, so the symbol
* is non-inlinable and has been stable since introduction.
*
* Entering via fentry runs us through __bpf_prog_enter -- the
* non-sleepable prolog that consumers of is_migration_disabled() live
* under.
*
* Loud warning: the prolog adds at most 1 to migration_disabled.
* Reading > 1 means something upstream in the
* bpf_struct_ops_link_create -> bpf_scx_reg path disabled migration
* before the prolog ran, invalidating the probe; audit and adjust.
*/
SEC("fentry/bpf_scx_reg") __weak
int scx_lib_init_probe(void *ctx)
{
if (bpf_core_field_exists(((struct task_struct *)0)->migration_disabled)) {
const struct task_struct *p = bpf_get_current_task_btf();
unsigned int md = p->migration_disabled;
if (md > 1)
bpf_printk("scx_lib_init_probe: unexpected migration_disabled=%u "
"upstream of BPF prolog; probe result unreliable",
md);
__scx_prolog_disables_migration = md > 0;
}
return 0;
}
/*
* Return true if task @p cannot migrate to a different CPU, false
* otherwise.
*
* IMPORTANT: designed for NON-SLEEPABLE BPF contexts only. Sleepable
* contexts (BPF_STRUCT_OPS_SLEEPABLE, SEC("syscall"),
* SEC("fentry.s/...")) enter via __bpf_prog_enter_sleepable() or
* __bpf_prog_enter_sleepable_recur(), both of which unconditionally
* call migrate_disable(); this helper can yield a false negative for
* p == current there, which can crash the scheduler.
*/
static inline bool is_migration_disabled(const struct task_struct *p)
{
/*
* Testing p->migration_disabled in a BPF code is tricky because the
* migration is _always_ disabled while running the BPF code.
* The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) for BPF
* code execution disable and re-enable the migration of the current
* task, respectively. So, the _current_ task of the sched_ext ops is
* always migration-disabled. Moreover, p->migration_disabled could be
* two or greater when a sched_ext ops BPF code (e.g., ops.tick) is
* executed in the middle of the other BPF code execution.
* Testing p->migration_disabled in BPF is tricky because the BPF prolog
* (__bpf_prog_enter) may call migrate_disable() for the current task,
* making migration_disabled == 1 even for tasks that are not truly
* migration-disabled.
*
* Therefore, we should decide that the _current_ task is
* migration-disabled only when its migration_disabled count is greater
* than one. In other words, when p->migration_disabled == 1, there is
* an ambiguity, so we should check if @p is the current task or not.
* Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for
* trampoline.c"), the BPF prolog calls migrate_disable() only when
* CONFIG_PREEMPT_RCU is enabled. Two fast paths cover the common cases:
*
* 1) CONFIG_PREEMPT_RCU: prolog always calls migrate_disable(), so
* migration_disabled == 1 for the current task is ambiguous.
* Disambiguate by checking p == current.
*
* 2) v6.18+ without CONFIG_PREEMPT_RCU: prolog never calls
* migrate_disable(), so migration_disabled == 1 is unambiguously
* a real migrate_disable() call.
*
* A slow path handles pre-v6.18 kernels without CONFIG_PREEMPT_RCU,
* where the prolog historically called migrate_disable() unconditionally
* but a cherry-picked downstream kernel may not. The runtime-probed flag
* __scx_prolog_disables_migration (set by scx_lib_init_probe) distinguishes
* the two cases without relying on the kernel version alone.
*/
if (bpf_core_field_exists(p->migration_disabled)) {
if (p->migration_disabled == 1)
return bpf_get_current_task_btf() != p;
else
return p->migration_disabled;
if (p->migration_disabled == 1) {
/* Fast path: prolog always disables migration */
if (CONFIG_PREEMPT_RCU)
return bpf_get_current_task_btf() != p;
/* Fast path: prolog never disables migration */
if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(6, 18, 0))
return true;
/* Slow path: pre-v6.18, !PREEMPT_RCU - use runtime flag */
return __scx_prolog_disables_migration ?
bpf_get_current_task_btf() != p : true;
}
return p->migration_disabled;
}
return false;
}
@@ -1021,7 +1094,20 @@ static inline u64 scx_clock_task(u32 cpu)
{
struct rq___local *rq = get_current_rq(cpu);
/* Equivalent to the kernel's rq_clock_task(). */
/*
* Equivalent to the kernel's rq_clock_task(): wall-clock time minus
* cumulative IRQ time (CONFIG_IRQ_TIME_ACCOUNTING) and hypervisor
* steal time (CONFIG_PARAVIRT_TIME_ACCOUNTING). Without those configs,
* it equals rq->clock.
*
* Conceptually this clock advances during idle (the idle task counts
* as a running task), but rq->clock_task is only updated on scheduling
* events. With NO_HZ_IDLE (the default), the periodic tick is stopped
* on idle CPUs, so rq->clock_task is not refreshed while a CPU is
* idle. Reading this clock for a remote idle CPU from a BPF timer
* callback returns the value from when the CPU last went idle, making
* the delta over an idle interval effectively zero.
*/
return rq ? rq->clock_task : 0;
}
@@ -1032,9 +1118,23 @@ static inline u64 scx_clock_pelt(u32 cpu)
/*
* Equivalent to the kernel's rq_clock_pelt(): subtracts
* lost_idle_time from clock_pelt to absorb the jump that occurs
* when clock_pelt resyncs with clock_task at idle exit. The result
* is a continuous, capacity-invariant clock safe for both task
* execution time stamping and cross-idle measurements.
* when clock_pelt resyncs with clock_task at idle exit. The intent
* is a continuous, capacity- and frequency-invariant clock that is
* frozen during idle, IRQ, and hypervisor steal.
*
* However, like scx_clock_task(), this clock has a stale-read issue
* for remote idle CPUs with NO_HZ_IDLE (the default). clock_pelt
* itself advances at wall-clock rate (hardware-clock based), but
* lost_idle_time is only updated via update_rq_clock_pelt(), which
* requires update_rq_clock() to be called. With NO_HZ_IDLE, the
* periodic tick is stopped on idle CPUs, so lost_idle_time is not
* refreshed during idle. Reading this clock for a remote idle CPU
* from a BPF timer callback therefore returns a value that drifts
* at wall-clock rate -- the same stale behaviour as scx_clock_task().
*
* Without NO_HZ_IDLE, periodic ticks keep lost_idle_time nearly in
* sync (stale by at most one tick period, ~1 ms), so the result is
* accurate.
*/
return rq ? (rq->clock_pelt - rq->lost_idle_time) : 0;
}

View File

@@ -92,15 +92,20 @@ int bpf_cpumask_populate(struct bpf_cpumask *dst, void *src, size_t src__sz) __k
/*
* v6.19: Introduce lockless peek API for user DSQs.
* v7.1: Fix scx_bpf_dsq_peek() spuriously returning NULL on non-empty
* FIFO DSQs (2f2ea7709266).
*
* Preserve the following macro until v6.21.
* The kfunc exists from v6.19 but can return NULL for a non-empty FIFO DSQ
* before the v7.1 fix. Require kernel version >= 7.1.0 before calling it;
* otherwise fall through to the bpf_iter_scx_dsq fallback below.
*/
static inline struct task_struct *__COMPAT_scx_bpf_dsq_peek(u64 dsq_id)
{
struct task_struct *p = NULL;
struct bpf_iter_scx_dsq it;
if (bpf_ksym_exists(scx_bpf_dsq_peek))
if (bpf_ksym_exists(scx_bpf_dsq_peek) &&
LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0))
return scx_bpf_dsq_peek(dsq_id);
if (!bpf_iter_scx_dsq_new(&it, dsq_id, 0))
p = bpf_iter_scx_dsq_next(&it);
@@ -238,6 +243,26 @@ static inline bool __COMPAT_is_enq_cpu_selected(u64 enq_flags)
scx_bpf_pick_any_cpu_node(cpus_allowed, node, flags) : \
scx_bpf_pick_any_cpu(cpus_allowed, flags))
/*
* v6.18: Add a helper to retrieve the current task running on a CPU.
*
* The kernel tree dropped this helper and scx_bpf_cpu_rq(), but schedulers in
* this tree still support pre-v6.18 kernels where scx_bpf_cpu_curr() doesn't
* resolve and the scx_bpf_cpu_rq() fallback still exists. Keep it until
* pre-v6.18 kernels fall out of the support window.
*/
static inline struct task_struct *__COMPAT_scx_bpf_cpu_curr(int cpu)
{
struct rq *rq;
if (bpf_ksym_exists(scx_bpf_cpu_curr))
return scx_bpf_cpu_curr(cpu);
rq = scx_bpf_cpu_rq(cpu);
return rq ? rq->curr : NULL;
}
/*
* v6.19: To work around BPF maximum parameter limit, the following kfuncs are
* replaced with variants that pack scalar arguments in a struct. Wrappers are
@@ -378,6 +403,17 @@ static inline void scx_bpf_task_set_dsq_vtime(struct task_struct *p, u64 vtime)
p->scx.dsq_vtime = vtime;
}
/*
* v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
* will eventually deprecate scx_bpf_reenqueue_local().
*/
void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
static inline bool __COMPAT_has_generic_reenq(void)
{
return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
}
/*
* v6.19: The new void variant can be called from anywhere while the older v1
* variant can only be called from ops.cpu_release(). The double ___ prefixes on
@@ -395,21 +431,31 @@ static inline bool __COMPAT_scx_bpf_reenqueue_local_from_anywhere(void)
static inline void scx_bpf_reenqueue_local(void)
{
if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
if (__COMPAT_has_generic_reenq())
scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
else if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
scx_bpf_reenqueue_local___v2___compat();
else
scx_bpf_reenqueue_local___v1();
}
/*
* v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
* will eventually deprecate scx_bpf_reenqueue_local().
*/
void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
static inline bool __COMPAT_has_generic_reenq(void)
static inline int scx_bpf_reenqueue_local_from_anywhere(void)
{
return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
/*
* The generic reenq kfunc and the v2 reenqueue-local variant can both be
* called from anywhere; v1 cannot. Test each ksym in its own branch with a
* distinct call: combining them with || would fold into a bitwise OR of the
* two ksym addresses, which the verifier rejects.
*/
if (__COMPAT_has_generic_reenq()) {
scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
return 0;
}
if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) {
scx_bpf_reenqueue_local___v2___compat();
return 0;
}
return -EOPNOTSUPP;
}
static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags)

View File

@@ -10,9 +10,14 @@
#include <bpf/btf.h>
#include <bpf/libbpf.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "enums_abi.autogen.h"
struct btf *__COMPAT_vmlinux_btf __attribute__((weak));
static inline void __COMPAT_load_vmlinux_btf(void)
@@ -23,6 +28,85 @@ static inline void __COMPAT_load_vmlinux_btf(void)
}
}
/*
* Recover the true value of a 64-bit enum enumerator whose kernel BTF entry
* was truncated to its low 32 bits.
*
* Kernels whose BTF was generated without BTF_KIND_ENUM64 support encode
* 64-bit enums as 8-byte BTF_KIND_ENUM entries whose enumerator values only
* carry the low 32 bits. This happens with pahole < 1.24, which predates
* ENUM64, and with pahole passing --skip_encoding_btf_enum64 (e.g. Google's
* Container-Optimized OS / GKE kernels deliberately pass it for backward
* compatibility with older BTF consumers). The high bits
* can't be recovered from kernel BTF, so substitute the value from the
* vmlinux.h this tree was built against, cross-checked against the low 32
* bits the kernel did provide.
*
* Note that this is a best-effort recovery, not a ground truth. The
* substitution assumes the running kernel agrees with this tree's vmlinux.h
* on the high 32 bits, but only the low 32 bits can actually be verified.
* The cross-check is vacuous for enumerators whose value has no low bits
* set (e.g. SCX_DSQ_FLAG_BUILTIN, __SCX_ENQ_INTERNAL_MASK,
* SCX_ENQ_CLEAR_OPSS, SCX_ECODE_*): their lo32 is 0 and matches anything,
* so those substitutions rest entirely on the high bits never moving. An
* enumerator missing from the table (a kernel newer than this tree's
* vmlinux.h, or a stale autogen table) can't be recovered at all. If a
* substitution is ever wrong, the scheduler operates on bogus values (e.g.
* dispatching to nonexistent DSQ ids or silently dropping flags) and can
* wildly malfunction, which is why the mismatch and table-miss paths refuse
* instead of guessing.
*/
static inline bool __COMPAT_recover_truncated_enum64(const char *type,
const char *name,
u32 lo32, u64 *v)
{
static bool warned;
size_t i;
for (i = 0; i < sizeof(__scx_enum_abi_vals) / sizeof(__scx_enum_abi_vals[0]); i++) {
const struct __scx_enum_abi_val *e = &__scx_enum_abi_vals[i];
if (strcmp(e->type, type) || strcmp(e->name, name))
continue;
if (e->val <= (u64)UINT32_MAX) {
*v = lo32;
return true;
}
if ((u32)e->val != lo32) {
fprintf(stderr, "ERROR: kernel BTF value of %s::%s (0x%x) doesn't match the low 32 bits of the vmlinux.h value (0x%llx); refusing to substitute\n",
type, name, lo32, (unsigned long long)e->val);
return false;
}
if (!warned) {
fprintf(stderr,
"WARNING: kernel BTF lacks BTF_KIND_ENUM64 encoding (generated by\n"
"WARNING: pahole < 1.24 or with --skip_encoding_btf_enum64), so 64-bit\n"
"WARNING: scx enum values are truncated to their low 32 bits in kernel\n"
"WARNING: BTF. Substituting the full 64-bit values from the vmlinux.h\n"
"WARNING: this binary was built against, cross-checked against the low\n"
"WARNING: 32 bits the kernel does provide. The high 32 bits cannot be\n"
"WARNING: verified: if the running kernel's actual values differ from\n"
"WARNING: the build-time vmlinux.h (e.g. an enum that moved in a newer\n"
"WARNING: kernel), the scheduler will operate on bogus values, such as\n"
"WARNING: dispatching to nonexistent DSQ ids, and can wildly malfunction.\n");
warned = true;
}
*v = e->val;
return true;
}
/*
* Unknown enumerator (likely a stale autogen table). Fail
* pessimistically to avoid returning an invalid value.
*/
fprintf(stderr, "ERROR: kernel BTF truncates 64-bit enum %s::%s to 0x%x; 64-bit variant not found in vmlinux.h\n",
type, name, lo32);
return false;
}
static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v)
{
const struct btf_type *t;
@@ -46,6 +130,19 @@ static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v
n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off);
SCX_BUG_ON(!n, "btf__name_by_offset()");
if (!strcmp(n, name)) {
/*
* Try to recover a 64-bit enum from an 8-byte
* BTF_KIND_ENUM that was encoded without ENUM64
* support (old pahole or
* --skip_encoding_btf_enum64). Only scx_*
* types are covered by the substitution table;
* non-scx types fall through to the raw value
* so this generic utility keeps working for
* them.
*/
if (t->size == 8 && !strncmp(type, "scx_", 4))
return __COMPAT_recover_truncated_enum64(type, name,
(u32)e[i].val, v);
*v = e[i].val;
return true;
}

View File

@@ -56,6 +56,10 @@
#define HAVE_SCX_DEQ_SLEEP
#define HAVE_SCX_DEQ_CORE_SCHED_EXEC
#define HAVE_SCX_DEQ_SCHED_CHANGE
#define HAVE_SCX_DSP_NONE
#define HAVE_SCX_DSP_LOCAL
#define HAVE_SCX_DSP_PREV
#define HAVE_SCX_DSP_RETRY
#define HAVE_SCX_DSQ_FLAG_BUILTIN
#define HAVE_SCX_DSQ_FLAG_LOCAL_ON
#define HAVE_SCX_DSQ_INVALID
@@ -188,7 +192,6 @@
#define HAVE_SCX_RQ_SUB_IDLE_RENOTIFY
#define HAVE_SCX_RQ_ROOT_IDLE_RENOTIFY
#define HAVE_SCX_RQ_IN_WAKEUP
#define HAVE_SCX_RQ_IN_BALANCE
#define HAVE_SCX_RQ_IN_DISPATCH
#define HAVE_SCX_SCHED_PCPU_BYPASSING
#define HAVE_SCX_SLICE_OOB_DUR_BITS

View File

@@ -0,0 +1,223 @@
/*
* WARNING: This file is autogenerated from gen_enum_defs.py [1].
*
* scx enumerator values from the vmlinux.h this tree is built against.
* Used as the substitution source when the running kernel's BTF lacks
* BTF_KIND_ENUM64 encoding and 64-bit enum values are truncated.
*
* [1] https://github.com/sched-ext/scx/blob/main/scripts/gen_enum_defs.py
*/
#ifndef __ENUMS_ABI_AUTOGEN_H__
#define __ENUMS_ABI_AUTOGEN_H__
struct __scx_enum_abi_val {
const char *type;
const char *name;
u64 val;
};
static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
__attribute__((unused)) = {
{ "scx_arena_consts", "SCX_ARENA_MIN_ORDER", 0x3LLU },
{ "scx_arena_consts", "SCX_ARENA_GROW_PAGES", 0x4LLU },
{ "scx_cap_flags", "__SCX_CAP_ENQ_IMMED", 0x0LLU },
{ "scx_cap_flags", "__SCX_CAP_ENQ", 0x1LLU },
{ "scx_cap_flags", "__SCX_CAP_PREEMPT", 0x2LLU },
{ "scx_cap_flags", "__SCX_CAP_PERF", 0x3LLU },
{ "scx_cap_flags", "__SCX_NR_CAPS", 0x4LLU },
{ "scx_cap_flags", "__SCX_CAP_ALL", 0xfLLU },
{ "scx_cap_flags", "SCX_CAP_ENQ_IMMED", 0x1LLU },
{ "scx_cap_flags", "SCX_CAP_ENQ", 0x2LLU },
{ "scx_cap_flags", "SCX_CAP_PREEMPT", 0x4LLU },
{ "scx_cap_flags", "SCX_CAP_PERF", 0x8LLU },
{ "scx_cap_flags", "SCX_CAP_BASE", 0x1LLU },
{ "scx_cap_flags", "SCX_CAPS_REENQ_ON_LOSS", 0x3LLU },
{ "scx_cid_consts", "SCX_CID_SHARD_SIZE_DFL", 0x18LLU },
{ "scx_cid_consts", "SCX_CID_SHARD_MAX_CPUS", 0x200LLU },
{ "scx_consts", "SCX_DSP_DFL_MAX_BATCH", 0x20LLU },
{ "scx_consts", "SCX_DSP_MAX_LOOPS", 0x20LLU },
{ "scx_consts", "SCX_WATCHDOG_MAX_TIMEOUT", 0x7530LLU },
{ "scx_consts", "SCX_RESCUE_DFL_BW_PPT", 0x14LLU },
{ "scx_consts", "SCX_RESCUE_MAX_BW_PPT", 0xfaLLU },
{ "scx_consts", "SCX_RESCUE_DISABLE", 0xffffffffLLU },
{ "scx_consts", "SCX_RESCUE_DFL_QUANTUM_US", 0x1388LLU },
{ "scx_consts", "SCX_RESCUE_MIN_QUANTUM_US", 0x3e8LLU },
{ "scx_consts", "SCX_RESCUE_MAX_QUANTUM_US", 0x186a0LLU },
{ "scx_consts", "SCX_RESCUE_MIN_SLICE_US", 0x3e8LLU },
{ "scx_consts", "SCX_RESCUE_OVERLOAD_MULT", 0x10LLU },
{ "scx_consts", "SCX_RESCUE_MIN_OVERLOAD_MS", 0x3e8LLU },
{ "scx_consts", "SCX_RESCUE_MAX_OVERLOAD_MS", 0x3a98LLU },
{ "scx_consts", "SCX_TID_CHUNK", 0x400LLU },
{ "scx_consts", "SCX_EXIT_BT_LEN", 0x40LLU },
{ "scx_consts", "SCX_EXIT_MSG_LEN", 0x400LLU },
{ "scx_consts", "SCX_EXIT_DUMP_DFL_LEN", 0x8000LLU },
{ "scx_consts", "SCX_CPUPERF_ONE", 0x400LLU },
{ "scx_consts", "SCX_TASK_ITER_BATCH", 0x20LLU },
{ "scx_consts", "SCX_BYPASS_HOST_NTH", 0x2LLU },
{ "scx_consts", "SCX_BYPASS_LB_DFL_INTV_US", 0x7a120LLU },
{ "scx_consts", "SCX_BYPASS_LB_DONOR_PCT", 0x7dLLU },
{ "scx_consts", "SCX_BYPASS_LB_MIN_DELTA_DIV", 0x4LLU },
{ "scx_consts", "SCX_BYPASS_LB_BATCH", 0x100LLU },
{ "scx_consts", "SCX_REENQ_MAX_REPEAT", 0x100LLU },
{ "scx_consts", "SCX_SUB_MAX_DEPTH", 0x4LLU },
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_RT", 0x0LLU },
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_DL", 0x1LLU },
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_STOP", 0x2LLU },
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_UNKNOWN", 0x3LLU },
{ "scx_deq_flags", "SCX_DEQ_SLEEP", 0x1LLU },
{ "scx_deq_flags", "SCX_DEQ_CORE_SCHED_EXEC", 0x100000000LLU },
{ "scx_deq_flags", "SCX_DEQ_SCHED_CHANGE", 0x200000000LLU },
{ "scx_dsp_verdict", "SCX_DSP_NONE", 0x0LLU },
{ "scx_dsp_verdict", "SCX_DSP_LOCAL", 0x1LLU },
{ "scx_dsp_verdict", "SCX_DSP_PREV", 0x2LLU },
{ "scx_dsp_verdict", "SCX_DSP_RETRY", 0x3LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_FLAG_BUILTIN", 0x8000000000000000LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_FLAG_LOCAL_ON", 0x4000000000000000LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_INVALID", 0x8000000000000000LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_GLOBAL", 0x8000000000000001LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL", 0x8000000000000002LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_BYPASS", 0x8000000000000003LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_REJECT", 0x8000000000000004LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_RESCUE", 0x8000000000000005LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL_ON", 0xc000000000000000LLU },
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL_CPU_MASK", 0xffffffffLLU },
{ "scx_dsq_iter_flags", "SCX_DSQ_ITER_REV", 0x10000LLU },
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_SLICE", 0x40000000LLU },
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_VTIME", 0x80000000LLU },
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_USER_FLAGS", 0x10000LLU },
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_ALL_FLAGS", 0xc0010000LLU },
{ "scx_dsq_lnode_flags", "SCX_DSQ_LNODE_ITER_CURSOR", 0x1LLU },
{ "scx_dsq_lnode_flags", "__SCX_DSQ_LNODE_PRIV_SHIFT", 0x10LLU },
{ "scx_enable_state", "SCX_ENABLING", 0x0LLU },
{ "scx_enable_state", "SCX_ENABLED", 0x1LLU },
{ "scx_enable_state", "SCX_DISABLING", 0x2LLU },
{ "scx_enable_state", "SCX_DISABLED", 0x3LLU },
{ "scx_enq_flags", "SCX_ENQ_WAKEUP", 0x1LLU },
{ "scx_enq_flags", "SCX_ENQ_HEAD", 0x10000LLU },
{ "scx_enq_flags", "SCX_ENQ_CPU_SELECTED", 0x100000LLU },
{ "scx_enq_flags", "SCX_ENQ_PREEMPT", 0x100000000LLU },
{ "scx_enq_flags", "SCX_ENQ_IMMED", 0x200000000LLU },
{ "scx_enq_flags", "SCX_ENQ_RESCUE", 0x400000000LLU },
{ "scx_enq_flags", "SCX_ENQ_REENQ", 0x10000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_LAST", 0x20000000000LLU },
{ "scx_enq_flags", "__SCX_ENQ_INTERNAL_MASK", 0xff00000000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_CLEAR_OPSS", 0x100000000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_DSQ_PRIQ", 0x200000000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_NESTED", 0x400000000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_GDSQ_FALLBACK", 0x800000000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_IGNORE_CAPS", 0x1000000000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_APPLY_SLICE", 0x2000000000000000LLU },
{ "scx_enq_flags", "SCX_ENQ_SLICE_DFL", 0x4000000000000000LLU },
{ "scx_ent_dsq_flags", "SCX_TASK_DSQ_ON_PRIQ", 0x1LLU },
{ "scx_ent_flags", "SCX_TASK_QUEUED", 0x1LLU },
{ "scx_ent_flags", "SCX_TASK_IN_CUSTODY", 0x2LLU },
{ "scx_ent_flags", "SCX_TASK_RESET_RUNNABLE_AT", 0x4LLU },
{ "scx_ent_flags", "SCX_TASK_DEQD_FOR_SLEEP", 0x8LLU },
{ "scx_ent_flags", "SCX_TASK_SUB_INIT", 0x10LLU },
{ "scx_ent_flags", "SCX_TASK_IMMED", 0x20LLU },
{ "scx_ent_flags", "SCX_TASK_PROTECTED", 0x40LLU },
{ "scx_ent_flags", "SCX_TASK_STATE_SHIFT", 0x8LLU },
{ "scx_ent_flags", "SCX_TASK_STATE_BITS", 0x3LLU },
{ "scx_ent_flags", "SCX_TASK_STATE_MASK", 0x700LLU },
{ "scx_ent_flags", "SCX_TASK_NONE", 0x0LLU },
{ "scx_ent_flags", "SCX_TASK_INIT_BEGIN", 0x100LLU },
{ "scx_ent_flags", "SCX_TASK_INIT", 0x200LLU },
{ "scx_ent_flags", "SCX_TASK_READY", 0x300LLU },
{ "scx_ent_flags", "SCX_TASK_ENABLED", 0x400LLU },
{ "scx_ent_flags", "SCX_TASK_DEAD", 0x500LLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_SHIFT", 0xcLLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_BITS", 0x3LLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_MASK", 0x7000LLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_NONE", 0x0LLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_KFUNC", 0x1000LLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_IMMED", 0x2000LLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_PREEMPTED", 0x3000LLU },
{ "scx_ent_flags", "SCX_TASK_REENQ_CAP", 0x4000LLU },
{ "scx_ent_flags", "SCX_TASK_CURSOR", 0xffffffff80000000LLU },
{ "scx_exit_code", "SCX_ECODE_RSN_HOTPLUG", 0x100000000LLU },
{ "scx_exit_code", "SCX_ECODE_RSN_CGROUP_OFFLINE", 0x200000000LLU },
{ "scx_exit_code", "SCX_ECODE_ACT_RESTART", 0x1000000000000LLU },
{ "scx_exit_flags", "SCX_EFLAG_INITIALIZED", 0x1LLU },
{ "scx_exit_kind", "SCX_EXIT_NONE", 0x0LLU },
{ "scx_exit_kind", "SCX_EXIT_DONE", 0x1LLU },
{ "scx_exit_kind", "SCX_EXIT_UNREG", 0x40LLU },
{ "scx_exit_kind", "SCX_EXIT_UNREG_BPF", 0x41LLU },
{ "scx_exit_kind", "SCX_EXIT_UNREG_KERN", 0x42LLU },
{ "scx_exit_kind", "SCX_EXIT_SYSRQ", 0x43LLU },
{ "scx_exit_kind", "SCX_EXIT_PARENT", 0x44LLU },
{ "scx_exit_kind", "SCX_EXIT_PARENT_KILL", 0x45LLU },
{ "scx_exit_kind", "SCX_EXIT_ERROR", 0x400LLU },
{ "scx_exit_kind", "SCX_EXIT_ERROR_BPF", 0x401LLU },
{ "scx_exit_kind", "SCX_EXIT_ERROR_STALL", 0x402LLU },
{ "scx_exit_kind", "SCX_EXIT_ERROR_REENQ", 0x403LLU },
{ "scx_exit_kind", "SCX_EXIT_ERROR_RESCUE", 0x404LLU },
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_UNLOCKED", 0x1LLU },
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_INIT_CIDS", 0x2LLU },
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_CPU_RELEASE", 0x4LLU },
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_DISPATCH", 0x8LLU },
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_ENQUEUE", 0x10LLU },
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_SELECT_CPU", 0x20LLU },
{ "scx_kick_flags", "SCX_KICK_IDLE", 0x1LLU },
{ "scx_kick_flags", "SCX_KICK_PREEMPT", 0x2LLU },
{ "scx_kick_flags", "SCX_KICK_WAIT", 0x4LLU },
{ "scx_opi", "SCX_OPI_BEGIN", 0x0LLU },
{ "scx_opi", "SCX_OPI_NORMAL_BEGIN", 0x0LLU },
{ "scx_opi", "SCX_OPI_NORMAL_END", 0x21LLU },
{ "scx_opi", "SCX_OPI_CPU_HOTPLUG_BEGIN", 0x21LLU },
{ "scx_opi", "SCX_OPI_CPU_HOTPLUG_END", 0x23LLU },
{ "scx_opi", "SCX_OPI_END", 0x23LLU },
{ "scx_ops_flags", "SCX_OPS_KEEP_BUILTIN_IDLE", 0x1LLU },
{ "scx_ops_flags", "SCX_OPS_ENQ_LAST", 0x2LLU },
{ "scx_ops_flags", "SCX_OPS_ENQ_EXITING", 0x4LLU },
{ "scx_ops_flags", "SCX_OPS_SWITCH_PARTIAL", 0x8LLU },
{ "scx_ops_flags", "SCX_OPS_ENQ_MIGRATION_DISABLED", 0x10LLU },
{ "scx_ops_flags", "SCX_OPS_ALLOW_QUEUED_WAKEUP", 0x20LLU },
{ "scx_ops_flags", "SCX_OPS_BUILTIN_IDLE_PER_NODE", 0x40LLU },
{ "scx_ops_flags", "SCX_OPS_ALWAYS_ENQ_IMMED", 0x80LLU },
{ "scx_ops_flags", "SCX_OPS_TID_TO_TASK", 0x100LLU },
{ "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x1ffLLU },
{ "scx_ops_flags", "__SCX_OPS_INTERNAL_MASK", 0xff00000000000000LLU },
{ "scx_ops_flags", "SCX_OPS_HAS_CPU_PREEMPT", 0x100000000000000LLU },
{ "scx_ops_state", "SCX_OPSS_NONE", 0x0LLU },
{ "scx_ops_state", "SCX_OPSS_QUEUEING", 0x1LLU },
{ "scx_ops_state", "SCX_OPSS_QUEUED", 0x2LLU },
{ "scx_ops_state", "SCX_OPSS_DISPATCHING", 0x3LLU },
{ "scx_ops_state", "SCX_OPSS_QSEQ_SHIFT", 0x2LLU },
{ "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_CORE", 0x1LLU },
{ "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_IN_NODE", 0x2LLU },
{ "scx_public_consts", "SCX_OPS_NAME_LEN", 0x80LLU },
{ "scx_public_consts", "SCX_SLICE_DFL", 0x1312d00LLU },
{ "scx_public_consts", "SCX_SLICE_BYPASS", 0x4c4b40LLU },
{ "scx_public_consts", "SCX_SLICE_INF", 0xffffffffffffffffLLU },
{ "scx_reenq_flags", "SCX_REENQ_ANY", 0x1LLU },
{ "scx_reenq_flags", "SCX_REENQ_CAP_REVOKE", 0x2LLU },
{ "scx_reenq_flags", "__SCX_REENQ_FILTER_MASK", 0xffffLLU },
{ "scx_reenq_flags", "__SCX_REENQ_USER_MASK", 0x1LLU },
{ "scx_reenq_flags", "SCX_REENQ_TSR_RQ_OPEN", 0x100000000LLU },
{ "scx_reenq_flags", "SCX_REENQ_TSR_NOT_FIRST", 0x200000000LLU },
{ "scx_reenq_flags", "__SCX_REENQ_TSR_MASK", 0xf00000000LLU },
{ "scx_rq_flags", "SCX_RQ_ONLINE", 0x1LLU },
{ "scx_rq_flags", "SCX_RQ_CAN_STOP_TICK", 0x2LLU },
{ "scx_rq_flags", "SCX_RQ_CLK_VALID", 0x20LLU },
{ "scx_rq_flags", "SCX_RQ_BAL_CB_PENDING", 0x40LLU },
{ "scx_rq_flags", "SCX_RQ_SUB_IDLE_RENOTIFY", 0x80LLU },
{ "scx_rq_flags", "SCX_RQ_ROOT_IDLE_RENOTIFY", 0x100LLU },
{ "scx_rq_flags", "SCX_RQ_IN_WAKEUP", 0x10000LLU },
{ "scx_rq_flags", "SCX_RQ_IN_DISPATCH", 0x20000LLU },
{ "scx_sched_pcpu_flags", "SCX_SCHED_PCPU_BYPASSING", 0x1LLU },
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_BITS", 0x2bLLU },
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_BITS", 0x14LLU },
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_MASK", 0x7ffffffffffLLU },
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_SHIFT", 0x2bLLU },
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_MASK", 0xfffffLLU },
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_PENDING", 0x8000000000000000LLU },
{ "scx_tg_flags", "SCX_TG_ONLINE", 0x1LLU },
{ "scx_tg_flags", "SCX_TG_INITED", 0x2LLU },
{ "scx_tg_flags", "SCX_TG_SUB_INIT", 0x4LLU },
{ "scx_wake_flags", "SCX_WAKE_FORK", 0x4LLU },
{ "scx_wake_flags", "SCX_WAKE_TTWU", 0x8LLU },
{ "scx_wake_flags", "SCX_WAKE_SYNC", 0x10LLU },
};
#endif /* __ENUMS_ABI_AUTOGEN_H__ */

View File

@@ -299,6 +299,7 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
u64 now = scx_bpf_now();
u64 nr_to_kick = nr_queued;
s32 i, curr_cpu;
int ret;
curr_cpu = bpf_get_smp_processor_id();
if (timer_pinned && (curr_cpu != central_cpu)) {
@@ -332,7 +333,10 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
scx_bpf_kick_cpu(cpu, SCX_KICK_PREEMPT);
}
bpf_timer_start(timer, TIMER_INTERVAL_NS, BPF_F_TIMER_CPU_PIN);
ret = bpf_timer_start(timer, TIMER_INTERVAL_NS,
timer_pinned ? BPF_F_TIMER_CPU_PIN : 0);
if (ret)
scx_bpf_error("bpf_timer_start failed (%d)", ret);
__sync_fetch_and_add(&nr_timers, 1);
return 0;
}

View File

@@ -937,7 +937,7 @@ void BPF_STRUCT_OPS(fcg_cgroup_move, struct task_struct *p,
if (!(from_cgc = find_cgrp_ctx(from)) || !(to_cgc = find_cgrp_ctx(to)))
return;
delta = time_delta(p->scx.dsq_vtime, from_cgc->tvtime_now);
delta = (s64)(p->scx.dsq_vtime - from_cgc->tvtime_now);
scx_bpf_task_set_dsq_vtime(p, to_cgc->tvtime_now + delta);
}

View File

@@ -1246,7 +1246,8 @@ static int monitor_timerfn(void *map, int *key, struct bpf_timer *timer)
scx_read_event(&events, SCX_EV_BYPASS_ACTIVATE));
}
bpf_timer_start(timer, ONE_SEC_IN_NS, 0);
if (bpf_timer_start(timer, ONE_SEC_IN_NS, 0))
scx_bpf_error("failed to re-arm stats timer");
return 0;
}
@@ -1268,7 +1269,8 @@ struct {
static int lowpri_timerfn(void *map, int *key, struct bpf_timer *timer)
{
scx_bpf_dsq_reenq(LOWPRI_DSQ, 0);
bpf_timer_start(timer, LOWPRI_INTV_NS, 0);
if (bpf_timer_start(timer, LOWPRI_INTV_NS, 0))
scx_bpf_error("failed to re-arm lowpri timer");
return 0;
}
@@ -1747,7 +1749,8 @@ static void rr_advance(void)
static int round_robin_timerfn(void *map, int *key, struct bpf_timer *timer)
{
rr_advance();
bpf_timer_start(timer, round_robin_ns, 0);
if (bpf_timer_start(timer, round_robin_ns, 0))
scx_bpf_error("failed to re-arm round-robin timer");
return 0;
}

View File

@@ -919,7 +919,6 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) {
if (setup_named_v1_root(root, sizeof(root), CG_NAMED_NAME))
ksft_exit_skip("cgroup v2 isn't mounted and could not setup named v1 hierarchy\n");
@@ -932,6 +931,7 @@ int main(int argc, char *argv[])
ksft_exit_skip("Failed to set memory controller\n");
post_v2_setup:
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -832,7 +832,6 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -840,6 +839,7 @@ int main(int argc, char *argv[])
if (cg_write(root, "cgroup.subtree_control", "+cpu"))
ksft_exit_skip("Failed to set cpu controller\n");
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -497,7 +497,6 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -505,6 +504,7 @@ int main(int argc, char *argv[])
if (cg_write(root, "cgroup.subtree_control", "+cpuset"))
ksft_exit_skip("Failed to set cpuset controller\n");
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -797,7 +797,6 @@ check_isolcpus()
EXPECTED_ISOLCPUS=$1
ISCPUS=${CGROUP2}/cpuset.cpus.isolated
ISOLCPUS=$(cat $ISCPUS)
HKICPUS=$(cat /sys/devices/system/cpu/isolated)
LASTISOLCPU=
SCHED_DOMAINS=/sys/kernel/debug/sched/domains
if [[ $EXPECTED_ISOLCPUS = . ]]
@@ -835,11 +834,6 @@ check_isolcpus()
ISOLCPUS=
EXPECTED_ISOLCPUS=$EXPECTED_SDOMAIN
#
# The inverse of HK_TYPE_DOMAIN cpumask in $HKICPUS should match $ISOLCPUS
#
[[ "$ISOLCPUS" != "$HKICPUS" ]] && return 1
#
# Use the sched domain in debugfs to check isolated CPUs, if available
#
@@ -1161,6 +1155,63 @@ test_isolated()
pause 0.05
}
#
# Select an online CPU isolated from scheduler domains at boot.
# $1: test name used in the skip message
#
get_boot_isolated_cpu()
{
TEST_NAME=$1
BOOT_ISOLATED_FILE=/sys/devices/system/cpu/isolated
[[ -r $BOOT_ISOLATED_FILE ]] || {
echo "$TEST_NAME test SKIPPED: boot isolation state unavailable"
return 1
}
BOOT_CPUS=$(cat $BOOT_ISOLATED_FILE)
[[ -n "$BOOT_CPUS" ]] || {
echo "$TEST_NAME test SKIPPED: no boot-isolated CPU"
return 1
}
BOOT_CPU=$(echo "$BOOT_CPUS" | sed -e 's/[,-].*//')
CPU_ONLINE=/sys/devices/system/cpu/cpu${BOOT_CPU}/online
[[ ! -e $CPU_ONLINE || $(cat $CPU_ONLINE) -eq 1 ]] || {
echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is offline"
return 1
}
}
#
# A CPU isolated at boot must stay isolated after it is released by a dynamic
# isolated partition.
#
test_boot_isolated()
{
TEST_NAME="Boot-isolated CPU partition release"
get_boot_isolated_cpu "$TEST_NAME" || return 0
echo "Running $TEST_NAME test ..."
cd $CGROUP2/test
echo member > cpuset.cpus.partition
echo $BOOT_CPU > cpuset.cpus
[[ $(cat cpuset.cpus.effective) = "$BOOT_CPU" ]] || {
echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is unavailable"
echo "" > cpuset.cpus
cd $CGROUP2
return 0
}
test_partition isolated
test_partition member
check_isolcpus "." || {
echo "Boot-isolated CPU $BOOT_CPU was lost after partition release"
exit 1
}
echo "" > cpuset.cpus
cd $CGROUP2
echo "$TEST_NAME test PASSED."
}
#
# Wait for inotify event for the given file and read it
# $1: cgroup file to wait for
@@ -1232,5 +1283,6 @@ trap cleanup 0 2 3 6
run_state_test TEST_MATRIX
run_remote_state_test REMOTE_TEST_MATRIX
test_isolated
test_boot_isolated
test_inotify
echo "All tests PASSED."

View File

@@ -1491,9 +1491,9 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "kselftest.h"
@@ -261,6 +262,59 @@ static int test_cgkill_forkbomb(const char *root)
return ret;
}
/*
* Test that a cgroup that was killed in the past can still be the target
* of clone3(CLONE_INTO_CGROUP): writing cgroup.kill must only kill the
* tasks in the cgroup at the time of the write, not tasks cloned into
* it afterwards.
*/
static int test_cgkill_clone_into_killed(const char *root)
{
pid_t pid;
int cgroup_fd = -EBADF;
int ret = KSFT_FAIL;
char *cgroup = NULL;
cgroup = cg_name(root, "cg_test_clone_into_killed");
if (!cgroup)
goto cleanup;
if (cg_create(cgroup))
goto cleanup;
/* Kill the cgroup while it is still empty. */
if (cg_write(cgroup, "cgroup.kill", "1"))
goto cleanup;
cgroup_fd = dirfd_open_opath(cgroup);
if (cgroup_fd < 0)
goto cleanup;
pid = clone_into_cgroup(cgroup_fd);
if (pid < 0) {
if (errno == ENOSYS)
ret = KSFT_SKIP;
goto cleanup;
}
if (pid == 0)
exit(EXIT_SUCCESS);
/* The child must not be SIGKILLed; it has to exit cleanly. */
if (clone_reap(pid, WEXITED) != EXIT_SUCCESS)
goto cleanup;
ret = KSFT_PASS;
cleanup:
if (cgroup_fd >= 0)
close(cgroup_fd);
if (cgroup)
cg_destroy(cgroup);
free(cgroup);
return ret;
}
#define T(x) { x, #x }
struct cgkill_test {
int (*fn)(const char *root);
@@ -269,6 +323,7 @@ struct cgkill_test {
T(test_cgkill_simple),
T(test_cgkill_tree),
T(test_cgkill_forkbomb),
T(test_cgkill_clone_into_killed),
};
#undef T
@@ -278,9 +333,9 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -145,7 +145,7 @@ static int cg_run_in_subcgroups(const char *parent,
return -1;
}
if (cg_run(child, fn, NULL)) {
if (cg_run(child, fn, arg)) {
cg_destroy(child);
free(child);
return -1;
@@ -426,7 +426,6 @@ int main(int argc, char **argv)
int i;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -441,6 +440,7 @@ int main(int argc, char **argv)
if (cg_write(root, "cgroup.subtree_control", "+memory"))
ksft_exit_skip("Failed to set memory controller\n");
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -1798,7 +1798,6 @@ int main(int argc, char **argv)
page_size = BUF_SIZE;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -1823,6 +1822,7 @@ int main(int argc, char **argv)
ksft_exit_skip("Failed to query cgroup mount option\n");
has_localevents = proc_status;
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -148,7 +148,6 @@ int main(int argc, char **argv)
char root[PATH_MAX];
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -163,6 +162,7 @@ int main(int argc, char **argv)
if (cg_write(root, "cgroup.subtree_control", "+pids"))
ksft_exit_skip("Failed to set pids controller\n");
ksft_set_plan(ARRAY_SIZE(tests));
for (int i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -819,7 +819,6 @@ int main(int argc, char **argv)
page_size = BUF_SIZE;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -836,6 +835,7 @@ int main(int argc, char **argv)
if (cg_write(root, "cgroup.subtree_control", "+memory"))
ksft_exit_skip("Failed to set memory controller\n");
ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:

View File

@@ -78,6 +78,12 @@ def cpumask_str(cpumask):
wq_type_len = 9
def wq_attrs(wq):
try:
return wq.attrs
except AttributeError:
return wq.unbound_attrs
def wq_type_str(wq):
if wq.flags & WQ_BH:
return f'{"bh":{wq_type_len}}'
@@ -85,7 +91,7 @@ def wq_type_str(wq):
if wq.flags & WQ_ORDERED:
return f'{"ordered":{wq_type_len}}'
else:
if wq.attrs.affn_strict:
if wq_attrs(wq).affn_strict:
return f'{"unbound,S":{wq_type_len}}'
else:
return f'{"unbound":{wq_type_len}}'
@@ -206,7 +212,7 @@ for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(
print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='')
if wq.flags & WQ_UNBOUND:
print(f' {cpumask_str(wq.attrs.cpumask):{ucpus_len}}', end='')
print(f' {cpumask_str(wq_attrs(wq).cpumask):{ucpus_len}}', end='')
else:
print(f' {"":{ucpus_len}}', end='')