6.18.46-rc1 review
Greg Kroah-Hartman
maintainerPATCH 145/217 mm: replace pmd_to_swp_entry() with softleaf_from_pmd()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
[ Upstream commit 0ac881efe16468503e8c1e7d8a7210b75f027ce3 ]
Introduce softleaf_from_pmd() to do the equivalent operation for PMDs that softleaf_from_pte() fulfils, and cascade changes through code base accordingly, introducing helpers as necessary.
We are then able to eliminate pmd_to_swp_entry(), is_pmd_migration_entry(), is_pmd_device_private_entry() and is_pmd_non_present_folio_entry().
This further establishes the use of leaf operations throughout the code base and further establishes the foundations for eliminating is_swap_pmd().
No functional change intended.
Stable-dep-of: 4165b7d1c45c ("userfaultfd: wait on source PMD during UFFDIO_MOVE")
--- fs/proc/task_mmu.c | 2 +- include/linux/swapops.h | 4 ++-- mm/hmm.c | 2 +- mm/huge_memory.c | 20 ++++++++++---------- mm/khugepaged.c | 2 +- mm/madvise.c | 2 +- mm/memory.c | 4 ++-- mm/mempolicy.c | 2 +- mm/migrate.c | 2 +- mm/page_vma_mapped.c | 2 +- mm/pagewalk.c | 2 +- mm/pgtable-generic.c | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-)
fs/proc/task_mmu.c +1 -1
@@ -2031,7 +2031,7 @@ static int pagemap_pmd_range_thp(pmd_t * flags |=PM_SOFT_DIRTY ;if (pmd_swp_uffd_wp (pmd)) flags |=PM_UFFD_WP ; -VM_WARN_ON_ONCE (!is_pmd_migration_entry (pmd)); +VM_WARN_ON_ONCE (!pmd_is_migration_entry (pmd)); page =pfn_swap_entry_to_page (entry); }
include/linux/swapops.h +2 -2
@@ -570,7 +570,7 @@ static inline pmd_t swp_entry_to_pmd(swpreturn __swp_entry_to_pmd (arch_entry); } -static inline int is_pmd_migration_entry (pmd_t pmd) +static inline int pmd_is_migration_entry (pmd_t pmd) {return is_swap_pmd (pmd) &&is_migration_entry (pmd_to_swp_entry (pmd)); }
@@ -599,7 +599,7 @@ static inline pmd_t swp_entry_to_pmd(swpreturn __pmd (0 ); } -static inline int is_pmd_migration_entry (pmd_t pmd) +static inline int pmd_is_migration_entry (pmd_t pmd) {return 0 ; }
mm/hmm.c +1 -1
@@ -407,7 +407,7 @@ again:if (pmd_none (pmd))return hmm_vma_walk_hole (start, end,-1 , walk); -if (thp_migration_supported () &&is_pmd_migration_entry (pmd)) { +if (thp_migration_supported () &&pmd_is_migration_entry (pmd)) {if (hmm_range_need_fault (hmm_vma_walk, hmm_pfns, npages,0 )) { hmm_vma_walk->last = addr;pmd_migration_entry_wait (walk->mm, pmdp);
mm/huge_memory.c +10 -10
@@ -1722,7 +1722,7 @@ int copy_huge_pmd(struct mm_struct *dst_if (unlikely (is_swap_pmd (pmd))) {swp_entry_t entry =pmd_to_swp_entry (pmd); -VM_BUG_ON (!is_pmd_migration_entry (pmd)); +VM_BUG_ON (!pmd_is_migration_entry (pmd));if (!is_readable_migration_entry (entry)) { entry =make_readable_migration_entry (swp_offset (entry));
@@ -2132,7 +2132,7 @@ bool madvise_free_huge_pmd(struct mmu_gaif (unlikely (!pmd_present (orig_pmd))) {VM_BUG_ON (thp_migration_supported () && - !is_pmd_migration_entry (orig_pmd)); + !pmd_is_migration_entry (orig_pmd)); goto out; }
@@ -2233,7 +2233,7 @@ int zap_huge_pmd(struct mmu_gather *tlb, }else if (thp_migration_supported ()) {swp_entry_t entry; -VM_BUG_ON (!is_pmd_migration_entry (orig_pmd)); +VM_BUG_ON (!pmd_is_migration_entry (orig_pmd)); entry =pmd_to_swp_entry (orig_pmd); folio =pfn_swap_entry_folio (entry); flush_needed =0 ;
@@ -2283,7 +2283,7 @@ static inline int pmd_move_must_withdrawstatic pmd_t move_soft_dirty_pmd (pmd_t pmd) {#ifdef CONFIG_MEM_SOFT_DIRTY -if (unlikely (is_pmd_migration_entry (pmd))) +if (unlikely (pmd_is_migration_entry (pmd))) pmd =pmd_swp_mksoft_dirty (pmd);else if (pmd_present (pmd)) pmd =pmd_mksoft_dirty (pmd);
@@ -2386,7 +2386,7 @@ int change_huge_pmd(struct mmu_gather *tstruct folio *folio =pfn_swap_entry_folio (entry);pmd_t newpmd; -VM_BUG_ON (!is_pmd_migration_entry (*pmd)); +VM_BUG_ON (!pmd_is_migration_entry (*pmd));if (is_writable_migration_entry (entry)) {/* * A protection check is difficult so
@@ -2574,7 +2574,7 @@ int move_pages_huge_pmd(struct mm_structif (!pmd_trans_huge (src_pmdval)) {spin_unlock (src_ptl); -if (is_pmd_migration_entry (src_pmdval)) { +if (pmd_is_migration_entry (src_pmdval)) {pmd_migration_entry_wait (mm, &src_pmdval);return -EAGAIN ; }
@@ -2863,7 +2863,7 @@ static void __split_huge_pmd_locked(struVM_BUG_ON (haddr & ~HPAGE_PMD_MASK );VM_BUG_ON_VMA (vma->vm_start > haddr, vma);VM_BUG_ON_VMA (vma->vm_end < haddr +HPAGE_PMD_SIZE , vma); -VM_BUG_ON (!is_pmd_migration_entry (*pmd) && !pmd_trans_huge (*pmd)); +VM_BUG_ON (!pmd_is_migration_entry (*pmd) && !pmd_trans_huge (*pmd));count_vm_event (THP_SPLIT_PMD );
@@ -2877,7 +2877,7 @@ static void __split_huge_pmd_locked(struzap_deposited_table (mm, pmd);if (!vma_is_dax (vma) &&vma_is_special_huge (vma))return ; -if (unlikely (is_pmd_migration_entry (old_pmd))) { +if (unlikely (pmd_is_migration_entry (old_pmd))) {swp_entry_t entry; entry =pmd_to_swp_entry (old_pmd);
@@ -2913,7 +2913,7 @@ static void __split_huge_pmd_locked(strureturn __split_huge_zero_page_pmd (vma, haddr, pmd); } - pmd_migration =is_pmd_migration_entry (*pmd); + pmd_migration =pmd_is_migration_entry (*pmd);if (unlikely (pmd_migration)) {swp_entry_t entry;
@@ -3073,7 +3073,7 @@ void split_huge_pmd_locked(struct vm_are pmd_t *pmd, bool freeze) {VM_WARN_ON_ONCE (!IS_ALIGNED (address,HPAGE_PMD_SIZE )); -if (pmd_trans_huge (*pmd) ||is_pmd_migration_entry (*pmd)) +if (pmd_trans_huge (*pmd) ||pmd_is_migration_entry (*pmd))__split_huge_pmd_locked (vma, pmd, address, freeze); }
mm/khugepaged.c +1 -1
@@ -939,7 +939,7 @@ static inline int check_pmd_state(pmd_t* collapse it. Migration success or failure will eventually end * up with a present PMD mapping a folio again. */ -if (is_pmd_migration_entry (pmde)) +if (pmd_is_migration_entry (pmde))return SCAN_PMD_MAPPED ;if (!pmd_present (pmde))return SCAN_PMD_NULL ;
mm/madvise.c +1 -1
@@ -391,7 +391,7 @@ static int madvise_cold_or_pageout_pte_rif (unlikely (!pmd_present (orig_pmd))) {VM_BUG_ON (thp_migration_supported () && - !is_pmd_migration_entry (orig_pmd)); + !pmd_is_migration_entry (orig_pmd)); goto huge_unlock; }
mm/memory.c +2 -2
@@ -6327,8 +6327,8 @@ retry_pud:if (unlikely (is_swap_pmd (vmf.orig_pmd))) {VM_BUG_ON (thp_migration_supported () && - !is_pmd_migration_entry (vmf.orig_pmd)); -if (is_pmd_migration_entry (vmf.orig_pmd)) + !pmd_is_migration_entry (vmf.orig_pmd)); +if (pmd_is_migration_entry (vmf.orig_pmd))pmd_migration_entry_wait (mm, vmf.pmd);return 0 ; }
mm/mempolicy.c +1 -1
@@ -651,7 +651,7 @@ static void queue_folios_pmd(pmd_t *pmd,struct folio *folio;struct queue_pages *qp = walk->private; -if (unlikely (is_pmd_migration_entry (*pmd))) { +if (unlikely (pmd_is_migration_entry (*pmd))) { qp->nr_failed++;return ; }
mm/migrate.c +1 -1
@@ -542,7 +542,7 @@ void pmd_migration_entry_wait(struct mm_spinlock_t *ptl; ptl =pmd_lock (mm, pmd); -if (!is_pmd_migration_entry (*pmd)) +if (!pmd_is_migration_entry (*pmd)) goto unlock;migration_entry_wait_on_locked (pmd_to_swp_entry (*pmd), ptl);return ;
mm/page_vma_mapped.c +1 -1
@@ -246,7 +246,7 @@ restart:*/ pmde =pmdp_get_lockless (pvmw->pmd); -if (pmd_trans_huge (pmde) ||is_pmd_migration_entry (pmde)) { +if (pmd_trans_huge (pmde) ||pmd_is_migration_entry (pmde)) { pvmw->ptl =pmd_lock (mm, pvmw->pmd); pmde = *pvmw->pmd;if (!pmd_present (pmde)) {
mm/pagewalk.c +1 -1
@@ -991,7 +991,7 @@ pmd_table: goto found; } }else if ((flags &FW_MIGRATION ) && -is_pmd_migration_entry (pmd)) { +pmd_is_migration_entry (pmd)) {swp_entry_t entry =pmd_to_swp_entry (pmd); page =pfn_swap_entry_to_page (entry);
mm/pgtable-generic.c +1 -1
@@ -291,7 +291,7 @@ pte_t *___pte_offset_map(pmd_t *pmd, unsif (pmdvalp) *pmdvalp = pmdval; -if (unlikely (pmd_none (pmdval) ||is_pmd_migration_entry (pmdval))) +if (unlikely (pmd_none (pmdval) ||pmd_is_migration_entry (pmdval))) goto nomap;if (unlikely (pmd_trans_huge (pmdval))) goto nomap;
Greg Kroah-Hartman
maintainerPATCH 146/217 userfaultfd: wait on source PMD during UFFDIO_MOVE
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Usama Arif <[email protected]>
[ Upstream commit 4165b7d1c45c2da0dfefe528f8d1fb7d79f0d344 ]
move_pages_huge_pmd() snapshots src_pmdval under src_ptl, drops the lock, and, for migration entries, waits with pmd_migration_entry_wait().
Passing &src_pmdval is wrong. pmd_migration_entry_wait() must lock and re-read the real page-table PMD; on split-PMD-lock kernels, a stack address also resolves to the wrong lock. softleaf_entry_wait_on_locked() then waits without a folio reference, which is safe only while serialized against migration-entry removal by the real PT lock.
Pass src_pmd, matching __handle_mm_fault() and hmm_vma_walk_pmd().
--- mm/huge_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/huge_memory.c +1 -1
@@ -2575,7 +2575,7 @@ int move_pages_huge_pmd(struct mm_structif (!pmd_trans_huge (src_pmdval)) {spin_unlock (src_ptl);if (pmd_is_migration_entry (src_pmdval)) { -pmd_migration_entry_wait (mm, &src_pmdval); +pmd_migration_entry_wait (mm, src_pmd);return -EAGAIN ; }return -ENOENT ;
Greg Kroah-Hartman
maintainerPATCH 147/217 KVM: x86: Cancel delayed I/O APIC EOI handling before destroying vCPUs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit 9910e835580fef3bef53b70241dd00c4bffad693 ]
Cancel (and flush) the I/O APIC's delayed EOI handling work during the "pre VM destroy" phase, before vCPUs are destroyed, as processing the EOI broadcast will inject another IRQ if the line is asserted, i.e. will try to deliver an IRQ to the target vCPU(s). Canceling the work after vCPUs are destroyed leads to UAF if the delayed work is processed after vCPUs are destroyed.
BUG: KASAN: slab-use-after-free in __kvm_irq_delivery_to_apic_fast+0x9bf/0xa20 arch/x86/kvm/lapic.c:1250 Read of size 8 at addr ffff8880499abea0 by task kworker/1:2/1218 CPU: 1 UID: 0 PID: 1218 Comm: kworker/1:2 Not tainted 7.1.0-rc7 #5 PREEMPT(lazy) Hardware name: QEMU Ubuntu 25.10 PC v2 (i440FX + PIIX, + 10.1 machine, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: events kvm_ioapic_eoi_inject_work Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 print_report+0x139/0x4ad mm/kasan/report.c:482 kasan_report+0xe4/0x1d0 mm/kasan/report.c:595 __kvm_irq_delivery_to_apic_fast+0x9bf/0xa20 arch/x86/kvm/lapic.c:1250 __kvm_irq_delivery_to_apic+0xd8/0xbf0 arch/x86/kvm/lapic.c:1345 kvm_irq_delivery_to_apic arch/x86/kvm/lapic.h:129 ioapic_service+0x308/0x590 arch/x86/kvm/ioapic.c:492 kvm_ioapic_eoi_inject_work+0x13c/0x190 arch/x86/kvm/ioapic.c:532 process_one_work+0xa59/0x19a0 kernel/workqueue.c:3314 process_scheduled_works kernel/workqueue.c:3397 worker_thread+0x5eb/0xe50 kernel/workqueue.c:3478 kthread+0x370/0x450 kernel/kthread.c:436 ret_from_fork+0x72b/0xd30 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK>
Note, the VM is unreachable once kvm_destroy_vm() starts, and scheduling new work via kvm_ioapic_send_eoi() can only be done via KVM_RUN, i.e. requires a live vCPU.
Alternatively, KVM could simply destroy the I/O APIC during the "pre" phase of VM destruction, but that gets more than a bit sketchy as KVM expects the I/O APIC to exist if ioapic_in_kernel() is true, and nested virtualization in particular has a bad habit of touching VM-scope state during vCPU destruction. E.g. attempting to free the PIC during the pre phase would lead to a NULL pointer dereference in kvm_cpu_has_extint(), and it's not hard to imagine the I/O APIC having a similar flaw.
Message-ID: <[email protected]>
--- arch/x86/kvm/x86.c | 6 ++++++ 1 file changed, 6 insertions(+)
arch/x86/kvm/x86.c +6 -0
@@ -13320,12 +13320,18 @@ void kvm_arch_pre_destroy_vm(struct kvm* iterating over vCPUs in a different task while vCPUs are being freed * is unsafe, i.e. will lead to use-after-free. The PIT also needs to * be stopped before IRQ routing is freed. +* +* Do NOT free the in-kernel PIC or I/O APIC here (but as above, make +* sure to flush any background work), as KVM expects interrupt routing +* structures to be valid until vCPUs are destroyed. */ cancel_delayed_work_sync (&kvm->arch.kvmclock_sync_work);cancel_delayed_work_sync (&kvm->arch.kvmclock_update_work);#ifdef CONFIG_KVM_IOAPIC kvm_free_pit (kvm); +if (kvm->arch.vioapic) +cancel_delayed_work_sync (&kvm->arch.vioapic->eoi_inject);#endif kvm_mmu_pre_destroy_vm (kvm);
Greg Kroah-Hartman
maintainerPATCH 148/217 ASoC: tas2562: Validate values for volume writes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Brown <[email protected]>
[ Upstream commit 8fb41964f7e4e4207c8999af2056894caa7a252a ]
tas2562_volume_control_put() does not do any validation of the control value written by userspace, it uses it to look up a value in a fixed size array which can easily be overflowed and then writes whatever value it gets back to the device. Add validation that we are loading a value we have in the array.
--- sound/soc/codecs/tas2562.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
sound/soc/codecs/tas2562.c +7 -2
@@ -472,10 +472,15 @@ static int tas2562_volume_control_put(st {struct snd_soc_component *component =snd_soc_kcontrol_component (kcontrol);struct tas2562_data *tas2562 =snd_soc_component_get_drvdata (component); -int ret; +int ret, index;u32 reg_val; - reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0 ]/2 ]; + index = ucontrol->value.integer.value[0 ] /2 ; +if (index <0 || index >=ARRAY_SIZE (float_vol_db_lookup)) +return -EINVAL ; + + reg_val = float_vol_db_lookup[index]; +/* * The device applies the 32-bit coefficient to the playback path on * the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the
Greg Kroah-Hartman
maintainerPATCH 149/217 ata: libata-scsi: terminate deferred commands on time out
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
[ Upstream commit 2e1d2e65e773d67dab163127f11a47dab0fbca9f ]
If a command times out while we have deferred non-NCQ commands waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred commands are never issued nor completed, thus leaving the SCSI host in a busy state (shost->host_failed != scsi_host_busy(shost)) which prevents the SCSI EH task from being woken up. Eventually, when the deferred commands also time out, the SCSI EH task is woken up and the timeout processing occurs.
Avoid this unnecessary SCSI EH task wake-up additional time by scheduling a retry of all waiting deferred QCs, using the eh_timed_out SCSI host template operation. The function ata_scsi_eh_timed_out() is introduced to implement this operation.
However, terminating deferred commands with DID_REQUEUE to force a retry by calling the function ata_scsi_requeue_deferred_qc() may still keep the SCSI host in a busy state because the block layer may immediately re-issue these commands. The solution to this is to schedule libata EH for the port which suffered the command timeout to prevent accepting any new command. ata_scsi_requeue_deferred_qc() is modified to add a call to ata_port_schedule_eh() for this purpose.
In addition to this change, ata_scsi_requeue_deferred_qc() is also modified to take a new timedout_scmd scsi command argument which indicates the SCSI command that timed out. With this additional argument, ata_scsi_requeue_deferred_qc() can now also terminate with DID_TIME_OUT any timed out deferred qc, which simplifies ata_scsi_cmd_error_handler(). In this case, ata_scsi_requeue_deferred_qc() returns SCSI_EH_DONE, with this return value propagated back to the ata_scsi_eh_timed_out() operation to indicate to scsi_timeout() that the timed out command was handled and no further processing is needed.
For non-timed out deferred qc that need to be retried, ata_scsi_requeue_deferred_qc() returns SCSI_EH_NOT_HANDLED, thus indicating to scsi_timeout() that the timed out command needs to go through the SCSI EH (and libata EH) processing by adding it to the EH work queue with scsi_eh_scmd_add().
One side effect of these changes is that the function atapi_qc_complete() needs to be modified to ensure that a deferred ATAPI command that needs to be retried is completed with DID_REQUEUE instead of the default SAM_STAT_GOOD status, and a command that timed out is completed with DID_TIME_OUT instead of SAM_STAT_CHECK_CONDITION.
--- drivers/ata/libata-eh.c | 29 ++--------- drivers/ata/libata-scsi.c | 115 ++++++++++++++++++++++++++++++++++++++++------ drivers/ata/libata.h | 3 - include/linux/libata.h | 2 4 files changed, 110 insertions(+), 39 deletions(-)
drivers/ata/libata-eh.c +6 -23
@@ -648,29 +648,12 @@ void ata_scsi_cmd_error_handler(struct Sset_host_byte (scmd,DID_OK );ata_qc_for_each_raw (ap, qc, i) { -if (qc->scsicmd != scmd) -continue ; -if ((qc->flags &ATA_QCFLAG_ACTIVE ) || - qc == qc->dev->link->deferred_qc) +if (qc->scsicmd == scmd && + qc->flags &ATA_QCFLAG_ACTIVE )break ; } -if (i <ATA_MAX_QUEUE && qc == qc->dev->link->deferred_qc) { -/* -* This is a deferred command that timed out while -* waiting for the command queue to drain. Since the qc -* is not active yet (deferred_qc is still set, so the -* deferred qc work has not issued the command yet), -* simply signal the timeout by finishing the SCSI -* command and clear the deferred qc to prevent the -* deferred qc work from issuing this qc. -*/ -WARN_ON_ONCE (qc->flags &ATA_QCFLAG_ACTIVE ); - qc->dev->link->deferred_qc =NULL ; -cancel_work (&qc->dev->link->deferred_qc_work); -set_host_byte (scmd,DID_TIME_OUT ); -scsi_eh_finish_cmd (scmd, &ap->eh_done_q); - }else if (i <ATA_MAX_QUEUE ) { +if (i <ATA_MAX_QUEUE ) {/* the scmd has an associated qc */ if (!(qc->flags &ATA_QCFLAG_EH )) {/* which hasn't failed yet, timeout */
@@ -944,10 +927,10 @@ static void ata_eh_set_pending(struct at ap->pflags |=ATA_PFLAG_EH_PENDING ;/* -* If we have a deferred qc, requeue it so that it is retried once EH -* completes. +* If we have deferred QCs, requeue them so that the SCSI EH task can +* run. */ -ata_scsi_requeue_deferred_qc (ap); +ata_scsi_requeue_deferred_qc (ap,NULL );if (!fastdrain)return ;
drivers/ata/libata-scsi.c +100 -15
150 lines not on this page · raw message
drivers/ata/libata.h +2 -1
@@ -172,7 +172,8 @@ int ata_scsi_dev_config(struct scsi_devi struct ata_device *dev);int __ata_scsi_queuecmd (struct scsi_cmnd *scmd,struct ata_device *dev);void ata_scsi_deferred_qc_work (struct work_struct *work); -void ata_scsi_requeue_deferred_qc (struct ata_port *ap); +enum scsi_timeout_action ata_scsi_requeue_deferred_qc (struct ata_port *ap, +struct scsi_cmnd *scmd);/* libata-eh.c */ extern unsigned int ata_internal_cmd_timeout (struct ata_device *dev,u8 cmd);
include/linux/libata.h +2 -0
@@ -1151,6 +1151,7 @@ extern int ata_scsi_ioctl(struct scsi_de#define ATA_SCSI_COMPAT_IOCTL /* empty */ #endif extern int ata_scsi_queuecmd (struct Scsi_Host *h,struct scsi_cmnd *cmd); +enum scsi_timeout_action ata_scsi_eh_timed_out (struct scsi_cmnd *cmd);#if IS_REACHABLE (CONFIG_ATA )bool ata_scsi_dma_need_drain (struct request *rq);#else
@@ -1460,6 +1461,7 @@ extern const struct attribute_group *ata .ioctl = ata_scsi_ioctl, \ATA_SCSI_COMPAT_IOCTL \ .queuecommand = ata_scsi_queuecmd, \ + .eh_timed_out = ata_scsi_eh_timed_out, \ .dma_need_drain = ata_scsi_dma_need_drain, \ .this_id = ATA_SHT_THIS_ID, \ .emulated = ATA_SHT_EMULATED, \
Greg Kroah-Hartman
maintainerPATCH 150/217 binfmt_misc: dont leak the user namespace when the mount fails
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit b8206f516fe7cbe785cf44bf09c17c438d7c3cad ]
bm_get_tree() takes a reference to the user namespace and hands it to get_tree_keyed() as the sget key. sget_fc() moves that reference into sb->s_fs_info and clears fc->s_fs_info, so from that point on the superblock owns it and bm_free() doesn't see it anymore.
The superblock drops it in ->put_super(). But generic_shutdown_super() only calls ->put_super() from inside the if (sb->s_root) branch, so nothing releases it when bm_fill_super() fails:
- The kzalloc_obj() failure leaves s_root NULL and the whole branch is skipped.
- A simple_fill_super() failure in the file loop leaves s_root set, but s_op still points at simple_super_operations, which has no ->put_super(). bm_fill_super() installs s_ops only once simple_fill_super() returned success, and installing it earlier wouldn't help either because simple_fill_super() overwrites s_op.
Either way vfs_get_super() calls deactivate_locked_super() and the reference is gone for good. binfmt_misc mounts are available in a user namespace and both the inode and the dentry cache are SLAB_ACCOUNT, so an unprivileged caller under a tight memory cgroup can fail simple_fill_super() on demand and leak one user namespace per attempt.
Drop the reference in ->kill_sb() instead, which runs unconditionally, the same way nfsd and rpc_pipefs release their keyed s_fs_info.
That also stops ->put_super() from clearing s_fs_info while the superblock is still on @fs_supers. generic_shutdown_super() leaves it there on purpose so that sget_fc() keeps finding it until kill_sb() has run, but a NULL s_fs_info makes test_keyed_super() miss it, so a concurrent mount for the same user namespace skips the grab_super() wait and creates a second superblock for a namespace that is still being torn down.
--- fs/binfmt_misc.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-)
fs/binfmt_misc.c +15 -17
@@ -928,18 +928,9 @@ static const struct file_operations bm_s/* Superblock handling */ -static void bm_put_super (struct super_block *sb) -{ -struct user_namespace *user_ns = sb->s_fs_info; - - sb->s_fs_info =NULL ; -put_user_ns (user_ns); -} -static const struct super_operations s_ops = { .statfs = simple_statfs, .evict_inode = bm_evict_inode, - .put_super = bm_put_super, };static int bm_fill_super (struct super_block *sb,struct fs_context *fc)
@@ -998,13 +989,12 @@ static int bm_fill_super(struct super_bl/* * When the binfmt_misc superblock for this userns is shutdown * ->enabled might have been set to false and we don't reinitialize -* ->enabled again in put_super() as someone might already be mounting -* binfmt_misc again. It also would be pointless since by the time -* ->put_super() is called we know that the binary type list for this -* bintfmt_misc mount is empty making load_misc_binary() return -* -ENOEXEC independent of whether ->enabled is true. Instead, if -* someone mounts binfmt_misc for the first time or again we simply -* reset ->enabled to true. +* ->enabled again during shutdown as someone might already be mounting +* binfmt_misc again. It also would be pointless since by then we know +* that the binary type list for this binfmt_misc mount is empty making +* load_misc_binary() return -ENOEXEC independent of whether ->enabled +* is true. Instead, if someone mounts binfmt_misc for the first time or +* again we simply reset ->enabled to true. */ misc->enabled = true;
@@ -1030,6 +1020,14 @@ static const struct fs_context_operation .get_tree = bm_get_tree, }; +static void bm_kill_sb (struct super_block *sb) +{ +struct user_namespace *user_ns = sb->s_fs_info; + +kill_litter_super (sb); +put_user_ns (user_ns); +} +static int bm_init_fs_context (struct fs_context *fc) { fc->ops = &bm_context_ops;
@@ -1046,7 +1044,7 @@ static struct file_system_type bm_fs_typ .name = "binfmt_misc", .init_fs_context = bm_init_fs_context, .fs_flags = FS_USERNS_MOUNT, - .kill_sb = kill_litter_super, + .kill_sb = bm_kill_sb, };MODULE_ALIAS_FS ("binfmt_misc" );
Greg Kroah-Hartman
maintainerPATCH 151/217 can: rcar_canfd: Invert reset assert order
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geert Uytterhoeven <[email protected]>
[ Upstream commit 41c13eaf39932fc79aa1ac245a9b97090fe23d5e ]
The two resets are asserted during cleanup in the same order as they were deasserted during probe. Invert the order to restore symmetry.
Stable-dep-of: bef9004c5b91 ("can: rcar_canfd: change the initializing flow for clocks and resets")
--- drivers/net/can/rcar/rcar_canfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/rcar/rcar_canfd.c +2 -2
@@ -2202,8 +2202,8 @@ fail_mode: fail_clk:clk_disable_unprepare (gpriv->clkp); fail_reset: -reset_control_assert (gpriv->rstc1);reset_control_assert (gpriv->rstc2); +reset_control_assert (gpriv->rstc1); fail_dev:return err; }
@@ -2224,8 +2224,8 @@ static void rcar_canfd_remove(struct pla/* Enter global sleep mode */ rcar_canfd_set_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GSLPR );clk_disable_unprepare (gpriv->clkp); -reset_control_assert (gpriv->rstc1);reset_control_assert (gpriv->rstc2); +reset_control_assert (gpriv->rstc1); }static int __maybe_unused rcar_canfd_suspend (struct device *dev)
Greg Kroah-Hartman
maintainerPATCH 152/217 can: rcar_canfd: Invert global vs. channel teardown
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geert Uytterhoeven <[email protected]>
[ Upstream commit 790ec4c453890f1221ea595674a1206bbee41dc4 ]
Global state is initialized and torn down before per-channel state. Invert the order to restore symmetry.
Stable-dep-of: bef9004c5b91 ("can: rcar_canfd: change the initializing flow for clocks and resets")
--- drivers/net/can/rcar/rcar_canfd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/net/can/rcar/rcar_canfd.c +3 -3
@@ -2213,14 +2213,14 @@ static void rcar_canfd_remove(struct plastruct rcar_canfd_global *gpriv =platform_get_drvdata (pdev);u32 ch; -rcar_canfd_reset_controller (gpriv); -rcar_canfd_disable_global_interrupts (gpriv); -for_each_set_bit (ch, &gpriv->channels_mask, gpriv->info->max_channels) {rcar_canfd_disable_channel_interrupts (gpriv->ch[ch]);rcar_canfd_channel_remove (gpriv, ch); } +rcar_canfd_disable_global_interrupts (gpriv); +rcar_canfd_reset_controller (gpriv); +/* Enter global sleep mode */ rcar_canfd_set_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GSLPR );clk_disable_unprepare (gpriv->clkp);
Greg Kroah-Hartman
maintainerPATCH 153/217 can: rcar_canfd: Use devm_clk_get_optional() for RAM clk
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Biju Das <[email protected]>
[ Upstream commit eda3d6c8d784835cec86f42b3f8118c9eb0cc58c ]
Replace devm_clk_get_optional_enabled()->devm_clk_get_optional() as the RAM clk needs to be enabled in resume for proper operation in STR mode for RZ/G3E SoC.
Stable-dep-of: bef9004c5b91 ("can: rcar_canfd: change the initializing flow for clocks and resets")
--- drivers/net/can/rcar/rcar_canfd.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
drivers/net/can/rcar/rcar_canfd.c +17 -6
@@ -468,6 +468,7 @@ struct rcar_canfd_global {struct platform_device *pdev;/* Respective platform device */ struct clk *clkp;/* Peripheral clock */ struct clk *can_clk;/* fCAN clock */ +struct clk *clk_ram;/* Clock RAM */ unsigned long channels_mask;/* Enabled channels mask */ bool extclk;/* CANFD or Ext clock */ bool fdmode;/* CAN FD or Classical CAN only mode */
@@ -1985,7 +1986,6 @@ static int rcar_canfd_probe(struct platfu32 rule_entry =0 ;bool fdmode = true;/* CAN FD only mode - default */ char name[9 ] ="channelX" ; -struct clk *clk_ram;int i; info =of_device_get_match_data (dev);
@@ -2075,10 +2075,10 @@ static int rcar_canfd_probe(struct platf gpriv->extclk = gpriv->info->external_clk; } - clk_ram =devm_clk_get_optional_enabled (dev,"ram_clk" ); -if (IS_ERR (clk_ram)) -return dev_err_probe (dev,PTR_ERR (clk_ram), -"cannot get enabled ram clock\n" ); + gpriv->clk_ram =devm_clk_get_optional (dev,"ram_clk" ); +if (IS_ERR (gpriv->clk_ram)) +return dev_err_probe (dev,PTR_ERR (gpriv->clk_ram), +"cannot get ram clock\n" ); addr =devm_platform_ioremap_resource (pdev,0 );if (IS_ERR (addr)) {
@@ -2144,10 +2144,18 @@ static int rcar_canfd_probe(struct platf goto fail_reset; } +/* Enable RAM clock */ + err =clk_prepare_enable (gpriv->clk_ram); +if (err) { +dev_err (dev,"failed to enable RAM clock: %pe\n" , +ERR_PTR (err)); + goto fail_clk; + } + err =rcar_canfd_reset_controller (gpriv);if (err) {dev_err (dev,"reset controller failed: %pe\n" ,ERR_PTR (err)); - goto fail_clk; + goto fail_ram_clk; }/* Controller in Global reset & Channel reset mode */
@@ -2199,6 +2207,8 @@ fail_channel:rcar_canfd_channel_remove (gpriv, ch); fail_mode:rcar_canfd_disable_global_interrupts (gpriv); +fail_ram_clk: +clk_disable_unprepare (gpriv->clk_ram); fail_clk:clk_disable_unprepare (gpriv->clkp); fail_reset:
@@ -2223,6 +2233,7 @@ static void rcar_canfd_remove(struct pla/* Enter global sleep mode */ rcar_canfd_set_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GSLPR ); +clk_disable_unprepare (gpriv->clk_ram);clk_disable_unprepare (gpriv->clkp);reset_control_assert (gpriv->rstc2);reset_control_assert (gpriv->rstc1);
Greg Kroah-Hartman
maintainerPATCH 154/217 can: rcar_canfd: Extract rcar_canfd_global_{,de}init()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geert Uytterhoeven <[email protected]>
[ Upstream commit fa5f4ec8fff8bc587a2cbf7101303306e045c11f ]
Extract the code to (de)initialize global state into separate functions, for future reuse.
Stable-dep-of: bef9004c5b91 ("can: rcar_canfd: change the initializing flow for clocks and resets")
--- drivers/net/can/rcar/rcar_canfd.c | 182 +++++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 78 deletions(-)
drivers/net/can/rcar/rcar_canfd.c +104 -78
@@ -1971,21 +1971,120 @@ static void rcar_canfd_channel_remove(st } } +static int rcar_canfd_global_init (struct rcar_canfd_global *gpriv) +{ +struct device *dev = &gpriv->pdev->dev; +u32 rule_entry =0 ; +u32 ch, sts; +int err; + + err =reset_control_reset (gpriv->rstc1); +if (err) +return err; + + err =reset_control_reset (gpriv->rstc2); +if (err) + goto fail_reset1; + +/* Enable peripheral clock for register access */ + err =clk_prepare_enable (gpriv->clkp); +if (err) { +dev_err (dev,"failed to enable peripheral clock: %pe\n" , +ERR_PTR (err)); + goto fail_reset2; + } + +/* Enable RAM clock */ + err =clk_prepare_enable (gpriv->clk_ram); +if (err) { +dev_err (dev, +"failed to enable RAM clock, error %d\n" , err); + goto fail_clk; + } + + err =rcar_canfd_reset_controller (gpriv); +if (err) { +dev_err (dev,"reset controller failed: %pe\n" ,ERR_PTR (err)); + goto fail_ram_clk; + } + +/* Controller in Global reset & Channel reset mode */ +rcar_canfd_configure_controller (gpriv); + +/* Configure per channel attributes */ +for_each_set_bit (ch, &gpriv->channels_mask, gpriv->info->max_channels) { +/* Configure Channel's Rx fifo */ +rcar_canfd_configure_rx (gpriv, ch); + +/* Configure Channel's Tx (Common) fifo */ +rcar_canfd_configure_tx (gpriv, ch); + +/* Configure receive rules */ +rcar_canfd_configure_afl_rules (gpriv, ch, rule_entry); + rule_entry +=RCANFD_CHANNEL_NUMRULES ; + } + +/* Configure common interrupts */ +rcar_canfd_enable_global_interrupts (gpriv); + +/* Start Global operation mode */ +rcar_canfd_update_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GMDC_MASK , +RCANFD_GCTR_GMDC_GOPM ); + +/* Verify mode change */ + err =readl_poll_timeout ((gpriv->base +RCANFD_GSTS ), sts, + !(sts &RCANFD_GSTS_GNOPM ),2 ,500000 ); +if (err) { +dev_err (dev,"global operational mode failed\n" ); + goto fail_mode; + } + +return 0 ; + +fail_mode: +rcar_canfd_disable_global_interrupts (gpriv); +fail_ram_clk: +clk_disable_unprepare (gpriv->clk_ram); +fail_clk: +clk_disable_unprepare (gpriv->clkp); +fail_reset2: +reset_control_assert (gpriv->rstc2); +fail_reset1: +reset_control_assert (gpriv->rstc1); +return err; +} + +static void rcar_canfd_global_deinit (struct rcar_canfd_global *gpriv,bool full) +{ +rcar_canfd_disable_global_interrupts (gpriv); + +if (full) { +rcar_canfd_reset_controller (gpriv); + +/* Enter global sleep mode */ +rcar_canfd_set_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GSLPR ); + } + +clk_disable_unprepare (gpriv->clk_ram); +clk_disable_unprepare (gpriv->clkp); +reset_control_assert (gpriv->rstc2); +reset_control_assert (gpriv->rstc1); +} +static int rcar_canfd_probe (struct platform_device *pdev) {struct phy *transceivers[RCANFD_NUM_CHANNELS ] = {NULL , };const struct rcar_canfd_hw_info *info;struct device *dev = &pdev->dev;void __iomem *addr; -u32 sts, ch, fcan_freq;struct rcar_canfd_global *gpriv;struct device_node *of_child;unsigned long channels_mask =0 ;int err, ch_irq, g_irq;int g_err_irq, g_recc_irq; -u32 rule_entry =0 ;bool fdmode = true;/* CAN FD only mode - default */ char name[9 ] ="channelX" ; +u32 ch, fcan_freq;int i; info =of_device_get_match_data (dev);
@@ -2127,67 +2226,9 @@ static int rcar_canfd_probe(struct platf } } - err =reset_control_reset (gpriv->rstc1); + err =rcar_canfd_global_init (gpriv);if (err) - goto fail_dev; - err =reset_control_reset (gpriv->rstc2); -if (err) { -reset_control_assert (gpriv->rstc1); - goto fail_dev; - } - -/* Enable peripheral clock for register access */ - err =clk_prepare_enable (gpriv->clkp); -if (err) { -dev_err (dev,"failed to enable peripheral clock: %pe\n" , -ERR_PTR (err)); - goto fail_reset; - } - -/* Enable RAM clock */ - err =clk_prepare_enable (gpriv->clk_ram); -if (err) { -dev_err (dev,"failed to enable RAM clock: %pe\n" , -ERR_PTR (err)); - goto fail_clk; - } - - err =rcar_canfd_reset_controller (gpriv); -if (err) { -dev_err (dev,"reset controller failed: %pe\n" ,ERR_PTR (err)); - goto fail_ram_clk; - } - -/* Controller in Global reset & Channel reset mode */ -rcar_canfd_configure_controller (gpriv); - -/* Configure per channel attributes */ -for_each_set_bit (ch, &gpriv->channels_mask, info->max_channels) { -/* Configure Channel's Rx fifo */ -rcar_canfd_configure_rx (gpriv, ch); - -/* Configure Channel's Tx (Common) fifo */ -rcar_canfd_configure_tx (gpriv, ch); - -/* Configure receive rules */ -rcar_canfd_configure_afl_rules (gpriv, ch, rule_entry); - rule_entry +=RCANFD_CHANNEL_NUMRULES ; - } - -/* Configure common interrupts */ -rcar_canfd_enable_global_interrupts (gpriv); - -/* Start Global operation mode */ -rcar_canfd_update_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GMDC_MASK , -RCANFD_GCTR_GMDC_GOPM ); - -/* Verify mode change */ - err =readl_poll_timeout ((gpriv->base +RCANFD_GSTS ), sts, - !(sts &RCANFD_GSTS_GNOPM ),2 ,500000 ); -if (err) { -dev_err (dev,"global operational mode failed\n" ); goto fail_mode; - }for_each_set_bit (ch, &gpriv->channels_mask, info->max_channels) { err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq,
@@ -2206,14 +2247,7 @@ fail_channel:for_each_set_bit (ch, &gpriv->channels_mask, info->max_channels)rcar_canfd_channel_remove (gpriv, ch); fail_mode: -rcar_canfd_disable_global_interrupts (gpriv); -fail_ram_clk: -clk_disable_unprepare (gpriv->clk_ram); -fail_clk: -clk_disable_unprepare (gpriv->clkp); -fail_reset: -reset_control_assert (gpriv->rstc2); -reset_control_assert (gpriv->rstc1); +rcar_canfd_global_deinit (gpriv, false); fail_dev:return err; }
@@ -2228,15 +2262,7 @@ static void rcar_canfd_remove(struct plarcar_canfd_channel_remove (gpriv, ch); } -rcar_canfd_disable_global_interrupts (gpriv); -rcar_canfd_reset_controller (gpriv); - -/* Enter global sleep mode */ -rcar_canfd_set_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GSLPR ); -clk_disable_unprepare (gpriv->clk_ram); -clk_disable_unprepare (gpriv->clkp); -reset_control_assert (gpriv->rstc2); -reset_control_assert (gpriv->rstc1); +rcar_canfd_global_deinit (gpriv, true); }static int __maybe_unused rcar_canfd_suspend (struct device *dev)
Greg Kroah-Hartman
maintainerPATCH 155/217 can: rcar_canfd: change the initializing flow for clocks and resets
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tu Nguyen <[email protected]>
[ Upstream commit bef9004c5b91debfceaea2841855a4ebe81ff2b3 ]
Testing CANFD on RZ/G3E shows that many registers do not reset to their initial values with the current flow of deasserting resets first and then enabling clocks.
Based on the HW manual, clocks should be supplied first and the resets deasserted afterward.
section 7.4.3 Procedure for Activating Modules: RZ/G2L section 4.4.9.3 Procedure for Starting up Units: RZ/G3E
So, update the order of the initializing flow for resets and clocks to match the hardware manual, resetting all CANFD registers to their initial values. Also update rcar_canfd_global_deinit() to assert resets before disabling clocks, so the teardown path mirrors the new init ordering.
--- drivers/net/can/rcar/rcar_canfd.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/net/can/rcar/rcar_canfd.c +16 -16
@@ -1978,20 +1978,12 @@ static int rcar_canfd_global_init(structu32 ch, sts;int err; - err =reset_control_reset (gpriv->rstc1); -if (err) -return err; - - err =reset_control_reset (gpriv->rstc2); -if (err) - goto fail_reset1; -/* Enable peripheral clock for register access */ err =clk_prepare_enable (gpriv->clkp);if (err) {dev_err (dev,"failed to enable peripheral clock: %pe\n" ,ERR_PTR (err)); - goto fail_reset2; +return err; }/* Enable RAM clock */
@@ -2002,10 +1994,18 @@ static int rcar_canfd_global_init(struct goto fail_clk; } + err =reset_control_reset (gpriv->rstc1); +if (err) + goto fail_ram_clk; + + err =reset_control_reset (gpriv->rstc2); +if (err) + goto fail_reset1; + err =rcar_canfd_reset_controller (gpriv);if (err) {dev_err (dev,"reset controller failed: %pe\n" ,ERR_PTR (err)); - goto fail_ram_clk; + goto fail_reset2; }/* Controller in Global reset & Channel reset mode */
@@ -2043,14 +2043,14 @@ static int rcar_canfd_global_init(struct fail_mode:rcar_canfd_disable_global_interrupts (gpriv); -fail_ram_clk: -clk_disable_unprepare (gpriv->clk_ram); -fail_clk: -clk_disable_unprepare (gpriv->clkp); fail_reset2:reset_control_assert (gpriv->rstc2); fail_reset1:reset_control_assert (gpriv->rstc1); +fail_ram_clk: +clk_disable_unprepare (gpriv->clk_ram); +fail_clk: +clk_disable_unprepare (gpriv->clkp);return err; }
@@ -2065,10 +2065,10 @@ static void rcar_canfd_global_deinit(strrcar_canfd_set_bit (gpriv->base,RCANFD_GCTR ,RCANFD_GCTR_GSLPR ); } -clk_disable_unprepare (gpriv->clk_ram); -clk_disable_unprepare (gpriv->clkp);reset_control_assert (gpriv->rstc2);reset_control_assert (gpriv->rstc1); +clk_disable_unprepare (gpriv->clk_ram); +clk_disable_unprepare (gpriv->clkp); }static int rcar_canfd_probe (struct platform_device *pdev)
Greg Kroah-Hartman
maintainerPATCH 156/217 futex: Fix race in futex_pivot_pending() during private hash resize
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yao Kai <[email protected]>
[ Upstream commit 8e7ff730dd96519a333d1570edf1c3fabb6d3629 ]
A task performing a custom private hash resize can remain blocked in uninterruptible sleep indefinitely. The hung-task detector reports:
INFO: task futex-resizer:314 blocked for more than 10 seconds. task:futex-resizer state:D stack:14824 pid:314 tgid:312 ppid:311 Call Trace: __schedule+0x521/0xf30 schedule+0x22/0xa0 futex_hash_allocate+0x3db/0x490 __do_sys_prctl+0x6f5/0xbd0 do_syscall_64+0xf9/0x530 entry_SYSCALL_64_after_hwframe+0x77/0x7f Kernel panic - not syncing: hung_task: blocked tasks
futex_pivot_pending() allows the resize request to continue when either no replacement hash is pending (hash_new == NULL) or the current hash reference count has reached zero.
After the final-reference wake, another futex task can complete the pivot between the two observations:
T1 T2
futex_hash_allocate()
wait_var_event(mm, ...)
futex_pivot_pending(mm)
hash_new != NULL
futex_hash()
futex_ref_get(old) -> false
futex_pivot_hash(mm)
hash_new = NULL
__futex_pivot_hash(mm, new)
rcu_assign_pointer(hash, new)
fph = rcu_dereference(hash) /* new */
futex_ref_is_dead(fph) -> false
schedule()The pivot changes the state from hash_new != NULL with a dead current hash to hash_new == NULL with a live current hash. Because futex_pivot_pending() reads hash_new and hash without serialization, the resize task can observe hash_new in the pre-pivot state and hash in the post-pivot state, causing futex_pivot_pending() to return false even though the pivot has completed. The task then goes to sleep after the wakeup has already been consumed.
Serialize state reads in futex_pivot_pending() using futex_mm_phash::lock. This guarantees that futex_pivot_pending() observes hash_new and hash atomically, eliminating the race condition.
--- kernel/futex/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/futex/core.c +2 -2
@@ -1774,12 +1774,12 @@ static bool futex_pivot_pending(struct m {struct futex_private_hash *fph; -guard (rcu)(); +guard (mutex)(&mm->futex_hash_lock);if (!mm->futex_phash_new)return true; - fph =rcu_dereference (mm->futex_phash); + fph =rcu_dereference_raw (mm->futex_phash);return futex_ref_is_dead (fph); }
Greg Kroah-Hartman
maintainerPATCH 157/217 sched_ext: Update p->scx.disallow warning in scx_init_task()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit 0454a604b98a9bf301e82860cd216ec4ac563668 ]
- Always trigger the warning if p->scx.disallow is set for fork inits. There is no reason to set it during forks.
- Flip the positions of if/else arms to ease adding error conditions.
Stable-dep-of: 5f8b69642d18 ("sched_ext: Take cgroup_lock() first in scx_cgroup_lock()")
--- kernel/sched/ext.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
kernel/sched/ext.c +4 -4
@@ -2841,7 +2841,10 @@ static int scx_init_task(struct task_strscx_set_task_state (p,SCX_TASK_INIT );if (p->scx.disallow) { -if (!fork) { +if (unlikely (fork)) { +scx_error (sch,"ops.init_task() set task->scx.disallow for %s[%d] during fork" , + p->comm, p->pid); + }else {struct rq *rq;struct rq_flags rf;
@@ -2860,9 +2863,6 @@ static int scx_init_task(struct task_str }task_rq_unlock (rq, p, &rf); - }else if (p->policy ==SCHED_EXT ) { -scx_error (sch,"ops.init_task() set task->scx.disallow for %s[%d] during fork" , - p->comm, p->pid); } }
Greg Kroah-Hartman
maintainerPATCH 158/217 sched_ext: Reorganize enable/disable path for multi-scheduler support
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit dbd542a8fac7bcfba91e353f2a522e1bf2fbee27 ]
In preparation for multiple scheduler support, reorganize the enable and disable paths to make scheduler instances explicit. Extract scx_root_disable() from scx_disable_workfn(). Rename scx_enable_workfn() to scx_root_enable_workfn(). Change scx_disable() to take @sch parameter and only queue disable_work if scx_claim_exit() succeeds for consistency. Move exit_kind validation into scx_claim_exit(). The sysrq handler now prints a message when no scheduler is loaded.
These changes don't materially affect user-visible behavior.
v2: Keep scx_enable() name as-is and only rename the workfn to
scx_root_enable_workfn(). Change scx_enable() return type to s32.
Stable-dep-of: 5f8b69642d18 ("sched_ext: Take cgroup_lock() first in scx_cgroup_lock()")
--- kernel/sched/ext.c | 78 +++++++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 35 deletions(-)
kernel/sched/ext.c +43 -35
@@ -3029,8 +3029,8 @@ void sched_ext_free(struct task_struct *raw_spin_unlock_irqrestore (&scx_tasks_lock, flags);/* -* @p is off scx_tasks and wholly ours. scx_enable()'s READY -> ENABLED -* transitions can't race us. Disable ops for @p. +* @p is off scx_tasks and wholly ours. scx_root_enable()'s READY -> +* ENABLED transitions can't race us. Disable ops for @p. */ if (scx_get_task_state (p) !=SCX_TASK_NONE ) {struct rq_flags rf;
@@ -3962,24 +3962,12 @@ static void free_kick_pseqs(void) } } -static void scx_disable_workfn (struct kthread_work *work) +static void scx_root_disable (struct scx_sched *sch) { - struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);struct scx_exit_info *ei = sch->exit_info;struct scx_task_iter sti;struct task_struct *p; -int kind, cpu; - - kind =atomic_read (&sch->exit_kind); -while (true) { -if (kind ==SCX_EXIT_DONE )/* already disabled? */ -return ; -WARN_ON_ONCE (kind ==SCX_EXIT_NONE ); -if (atomic_try_cmpxchg (&sch->exit_kind, &kind,SCX_EXIT_DONE )) -break ; - } - ei->kind = kind; - ei->reason =scx_exit_reason (ei->kind); +int cpu;/* guarantee forward progress by bypassing scx_ops */ scx_bypass (true);
@@ -4124,6 +4112,9 @@ static bool scx_claim_exit(struct scx_sclockdep_assert_preemption_disabled (); +if (WARN_ON_ONCE (kind ==SCX_EXIT_NONE || kind ==SCX_EXIT_DONE )) + kind =SCX_EXIT_ERROR ; +if (!atomic_try_cmpxchg (&sch->exit_kind, &none, kind))return false;
@@ -4136,21 +4127,31 @@ static bool scx_claim_exit(struct scx_screturn true; } -static void scx_disable (enum scx_exit_kind kind) +static void scx_disable_workfn (struct kthread_work *work) { -struct scx_sched *sch; + struct scx_sched *sch = container_of(work, struct scx_sched, disable_work); +struct scx_exit_info *ei = sch->exit_info; +int kind; -if (WARN_ON_ONCE (kind ==SCX_EXIT_NONE || kind ==SCX_EXIT_DONE )) - kind =SCX_EXIT_ERROR ; + kind =atomic_read (&sch->exit_kind); +while (true) { +if (kind ==SCX_EXIT_DONE )/* already disabled? */ +return ; +WARN_ON_ONCE (kind ==SCX_EXIT_NONE ); +if (atomic_try_cmpxchg (&sch->exit_kind, &kind,SCX_EXIT_DONE )) +break ; + } + ei->kind = kind; + ei->reason =scx_exit_reason (ei->kind); -rcu_read_lock (); - sch =rcu_dereference (scx_root); -if (sch) { -guard (preempt)(); -scx_claim_exit (sch, kind); +scx_root_disable (sch); +} + +static void scx_disable (struct scx_sched *sch,enum scx_exit_kind kind) +{ +guard (preempt)(); +if (scx_claim_exit (sch, kind))kthread_queue_work (sch->helper, &sch->disable_work); - } -rcu_read_unlock (); }static void dump_newline (struct seq_buf *s)
@@ -4662,10 +4663,9 @@ struct scx_enable_cmd {int ret; }; -static void scx_enable_workfn (struct kthread_work *work) +static void scx_root_enable_workfn (struct kthread_work *work) { -struct scx_enable_cmd *cmd = - container_of(work, struct scx_enable_cmd, work); + struct scx_enable_cmd *cmd = container_of(work, struct scx_enable_cmd, work);struct sched_ext_ops *ops = cmd->ops;struct scx_sched *sch;struct scx_task_iter sti;
@@ -4915,12 +4915,12 @@ err_disable:* Flush scx_disable_work to ensure that error is reported before init * completion. sch's base reference will be put by bpf_scx_unreg(). */ -scx_error (sch,"scx_enable() failed (%d)" , ret); +scx_error (sch,"scx_root_enable() failed (%d)" , ret);kthread_flush_work (&sch->disable_work); cmd->ret =0 ; } -static int scx_enable (struct sched_ext_ops *ops,struct bpf_link *link) +static s32 scx_enable (struct sched_ext_ops *ops,struct bpf_link *link) {static struct kthread_worker *helper;static DEFINE_MUTEX (helper_mutex);
@@ -4946,7 +4946,7 @@ static int scx_enable(struct sched_ext_omutex_unlock (&helper_mutex); } -kthread_init_work (&cmd.work, scx_enable_workfn); +kthread_init_work (&cmd.work, scx_root_enable_workfn); cmd.ops = ops;kthread_queue_work (READ_ONCE (helper), &cmd.work);
@@ -5089,7 +5089,7 @@ static void bpf_scx_unreg(void *kdata, sstruct sched_ext_ops *ops = kdata;struct scx_sched *sch = ops->priv; -scx_disable (SCX_EXIT_UNREG ); +scx_disable (sch,SCX_EXIT_UNREG );kthread_flush_work (&sch->disable_work);kobject_put (&sch->kobj); }
@@ -5217,7 +5217,15 @@ static struct bpf_struct_ops bpf_sched_estatic void sysrq_handle_sched_ext_reset (u8 key) { -scx_disable (SCX_EXIT_SYSRQ ); +struct scx_sched *sch; + +rcu_read_lock (); + sch =rcu_dereference (scx_root); +if (likely (sch)) +scx_disable (sch,SCX_EXIT_SYSRQ ); +else +pr_info ("sched_ext: BPF schedulers not loaded\n" ); +rcu_read_unlock (); }static const struct sysrq_key_op sysrq_sched_ext_reset_op = {
Greg Kroah-Hartman
maintainerPATCH 159/217 sched_ext: Take cgroup_lock() first in scx_cgroup_lock()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit 5f8b69642d18e1f3e11996707842ac530444e959 ]
scx_cgroup_lock() write-locks scx_cgroup_ops_rwsem and then takes cgroup_lock(), which can deadlock through kernfs:
scx enable/disable cgroup rmdir cpu.weight write ------------------ ------------ ----------------
cgroup_lock()
percpu_down_write(rwsem)
cgroup_lock()
kernfs_get_active() percpu_down_read(rwsem)
kernfs_drain()
The enable path waits for the rmdir to release cgroup_mutex. The rmdir, deactivating the cpu controller's files, waits in kernfs_drain() for the write's active reference. The write, in scx_group_set_weight(), waits for the rwsem behind the pending writer.
Take cgroup_lock() first. The set_* paths take no cgroup locks inside the read side, so a pending write-lock then only waits for read sections that always run to completion, and no dependency from the rwsem back to cgroup_mutex remains.
--- kernel/sched/ext.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/sched/ext.c +7 -2
@@ -3313,16 +3313,21 @@ void scx_group_set_bandwidth(struct task percpu_up_read(&scx_cgroup_ops_rwsem); } +/* +* cgroup_lock() must nest outside the rwsem write side: a writer waiting +* for cgroup_mutex deadlocks with cgroup teardown, which holds it while +* draining a set_* file write blocked on the rwsem behind the writer. +*/ static void scx_cgroup_lock (void ) { -percpu_down_write (&scx_cgroup_ops_rwsem);cgroup_lock (); +percpu_down_write (&scx_cgroup_ops_rwsem); }static void scx_cgroup_unlock (void ) { -cgroup_unlock ();percpu_up_write (&scx_cgroup_ops_rwsem); +cgroup_unlock (); }#else /* CONFIG_EXT_GROUP_SCHED */
Greg Kroah-Hartman
maintainerPATCH 160/217 ring-buffer: Add helper functions for allocations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
[ Upstream commit b1e7a590a0133606d3efd41aee38cdeac630b52f ]
The allocation of the per CPU buffer descriptor, the buffer page descriptors and the buffer page data itself can be pretty ugly:
kzalloc_node(ALIGN(sizeof(struct buffer_page), cache_line_size()),
GFP_KERNEL, cpu_to_node(cpu));And the data pages:
page = alloc_pages_node(cpu_to_node(cpu),
GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_COMP | __GFP_ZERO, order); if (!page) return NULL;
bpage->page = page_address(page);
rb_init_page(bpage->page);
Add helper functions to make the code easier to read.
This does make all allocations of the data page (bpage->page) allocated with the __GFP_RETRY_MAYFAIL flag (and not just the bulk allocator). Which is actually better, as allocating the data page for the ring buffer tracing should try hard but not trigger the OOM killer.
Stable-dep-of: 7c727dfce6be ("ring-buffer: Prevent resizing of persistent ring buffer")
--- kernel/trace/ring_buffer.c | 97 ++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 44 deletions(-)
kernel/trace/ring_buffer.c +53 -44
@@ -405,6 +405,41 @@ static void free_buffer_page(struct buff }/* +* For best performance, allocate cpu buffer data cache line sized +* and per CPU. +*/ +#define alloc_cpu_buffer (cpu) (struct ring_buffer_per_cpu *) \ + kzalloc_node(ALIGN(sizeof(struct ring_buffer_per_cpu), \ + cache_line_size()), GFP_KERNEL, cpu_to_node(cpu)); + +#define alloc_cpu_page (cpu) (struct buffer_page *) \ + kzalloc_node(ALIGN(sizeof(struct buffer_page), \ + cache_line_size()), GFP_KERNEL, cpu_to_node(cpu)); + +static struct buffer_data_page *alloc_cpu_data (int cpu,int order) +{ +struct buffer_data_page *dpage; +struct page *page; +gfp_t mflags; + +/* +* __GFP_RETRY_MAYFAIL flag makes sure that the allocation fails +* gracefully without invoking oom-killer and the system is not +* destabilized. +*/ + mflags =GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_COMP | __GFP_ZERO; + + page =alloc_pages_node (cpu_to_node (cpu), mflags, order); +if (!page) +return NULL ; + + dpage =page_address (page); +rb_init_page (dpage); + +return dpage; +} + +/* * We need to fit the time_stamp delta into 27 bits. */ static inline bool test_time_stamp (u64 delta)
@@ -2214,7 +2249,6 @@ static int __rb_allocate_pages(struct ristruct ring_buffer_cpu_meta *meta =NULL ;struct buffer_page *bpage, *tmp;bool user_thread = current->mm !=NULL ; -gfp_t mflags;long i;/*
@@ -2229,13 +2263,6 @@ static int __rb_allocate_pages(struct rireturn -ENOMEM ;/* -* __GFP_RETRY_MAYFAIL flag makes sure that the allocation fails -* gracefully without invoking oom-killer and the system is not -* destabilized. -*/ - mflags =GFP_KERNEL | __GFP_RETRY_MAYFAIL; - -/* * If a user thread allocates too much, and si_mem_available() * reports there's enough memory, even though there is not. * Make sure the OOM killer kills this thread. This can happen
@@ -2251,10 +2278,8 @@ static int __rb_allocate_pages(struct ri meta =rb_range_meta (buffer, nr_pages, cpu_buffer->cpu);for (i =0 ; i < nr_pages; i++) { -struct page *page; - bpage =kzalloc_node (ALIGN (sizeof (*bpage),cache_line_size ()), - mflags,cpu_to_node (cpu_buffer->cpu)); + bpage =alloc_cpu_page (cpu_buffer->cpu);if (!bpage) goto free_pages;
@@ -2277,13 +2302,10 @@ static int __rb_allocate_pages(struct ri bpage->range =1 ; bpage->id = i +1 ; }else { - page =alloc_pages_node (cpu_to_node (cpu_buffer->cpu), - mflags | __GFP_COMP | __GFP_ZERO, - cpu_buffer->buffer->subbuf_order); -if (!page) +int order = cpu_buffer->buffer->subbuf_order; + bpage->page =alloc_cpu_data (cpu_buffer->cpu, order); +if (!bpage->page) goto free_pages; - bpage->page =page_address (page); -rb_init_page (bpage->page); } bpage->order = cpu_buffer->buffer->subbuf_order;
@@ -2334,14 +2356,12 @@ static int rb_allocate_pages(struct ringstatic struct ring_buffer_per_cpu *rb_allocate_cpu_buffer (struct trace_buffer *buffer,long nr_pages,int cpu) { - struct ring_buffer_per_cpu *cpu_buffer __free(kfree) = NULL; + struct ring_buffer_per_cpu *cpu_buffer __free(kfree) = +alloc_cpu_buffer (cpu);struct ring_buffer_cpu_meta *meta;struct buffer_page *bpage; -struct page *page;int ret; - cpu_buffer =kzalloc_node (ALIGN (sizeof (*cpu_buffer),cache_line_size ()), -GFP_KERNEL ,cpu_to_node (cpu));if (!cpu_buffer)return NULL ;
@@ -2357,8 +2377,7 @@ rb_allocate_cpu_buffer(struct trace_buffinit_waitqueue_head (&cpu_buffer->irq_work.full_waiters);mutex_init (&cpu_buffer->mapping_lock); - bpage =kzalloc_node (ALIGN (sizeof (*bpage),cache_line_size ()), -GFP_KERNEL ,cpu_to_node (cpu)); + bpage =alloc_cpu_page (cpu);if (!bpage)return NULL ; bpage->order = cpu_buffer->buffer->subbuf_order;
@@ -2381,13 +2400,10 @@ rb_allocate_cpu_buffer(struct trace_buffrb_meta_buffer_update (cpu_buffer, bpage); bpage->range =1 ; }else { - page =alloc_pages_node (cpu_to_node (cpu), -GFP_KERNEL | __GFP_COMP | __GFP_ZERO, - cpu_buffer->buffer->subbuf_order); -if (!page) +int order = cpu_buffer->buffer->subbuf_order; + bpage->page =alloc_cpu_data (cpu, order); +if (!bpage->page) goto fail_free_reader; - bpage->page =page_address (page); -rb_init_page (bpage->page); }INIT_LIST_HEAD (&cpu_buffer->reader_page->list);
@@ -6494,7 +6510,6 @@ ring_buffer_alloc_read_page(struct tracestruct ring_buffer_per_cpu *cpu_buffer;struct buffer_data_read_page *bpage =NULL ;unsigned long flags; -struct page *page;if (!cpumask_test_cpu (cpu, buffer->cpumask))return ERR_PTR (-ENODEV );
@@ -6516,22 +6531,16 @@ ring_buffer_alloc_read_page(struct tracearch_spin_unlock (&cpu_buffer->lock);local_irq_restore (flags); -if (bpage->data) - goto out; - - page =alloc_pages_node (cpu_to_node (cpu), -GFP_KERNEL | __GFP_NORETRY | __GFP_COMP | __GFP_ZERO, - cpu_buffer->buffer->subbuf_order); -if (!page) { -kfree (bpage); -return ERR_PTR (-ENOMEM ); +if (bpage->data) { +rb_init_page (bpage->data); + }else { + bpage->data =alloc_cpu_data (cpu, cpu_buffer->buffer->subbuf_order); +if (!bpage->data) { +kfree (bpage); +return ERR_PTR (-ENOMEM ); + } } - bpage->data =page_address (page); - - out: -rb_init_page (bpage->data); -return bpage; }EXPORT_SYMBOL_GPL (ring_buffer_alloc_read_page);
Greg Kroah-Hartman
maintainerPATCH 161/217 ring-buffer: Store bpage pointers into subbuf_ids
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit e682207bf7ae3f81885f612ada1ac44d027158d4 ]
The subbuf_ids field allows to point to a specific page from the ring-buffer based on its ID. As a preparation or the upcoming ring-buffer remote support, point this array to the buffer_page instead of the buffer_data_page.
Stable-dep-of: 7c727dfce6be ("ring-buffer: Prevent resizing of persistent ring buffer")
--- kernel/trace/ring_buffer.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
kernel/trace/ring_buffer.c +11 -8
@@ -557,7 +557,7 @@ struct ring_buffer_per_cpu {unsigned int mapped;unsigned int user_mapped;/* user space mapping */ struct mutex mapping_lock; -unsigned long *subbuf_ids;/* ID to subbuf VA */ +struct buffer_page **subbuf_ids;/* ID to subbuf VA */ struct trace_buffer_meta *meta_page;struct ring_buffer_cpu_meta *ring_meta;
@@ -7039,7 +7039,7 @@ static void rb_free_meta_page(struct rin }static void rb_setup_ids_meta_page (struct ring_buffer_per_cpu *cpu_buffer, -unsigned long *subbuf_ids) +struct buffer_page **subbuf_ids) {struct trace_buffer_meta *meta = cpu_buffer->meta_page;unsigned int nr_subbufs = cpu_buffer->nr_pages +1 ;
@@ -7048,7 +7048,7 @@ static void rb_setup_ids_meta_page(strucint id =0 ; id =rb_page_id (cpu_buffer, cpu_buffer->reader_page, id); - subbuf_ids[id++] = (unsigned long )cpu_buffer->reader_page->page; + subbuf_ids[id++] = cpu_buffer->reader_page; cnt++; first_subbuf = subbuf =rb_set_head_page (cpu_buffer);
@@ -7058,7 +7058,7 @@ static void rb_setup_ids_meta_page(strucif (WARN_ON (id >= nr_subbufs))break ; - subbuf_ids[id] = (unsigned long )subbuf->page; + subbuf_ids[id] = subbuf;rb_inc_page (&subbuf); id++;
@@ -7067,7 +7067,7 @@ static void rb_setup_ids_meta_page(strucWARN_ON (cnt != nr_subbufs); -/* install subbuf ID to kern VA translation */ +/* install subbuf ID to bpage translation */ cpu_buffer->subbuf_ids = subbuf_ids; meta->meta_struct_len =sizeof (*meta);
@@ -7223,13 +7223,15 @@ static int __rb_map_vma(struct ring_buff }while (p < nr_pages) { +struct buffer_page *subbuf;struct page *page;int off =0 ;if (WARN_ON_ONCE (s >= nr_subbufs))return -EINVAL ; - page =virt_to_page ((void *)cpu_buffer->subbuf_ids[s]); + subbuf = cpu_buffer->subbuf_ids[s]; + page =virt_to_page ((void *)subbuf->page);for (; off < (1 << (subbuf_order)); off++, page++) {if (p >= nr_pages)
@@ -7256,7 +7258,8 @@ int ring_buffer_map(struct trace_buffer struct vm_area_struct *vma) {struct ring_buffer_per_cpu *cpu_buffer; -unsigned long flags, *subbuf_ids; +struct buffer_page **subbuf_ids; +unsigned long flags;int err;if (!cpumask_test_cpu (cpu, buffer->cpumask))
@@ -7280,7 +7283,7 @@ int ring_buffer_map(struct trace_bufferif (err)return err; -/* subbuf_ids include the reader while nr_pages does not */ +/* subbuf_ids includes the reader while nr_pages does not */ subbuf_ids =kcalloc (cpu_buffer->nr_pages +1 ,sizeof (*subbuf_ids),GFP_KERNEL );if (!subbuf_ids) {rb_free_meta_page (cpu_buffer);
Greg Kroah-Hartman
maintainerPATCH 162/217 ring-buffer: Prevent resizing of persistent ring buffer
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit 7c727dfce6be04dd009b29091a4a17d952dbfe03 ]
Dynamically resizing a persistent ring buffer is not possible. Disable the feature.
--- kernel/trace/ring_buffer.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/trace/ring_buffer.c +2 -0
@@ -2399,6 +2399,8 @@ rb_allocate_cpu_buffer(struct trace_buffif (cpu_buffer->ring_meta->head_buffer)rb_meta_buffer_update (cpu_buffer, bpage); bpage->range =1 ; + +atomic_inc (&cpu_buffer->resize_disabled); }else {int order = cpu_buffer->buffer->subbuf_order; bpage->page =alloc_cpu_data (cpu, order);
Greg Kroah-Hartman
maintainerPATCH 163/217 mm/page_table_check: skip special zero mappings
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
[ Upstream commit 8db4bab826ccc9ec10fa41736a48031cd338d392 ]
page_table_check_set() and page_table_check_clear() account mappings based on PageAnon(). Shared zero-page PTEs and huge zero PMDs are special mappings, but page_table_check can still account them as file-backed pages.
An unprivileged process can populate enough zero mappings to overflow file_map_count and hit the existing BUG_ON(). The PTE path can do this with the shared zero page, and the PMD path can do the same with huge zero mappings.
Skip special zero mappings in the user page-table accounting paths. Keep the PTE-side pte_special() check, and identify huge zero PMDs from the mapped folio instead of pmd_special(). That covers architectures where pmd_special() is a no-op without adding huge_zero_pfn checks to the generic counter helpers.
--- mm/page_table_check.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
mm/page_table_check.c +16 -4
@@ -150,18 +150,29 @@ void __page_table_check_pte_clear(structif (&init_mm == mm)return ; -if (pte_user_accessible_page (pte)) { +if (pte_user_accessible_page (pte) && !pte_special (pte)) {page_table_check_clear (pte_pfn (pte),PAGE_SIZE >>PAGE_SHIFT ); } }EXPORT_SYMBOL (__page_table_check_pte_clear); +static inline bool page_table_check_huge_zero_pmd (pmd_t pmd) +{ +unsigned long pfn =pmd_pfn (pmd); + +if (!pfn_valid (pfn)) +return false; + +return is_huge_zero_folio (page_folio (pfn_to_page (pfn))); +} +void __page_table_check_pmd_clear (struct mm_struct *mm,pmd_t pmd) {if (&init_mm == mm)return ; -if (pmd_user_accessible_page (pmd)) { +if (pmd_user_accessible_page (pmd) && + !page_table_check_huge_zero_pmd (pmd)) {page_table_check_clear (pmd_pfn (pmd),PMD_SIZE >>PAGE_SHIFT ); } }
@@ -205,7 +216,7 @@ void __page_table_check_ptes_set(structfor (i =0 ; i < nr; i++)__page_table_check_pte_clear (mm,ptep_get (ptep + i)); -if (pte_user_accessible_page (pte)) +if (pte_user_accessible_page (pte) && !pte_special (pte))page_table_check_set (pte_pfn (pte), nr,pte_write (pte)); }EXPORT_SYMBOL (__page_table_check_ptes_set);
@@ -231,7 +242,8 @@ void __page_table_check_pmds_set(structfor (i =0 ; i < nr; i++)__page_table_check_pmd_clear (mm, *(pmdp + i)); -if (pmd_user_accessible_page (pmd)) +if (pmd_user_accessible_page (pmd) && + !page_table_check_huge_zero_pmd (pmd))page_table_check_set (pmd_pfn (pmd), stride * nr,pmd_write (pmd)); }EXPORT_SYMBOL (__page_table_check_pmds_set);
Greg Kroah-Hartman
maintainerPATCH 164/217 drm/amd/pm: adjust the visibility of pp_table sysfs node
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Wang <[email protected]>
[ Upstream commit 5de8ce0f3709ad93ca5a579aa45cf1b52d72bc90 ]
v1:
- make pp_table invisible on VF mode (only valid on BM)
- make pp_table invisible on Mi* chips (Not supported)
- make pp_table invisible if scpm feature is enabled.
v2:
move pp_table invisible code logic into amdgpu_dpm_get_pp_table() function.
v3:
add table buffer pointer check both on powerplay & swsmu.
Stable-dep-of: bb493058c35c ("drm/amd/pm: fix pptable use-after-free")
--- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 12 +++++++++--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 11 ++++++++++- drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c | 5 ++++- drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +9 -3
@@ -1188,8 +1188,11 @@ int amdgpu_dpm_get_pp_table(struct amdgpconst struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;int ret =0 ; -if (!pp_funcs->get_pp_table) -return 0 ; +if (!table) +return -EINVAL ; + +if (amdgpu_sriov_vf (adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) +return -EOPNOTSUPP ;mutex_lock (&adev->pm.mutex); ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle,
@@ -1721,7 +1724,10 @@ int amdgpu_dpm_set_pp_table(struct amdgpconst struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;int ret =0 ; -if (!pp_funcs->set_pp_table) +if (!buf || !size) +return -EINVAL ; + +if (amdgpu_sriov_vf (adev) || !pp_funcs->set_pp_table || adev->scpm_enabled)return -EOPNOTSUPP ;mutex_lock (&adev->pm.mutex);
drivers/gpu/drm/amd/pm/amdgpu_pm.c +10 -1
@@ -2502,7 +2502,7 @@ static struct amdgpu_device_attr amdgpu_AMDGPU_DEVICE_ATTR_RO (pp_num_states,ATTR_FLAG_BASIC |ATTR_FLAG_ONEVF ),AMDGPU_DEVICE_ATTR_RO (pp_cur_state,ATTR_FLAG_BASIC |ATTR_FLAG_ONEVF ),AMDGPU_DEVICE_ATTR_RW (pp_force_state,ATTR_FLAG_BASIC |ATTR_FLAG_ONEVF ), -AMDGPU_DEVICE_ATTR_RW (pp_table,ATTR_FLAG_BASIC |ATTR_FLAG_ONEVF ), +AMDGPU_DEVICE_ATTR_RW (pp_table,ATTR_FLAG_BASIC ),AMDGPU_DEVICE_ATTR_RW (pp_dpm_sclk,ATTR_FLAG_BASIC |ATTR_FLAG_ONEVF , .attr_update = pp_dpm_clk_default_attr_update), AMDGPU_DEVICE_ATTR_RW(pp_dpm_mclk, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF,
@@ -2639,6 +2639,15 @@ static int default_attr_update(struct amif (amdgpu_dpm_get_apu_thermal_limit (adev, &limit) == -EOPNOTSUPP ) *states =ATTR_STATE_UNSUPPORTED ; + }else if (DEVICE_ATTR_IS (pp_table)) { +int ret; +char *tmp =NULL ; + + ret =amdgpu_dpm_get_pp_table (adev, &tmp); +if (ret == -EOPNOTSUPP || !tmp) + *states =ATTR_STATE_UNSUPPORTED ; +else + *states =ATTR_STATE_SUPPORTED ; }switch (gc_ver) {
drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c +4 -1
@@ -634,9 +634,12 @@ static int pp_dpm_get_pp_table(void *han {struct pp_hwmgr *hwmgr = handle; -if (!hwmgr || !hwmgr->pm_en || !hwmgr->soft_pp_table) +if (!hwmgr || !hwmgr->pm_en || !table)return -EINVAL ; +if (!hwmgr->soft_pp_table) +return -EOPNOTSUPP ; + *table = (char *)hwmgr->soft_pp_table;return hwmgr->soft_pp_table_size; }
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +1 -1
@@ -620,7 +620,7 @@ static int smu_sys_get_pp_table(void *hareturn -EOPNOTSUPP ;if (!smu_table->power_play_table && !smu_table->hardcode_pptable) -return -EINVAL ; +return -EOPNOTSUPP ;if (smu_table->hardcode_pptable) *table = smu_table->hardcode_pptable;
Greg Kroah-Hartman
maintainerPATCH 165/217 drm/amd/pm: fix pptable use-after-free
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Wang <[email protected]>
[ Upstream commit bb493058c35c8676e48269ab6732688ea733d23c ]
amdgpu_dpm_get_pp_table() returns a pointer to a driver-owned power table after dropping adev->pm.mutex. The sysfs path then copies from that pointer. A concurrent pp_table write can replace and free the allocation during the copy, causing a use-after-free.
Change the DPM interface to copy into caller-provided storage while the mutex is held. Keep the size-only query for attribute discovery without exposing the driver-owned pointer.
--- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 14 +++++++++++--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 13 +++---------- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 3 ++- 3 files changed, 16 insertions(+), 14 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +11 -3
@@ -1183,12 +1183,14 @@ int amdgpu_dpm_dispatch_task(struct amdgreturn ret; } -int amdgpu_dpm_get_pp_table (struct amdgpu_device *adev,char **table) +int amdgpu_dpm_get_pp_table (struct amdgpu_device *adev,char *table, +size_t size) {const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; +char *pptable =NULL ;int ret =0 ; -if (!table) +if ((!table && size) || (table && !size))return -EINVAL ;if (amdgpu_sriov_vf (adev) || !pp_funcs->get_pp_table || adev->scpm_enabled)
@@ -1196,7 +1198,13 @@ int amdgpu_dpm_get_pp_table(struct amdgpmutex_lock (&adev->pm.mutex); ret = pp_funcs->get_pp_table (adev->powerplay.pp_handle, - table); + &pptable); +if (ret >0 && !pptable) { + ret = -EINVAL ; + }else if (ret >0 && table) { + ret =min_t (size_t, ret, size); +memcpy (table, pptable, ret); + }mutex_unlock (&adev->pm.mutex);return ret;
drivers/gpu/drm/amd/pm/amdgpu_pm.c +3 -10
@@ -556,25 +556,19 @@ static ssize_t amdgpu_get_pp_table(struc {struct drm_device *ddev =dev_get_drvdata (dev);struct amdgpu_device *adev =drm_to_adev (ddev); -char *table =NULL ;int size, ret; ret =amdgpu_pm_get_access_if_active (adev);if (ret)return ret; - size =amdgpu_dpm_get_pp_table (adev, &table); + size =amdgpu_dpm_get_pp_table (adev, buf,PAGE_SIZE -1 );amdgpu_pm_put_access (adev);if (size <=0 )return size; -if (size >=PAGE_SIZE ) - size =PAGE_SIZE -1 ; - -memcpy (buf, table, size); -return size; }
@@ -2641,10 +2635,9 @@ static int default_attr_update(struct am *states =ATTR_STATE_UNSUPPORTED ; }else if (DEVICE_ATTR_IS (pp_table)) {int ret; -char *tmp =NULL ; - ret =amdgpu_dpm_get_pp_table (adev, &tmp); -if (ret == -EOPNOTSUPP || !tmp) + ret =amdgpu_dpm_get_pp_table (adev,NULL ,0 ); +if (ret <=0 ) *states =ATTR_STATE_UNSUPPORTED ;else *states =ATTR_STATE_SUPPORTED ;
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +2 -1
@@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(structint amdgpu_dpm_dispatch_task (struct amdgpu_device *adev,enum amd_pp_task task_id,enum amd_pm_state_type *user_state); -int amdgpu_dpm_get_pp_table (struct amdgpu_device *adev,char **table); +int amdgpu_dpm_get_pp_table (struct amdgpu_device *adev,char *table, +size_t size); int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev, uint32_t type, long *input,
Greg Kroah-Hartman
maintainerPATCH 166/217 ASoC: SOF: ipc4-topology: Refresh copier IPC payload before widget setup
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
commit 0c0e418dbcf0582bf80d8dbfd9b306607c065992 upstream.
The ipc_config_data buffer for copier widgets is built once during ipc_prepare (called from sof_pcm_setup_connected_widgets) and cached for reuse. For host copiers this buffer contains the copier_data with gtw_cfg.node_id (host DMA ID). For DAI copiers it additionally includes a dma_config_tlv trailer with stream_id and dma_channel_id for HDA link DMA.
On suspend/resume, both host and link DMA streams are released and re-allocated with potentially different stream tags. The underlying copier_data and dma_config_tlv structures are correctly updated by host_config and sdw_hda_dai_hw_params respectively. However, since the widget list (spcm->stream[].list) persists across suspend, sof_pcm_hw_params skips sof_pcm_setup_connected_widgets and ipc_prepare never runs again to rebuild ipc_config_data. The stale cached payload is then sent to firmware with boot-time DMA channel assignments, causing DMA channel conflicts that lead to firmware errors and crashes.
Fix this by refreshing copier_data and dma_config_tlv portions of ipc_config_data in sof_ipc4_widget_setup right before the IPC message is sent. This ensures the payload always reflects the current DMA state regardless of whether ipc_prepare ran.
For DAI copiers, the gtw_cfg.config_length in copier_data is temporarily inflated to include the TLV size (matching the ipc_config_data layout) before copying, then restored, mirroring what sof_ipc4_prepare_copier_module does when first building the buffer.
--- sound/soc/sof/ipc4-topology.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
sound/soc/sof/ipc4-topology.c +33 -0
@@ -3007,6 +3007,15 @@ static int sof_ipc4_widget_setup(struct ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; +/* +* Refresh copier_data in ipc_config_data for host copiers. +* The node_id may have been updated by host_config after +* ipc_prepare, e.g. when host stream tags change after a +* suspend/resume cycle. +*/ +if (swidget->id != snd_soc_dapm_buffer) +memcpy (ipc_data, &ipc4_copier->data,sizeof (ipc4_copier->data)); + msg = &ipc4_copier->msg;break ; }
@@ -3015,6 +3024,9 @@ static int sof_ipc4_widget_setup(struct {struct snd_sof_dai *dai = swidget->private;struct sof_ipc4_copier *ipc4_copier = dai->private; +struct sof_ipc4_copier_data *copier_data; +u32 gtw_cfg_config_length; +u32 tlv_size; pipeline = pipe_widget->private;if (pipeline->use_chain_dma)
@@ -3023,6 +3035,27 @@ static int sof_ipc4_widget_setup(struct ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; +/* +* Refresh copier_data and dma_config_tlv in ipc_config_data. +* These may have been updated after ipc_prepare, e.g. when +* link DMA stream tags change after a suspend/resume cycle. +* +* copier_data->gtw_cfg.config_length does not include the +* TLV size (it was restored after sof_ipc4_prepare_copier_module), +* so temporarily inflate it to match the ipc_config_data layout. +*/ + copier_data = &ipc4_copier->data; + gtw_cfg_config_length = copier_data->gtw_cfg.config_length *4 ; + tlv_size = ipc_size -sizeof (*copier_data) - gtw_cfg_config_length; + + copier_data->gtw_cfg.config_length += tlv_size /4 ; +memcpy (ipc_data, copier_data,sizeof (*copier_data)); + copier_data->gtw_cfg.config_length = gtw_cfg_config_length /4 ; + +if (tlv_size) +memcpy (ipc_data +sizeof (*copier_data) + gtw_cfg_config_length, + &ipc4_copier->dma_config_tlv, tlv_size); + msg = &ipc4_copier->msg;break ; }
Greg Kroah-Hartman
maintainerPATCH 167/217 net: ntb_netdev: Introduce per-queue context
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit ee970634c7773f248a0a27d8a645500371ae5249 upstream.
Prepare ntb_netdev for multi-queue operation by moving queue-pair state out of struct ntb_netdev.
Introduce struct ntb_netdev_queue to carry the ntb_transport_qp pointer, the per-QP TX timer and queue id. Pass this object as the callback context and convert the RX/TX handlers and link event path accordingly.
The probe path allocates a fixed upper bound for netdev queues while instantiating only a single ntb_transport queue pair, preserving the previous behavior. Also store client_dev for future queue pair creation/removal via the ntb_transport API.
Stable-dep-of: d2121faf133a ("NTB: ntb_netdev: Preserve RX queue depth on allocation failure")
--- drivers/net/ntb_netdev.c | 275 +++++++++++++++++++++++++++++++---------------- 1 file changed, 187 insertions(+), 88 deletions(-)
drivers/net/ntb_netdev.c +187 -88
@@ -53,6 +53,7 @@#include <linux/pci.h> #include <linux/ntb.h> #include <linux/ntb_transport.h> +#include <linux/slab.h> #define NTB_NETDEV_VER "0.7"
@@ -70,11 +71,24 @@ static unsigned int tx_start = 10;/* Number of descriptors still available before stop upper layer tx */ static unsigned int tx_stop =5 ; +#define NTB_NETDEV_MAX_QUEUES 64 +#define NTB_NETDEV_DEFAULT_QUEUES 1 + +struct ntb_netdev ; + +struct ntb_netdev_queue { +struct ntb_netdev *ntdev; +struct ntb_transport_qp *qp; +struct timer_list tx_timer; +u16 qid; +}; +struct ntb_netdev {struct pci_dev *pdev; +struct device *client_dev;struct net_device *ndev; -struct ntb_transport_qp *qp; -struct timer_list tx_timer; +unsigned int num_queues; +struct ntb_netdev_queue *queues; };#define NTB_TX_TIMEOUT_MS 1000
@@ -82,14 +96,17 @@ struct ntb_netdev {static void ntb_netdev_event_handler (void *data,int link_is_up) { -struct net_device *ndev = data; -struct ntb_netdev *dev =netdev_priv (ndev); +struct ntb_netdev_queue *q = data; +struct ntb_netdev *dev = q->ntdev; +struct net_device *ndev; + + ndev = dev->ndev; -netdev_dbg (ndev,"Event %x, Link %x\n" , link_is_up, -ntb_transport_link_query (dev->qp)); +netdev_dbg (ndev,"Event %x, Link %x, qp %u\n" , link_is_up, +ntb_transport_link_query (q->qp), q->qid);if (link_is_up) { -if (ntb_transport_link_query (dev->qp)) +if (ntb_transport_link_query (q->qp))netif_carrier_on (ndev); }else {netif_carrier_off (ndev);
@@ -99,10 +116,13 @@ static void ntb_netdev_event_handler(voistatic void ntb_netdev_rx_handler (struct ntb_transport_qp *qp,void *qp_data,void *data,int len) { -struct net_device *ndev = qp_data; +struct ntb_netdev_queue *q = qp_data; +struct ntb_netdev *dev = q->ntdev; +struct net_device *ndev;struct sk_buff *skb;int rc; + ndev = dev->ndev; skb = data;if (!skb)return ;
@@ -118,6 +138,7 @@ static void ntb_netdev_rx_handler(structskb_put (skb, len); skb->protocol =eth_type_trans (skb, ndev); skb->ip_summed =CHECKSUM_NONE ; +skb_record_rx_queue (skb, q->qid);if (netif_rx (skb) ==NET_RX_DROP ) { ndev->stats.rx_errors++;
@@ -144,42 +165,43 @@ enqueue_again: }static int __ntb_netdev_maybe_stop_tx (struct net_device *netdev, -struct ntb_transport_qp *qp,int size) +struct ntb_netdev_queue *q,int size) { -struct ntb_netdev *dev =netdev_priv (netdev); +netif_stop_subqueue (netdev, q->qid); -netif_stop_queue (netdev);/* Make sure to see the latest value of ntb_transport_tx_free_entry() * since the queue was last started. */ smp_mb (); -if (likely (ntb_transport_tx_free_entry (qp) < size)) { -mod_timer (&dev->tx_timer, jiffies +usecs_to_jiffies (tx_time)); +if (likely (ntb_transport_tx_free_entry (q->qp) < size)) { +mod_timer (&q->tx_timer, jiffies +usecs_to_jiffies (tx_time));return -EBUSY ; } -netif_start_queue (netdev); +netif_start_subqueue (netdev, q->qid);return 0 ; }static int ntb_netdev_maybe_stop_tx (struct net_device *ndev, -struct ntb_transport_qp *qp,int size) +struct ntb_netdev_queue *q,int size) { -if (netif_queue_stopped (ndev) || - (ntb_transport_tx_free_entry (qp) >= size)) +if (__netif_subqueue_stopped (ndev, q->qid) || + (ntb_transport_tx_free_entry (q->qp) >= size))return 0 ; -return __ntb_netdev_maybe_stop_tx (ndev, qp, size); +return __ntb_netdev_maybe_stop_tx (ndev, q, size); }static void ntb_netdev_tx_handler (struct ntb_transport_qp *qp,void *qp_data,void *data,int len) { -struct net_device *ndev = qp_data; +struct ntb_netdev_queue *q = qp_data; +struct ntb_netdev *dev = q->ntdev; +struct net_device *ndev;struct sk_buff *skb; -struct ntb_netdev *dev =netdev_priv (ndev); + ndev = dev->ndev; skb = data;if (!skb || !ndev)return ;
@@ -194,13 +216,13 @@ static void ntb_netdev_tx_handler(structdev_kfree_skb_any (skb); -if (ntb_transport_tx_free_entry (dev->qp) >= tx_start) { +if (ntb_transport_tx_free_entry (qp) >= tx_start) {/* Make sure anybody stopping the queue after this sees the new * value of ntb_transport_tx_free_entry() */ smp_mb (); -if (netif_queue_stopped (ndev)) -netif_wake_queue (ndev); +if (__netif_subqueue_stopped (ndev, q->qid)) +netif_wake_subqueue (ndev, q->qid); } }
@@ -208,16 +230,20 @@ static netdev_tx_t ntb_netdev_start_xmit struct net_device *ndev) {struct ntb_netdev *dev =netdev_priv (ndev); +u16 qid =skb_get_queue_mapping (skb); +struct ntb_netdev_queue *q;int rc; -ntb_netdev_maybe_stop_tx (ndev, dev->qp, tx_stop); + q = &dev->queues[qid]; + +ntb_netdev_maybe_stop_tx (ndev, q, tx_stop); - rc =ntb_transport_tx_enqueue (dev->qp, skb, skb->data, skb->len); + rc =ntb_transport_tx_enqueue (q->qp, skb, skb->data, skb->len);if (rc) goto err;/* check for next submit */ -ntb_netdev_maybe_stop_tx (ndev, dev->qp, tx_stop); +ntb_netdev_maybe_stop_tx (ndev, q, tx_stop);return NETDEV_TX_OK ;
@@ -229,80 +255,104 @@ err:static void ntb_netdev_tx_timer (struct timer_list *t) { -struct ntb_netdev *dev =timer_container_of (dev, t, tx_timer); -struct net_device *ndev = dev->ndev; +struct ntb_netdev_queue *q =timer_container_of (q, t, tx_timer); +struct ntb_netdev *dev = q->ntdev; +struct net_device *ndev; + + ndev = dev->ndev; -if (ntb_transport_tx_free_entry (dev->qp) < tx_stop) { -mod_timer (&dev->tx_timer, jiffies +usecs_to_jiffies (tx_time)); +if (ntb_transport_tx_free_entry (q->qp) < tx_stop) { +mod_timer (&q->tx_timer, jiffies +usecs_to_jiffies (tx_time)); }else {/* Make sure anybody stopping the queue after this sees the new * value of ntb_transport_tx_free_entry() */ smp_mb (); -if (netif_queue_stopped (ndev)) -netif_wake_queue (ndev); +if (__netif_subqueue_stopped (ndev, q->qid)) +netif_wake_subqueue (ndev, q->qid); } }static int ntb_netdev_open (struct net_device *ndev) {struct ntb_netdev *dev =netdev_priv (ndev); +struct ntb_netdev_queue *queue;struct sk_buff *skb; -int rc, i, len; +int rc =0 , i, len; +unsigned int q; -/* Add some empty rx bufs */ -for (i =0 ; i <NTB_RXQ_SIZE ; i++) { - skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); -if (!skb) { - rc = -ENOMEM ; - goto err; - } +/* Add some empty rx bufs for each queue */ +for (q =0 ; q < dev->num_queues; q++) { + queue = &dev->queues[q]; - rc =ntb_transport_rx_enqueue (dev->qp, skb, skb->data, - ndev->mtu +ETH_HLEN ); -if (rc) { -dev_kfree_skb (skb); - goto err; +for (i =0 ; i <NTB_RXQ_SIZE ; i++) { + skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); +if (!skb) { + rc = -ENOMEM ; + goto err; + } + + rc =ntb_transport_rx_enqueue (queue->qp, skb, skb->data, + ndev->mtu +ETH_HLEN ); +if (rc) { +dev_kfree_skb (skb); + goto err; + } } - } -timer_setup (&dev->tx_timer, ntb_netdev_tx_timer,0 ); +timer_setup (&queue->tx_timer, ntb_netdev_tx_timer,0 ); + }netif_carrier_off (ndev); -ntb_transport_link_up (dev->qp); + +for (q =0 ; q < dev->num_queues; q++) +ntb_transport_link_up (dev->queues[q].qp); +netif_start_queue (ndev);return 0 ; err: -while ((skb =ntb_transport_rx_remove (dev->qp, &len))) -dev_kfree_skb (skb); +for (q =0 ; q < dev->num_queues; q++) { + queue = &dev->queues[q]; + +while ((skb =ntb_transport_rx_remove (queue->qp, &len))) +dev_kfree_skb (skb); + }return rc; }static int ntb_netdev_close (struct net_device *ndev) {struct ntb_netdev *dev =netdev_priv (ndev); +struct ntb_netdev_queue *queue;struct sk_buff *skb; +unsigned int q;int len; -ntb_transport_link_down (dev->qp); -while ((skb =ntb_transport_rx_remove (dev->qp, &len))) -dev_kfree_skb (skb); +for (q =0 ; q < dev->num_queues; q++) { + queue = &dev->queues[q]; -timer_delete_sync (&dev->tx_timer); +ntb_transport_link_down (queue->qp); + +while ((skb =ntb_transport_rx_remove (queue->qp, &len))) +dev_kfree_skb (skb); +timer_delete_sync (&queue->tx_timer); + }return 0 ; }static int ntb_netdev_change_mtu (struct net_device *ndev,int new_mtu) {struct ntb_netdev *dev =netdev_priv (ndev); +struct ntb_netdev_queue *queue;struct sk_buff *skb; -int len, rc; +unsigned int q, i; +int len, rc =0 ; -if (new_mtu >ntb_transport_max_size (dev->qp) -ETH_HLEN ) +if (new_mtu >ntb_transport_max_size (dev->queues[0 ].qp) -ETH_HLEN )return -EINVAL ;if (!netif_running (ndev)) {
@@ -311,41 +361,54 @@ static int ntb_netdev_change_mtu(struct }/* Bring down the link and dispose of posted rx entries */ -ntb_transport_link_down (dev->qp); +for (q =0 ; q < dev->num_queues; q++) +ntb_transport_link_down (dev->queues[q].qp);if (ndev->mtu < new_mtu) { -int i; - -for (i =0 ; (skb =ntb_transport_rx_remove (dev->qp, &len)); i++) -dev_kfree_skb (skb); - -for (; i; i--) { - skb =netdev_alloc_skb (ndev, new_mtu +ETH_HLEN ); -if (!skb) { - rc = -ENOMEM ; - goto err; - } +for (q =0 ; q < dev->num_queues; q++) { + queue = &dev->queues[q]; - rc =ntb_transport_rx_enqueue (dev->qp, skb, skb->data, - new_mtu +ETH_HLEN ); -if (rc) { +for (i =0 ; + (skb =ntb_transport_rx_remove (queue->qp, &len)); + i++)dev_kfree_skb (skb); - goto err; + +for (; i; i--) { + skb =netdev_alloc_skb (ndev, + new_mtu +ETH_HLEN ); +if (!skb) { + rc = -ENOMEM ; + goto err; + } + + rc =ntb_transport_rx_enqueue (queue->qp, skb, + skb->data, + new_mtu + +ETH_HLEN ); +if (rc) { +dev_kfree_skb (skb); + goto err; + } } } }WRITE_ONCE (ndev->mtu, new_mtu); -ntb_transport_link_up (dev->qp); +for (q =0 ; q < dev->num_queues; q++) +ntb_transport_link_up (dev->queues[q].qp);return 0 ; err: -ntb_transport_link_down (dev->qp); +for (q =0 ; q < dev->num_queues; q++) { +struct ntb_netdev_queue *queue = &dev->queues[q]; + +ntb_transport_link_down (queue->qp); -while ((skb =ntb_transport_rx_remove (dev->qp, &len))) -dev_kfree_skb (skb); +while ((skb =ntb_transport_rx_remove (queue->qp, &len))) +dev_kfree_skb (skb); + }netdev_err (ndev,"Error changing MTU, device inoperable\n" );return rc;
@@ -404,6 +467,7 @@ static int ntb_netdev_probe(struct devicstruct net_device *ndev;struct pci_dev *pdev;struct ntb_netdev *dev; +unsigned int q;int rc; ntb =dev_ntb (client_dev->parent);
@@ -411,7 +475,7 @@ static int ntb_netdev_probe(struct devicif (!pdev)return -ENODEV ; - ndev =alloc_etherdev (sizeof (*dev)); + ndev =alloc_etherdev_mq (sizeof (*dev),NTB_NETDEV_MAX_QUEUES );if (!ndev)return -ENOMEM ;
@@ -420,6 +484,16 @@ static int ntb_netdev_probe(struct devic dev =netdev_priv (ndev); dev->ndev = ndev; dev->pdev = pdev; + dev->client_dev = client_dev; + dev->num_queues =0 ; + + dev->queues =kzalloc_objs (*dev->queues,NTB_NETDEV_MAX_QUEUES , +GFP_KERNEL ); +if (!dev->queues) { + rc = -ENOMEM ; + goto err_free_netdev; + } + ndev->features =NETIF_F_HIGHDMA ; ndev->priv_flags |=IFF_LIVE_ADDR_CHANGE ;
@@ -436,26 +510,47 @@ static int ntb_netdev_probe(struct devic ndev->min_mtu =0 ; ndev->max_mtu =ETH_MAX_MTU ; - dev->qp =ntb_transport_create_queue (ndev, client_dev, - &ntb_netdev_handlers); -if (!dev->qp) { +for (q =0 ; q <NTB_NETDEV_DEFAULT_QUEUES ; q++) { +struct ntb_netdev_queue *queue = &dev->queues[q]; + + queue->ntdev = dev; + queue->qid = q; + queue->qp =ntb_transport_create_queue (queue, client_dev, + &ntb_netdev_handlers); +if (!queue->qp) +break ; + + dev->num_queues++; + } + +if (!dev->num_queues) { rc = -EIO ; - goto err; + goto err_free_queues; } - ndev->mtu =ntb_transport_max_size (dev->qp) -ETH_HLEN ; + rc =netif_set_real_num_queues (ndev, dev->num_queues, dev->num_queues); +if (rc) + goto err_free_qps; + + ndev->mtu =ntb_transport_max_size (dev->queues[0 ].qp) -ETH_HLEN ; rc =register_netdev (ndev);if (rc) - goto err1; + goto err_free_qps;dev_set_drvdata (client_dev, ndev); -dev_info (&pdev->dev,"%s created\n" , ndev->name); +dev_info (&pdev->dev,"%s created with %u queue pairs\n" , + ndev->name, dev->num_queues);return 0 ; -err1: -ntb_transport_free_queue (dev->qp); -err: +err_free_qps: +for (q =0 ; q < dev->num_queues; q++) +ntb_transport_free_queue (dev->queues[q].qp); + +err_free_queues: +kfree (dev->queues); + +err_free_netdev:free_netdev (ndev);return rc; }
@@ -464,9 +559,13 @@ static void ntb_netdev_remove(struct dev {struct net_device *ndev =dev_get_drvdata (client_dev);struct ntb_netdev *dev =netdev_priv (ndev); +unsigned int q;unregister_netdev (ndev); -ntb_transport_free_queue (dev->qp); +for (q =0 ; q < dev->num_queues; q++) +ntb_transport_free_queue (dev->queues[q].qp); + +kfree (dev->queues);free_netdev (ndev); }
Greg Kroah-Hartman
maintainerPATCH 168/217 NTB: ntb_netdev: Preserve RX queue depth on allocation failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit d2121faf133ac3bf9531b53a7e21273649a08517 upstream.
ntb_netdev_rx_handler() hands the received skb to the network stack before allocating its replacement. If the allocation fails, nothing is reposted. Every failure therefore takes one buffer out of the RX queue while the interface remains up, and enough failures eventually stall reception.
A retry path could refill the queue later, but ntb_netdev has none. Allocate the replacement first instead. If that fails, drop the packet and repost the same skb. This keeps the queue full and lets packet delivery resume as soon as memory is available again.
--- drivers/net/ntb_netdev.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
drivers/net/ntb_netdev.c +8 -7
@@ -118,8 +118,8 @@ static void ntb_netdev_rx_handler(struct {struct ntb_netdev_queue *q = qp_data;struct ntb_netdev *dev = q->ntdev; +struct sk_buff *skb, *new_skb;struct net_device *ndev; -struct sk_buff *skb;int rc; ndev = dev->ndev;
@@ -135,6 +135,12 @@ static void ntb_netdev_rx_handler(struct goto enqueue_again; } + new_skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); +if (!new_skb) { + ndev->stats.rx_dropped++; + goto enqueue_again; + } +skb_put (skb, len); skb->protocol =eth_type_trans (skb, ndev); skb->ip_summed =CHECKSUM_NONE ;
@@ -148,12 +154,7 @@ static void ntb_netdev_rx_handler(struct ndev->stats.rx_bytes += len; } - skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); -if (!skb) { - ndev->stats.rx_errors++; - ndev->stats.rx_frame_errors++; -return ; - } + skb = new_skb; enqueue_again: rc =ntb_transport_rx_enqueue (qp, skb, skb->data, ndev->mtu +ETH_HLEN );
Greg Kroah-Hartman
maintainerPATCH 169/217 arm64: tegra: Add EL2 virtual timer interrupt for Tegra194
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jon Hunter <[email protected]>
[ Upstream commit a7c28483fd57dd0e1487024af70622315320774b ]
Commit d87773de9efe ("clocksource/drivers/arm_arch_timer: Default to EL2 virtual timer when running VHE") updated the ARM arch timer driver to use the virtual timer by default if the CPU is running at EL2 with VHE enabled. If the CPU is running at EL2 with VHE enabled but there is no interrupt provided for the virtual timer, then the following warning is displayed:
arch_timer: [Firmware Bug]: VHE-capable CPU without EL2 virtual timer
interrupt
This warning is observed on Tegra194 platforms. Tegra194 SoC includes NVIDIA Carmel ARM v8.2 CPUs and support an EL2 virtual timer. Fix the above warning by adding the PPI for the EL2 virtual timer interrupt for Tegra194.
--- arch/arm64/boot/dts/nvidia/tegra194.dtsi | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/boot/dts/nvidia/tegra194.dtsi +2 -0
@@ -3177,6 +3177,8 @@ timer { <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, <GIC_PPI 10 + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 15 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>; interrupt-parent = <&gic>; always-on;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 170/217 crypto: ccm - Set rfc4309 maxauthsize from child
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Herbert Xu <[email protected]>
[ Upstream commit 438f4896f78f69ec73d5f32d2c024193f1223569 ]
Set the maxauthsize of rfc4309 using that of the child algorithm.
--- crypto/ccm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
crypto/ccm.c +1 -1
@@ -747,7 +747,7 @@ static int crypto_rfc4309_create(struct crypto_template *tmpl, inst->alg.ivsize =8 ; inst->alg.chunksize =crypto_aead_alg_chunksize (alg); - inst->alg.maxauthsize =16 ; + inst->alg.maxauthsize =crypto_aead_alg_maxauthsize (alg); inst->alg.base.cra_ctxsize =sizeof (struct crypto_rfc4309_ctx );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 171/217 crypto: tegra - fix rctx->cryptlen calculation in tegra_gcm_do_one_req()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vladislav Dronov <[email protected]>
[ Upstream commit 360f2974fcea49c61f6d6f81554741a9eeee7168 ]
Perform rctx->cryptlen calculation in tegra_gcm_do_one_req() the same way it is done in tegra_ccm_crypt_init(). The current formulae may lead to a crash if a caller does not call tegra_gcm_setauthsize() and so ctx->authsize remains zero. Then a decrypt operation with incorrect rctx->cryptlen will lead to a write beyound rctx->dst_sg buffer.
As a follow-up cleanup delete struct tegra_aead_ctx->authsize field since it appears to be completely unused. Also simplify tegra_ccm_setauthsize() and tegra_gcm_setauthsize() functions respectively.
--- drivers/crypto/tegra/tegra-se-aes.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-)
drivers/crypto/tegra/tegra-se-aes.c +3 -19
@@ -45,7 +45,6 @@ struct tegra_aes_reqctx {struct tegra_aead_ctx {struct tegra_se *se; -unsigned int authsize;u32 alg;u32 key_id;u32 keylen;
@@ -1290,7 +1289,7 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq)if (rctx->encrypt) rctx->cryptlen = req->cryptlen;else - rctx->cryptlen = req->cryptlen - ctx->authsize; + rctx->cryptlen = req->cryptlen - rctx->authsize;memcpy (rctx->iv, req->iv,GCM_AES_IV_SIZE ); rctx->iv[3 ] = (1 <<24 );
@@ -1394,8 +1393,6 @@ static int tegra_aead_cra_init(struct crypto_aead *tfm)static int tegra_ccm_setauthsize (struct crypto_aead *tfm,unsigned int authsize) { -struct tegra_aead_ctx *ctx =crypto_aead_ctx (tfm); -switch (authsize) {case 4 :case 6 :
@@ -1404,28 +1401,15 @@ static int tegra_ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsizecase 12 :case 14 :case 16 : -break ; +return 0 ;default :return -EINVAL ; } - - ctx->authsize = authsize; - -return 0 ; }static int tegra_gcm_setauthsize (struct crypto_aead *tfm,unsigned int authsize) { -struct tegra_aead_ctx *ctx =crypto_aead_ctx (tfm); -int ret; - - ret =crypto_gcm_check_authsize (authsize); -if (ret) -return ret; - - ctx->authsize = authsize; - -return 0 ; +return crypto_gcm_check_authsize (authsize); }static void tegra_aead_cra_exit (struct crypto_aead *tfm)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 172/217 ovpn: fix NULL dereference when killing missing key
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ralf Lici <[email protected]>
[ Upstream commit 41d44ac7a61e2f74453af40d4fe1b82af9ea0ada ]
ovpn_crypto_kill_key assumes both crypto slots are populated and dereferences each slot before checking it. That is not guaranteed: a peer can have only one installed key, and the kill path may be asked to remove a key that is not present.
Read each slot once while holding the crypto state lock, check for NULL before looking at key_id, and only replace the slot that actually matches.
--- drivers/net/ovpn/crypto.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/net/ovpn/crypto.c +10 -6
@@ -58,15 +58,19 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs)bool ovpn_crypto_kill_key (struct ovpn_crypto_state *cs,u8 key_id) {struct ovpn_crypto_key_slot *ks =NULL ; +struct ovpn_crypto_key_slot *tmp; +int slot =0 ;spin_lock_bh (&cs->lock); -if (rcu_access_pointer (cs->slots[0 ])->key_id == key_id) { - ks =rcu_replace_pointer (cs->slots[0 ],NULL , -lockdep_is_held (&cs->lock)); - }else if (rcu_access_pointer (cs->slots[1 ])->key_id == key_id) { - ks =rcu_replace_pointer (cs->slots[1 ],NULL , -lockdep_is_held (&cs->lock)); + tmp =rcu_access_pointer (cs->slots[slot]); +if (!tmp || tmp->key_id != key_id) { + slot =1 ; + tmp =rcu_access_pointer (cs->slots[slot]); } + +if (tmp && tmp->key_id == key_id) + ks =rcu_replace_pointer (cs->slots[slot],NULL , +lockdep_is_held (&cs->lock));spin_unlock_bh (&cs->lock);if (ks)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 173/217 ovpn: finish crypto callback cleanup before peer release
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ralf Lici <[email protected]>
[ Upstream commit 9e163917a86c6adfbe150e13f4c73653a54616de ]
Crypto completion callbacks hold both key-slot and peer references. The peer reference pins the netdev, and dropping the last peer reference can let netdev unregistration and module removal make progress.
Do not release that peer reference before the callback has finished its own cleanup. If ovpn_crypto_key_slot_put runs after ovpn_peer_put, it can schedule an RCU callback backed by module text after ovpn_cleanup rcu_barrier has already run. The TX error path also freed the remaining skb after ovpn_peer_put, leaving callback cleanup outside the peer/netdev lifetime window.
Release the key slot and free any remaining skb first, then drop the peer reference as the last callback action.
--- drivers/net/ovpn/io.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/net/ovpn/io.c +5 -5
@@ -199,10 +199,10 @@ void ovpn_decrypt_post(void *data, int ret)ovpn_dev_dstats_rx_dropped (peer->ovpn->dev);kfree_skb (skb); drop_nocount: -if (likely (peer)) -ovpn_peer_put (peer);if (likely (ks))ovpn_crypto_key_slot_put (ks); +if (likely (peer)) +ovpn_peer_put (peer); }/* RX path entry point: decrypt packet and forward it to the device */
@@ -299,11 +299,11 @@ void ovpn_encrypt_post(void *data, int ret) err:if (unlikely (skb))ovpn_dev_dstats_tx_dropped (peer->ovpn->dev); -if (likely (peer)) -ovpn_peer_put (peer); +kfree_skb (skb);if (likely (ks))ovpn_crypto_key_slot_put (ks); -kfree_skb (skb); +if (likely (peer)) +ovpn_peer_put (peer); }static bool ovpn_encrypt_one (struct ovpn_peer *peer,struct sk_buff *skb)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 174/217 riscv: ftrace: Fix ftrace_modify_call failure on kprobed functions
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pu Lehui <[email protected]>
[ Upstream commit 2820d227ad4ee70805d693d698437cc3e88d6c3d ]
We are frequently hitting the following splat during the riscv bpf selftests:
00000000026dc75a: expected (7c3ff297) but got (00100073) ------------[ ftrace bug ]------------
ftrace failed to modify
[<ffffffff03c44c1c>] bpf_kfunc_common_test+0x4/0x20 [bpf_testmod] actual: e7:82:c2:ce
Updating ftrace call site to call a different ftrace function ftrace record flags: 80100002
(2)
expected tramp: ffffffff80043904
------------[ cut here ]------------ WARNING: kernel/trace/ftrace.c:2278 at ftrace_bug+0x46e/0x4b0, CPU#1: test_progs/98 ... [<ffffffff80008f4e>] ftrace_bug+0x46e/0x4b0 [<ffffffff803d3e86>] ftrace_replace_code+0x16e/0x170 [<ffffffff803d42b6>] ftrace_modify_all_code+0x12e/0x1b8 [<ffffffff800430f4>] arch_ftrace_update_code+0x14/0x28 [<ffffffff803e0324>] ftrace_startup+0x14c/0x2a0 [<ffffffff803e133c>] ftrace_startup_subops+0x584/0x1050 [<ffffffff804500e6>] register_ftrace_graph+0x4e6/0x1018 [<ffffffff804cf9f6>] register_fprobe_ips+0xc66/0x12f8 [<ffffffff8049abe8>] bpf_kprobe_multi_link_attach+0x5d8/0xe68 [<ffffffff8050fcaa>] __sys_bpf+0x3d5a/0x47f0 [<ffffffff805107ee>] __riscv_sys_bpf+0xae/0x168 [<ffffffff80034d78>] syscall_handler+0x60/0x100 [<ffffffff8228b4f4>] do_trap_ecall_u+0x174/0x208 [<ffffffff822b69c4>] handle_exception+0x16c/0x178
After debugging, it can be triggered by similar commands below:
``` echo do_nanosleep > set_ftrace_filter echo function > current_tracer echo 'p do_nanosleep' > kprobe_events echo 1 > events/kprobes/enable echo 'f do_nanosleep' > dynamic_events echo 1 > events/fprobes/enable ```
The reason is that attaching a kprobe to an ftrace-traced function entry replaces its initial auipc insn with ebreak. When ftrace_modify_call later runs, it expects auipc insn, so verification fails and triggers ftrace_bug.
The expected auipc logic remains conceptually unchanged, and kprobe single-stepping ensures normal execution. Therefore, if the first insn is ebreak, bypassing the check to continue patching the jalr insn is safe and avoids ftrace failures.
--- arch/riscv/kernel/ftrace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/riscv/kernel/ftrace.c +4 -1
@@ -12,6 +12,7 @@#include <linux/stop_machine.h> #include <asm/cacheflush.h> #include <asm/text-patching.h> +#include <asm/insn.h> #ifdef CONFIG_DYNAMIC_FTRACE void ftrace_arch_code_modify_prepare (void )
@@ -63,7 +64,9 @@ static int __ftrace_modify_call(unsigned long source, unsigned long target, boolif (copy_from_kernel_nofault (replaced, (void *)source,2 *MCOUNT_INSN_SIZE ))return -EFAULT ; -if (replaced[0 ] != call[0 ]) { +/* Bypass the check if the auipc insn is a kprobe breakpoint */ +if (replaced[0 ] != call[0 ] && + !(riscv_insn_is_ebreak (replaced[0 ]) ||riscv_insn_is_c_ebreak (replaced[0 ]))) {pr_err ("%p: expected (%08x) but got (%08x)\n" , (void *)source, call[0 ], replaced[0 ]);return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 175/217 perf: Reject exited events as group leaders
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit fa091f46c3833fb22384f10eade2b4e1e1d0b278 ]
perf_event_remove_on_exec() sets remove-on-exec events to the EXIT state and detaches their group relationships. The event's file descriptor can remain open, however, and perf_event_open() currently accepts that event as a group leader because its early validation rejects only REVOKED and DEAD events.
A new sibling can consequently be linked to the detached leader. When the leader is closed, perf_group_detach() observes that its PERF_ATTACH_GROUP bit is already clear and skips the new sibling. The sibling then retains a group_leader pointer to the freed event.
Reject group leaders in the EXIT state. Perform the check while holding the shared context mutex so that an exec in the target task cannot detach the leader between validation and group attachment.
--- kernel/events/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
kernel/events/core.c +7 -1
@@ -13549,7 +13549,7 @@ SYSCALL_DEFINE5(perf_event_open, goto err_fd; } group_leader =fd_file (group)->private_data; -if (group_leader->state <=PERF_EVENT_STATE_REVOKED ) { +if (group_leader->state <=PERF_EVENT_STATE_EXIT ) { err = -ENODEV ; goto err_fd; }
@@ -13680,6 +13680,12 @@ SYSCALL_DEFINE5(perf_event_open,if (group_leader->ctx != ctx) goto err_locked; +/* Recheck under ctx::mutex to serialize against remove-on-exec. */ +if (group_leader->state <=PERF_EVENT_STATE_EXIT ) { + err = -ENODEV ; + goto err_locked; + } +/* * Only a group leader can be exclusive or pinned */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 176/217 gpio: ml-ioh: share the register lock across channels
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junjie Cao <[email protected]>
[ Upstream commit a9253ee6771c8ab3c6de07ea75d9e2c1cef3cd97 ]
Suspend and resume hold channel 0's lock while saving and restoring registers for all eight channels. Code paths using the other seven locks can therefore run concurrently with PM.
Use one controller-wide lock shared by all channels.
--- drivers/gpio/gpio-ml-ioh.c | 69 ++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 32 deletions(-)
drivers/gpio/gpio-ml-ioh.c +37 -32
@@ -73,7 +73,7 @@ struct ioh_gpio_reg_data {* @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM * @ch: Indicate GPIO channel * @irq_base: Save base of IRQ number for interrupt -* @spinlock: Used for register access protection +* @spinlock: Shared register access lock */ struct ioh_gpio {void __iomem *base;
@@ -84,7 +84,12 @@ struct ioh_gpio {u32 gpio_use_sel;int ch;int irq_base; +raw_spinlock_t *spinlock; +}; + +struct ioh_gpio_device {raw_spinlock_t spinlock; +struct ioh_gpio chip[8 ]; };static const int num_ports[] = {6 ,12 ,16 ,16 ,15 ,16 ,16 ,12 };
@@ -95,7 +100,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)struct ioh_gpio *chip =gpiochip_get_data (gpio);unsigned long flags; -raw_spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (chip->spinlock, flags); reg_val =ioread32 (&chip->reg->regs[chip->ch].po);if (val) reg_val |=BIT (nr);
@@ -103,7 +108,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT (nr);iowrite32 (reg_val, &chip->reg->regs[chip->ch].po); -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (chip->spinlock, flags);return 0 ; }
@@ -123,7 +128,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,u32 reg_val;unsigned long flags; -raw_spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (chip->spinlock, flags); pm =ioread32 (&chip->reg->regs[chip->ch].pm); pm &=BIT (num_ports[chip->ch]) -1 ; pm |=BIT (nr);
@@ -136,7 +141,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, reg_val &= ~BIT (nr);iowrite32 (reg_val, &chip->reg->regs[chip->ch].po); -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (chip->spinlock, flags);return 0 ; }
@@ -147,12 +152,12 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)u32 pm;unsigned long flags; -raw_spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (chip->spinlock, flags); pm =ioread32 (&chip->reg->regs[chip->ch].pm); pm &=BIT (num_ports[chip->ch]) -1 ; pm &= ~BIT (nr);iowrite32 (pm, &chip->reg->regs[chip->ch].pm); -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (chip->spinlock, flags);return 0 ; }
@@ -256,7 +261,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type)dev_dbg (chip->dev,"%s:irq=%d type=%d ch=%d pos=%d type=%d\n" , __func__, irq, type, ch, im_pos, type); -raw_spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (chip->spinlock, flags);switch (type) {case IRQ_TYPE_EDGE_RISING :
@@ -296,7 +301,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) ien =ioread32 (&chip->reg->regs[chip->ch].ien);iowrite32 (ien |BIT (ch), &chip->reg->regs[chip->ch].ien); end: -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (chip->spinlock, flags);return 0 ; }
@@ -326,11 +331,11 @@ static void ioh_irq_disable(struct irq_data *d)unsigned long flags;u32 ien; -raw_spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (chip->spinlock, flags); ien =ioread32 (&chip->reg->regs[chip->ch].ien); ien &= ~BIT (d->irq - chip->irq_base);iowrite32 (ien, &chip->reg->regs[chip->ch].ien); -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (chip->spinlock, flags); }static void ioh_irq_enable (struct irq_data *d)
@@ -340,11 +345,11 @@ static void ioh_irq_enable(struct irq_data *d)unsigned long flags;u32 ien; -raw_spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (chip->spinlock, flags); ien =ioread32 (&chip->reg->regs[chip->ch].ien); ien |=BIT (d->irq - chip->irq_base);iowrite32 (ien, &chip->reg->regs[chip->ch].ien); -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (chip->spinlock, flags); }static irqreturn_t ioh_gpio_handler (int irq,void *dev_id)
@@ -407,8 +412,8 @@ static int ioh_gpio_probe(struct pci_dev *pdev,int ret;int i, j;struct ioh_gpio *chip; +struct ioh_gpio_device *priv;void __iomem *base; -void *chip_save;int irq_base; ret =pcim_enable_device (pdev);
@@ -429,18 +434,18 @@ static int ioh_gpio_probe(struct pci_dev *pdev,return -ENOMEM ; } - chip_save =devm_kcalloc (dev,8 ,sizeof (*chip),GFP_KERNEL ); -if (chip_save ==NULL ) { + priv =devm_kzalloc (dev,sizeof (*priv),GFP_KERNEL ); +if (!priv)return -ENOMEM ; - } - chip = chip_save; +raw_spin_lock_init (&priv->spinlock); + chip = priv->chip;for (i =0 ; i <8 ; i++, chip++) { chip->dev = dev; chip->base = base; chip->reg = chip->base; chip->ch = i; -raw_spin_lock_init (&chip->spinlock); + chip->spinlock = &priv->spinlock;ioh_gpio_setup (chip, num_ports[i]); ret =devm_gpiochip_add_data (dev, &chip->gpio, chip);if (ret) {
@@ -449,7 +454,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, } } - chip = chip_save; + chip = priv->chip;for (j =0 ; j <8 ; j++, chip++) { irq_base =devm_irq_alloc_descs (dev,-1 ,IOH_IRQ_BASE , num_ports[j],NUMA_NO_NODE );
@@ -466,7 +471,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev,return ret; } - chip = chip_save; + chip = priv->chip; ret =devm_request_irq (dev, pdev->irq, ioh_gpio_handler,IRQF_SHARED ,KBUILD_MODNAME , chip);if (ret !=0 ) {
@@ -474,33 +479,33 @@ static int ioh_gpio_probe(struct pci_dev *pdev,return ret; } -pci_set_drvdata (pdev, chip); +pci_set_drvdata (pdev, priv);return 0 ; }static int __maybe_unused ioh_gpio_suspend (struct device *dev) { -struct ioh_gpio *chip =dev_get_drvdata (dev); +struct ioh_gpio_device *priv =dev_get_drvdata (dev);unsigned long flags; -raw_spin_lock_irqsave (&chip->spinlock, flags); -ioh_gpio_save_reg_conf (chip); -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_lock_irqsave (&priv->spinlock, flags); +ioh_gpio_save_reg_conf (priv->chip); +raw_spin_unlock_irqrestore (&priv->spinlock, flags);return 0 ; }static int __maybe_unused ioh_gpio_resume (struct device *dev) { -struct ioh_gpio *chip =dev_get_drvdata (dev); +struct ioh_gpio_device *priv =dev_get_drvdata (dev);unsigned long flags; -raw_spin_lock_irqsave (&chip->spinlock, flags); -iowrite32 (0x01 , &chip->reg->srst); -iowrite32 (0x00 , &chip->reg->srst); -ioh_gpio_restore_reg_conf (chip); -raw_spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_lock_irqsave (&priv->spinlock, flags); +iowrite32 (0x01 , &priv->chip->reg->srst); +iowrite32 (0x00 , &priv->chip->reg->srst); +ioh_gpio_restore_reg_conf (priv->chip); +raw_spin_unlock_irqrestore (&priv->spinlock, flags);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 177/217 ASoC: tas2781: fix clang build error for goto bypassing cleanup variable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shenghao Ding <[email protected]>
[ Upstream commit 566fec6a33075a0ea5c441c26571221f17f4ed98 ]
Remove invalid goto exit paths that jump across guard(mutex) cleanup variable initialization, replace them with direct kfree(src) and return, to fix the s390 clang build error in acoustic_ctl_write().
--- sound/soc/codecs/tas2781-i2c.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
sound/soc/codecs/tas2781-i2c.c +9 -9
@@ -1533,8 +1533,8 @@ static ssize_t acoustic_ctl_write(struct file *file,if (src[0 ] > max_pkg_len && src[0 ] != count) {dev_err (priv->dev,"pkg(%u), max(%u), count(%u) mismatch.\n" , src[0 ], max_pkg_len, (unsigned int )count); - ret =0 ; - goto exit; +kfree (src); +return 0 ; }switch (src[1 ]) {
@@ -1548,14 +1548,14 @@ static ssize_t acoustic_ctl_write(struct file *file,break ;default :dev_err (priv->dev,"%s Wrong code %02x.\n" , __func__, src[1 ]); - ret =0 ; - goto exit; +kfree (src); +return 0 ; }if (len <1 ) {dev_err (priv->dev,"pkg fmt invalid %02x.\n" , len); - ret =0 ; - goto exit; +kfree (src); +return 0 ; }for (j =0 ; j < priv->ndev; j++)
@@ -1565,8 +1565,8 @@ static ssize_t acoustic_ctl_write(struct file *file, }if (j >= priv->ndev) {dev_err (priv->dev,"no such device 0x%02x.\n" , src[2 ]); - ret =0 ; - goto exit; +kfree (src); +return 0 ; } reg =TASDEVICE_REG (src[3 ], src[4 ], src[5 ]);
@@ -1597,7 +1597,7 @@ static ssize_t acoustic_ctl_write(struct file *file,dev_err (priv->dev,"i2c communication error.\n" ); else ret = count; -exit: +kfree (src);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 178/217 netfilter: ipset: fix refcount race between list:set GC and swap
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei (Microsoft) <[email protected]>
[ Upstream commit 0c88868271653537ed443272dd8e7d13634d214b ]
__ip_set_put_byindex() resolved the index to a set pointer under RCU, then took ip_set_ref_lock in __ip_set_put() to decrement set->ref. ip_set_swap() holds that same lock while swapping both the ip_set_list slots and the two sets' ref counters, so it can interleave between the dereference and the lock acquisition, leaving the caller to decrement a set whose reference already moved to the other index and hit BUG_ON(set->ref == 0). list_set_gc() reaches this from timer softirq, which the nfnl mutex does not serialize against swap: an expiring list:set member calls list_set_del() -> ip_set_put_byindex() while IPSET_CMD_SWAP runs on the referenced sets.
Resolve the index and decrement under ip_set_ref_lock, as ip_set_swap() already does, keeping the refcount tied to the index rather than to a stale set pointer.
kernel BUG at net/netfilter/ipset/ip_set_core.c:685!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ip_set_put_byindex (net/netfilter/ipset/ip_set_core.c:870) Call Trace: <IRQ> list_set_del (net/netfilter/ipset/ip_set_list_set.c:159) set_cleanup_entries (net/netfilter/ipset/ip_set_list_set.c:181) list_set_gc (net/netfilter/ipset/ip_set_list_set.c:578) call_timer_fn (kernel/time/timer.c:1748) __run_timers (kernel/time/timer.c:1799 kernel/time/timer.c:2374) run_timer_softirq (kernel/time/timer.c:2405) </IRQ> Kernel panic - not syncing: Fatal exception in interrupt
--- net/netfilter/ipset/ip_set_core.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
net/netfilter/ipset/ip_set_core.c +13 -6
@@ -679,11 +679,18 @@ __ip_set_get(struct ip_set *set) }static void -__ip_set_put (struct ip_set *set) +__ip_set_put_locked (struct ip_set *set) { -write_lock_bh (&ip_set_ref_lock); +lockdep_assert_held (&ip_set_ref_lock);BUG_ON (set->ref ==0 ); set->ref--; +} + +static void +__ip_set_put (struct ip_set *set) +{ +write_lock_bh (&ip_set_ref_lock); +__ip_set_put_locked (set);write_unlock_bh (&ip_set_ref_lock); }
@@ -854,11 +861,11 @@ __ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index) {struct ip_set *set; -rcu_read_lock (); - set =rcu_dereference (inst->ip_set_list)[index]; +write_lock_bh (&ip_set_ref_lock); + set =ip_set (inst, index);if (set) -__ip_set_put (set); -rcu_read_unlock (); +__ip_set_put_locked (set); +write_unlock_bh (&ip_set_ref_lock); }void
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 179/217 netfilter: nf_tables_offload: suppress WARN_ON_ONCE for ENOMEM in abort path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexey Velichayshiy <[email protected]>
[ Upstream commit d02f592064347e0c1e0d84f24941ad338838cc48 ]
In nft_flow_rule_offload_abort(), WARN_ON_ONCE(err) is triggered on every error during rollback, including -ENOMEM. Memory allocation failures are expected under low-memory conditions and do not indicate a kernel bug.
Trace for example:nft_flow_offload_chain() // FLOW_BLOCK_BIND
nft_flow_block_chain()
nft_chain_offload_cmd()
nft_block_offload_cmd()
->ndo_setup_tc()
nsim_setup_tc()
flow_block_cb_setup_simple()
flow_block_cb_alloc() // fails to -ENOMEMThe warning was reproduced on the 5.10 stable kernel under memory pressure via fault injection, but the underlying bug exists in mainline as well, as demonstrated by the ENOMEM trace above. The following splat was triggered during nf_tables transaction processing:
WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Modules linked in: CPU: 0 PID: 8567 Comm: syz-executor.0 Not tainted 5.10.260-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] RIP: 0010:nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Call Trace: nf_tables_commit+0x3bd/0x4bd0 net/netfilter/nf_tables_api.c:8604 nfnetlink_rcv_batch+0xb1e/0x1f20 net/netfilter/nfnetlink.c:509 nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:579 [inline] nfnetlink_rcv+0x3b3/0x420 net/netfilter/nfnetlink.c:597 netlink_unicast_kernel net/netlink/af_netlink.c:1314 [inline] netlink_unicast+0x6cd/0xa00 net/netfilter/af_netlink.c:1340 netlink_sendmsg+0x906/0xe10 net/netfilter/af_netlink.c:1919 sock_sendmsg_nosec net/socket.c:651 [inline] __sock_sendmsg+0x155/0x190 net/socket.c:663 ____sys_sendmsg+0x705/0x870 net/socket.c:2379 ___sys_sendmsg+0x100/0x170 net/socket.c:2433 __sys_sendmsg+0xe9/0x1c0 net/socket.c:2462 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1
Change the condition to WARN_ON_ONCE(err && err != -ENOMEM) so that warnings are only emitted for unexpected errors. This aligns with the common kernel practice of not warning on -ENOMEM.
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
--- net/netfilter/nf_tables_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/nf_tables_offload.c +1 -1
@@ -558,7 +558,7 @@ static void nft_flow_rule_offload_abort(struct net *net,break ; } -if (WARN_ON_ONCE (err)) +if (WARN_ON_ONCE (err && err != -ENOMEM ))break ; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 180/217 netfilter: flowtable: publish GC-visible tuple last
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jérémy Jean <[email protected]>
[ Upstream commit 2014ac62df9d45bb9a004a043e85df7be09ed780 ]
nf_flow_table_iterate() only treats original-direction tuple nodes as owning entries. Publishing the original node first lets GC observe and free a flow while flow_offload_add() is still inserting the reply node. Publish the reply node first and the original node last so GC never sees a partially installed flow.
KASAN can trigger slab-use-after-free read and write reports in the flowtable/rhashtable path (rht_deferred_worker, jhash, flow_offload_del, flow_offload_lookup, etc.).
--- net/netfilter/nf_flow_table_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
net/netfilter/nf_flow_table_core.c +4 -3
@@ -324,17 +324,18 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) flow->timeout = nf_flowtable_time_stamp +flow_offload_get_timeout (flow); err =rhashtable_insert_fast (&flow_table->rhashtable, - &flow->tuplehash[0 ].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY ].node, nf_flow_offload_rhash_params);if (err <0 )return err; +/* GC only iterates original-direction entries; publish original last. */ err =rhashtable_insert_fast (&flow_table->rhashtable, - &flow->tuplehash[1 ].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL ].node, nf_flow_offload_rhash_params);if (err <0 ) {rhashtable_remove_fast (&flow_table->rhashtable, - &flow->tuplehash[0 ].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY ].node, nf_flow_offload_rhash_params);return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 181/217 netfilter: ipset: fix list type element drift bug
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 4cbd69766b35a089664cadb1f613bb85f7ef77a9 ]
If list_set_uadd() calls list_set_replace() to swap an expired entry, the element count remains the same, therefore the increment must be elided.
--- net/netfilter/ipset/ip_set_list_set.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/netfilter/ipset/ip_set_list_set.c +5 -2
@@ -301,9 +301,12 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, e->set = set;INIT_LIST_HEAD (&e->list);list_set_init_extensions (set, ext, e); -if (n) +if (n) {list_set_replace (set, e, n); -else if (next) +return 0 ; + } + +if (next)list_add_tail_rcu (&e->list, &next->list);else if (prev)list_add_rcu (&e->list, &prev->list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 182/217 netfilter: ipset: let destroy callbacks adjust ext mem size
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 490937b88cb592cc0c5367758edd700fd5abd15c ]
For bitmap this change makes no difference, because destructors are called synchronously.
List type however calls them via call_rcu() so accounting decrement can happen after list_set_flush() set ext_size to 0.
'set->elements = 0' can be removed for the same reason in the list type case, it calls 'set->elements--' for each element.
--- net/netfilter/ipset/ip_set_bitmap_gen.h | 2 +- net/netfilter/ipset/ip_set_list_set.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-)
net/netfilter/ipset/ip_set_bitmap_gen.h +1 -1
@@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set)mtype_ext_cleanup (set);bitmap_zero (map->members, map->elements); set->elements =0 ; -atomic64_set (&set->ext_size,0 ); +DEBUG_NET_WARN_ON_ONCE (atomic64_read (&set->ext_size) >0 ); }/* Calculate the actual memory size of the set data */
net/netfilter/ipset/ip_set_list_set.c +1 -2
@@ -423,8 +423,7 @@ list_set_flush(struct ip_set *set)list_for_each_entry_safe (e, n, &map->members, list)list_set_del (set, e); - set->elements =0 ; -atomic64_set (&set->ext_size,0 ); +DEBUG_NET_WARN_ON_ONCE (set->elements >0 ); } static void
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 183/217 eth: bnxt: cancel IRQ notifier before freeing affinity mask
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit c703f62c30f2db7f40ad575f0034636d8a401fac ]
bnxt_irq_affinity_notify() copies into irq->cpu_mask. Cancel the notifier before freeing irq->cpu_mask.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +2 -2
@@ -11728,14 +11728,14 @@ static void bnxt_free_irq(struct bnxt *bp) irq = &bp->irq_tbl[map_idx];if (irq->requested) { +bnxt_release_irq_notifier (irq); +if (irq->have_cpumask) {irq_update_affinity_hint (irq->vector,NULL );free_cpumask_var (irq->cpu_mask); irq->have_cpumask =0 ; } -bnxt_release_irq_notifier (irq); -free_irq (irq->vector, bp->bnapi[i]); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 184/217 eth: bnxt: keep the aRFS rmap updated when TPH is enabled
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 4b5cb58a4443fff67aa18a0d7b645b2220f2fcf8 ]
The TPH support must have broken aRFS in bnxt. IRQ can only have one notifier, so installing the TPH notifier is overriding the one implicitly installed by irq_cpu_rmap_add().
Make sure we call cpu_rmap_update() from the TPH notifier.
We need to be careful with the ordering and not free the rmap until we unregistered the notifier. Note that moving the rmap freeing after the early return in bnxt_free_irq() is fine - there's no path that could leave rmap with irq_tbl being NULL.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +15 -4
@@ -11643,6 +11643,16 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, irq = container_of(notify, struct bnxt_irq, affinity_notify); +#ifdef CONFIG_RFS_ACCEL +if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) { + err =cpu_rmap_update (irq->bp->dev->rx_cpu_rmap, irq->ring_nr, + mask); +if (err) +netdev_warn (irq->bp->dev, +"aRFS rmap update failed: %d\n" , err); + } +#endif +if (!irq->bp->tph_mode)return ;
@@ -11716,10 +11726,6 @@ static void bnxt_free_irq(struct bnxt *bp)struct bnxt_irq *irq;int i; -#ifdef CONFIG_RFS_ACCEL -free_irq_cpu_rmap (bp->dev->rx_cpu_rmap); - bp->dev->rx_cpu_rmap =NULL ; -#endif if (!bp->irq_tbl || !bp->bnapi)return ;
@@ -11745,6 +11751,11 @@ static void bnxt_free_irq(struct bnxt *bp)/* Disable TPH support */ pcie_disable_tph (bp->pdev); bp->tph_mode =0 ; + +#ifdef CONFIG_RFS_ACCEL +free_irq_cpu_rmap (bp->dev->rx_cpu_rmap); + bp->dev->rx_cpu_rmap =NULL ; +#endif }static int bnxt_request_irq (struct bnxt *bp)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 185/217 ipvlan: inherit needed_headroom and needed_tailroom from phy_dev
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit e16e960d55a40d36bd7c2494cc005e757dc9a1ef ]
ipvlan devices inherit hard_header_len from phy_dev during ipvlan_init(), but leave needed_headroom and needed_tailroom set to 0.
When the underlying phy_dev (or stacked lower device) requires extra headroom or tailroom for headers/trailers (e.g. macsec, ipsec, wireguard, tunnels, or veth with rx headroom), upper layers calculating packet headroom and tailroom fail to reserve sufficient space.
This can result in reallocation overhead, skb headroom underflows, or KASAN slab-use-after-free crashes when dev_hard_header() / ipvlan_hard_header() prepends header data or when lower devices append tailroom.
Fix this by:
1. Inheriting needed_headroom and needed_tailroom from phy_dev in ipvlan_init().
2. Propagating needed_headroom and needed_tailroom updates to attached ipvlans in ipvlan_device_event() when receiving NETDEV_FEAT_CHANGE events.
--- drivers/net/ipvlan/ipvlan_main.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/ipvlan/ipvlan_main.c +4 -0
@@ -146,6 +146,8 @@ static int ipvlan_init(struct net_device *dev) dev->lltx = true;netif_inherit_tso_max (dev, phy_dev); dev->hard_header_len = phy_dev->hard_header_len; + dev->needed_headroom = phy_dev->needed_headroom; + dev->needed_tailroom = phy_dev->needed_tailroom;netdev_lockdep_set_classes (dev);
@@ -773,6 +775,8 @@ static int ipvlan_device_event(struct notifier_block *unused,case NETDEV_FEAT_CHANGE :list_for_each_entry (ipvlan, &port->ipvlans, pnode) {netif_inherit_tso_max (ipvlan->dev, dev); + ipvlan->dev->needed_headroom = dev->needed_headroom; + ipvlan->dev->needed_tailroom = dev->needed_tailroom;netdev_update_features (ipvlan->dev); }break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 186/217 macvlan: inherit needed_headroom and needed_tailroom from lowerdev
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit cef51860becd9700217c81732ca1eb1ea6ed6fe1 ]
macvlan devices inherit hard_header_len from lowerdev during macvlan_init(), but leave needed_headroom and needed_tailroom set to 0.
When the underlying lowerdev requires extra headroom or tailroom for headers/trailers (e.g. macsec, ipsec, wireguard, tunnels, or veth with rx headroom), upper layers calculating packet headroom and tailroom fail to reserve sufficient space.
This can result in reallocation overhead, skb headroom underflows, or KASAN slab-use-after-free crashes when dev_hard_header() / macvlan_hard_header() prepends header data or when lower devices append tailroom.
Fix this by:
1. Inheriting needed_headroom and needed_tailroom from lowerdev in macvlan_init().
2. Propagating needed_headroom and needed_tailroom updates to attached macvlans in macvlan_device_event() when receiving NETDEV_FEAT_CHANGE events.
--- drivers/net/macvlan.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/macvlan.c +4 -0
@@ -943,6 +943,8 @@ static int macvlan_init(struct net_device *dev) dev->lltx = true;netif_inherit_tso_max (dev, lowerdev); dev->hard_header_len = lowerdev->hard_header_len; + dev->needed_headroom = lowerdev->needed_headroom; + dev->needed_tailroom = lowerdev->needed_tailroom;macvlan_set_lockdep_class (dev); vlan->pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
@@ -1817,6 +1819,8 @@ static int macvlan_device_event(struct notifier_block *unused,case NETDEV_FEAT_CHANGE :list_for_each_entry (vlan, &port->vlans, list) {netif_inherit_tso_max (vlan->dev, dev); + vlan->dev->needed_headroom = dev->needed_headroom; + vlan->dev->needed_tailroom = dev->needed_tailroom;netdev_update_features (vlan->dev); }break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 187/217 veth: fix queue index used to wake the peer txq in veth_poll
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jonas Köppeler <[email protected]>
[ Upstream commit 60db47f02bfa2aa688938aa199117ec4f8e31d23 ]
veth_poll() derives the index of the peer TX queue to wake from rq->xdp_rxq.queue_index. That field is only initialized by xdp_rxq_info_reg() in veth_enable_xdp_range(), which runs only when an XDP program is attached. On the plain GRO/NAPI path (veth_napi_enable_range()) xdp_rxq_info_reg() is never called, so queue_index stays 0 for every queue, as priv->rq is zero-allocated.
So in a multi-queue setup with GRO enabled and no XDP program attached, every NAPI instance looks at the peer's TX queue 0. If veth_xmit() stops peer TX queue 1 because the ptr_ring is full (NETDEV_TX_BUSY), nothing ever wakes it again: the poller draining queue 1 wakes queue 0 instead. veth implements no ndo_tx_timeout, so the netdev watchdog does not kick in either, and the queue stays stopped indefinitely.
Derive the index from the position of the rq within priv->rq instead, which is correct regardless of whether XDP was ever enabled.
Scripts to reproduce the stall are available at
https://github.com/netoptimizer/veth-backpressure-performance-testing
--- drivers/net/veth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/veth.c +1 -1
@@ -967,7 +967,7 @@ static int veth_poll(struct napi_struct *napi, int budget)struct veth_rq *rq = container_of(napi, struct veth_rq, xdp_napi);struct veth_priv *priv =netdev_priv (rq->dev); -int queue_idx = rq->xdp_rxq.queue_index; +int queue_idx = rq - priv->rq;struct netdev_queue *peer_txq;struct veth_stats stats = {};struct net_device *peer_dev;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 188/217 tcp: fix icsk_ack.ato bitfield overflow
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 60837e4b840a9c3f7ec826e3584df0bc6542a2c2 ]
On cross-region connections we observed delayed ACKs suddenly turning into immediate ACKs plus a TCP_MAX_QUICKACKS burst, as if the connection had just received its first data segment.
Commit 95b9a87c6a6b ("tcp: record last received ipv6 flowlabel") squeezed icsk_ack.ato into 8 bits, sized for TCP_DELACK_MAX. But both writers still bound ato by icsk_rto, which can be well above 255 jiffies, so the bitfield assignment silently wraps mod 256: repeated delack timer misses double ato up to icsk_rto, storing 320 as 64 and 256 as 0, and ato == 0 is the "first data packet" sentinel in tcp_event_data_recv().
Clamp both writers to TCP_DELACK_MAX, which the static_assert already guarantees to fit and tcp_send_delayed_ack() effectively caps ato at anyway.
--- net/ipv4/tcp_input.c | 6 +++--- net/ipv4/tcp_timer.c | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-)
net/ipv4/tcp_input.c +3 -3
@@ -1006,9 +1006,9 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)/* The fastest case is the first. */ icsk->icsk_ack.ato = (icsk->icsk_ack.ato >>1 ) +TCP_ATO_MIN /2 ; }else if (m < icsk->icsk_ack.ato) { - icsk->icsk_ack.ato = (icsk->icsk_ack.ato >>1 ) + m; -if (icsk->icsk_ack.ato > icsk->icsk_rto) - icsk->icsk_ack.ato = icsk->icsk_rto; + icsk->icsk_ack.ato =min3 ((icsk->icsk_ack.ato >>1 ) + (u32 )m, + icsk->icsk_rto, + (u32 )TCP_DELACK_MAX ); }else if (m > icsk->icsk_rto) {/* Too long gap. Apparently sender failed to * restart window, so that we send ACKs quickly.
net/ipv4/tcp_timer.c +3 -1
@@ -333,7 +333,9 @@ void tcp_delack_timer_handler(struct sock *sk)if (inet_csk_ack_scheduled (sk)) {if (!inet_csk_in_pingpong_mode (sk)) {/* Delayed ACK missed: inflate ATO. */ - icsk->icsk_ack.ato =min_t (u32, icsk->icsk_ack.ato <<1 , icsk->icsk_rto); + icsk->icsk_ack.ato =min3 ((u32 )icsk->icsk_ack.ato <<1 , + icsk->icsk_rto, + (u32 )TCP_DELACK_MAX ); }else {/* Delayed ACK missed: leave pingpong mode and * deflate ATO.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 189/217 net: phy: realtek: fix EEE advertisement write on the internal PHY MMD path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleksij Rempel <[email protected]>
[ Upstream commit 202fef9bbbf5784487eec27581389c6fb97c350d ]
In rtlgen_write_mmd(), the MDIO_AN_EEE_ADV case swaps the arguments to rtlgen_write_vend2(): it passes the MMD register number as the OCP address and the OCP address constant as the value. The caller's value is discarded and the write lands on the wrong register, so the EEE advertisement cannot be configured on the affected PHYs.
Mirror rtlgen_read_mmd() and write the value to RTL_MDIO_AN_EEE_ADV.
--- drivers/net/phy/realtek/realtek_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/phy/realtek/realtek_main.c +1 -1
@@ -1164,7 +1164,7 @@ static int rtlgen_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,if (devnum ==MDIO_MMD_VEND2 ) ret =rtlgen_write_vend2 (phydev, regnum, val);else if (devnum ==MDIO_MMD_AN && regnum ==MDIO_AN_EEE_ADV ) - ret =rtlgen_write_vend2 (phydev, regnum,RTL_MDIO_AN_EEE_ADV ); + ret =rtlgen_write_vend2 (phydev,RTL_MDIO_AN_EEE_ADV , val);else ret = -EOPNOTSUPP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 190/217 net: packet: fix wrong transport_header when sending VLAN-tagged frame
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wei Fang <[email protected]>
[ Upstream commit 01fdecc0480d916c799dbee584833a4a37e94d06 ]
In packet_parse_headers(), when processing a VLAN-tagged frame, skb_set_network_header() is called to advance network_header past the VLAN tag to the inner protocol header. skb_probe_transport_header() is then called with skb->protocol still set to the outer VLAN EtherType (e.g. ETH_P_8021Q), while nhoff (derived from skb_network_offset()) already points past the VLAN tag to the inner protocol header.
In __skb_flow_dissect(), proto is initialized to ETH_P_8021Q and nhoff points past the VLAN tag. When the dissector hits case ETH_P_8021Q, it reads a struct vlan_hdr at nhoff via __skb_header_pointer(), but that offset contains the inner protocol header (e.g. an IP header). The bytes are misinterpreted as a VLAN header, yielding a garbage encapsulated EtherType that matches no known protocol. The dissector returns false, so skb_probe_transport_header() never calls skb_set_transport_header(), leaving transport_header at its uninitialized sentinel value (~0U).
Move skb_probe_transport_header() to before skb_set_network_header(). At the time skb_probe_transport_header() is called, network_header still points to the VLAN header, so nhoff correctly points to the VLAN header. The flow dissector can then parse the VLAN header, extract the inner EtherType, and advance nhoff to the inner protocol header, allowing transport_header to be set correctly.
--- net/packet/af_packet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/packet/af_packet.c +2 -2
@@ -1942,13 +1942,13 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) sock->type == SOCK_RAW) skb->protocol =dev_parse_header_protocol (skb); +skb_probe_transport_header (skb); +/* Move network header to the right position for VLAN tagged packets */ if (likely (skb->dev->type ==ARPHRD_ETHER ) &ð_type_vlan (skb->protocol) &&vlan_get_protocol_and_depth (skb, skb->protocol, &depth) !=0 )skb_set_network_header (skb, depth); - -skb_probe_transport_header (skb); }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 191/217 net: tap: fix wrong transport_header when sending VLAN-tagged frame
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wei Fang <[email protected]>
[ Upstream commit cbb35cbe8db268fefe34c23df15348cf99025298 ]
In tap_get_user_xdp(), when processing a VLAN-tagged frame (e.g. ETH_P_8021Q), skb_set_network_header() is called first to advance network_header past the VLAN tag to the inner protocol header. skb_probe_transport_header() is then called with skb->protocol still set to ETH_P_8021Q, while nhoff (derived from skb_network_offset()) already points past the VLAN tag to the inner protocol header.
In __skb_flow_dissect(), proto is initialized to ETH_P_8021Q and nhoff points past the VLAN tag. When the dissector hits case ETH_P_8021Q, it reads a struct vlan_hdr at the current nhoff via __skb_header_pointer(), but that offset contains the inner protocol header (e.g. an IP header). The bytes are misinterpreted as a VLAN header, yielding a garbage encapsulated EtherType that matches no known protocol. The dissector returns false, so skb_probe_transport_header() never calls skb_set_transport_header(), leaving transport_header at its uninitialized sentinel value (~0U).
Move skb_set_network_header() to after skb_probe_transport_header(). At the time skb_probe_transport_header() is called, network_header still points to the VLAN header (offset ETH_HLEN), so nhoff is correct and the flow dissector can parse the VLAN header, extract the inner EtherType, and advance nhoff to the inner protocol header, allowing transport_header to be set correctly.
--- drivers/net/tap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/tap.c +2 -1
@@ -1091,12 +1091,13 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) } } +skb_probe_transport_header (skb); +/* Move network header to the right position for VLAN tagged packets */ if (eth_type_vlan (skb->protocol) &&vlan_get_protocol_and_depth (skb, skb->protocol, &depth) !=0 )skb_set_network_header (skb, depth); -skb_probe_transport_header (skb);dev_queue_xmit (skb);rcu_read_unlock ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 192/217 net: ngbe: fix NULL pointer dereference in non-MSI-X interrupt enabling
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiawen Wu <[email protected]>
[ Upstream commit 5f3a13e0bb5ebcc1ca2dfda42ea40b9f3c2be6ea ]
In non-MSI-X mode (such as legacy INTx or single MSI), wx->msix_entry is not allocated or initialized. Calling NGBE_INTR_MISC(wx) dereferences wx->msix_entry->entry, leading to a NULL pointer dereference crash.
This issue was introduced by fixing the IRQ vector when the number of VFs is 7. Fix the issue by explicitly checking pdev->msix_enabled to determine the correct vector index.
Additionally, as a side fix, set the interrupt mask to BIT(0) for the non-MSI-X fallback. In MSI/INTx mode, the MISC and queue interrupts share vector 0, and the WX_PX_MISC_IVAR register is only valid in the MSI-X case. Thus, BIT(0) is the correct mask for the miscellaneous cause when MSI-X is disabled.
--- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +3 -1
@@ -160,8 +160,10 @@ static void ngbe_irq_enable(struct wx *wx, bool queues)/* mask interrupt */ if (queues)wx_intr_enable (wx,NGBE_INTR_ALL ); -else +else if (wx->pdev->msix_enabled)wx_intr_enable (wx,NGBE_INTR_MISC (wx)); +else +wx_intr_enable (wx,BIT (0 )); }/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 193/217 net/tls: Fail tls_sw_splice_read() after a failed async decrypt
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 976df67f463db1fddaf2a32fb04f57ad2891a23d ]
When an async decrypt fails, tls_decrypt_done() records the error in ctx->async_wait.err and calls tls_err_abort(), which stores it in sk_err. tls_sw_recvmsg() and tls_sw_read_sock() each read async_wait.err once they hold the reader lock and fail the call: a record that did not authenticate breaks the connection.
tls_sw_splice_read() has no such check, and sk_err does not stand in for one. tls_rx_rec_wait() tests sk_err only inside the loop it skips whenever a record is already parsed, and the first reader to reach sock_error() clears it, while async_wait.err persists. A splice therefore keeps delivering records on a connection that recvmsg() and read_sock() refuse to read.
Read async_wait.err in tls_sw_splice_read() as the other two readers do.
--- net/tls/tls_sw.c | 5 +++++ 1 file changed, 5 insertions(+)
net/tls/tls_sw.c +5 -0
@@ -2319,6 +2319,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,if (err <0 )return err; +/* If crypto failed the connection is broken */ + err = ctx->async_wait.err; +if (err) + goto splice_read_end; +if (!skb_queue_empty (&ctx->rx_list)) { skb =__skb_dequeue (&ctx->rx_list); }else {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 194/217 ASoC: xilinx: formatter_pcm: pass aud_drv_data to irq handlers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rosen Penev <[email protected]>
[ Upstream commit f12afefb7b01f94d6d66d397f323a9914edbf70e ]
The irq handlers take a struct device pointer and call dev_get_drvdata() to obtain the driver data. However, the driver data is only set at the end of probe, after devm_request_irq(), so an interrupt taken in between causes the handlers to pass a NULL pointer to readl() and crash.
Pass the private data directly as the devm_request_irq() argument instead of the device pointer, matching what the handlers expect.
--- sound/soc/xilinx/xlnx_formatter_pcm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
sound/soc/xilinx/xlnx_formatter_pcm.c +4 -6
@@ -281,8 +281,7 @@ static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg) {u32 val;void __iomem *reg; -struct device *dev = arg; -struct xlnx_pcm_drv_data *adata =dev_get_drvdata (dev); +struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio +XLNX_MM2S_OFFSET +XLNX_AUD_STS ; val =readl (reg);
@@ -300,8 +299,7 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) {u32 val;void __iomem *reg; -struct device *dev = arg; -struct xlnx_pcm_drv_data *adata =dev_get_drvdata (dev); +struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio +XLNX_S2MM_OFFSET +XLNX_AUD_STS ; val =readl (reg);
@@ -637,7 +635,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) } ret =devm_request_irq (dev, aud_drv_data->mm2s_irq, xlnx_mm2s_irq_handler,0 , -"xlnx_formatter_pcm_mm2s_irq" , dev); +"xlnx_formatter_pcm_mm2s_irq" , aud_drv_data);if (ret) {dev_err (dev,"xlnx audio mm2s irq request failed\n" ); goto clk_err;
@@ -664,7 +662,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) ret =devm_request_irq (dev, aud_drv_data->s2mm_irq, xlnx_s2mm_irq_handler,0 ,"xlnx_formatter_pcm_s2mm_irq" , - dev); + aud_drv_data);if (ret) {dev_err (dev,"xlnx audio s2mm irq request failed\n" ); goto clk_err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 195/217 regmap: sdw-mbq: Fix swap of timeout and retry times
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Charles Keepax <[email protected]>
[ Upstream commit ae7fd6ff4c6713270d2efe6db87a4a58ccb7cc61 ]
When polling Function Busy using read_poll_timeout() the total timeout and retry delay arguments are swapped. This leads to only a single retry being processed, it seems the existing users typically do succeed before the first retry.
Swap the arguments over to ensure the correct polling time.
--- drivers/base/regmap/regmap-sdw-mbq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/base/regmap/regmap-sdw-mbq.c +1 -1
@@ -57,7 +57,7 @@ static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,if (ctx->readable_reg (dev, reg)) { ret =read_poll_timeout (sdw_read_no_pm, val, val <0 || !(val &SDCA_CTL_ENTITY_0_FUNCTION_BUSY ), - ctx->cfg.timeout_us, ctx->cfg.retry_us, + ctx->cfg.retry_us, ctx->cfg.timeout_us, false, slave, reg);if (val <0 )return val;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 196/217 af_packet: Dont send zero-byte data in tpacket_snd().
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 6bcd76c134c55c697148acb5c0194e9666abdf84 ]
syzbot reported a WARNING in __dev_queue_xmit() triggered via tpacket_snd():
skb_assert_len
WARNING: at include/linux/skbuff.h:2753 skb_assert_len WARNING: at __dev_queue_xmit+0x21bc/0x4970 net/core/dev.c:4781 Call Trace: <TASK> dev_queue_xmit include/linux/netdevice.h:3448 [inline] packet_xmit+0x243/0x310 net/packet/af_packet.c:276 tpacket_snd net/packet/af_packet.c:2907 [inline] packet_sendmsg+0x28d6/0x4eb0 net/packet/af_packet.c:3134
When sending 0-byte packets via TPACKET ring buffer on devices with no hard header (e.g. dev->hard_header_len == 0), tpacket_fill_skb() populates an skb with skb->len == 0 and returns 0. tpacket_snd() then forwards this empty skb to packet_xmit(), causing __dev_queue_xmit() to hit skb_assert_len(skb).
Similar checks exist in packet_snd() via commit dc633700f00f ("net/af_packet: check len when min_header_len equals to 0") and in packet_sendmsg_spkt() via commit 6a341729fb31 ("af_packet: Don't send zero-byte data in packet_sendmsg_spkt().").
Return -EINVAL in tpacket_fill_skb() when skb->len is zero to reject zero-length packets in tpacket_snd().
--- net/packet/af_packet.c | 3 +++ 1 file changed, 3 insertions(+)
net/packet/af_packet.c +3 -0
@@ -2659,6 +2659,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, len = ((to_write > len_max) ? len_max : to_write); } +if (unlikely (!skb->len)) +return -EINVAL ; +packet_parse_headers (skb, sock);return tp_len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 197/217 net/sched: act_api: fix TOCTOU NULL deref on a->goto_chain
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit f60b396ee174206fe08ebf997d16cd3801b77b22 ]
tcf_action_exec() handles TC_ACT_GOTO_CHAIN by first checking rcu_access_pointer(a->goto_chain) and then calling tcf_action_goto_chain_exec(), which does a second, independent rcu_dereference_bh(a->goto_chain) read and immediately dereferences chain->filter_chain. A concurrent tcf_action_set_ctrlact() (e.g. the gact replace path) can clear a->goto_chain between the two reads, so the second read returns NULL and tcf_action_goto_chain_exec() dereferences NULL.
Fix the race by doing a single rcu_dereference_bh() read of a->goto_chain in tcf_action_exec(), checking it once for NULL, and passing the resulting chain pointer into tcf_action_goto_chain_exec(). This turns the split check/use into a single check/use on one value.
--- net/sched/act_api.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
net/sched/act_api.c +5 -5
@@ -41,11 +41,9 @@ int tcf_dev_queue_xmit(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb)) }EXPORT_SYMBOL_GPL (tcf_dev_queue_xmit); -static void tcf_action_goto_chain_exec (const struct tc_action *a, +static void tcf_action_goto_chain_exec (const struct tcf_chain *chain,struct tcf_result *res) { -const struct tcf_chain *chain =rcu_dereference_bh (a->goto_chain); - res->goto_tp =rcu_dereference_bh (chain->filter_chain); }
@@ -1170,12 +1168,14 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,return TC_ACT_OK ; } }else if (TC_ACT_EXT_CMP (ret,TC_ACT_GOTO_CHAIN )) { -if (unlikely (!rcu_access_pointer (a->goto_chain))) { +struct tcf_chain *chain =rcu_dereference_bh (a->goto_chain); + +if (unlikely (!chain)) {tcf_set_drop_reason (skb,SKB_DROP_REASON_TC_CHAIN_NOTFOUND );return TC_ACT_SHOT ; } -tcf_action_goto_chain_exec (a, res); +tcf_action_goto_chain_exec (chain, res); }if (ret !=TC_ACT_PIPE )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 198/217 net/sched: cls_u32: skip hash tables in u32_bind_class()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Changzhong <[email protected]>
[ Upstream commit 6d3724e616faf952c3adcf8414fc21a828ef3709 ]
u32_walk() enumerates both struct tc_u_hnode and struct tc_u_knode through the walker callback. u32_bind_class() unconditionally casts the passed fh to tc_u_knode and accesses &n->res, so when fh is actually a tc_u_hnode, which has no tcf_result member, this results in a slab-out-of-bounds read of res->classid in tc_cls_bind_class().
The issue can be reproduced with the following commands:
tc qdisc add dev lo root handle 1: hfsc
tc class add dev lo parent 1: classid 1:1 hfsc sc rate 1000kbit
tc filter add dev lo parent 1:1 protocol ip prio 1 u32 match u32 0 0 flowid 1:1 tc class add dev lo parent 1: classid 1:2 hfsc sc rate 2000kbit
Fix this by skipping hash tables via the TC_U32_KEY(handle) check.
--- net/sched/cls_u32.c | 3 +++ 1 file changed, 3 insertions(+)
net/sched/cls_u32.c +3 -0
@@ -1336,6 +1336,9 @@ static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q, {struct tc_u_knode *n = fh; +if (TC_U32_KEY (n->handle) ==0 ) +return ; +tc_cls_bind_class (classid, cl, q, &n->res, base); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 199/217 m68k: Define NR_CPUS to 1
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Uwe Kleine-König <[email protected]>
[ Upstream commit 1fd495ef09eef96169a379a749c24b5e69974bb8 ]
This fixes a Kconfig warning
fs/erofs/Kconfig:137:warning: range is invalid
which originates from EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS using NR_CPUS which up to now didn't exist for ARCH=m68k. All other architectures define this symbol, so fix the outlier.
[geert] This also fixes:
- CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS being set to the literal NR_CPUS instead of a number by automatic configs like "make allmodconfig" or "make olddefconfig",
- An infinite loop in manual configs like "make oldconfig" when CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS is not present or has an invalid value in your existing .config.
--- arch/m68k/Kconfig.cpu | 4 ++++ 1 file changed, 4 insertions(+)
arch/m68k/Kconfig.cpu +4 -0
@@ -548,6 +548,10 @@ config CACHE_COPYBACK endchoice endif # HAVE_CACHE_CB +config NR_CPUS + int + default "1" + # Coldfire cores that do not have a data cache configured can do coherent DMA. config COLDFIRE_COHERENT_DMA bool
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 200/217 regmap: sdw-mbq: dont call an unset readable_reg callback
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrey Golovko <[email protected]>
[ Upstream commit 00268f9452d2a0d660aa9c1bb0ca07a994af6a4f ]
regmap_sdw_mbq_poll_busy() decides whether to poll the Function Busy bit by calling ctx->readable_reg(), which is a straight copy of config->readable_reg. That callback is optional: regmap_readable() treats a NULL ->readable_reg as "every register is readable", and drivers rely on that. es9356 and tac5xx2-sdw both build an MBQ regmap without one.
Since commit ca1b11b36d82 ("regmap: sdw-mbq: Allow defers on undeferrable controls") the poll runs on every -ENODATA, not only for Controls the driver marked deferrable, so any of those devices answering COMMAND_IGNORED takes the kernel through a NULL function pointer.
Treat a missing callback the way the rest of regmap does and poll.
--- drivers/base/regmap/regmap-sdw-mbq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/base/regmap/regmap-sdw-mbq.c +1 -1
@@ -54,7 +54,7 @@ static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, reg =SDW_SDCA_CTL (SDW_SDCA_CTL_FUNC (reg),0 ,SDCA_CTL_ENTITY_0_FUNCTION_STATUS ,0 ); -if (ctx->readable_reg (dev, reg)) { +if (!ctx->readable_reg || ctx->readable_reg (dev, reg)) { ret = read_poll_timeout(sdw_read_no_pm, val, val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY), ctx->cfg.retry_us, ctx->cfg.timeout_us,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 201/217 net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Siddharth Vadapalli <[email protected]>
[ Upstream commit 36a05d2820077bb3955acb8111e1041d39148037 ]
On the packet reception path, the ID of the MAC Port on which the packet was received, is embedded in the RX DMA Descriptor's metadata. The ID is extracted using the helper function cppi5_desc_get_tags_ids() which fills in the 16-bit Source Tag into the 'port_id' variable. However, it is only the lower 8-bits of the 16-bit Source Tag that represent the MAC Port ID, while the upper 8-bits are Hardware-Reserved and carry an arbitrary value. With the existing logic, sporadic kernel crash is observed due to the subsequent driver code accessing out-of-bound memory because of an invalid port_id.
Hence, fix the port_id extraction logic to use only the lower 8-bits of the Source Tag as the MAC Port ID.
--- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/ethernet/ti/am65-cpsw-nuss.c +2 -0
@@ -1312,6 +1312,8 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_rx_flow *flow,k3_udma_glue_rx_cppi5_to_dma_addr (rx_chn->rx_chn, &buf_dma); pkt_len =cppi5_hdesc_get_pktlen (desc_rx);cppi5_desc_get_tags_ids (&desc_rx->hdr, &port_id,NULL ); +/* Port ID is contained in the lower 8-bits of the 16-bit Source Tag */ + port_id &=0xFF ;dev_dbg (dev,"%s rx port_id:%d\n" , __func__, port_id); port =am65_common_get_port (common, port_id); ndev = port->ndev;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 202/217 accel/amdxdna: Skip unmapped range in aie2_populate_range()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lizhi Hou <[email protected]>
[ Upstream commit 6c916e301fa10de9158b922474ade7b43d726cda ]
aie2_populate_range() incorrectly failed jobs for BOs with multiple
mmaps: if the unmapped entry appeared first in umap_list, the loop would pick it up, call hmm_range_fault() on a gone VMA, and return -EFAULT without ever trying the remaining valid mapps.
Fix it by skipping unmapped entries. After the loop, if the map list is empty or all maps are valid, map_invalid can be cleared normally.
--- drivers/accel/amdxdna/aie2_ctx.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/accel/amdxdna/aie2_ctx.c +16 -0
@@ -794,6 +794,16 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) found = false;down_write (&xdna->notifier_lock);list_for_each_entry (mapp, &abo->mem.umap_list, node) { +/* +* Skip entries that have already been unmapped. +* +* If userspace unmaps the address and later submits I/O using +* it, the IOMMU will reject the access and report a fault. +* Ignore such entries here. +*/ +if (mapp->unmapped) +continue ; +if (mapp->invalid &&kref_get_unless_zero (&mapp->refcnt)) { found = true;break ;
@@ -801,6 +811,12 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) }if (!found) { +/* +* This also covers the case where all mappings have been +* removed. There are no invalid mappings left to process. +* Any subsequent I/O using the unmapped address will be +* rejected by the IOMMU. +*/ abo->mem.map_invalid = false;up_write (&xdna->notifier_lock);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 203/217 net/sched: cls_bpf: reject dev-bound programs bound to a different device
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 120977e2c096deea4e866e4273be9220b957c29e ]
cls_bpf_prog_from_efd() obtained a SCHED_CLS program via bpf_prog_get_type_dev() but never verified that a device-bound (offloaded) program's bound netdev matches the TC netdev the classifier is being attached to. This let a program loaded with prog_ifindex for device A be attached via cls_bpf + skip_sw to device B; deleting device A then destroyed the program's offload state while it was still attached to device B, triggering a netdevsim WARN (panic with panic_on_warn=1).
Mirror the XDP attach path (net/core/dev.c) and reject the attach with -EINVAL when a dev-bound program's bound device does not match the target device.
--- net/sched/cls_bpf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
net/sched/cls_bpf.c +16 -2
@@ -374,7 +374,8 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog) }static int cls_bpf_prog_from_efd (struct nlattr **tb,struct cls_bpf_prog *prog, -u32 gen_flags,const struct tcf_proto *tp) +u32 gen_flags,const struct tcf_proto *tp, +struct netlink_ext_ack *extack) {struct bpf_prog *fp;char *name =NULL ;
@@ -388,6 +389,19 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,if (IS_ERR (fp))return PTR_ERR (fp); +if (bpf_prog_is_dev_bound (fp->aux)) { +struct tcf_block *block = tp->chain->block; +struct net_device *dev; + + dev = block->q ?qdisc_dev (block->q) :NULL ; +if (!dev || !bpf_offload_dev_match (fp, dev)) { +NL_SET_ERR_MSG (extack, +"Program is bound to a different device" ); +bpf_prog_put (fp); +return -EINVAL ; + } + } +if (tb[TCA_BPF_NAME ]) { name =nla_memdup (tb[TCA_BPF_NAME ],GFP_KERNEL );if (!name) {
@@ -492,7 +506,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, prog->gen_flags = gen_flags; ret = is_bpf ?cls_bpf_prog_from_ops (tb, prog) : -cls_bpf_prog_from_efd (tb, prog, gen_flags, tp); +cls_bpf_prog_from_efd (tb, prog, gen_flags, tp, extack);if (ret <0 ) goto errout_idr;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 204/217 firewire: ohci: split page allocation from dma mapping
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit f2ae92780ab93ed7e59e469cfd5c7269bd68ddb4 ]
1394 OHCI PCI driver had long been the only user of dma_alloc_pages(). Although tee subsystem recently started using it, they are still a few users of the infrequently-used function.
In the discussion for dma-mapping function, Jason Gunthorpe shows his opinion about the design of public API for the function. According to it, the users provide physical address to the function, then receive DMA mapping address, regardless of the location of mapping target.
With the above aspects, this commit eliminates the use of dma_alloc_pages() from this subsystem.
Stable-dep-of: 42d217add8d8 ("firewire: ohci: fix NULL pointer dereference in ar_context_release")
--- drivers/firewire/ohci.c | 77 +++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 26 deletions(-)
drivers/firewire/ohci.c +51 -26
@@ -539,18 +539,22 @@ static void ar_context_link_page(struct ar_context *ctx, unsigned int index)static void ar_context_release (struct ar_context *ctx) {struct device *dev = ctx->ohci->card.device; -unsigned int i;if (!ctx->buffer)return ; -vunmap (ctx->buffer); +for (int i =0 ; i <AR_BUFFERS ; ++i) { +dma_addr_t dma_addr =page_private (ctx->pages[i]); -for (i =0 ; i <AR_BUFFERS ; i++) { -if (ctx->pages[i]) -dma_free_pages (dev,PAGE_SIZE , ctx->pages[i], -ar_buffer_bus (ctx, i),DMA_FROM_DEVICE ); +dma_unmap_page (dev, dma_addr,PAGE_SIZE ,DMA_FROM_DEVICE ); +set_page_private (ctx->pages[i],0 ); } + +vunmap (ctx->buffer); + ctx->buffer =NULL ; + +release_pages (ctx->pages,AR_BUFFERS ); +memset (ctx->pages,0 ,sizeof (ctx->pages)); }static void ar_context_abort (struct ar_context *ctx,const char *error_msg)
@@ -845,31 +849,57 @@ static int ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, {struct device *dev = ohci->card.device;unsigned int i; -dma_addr_t dma_addr;struct page *pages[AR_BUFFERS +AR_WRAPAROUND_PAGES ]; +void *vaddr;struct descriptor *d; ctx->regs = regs; ctx->ohci = ohci;INIT_WORK (&ctx->work, ohci_ar_context_work); -for (i =0 ; i <AR_BUFFERS ; i++) { - ctx->pages[i] =dma_alloc_pages (dev,PAGE_SIZE , &dma_addr, -DMA_FROM_DEVICE ,GFP_KERNEL ); -if (!ctx->pages[i]) - goto out_of_memory; -set_page_private (ctx->pages[i], dma_addr); -dma_sync_single_for_device (dev, dma_addr,PAGE_SIZE , -DMA_FROM_DEVICE ); +// Retrieve noncontiguous pages. The descriptors for 1394 OHCI AR DMA contexts have a set +// of address and length per each. The reason to use pages is to construct contiguous +// address range in kernel virtual address space. +unsigned long nr_populated =alloc_pages_bulk (GFP_KERNEL |GFP_DMA32 ,AR_BUFFERS , pages); + +if (nr_populated !=AR_BUFFERS ) { +release_pages (pages, nr_populated); +return -ENOMEM ; } -for (i =0 ; i <AR_BUFFERS ; i++) - pages[i] = ctx->pages[i]; +// Map the pages into contiguous kernel virtual addresses so that the packet data +// across the pages can be referred as being contiguous, especially across the last +// and first pages. for (i =0 ; i <AR_WRAPAROUND_PAGES ; i++) - pages[AR_BUFFERS + i] = ctx->pages[i]; - ctx->buffer =vmap (pages,ARRAY_SIZE (pages),VM_MAP ,PAGE_KERNEL ); -if (!ctx->buffer) - goto out_of_memory; + pages[AR_BUFFERS + i] = pages[i]; + vaddr =vmap (pages,ARRAY_SIZE (pages),VM_MAP ,PAGE_KERNEL ); +if (!vaddr) { +release_pages (pages, nr_populated); +return -ENOMEM ; + } + +// Retrieve DMA mapping addresses for the pages. They are not contiguous. Maintain the cache +// coherency for the pages by hand. +for (i =0 ; i <AR_BUFFERS ; i++) { +// The dma_map_phys() with a physical address per page is available here, instead. +dma_addr_t dma_addr =dma_map_page (dev, pages[i],0 ,PAGE_SIZE ,DMA_FROM_DEVICE ); +if (dma_mapping_error (dev, dma_addr)) +break ; +set_page_private (pages[i], dma_addr); +dma_sync_single_for_device (dev, dma_addr,PAGE_SIZE ,DMA_FROM_DEVICE ); + } +if (i <AR_BUFFERS ) { +while (i-- >0 ) { +dma_addr_t dma_addr =page_private (pages[i]); +dma_unmap_page (dev, dma_addr,PAGE_SIZE ,DMA_FROM_DEVICE ); + } +vunmap (vaddr); +release_pages (pages, nr_populated); +return -ENOMEM ; + } + + ctx->buffer = vaddr; +memcpy (ctx->pages, pages,sizeof (ctx->pages)); ctx->descriptors = ohci->misc_buffer + descriptors_offset; ctx->descriptors_bus = ohci->misc_buffer_bus + descriptors_offset;
@@ -886,11 +916,6 @@ static int ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, }return 0 ; - -out_of_memory: -ar_context_release (ctx); - -return -ENOMEM ; }static void ar_context_run (struct ar_context *ctx)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 205/217 firewire: ohci: fix NULL pointer dereference in ar_context_release
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Nogikh <[email protected]>
[ Upstream commit 42d217add8d80d6e7d9f58f80d11ea9b07ea113e ]
During the error handling path of the driver's probe function, a NULL pointer dereference can occur in ar_context_release().
When pci_probe() fails early (e.g., if pcim_enable_device() or MMIO mapping fails), the devres cleanup mechanism invokes release_ohci(). This function unconditionally calls ar_context_release() to clean up the asynchronous receive contexts. However, if ar_context_init() was not yet called, ctx->ohci remains NULL (as the fw_ohci structure is zero-initialized by devres_alloc()).
ar_context_release() immediately dereferences ctx->ohci to get the dev pointer before checking if the context was actually initialized, leading to a crash:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:ar_context_release+0x3f/0x380 drivers/firewire/ohci.c:543 Call Trace: release_ohci+0x3f/0x60 drivers/firewire/ohci.c:3567 release_nodes drivers/base/devres.c:546 [inline] devres_release_all+0x1a8/0x260 drivers/base/devres.c:576 device_unbind_cleanup drivers/base/dd.c:597 [inline] really_probe+0x451/0xae0 drivers/base/dd.c:772
To fix this, move the assignment of the dev pointer after the !ctx->buffer check. If ctx->buffer is NULL, it indicates that the context was never successfully initialized and there is nothing to release, safely avoiding the dereference of the uninitialized ctx->ohci pointer.
--- drivers/firewire/ohci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/firewire/ohci.c +3 -1
@@ -538,11 +538,13 @@ static void ar_context_link_page(struct ar_context *ctx, unsigned int index)static void ar_context_release (struct ar_context *ctx) { -struct device *dev = ctx->ohci->card.device; +struct device *dev;if (!ctx->buffer)return ; + dev = ctx->ohci->card.device; +for (int i =0 ; i <AR_BUFFERS ; ++i) {dma_addr_t dma_addr =page_private (ctx->pages[i]);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 206/217 drm/xe/oa: Fix sync entry leak on OA config emit failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linmao Li <[email protected]>
[ Upstream commit 8d33c4987cd162527375a3905017ae129ba7c3fe ]
xe_oa_emit_oa_config() releases the sync entries and the syncs array only on its success path. When it fails before the point of no return (fence allocation, config buffer allocation or batch submission), it returns without touching stream->syncs.
The stream open path handles such failures in the caller, but xe_oa_config_locked() propagates the error without any cleanup, so the syncs array and the fence references held by the parsed entries are leaked. The next config ioctl overwrites stream->syncs, making the memory unreachable for good.
Clean up the parsed syncs when xe_oa_emit_oa_config() fails, matching the cleanup done by the stream open error path.
--- drivers/gpu/drm/xe/xe_oa.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/gpu/drm/xe/xe_oa.c +4 -0
@@ -1567,6 +1567,10 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg) config =xchg (&stream->oa_config, config);drm_dbg (&stream->oa->xe->drm,"changed to oa config uuid=%s\n" , stream->oa_config->uuid); + }else { +while (param.num_syncs--) +xe_sync_entry_cleanup (¶m.syncs[param.num_syncs]); +kfree (param.syncs); } err_config_put:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 207/217 drm/log: Fix out-of-bounds read on empty message length
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shixiong Ou <[email protected]>
[ Upstream commit 60baa179ed1333535f6e2da4133511db55278ee4 ]
drm_log_draw_kmsg_record() accesses s[len - 1] to strip the trailing newline, but len is unsigned int. If len is 0, the subtraction wraps to UINT_MAX, causing an out-of-bounds read.
Add an early return when len is 0.
--- drivers/gpu/drm/clients/drm_log.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/clients/drm_log.c +3 -0
@@ -160,6 +160,9 @@ static void drm_log_draw_kmsg_record(struct drm_log_scanout *scanout, {u32 prefix_len =0 ; +if (!len) +return ; +if (len >TS_PREFIX_LEN && s[0 ] =='[' && s[6 ] =='.' && s[TS_PREFIX_LEN ] ==']' ) prefix_len =TS_PREFIX_LEN +1 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 208/217 drm/client: Remove pitch from struct drm_client_buffer
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit dce4657ff526b65007fe8d5c92968a933cc7c9da ]
Only the client-buffer setup uses the pitch field from struct drm_client_buffer. Remove the field and pass the value among setup helpers.
Clients that need the pitch should rather look at the framebuffer's pitches[0] directly.
Stable-dep-of: f4f2bba28df9 ("drm/log: Fix infinite loop when scale is too large for display")
--- drivers/gpu/drm/drm_client.c | 14 +++++++------- include/drm/drm_client.h | 5 ----- 2 files changed, 7 insertions(+), 12 deletions(-)
drivers/gpu/drm/drm_client.c +7 -7
@@ -184,7 +184,7 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)static struct drm_client_buffer *drm_client_buffer_create (struct drm_client_dev *client,u32 width,u32 height, -u32 format,u32 *handle) +u32 format,u32 *handle,u32 *pitch) {const struct drm_format_info *info =drm_format_info (format);struct drm_mode_create_dumb dumb_args = { };
@@ -212,9 +212,9 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, goto err_delete; } - buffer->pitch = dumb_args.pitch; buffer->gem = obj; *handle = dumb_args.handle; + *pitch = dumb_args.pitch;return buffer;
@@ -349,7 +349,7 @@ static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)static int drm_client_buffer_addfb (struct drm_client_buffer *buffer,u32 width,u32 height,u32 format, -u32 handle) +u32 handle,u32 pitch) {struct drm_client_dev *client = buffer->client;struct drm_mode_fb_cmd2 fb_req = { };
@@ -359,7 +359,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer, fb_req.height = height; fb_req.pixel_format = format; fb_req.handles[0 ] = handle; - fb_req.pitches[0 ] = buffer->pitch; + fb_req.pitches[0 ] = pitch; ret =drm_mode_addfb2 (client->dev, &fb_req, client->file);if (ret)
@@ -395,15 +395,15 @@ struct drm_client_buffer * drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) { struct drm_client_buffer *buffer; -u32 handle; +u32 handle, pitch;int ret; buffer =drm_client_buffer_create (client, width, height, format, - &handle); + &handle, &pitch);if (IS_ERR (buffer))return buffer; - ret =drm_client_buffer_addfb (buffer, width, height, format, handle); + ret =drm_client_buffer_addfb (buffer, width, height, format, handle, pitch);/* * The handle is only needed for creating the framebuffer, destroy it
include/drm/drm_client.h +0 -5
@@ -173,11 +173,6 @@ struct drm_client_buffer {*/ struct drm_client_dev *client; -/** -* @pitch: Buffer pitch -*/ -u32 pitch; -/** * @gem: GEM object backing this buffer *
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/217 drm/client: Move dumb-buffer handling to drm_client_framebuffer_create()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit 2d262a483c7a128e50cdb64a0ec20c6d78df66cc ]
Dumb-buffer creation within the client code is asymetrically balanced across drm_client_buffer_create() and drm_client_framebuffer_create(). Put all dumb-buffer code into drm_client_framebuffer_create() and leave client-buffer initialization to drm_client_buffer_create(). Clarifies responsibility between these functions.
Apart form the architectural improvements, drm_client_buffer_create() can now be exported if needed by clients. The client will be able to initialize buffers that have been created from other interfaces than dumb buffers.
Stable-dep-of: f4f2bba28df9 ("drm/log: Fix infinite loop when scale is too large for display")
--- drivers/gpu/drm/drm_client.c | 56 +++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-)
drivers/gpu/drm/drm_client.c +29 -27
@@ -184,11 +184,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)static struct drm_client_buffer *drm_client_buffer_create (struct drm_client_dev *client,u32 width,u32 height, -u32 format,u32 *handle,u32 *pitch) +u32 format,u32 handle,u32 pitch) { -const struct drm_format_info *info =drm_format_info (format); -struct drm_mode_create_dumb dumb_args = { }; -struct drm_device *dev = client->dev;struct drm_client_buffer *buffer;struct drm_gem_object *obj;int ret;
@@ -199,28 +196,18 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, buffer->client = client; - dumb_args.width = width; - dumb_args.height = height; - dumb_args.bpp =drm_format_info_bpp (info,0 ); - ret =drm_mode_create_dumb (dev, &dumb_args, client->file); -if (ret) - goto err_delete; - - obj =drm_gem_object_lookup (client->file, dumb_args.handle); + obj =drm_gem_object_lookup (client->file, handle);if (!obj) { ret = -ENOENT ; goto err_delete; } buffer->gem = obj; - *handle = dumb_args.handle; - *pitch = dumb_args.pitch;return buffer; err_delete: -drm_client_buffer_delete (buffer); - +kfree (buffer);return ERR_PTR (ret); }
@@ -394,16 +381,30 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,struct drm_client_buffer *drm_client_framebuffer_create (struct drm_client_dev *client,u32 width,u32 height,u32 format) { +const struct drm_format_info *info =drm_format_info (format); +struct drm_device *dev = client->dev; +struct drm_mode_create_dumb dumb_args = { };struct drm_client_buffer *buffer; -u32 handle, pitch;int ret; + dumb_args.width = width; + dumb_args.height = height; + dumb_args.bpp =drm_format_info_bpp (info,0 ); + ret =drm_mode_create_dumb (dev, &dumb_args, client->file); +if (ret) +return ERR_PTR (ret); + buffer =drm_client_buffer_create (client, width, height, format, - &handle, &pitch); -if (IS_ERR (buffer)) -return buffer; + dumb_args.handle, dumb_args.pitch); +if (IS_ERR (buffer)) { + ret =PTR_ERR (buffer); + goto err_drm_mode_destroy_dumb; + } - ret =drm_client_buffer_addfb (buffer, width, height, format, handle, pitch); + ret =drm_client_buffer_addfb (buffer, width, height, format, + dumb_args.handle, dumb_args.pitch); +if (ret) + goto err_drm_client_buffer_delete;/* * The handle is only needed for creating the framebuffer, destroy it
@@ -411,14 +412,15 @@ drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 heig* object as DMA-buf. The framebuffer and our buffer structure are still * holding references to the GEM object to prevent its destruction. */ -drm_mode_destroy_dumb (client->dev, handle, client->file); - -if (ret) { -drm_client_buffer_delete (buffer); -return ERR_PTR (ret); - } +drm_mode_destroy_dumb (client->dev, dumb_args.handle, client->file);return buffer; + +err_drm_client_buffer_delete: +drm_client_buffer_delete (buffer); +err_drm_mode_destroy_dumb: +drm_mode_destroy_dumb (client->dev, dumb_args.handle, client->file); +return ERR_PTR (ret); }EXPORT_SYMBOL (drm_client_framebuffer_create);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/217 drm/client: Inline drm_client_buffer_addfb() and _rmfb()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit 7cc0f6171b9f93da2815d95d4410f14583cba58f ]
Creating and deleting a client buffer always creates and deletes the underlying DRM framebuffer. Inline the helper functions into their callers.
With the _addfb code being inlined into drm_client_buffer_create(), clean up the function's error rollback to release the framebuffer's handle and GEM buffer object as needed.
Move the _rmfb code into drm_client_buffer_delete() rather than its current location in drm_client_framebuffer_delete(). The former is now the inverse of drm_client_buffer_create(). Makes no difference for cleaning up. Also prepares for the removal of drm_client_framebuffer_delete().
Stable-dep-of: f4f2bba28df9 ("drm/log: Fix infinite loop when scale is too large for display")
--- drivers/gpu/drm/drm_client.c | 93 ++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 53 deletions(-)
drivers/gpu/drm/drm_client.c +40 -53
@@ -174,6 +174,13 @@ EXPORT_SYMBOL(drm_client_release);static void drm_client_buffer_delete (struct drm_client_buffer *buffer) { +int ret; + + ret =drm_mode_rmfb (buffer->client->dev, buffer->fb->base.id, buffer->client->file); +if (ret) +drm_err (buffer->client->dev, +"Error removing FB:%u (%d)\n" , buffer->fb->base.id, ret); +if (buffer->gem) {drm_gem_vunmap (buffer->gem, &buffer->map);drm_gem_object_put (buffer->gem);
@@ -186,8 +193,21 @@ static struct drm_client_buffer * drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format, u32 handle, u32 pitch) { + struct drm_mode_fb_cmd2 fb_req = { + .width = width, + .height = height, + .pixel_format = format, + .handles = { + handle, + }, + .pitches = { + pitch, + }, + }; +struct drm_device *dev = client->dev;struct drm_client_buffer *buffer;struct drm_gem_object *obj; +struct drm_framebuffer *fb;int ret; buffer =kzalloc (sizeof (*buffer),GFP_KERNEL );
@@ -202,10 +222,30 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, goto err_delete; } + ret =drm_mode_addfb2 (dev, &fb_req, client->file); +if (ret) + goto err_drm_gem_object_put; + + fb =drm_framebuffer_lookup (dev, client->file, fb_req.fb_id); +if (drm_WARN_ON (dev, !fb)) { + ret = -ENOENT ; + goto err_drm_mode_rmfb; + } + +/* drop the reference we picked up in framebuffer lookup */ +drm_framebuffer_put (fb); + +strscpy (fb->comm, client->name,TASK_COMM_LEN ); + buffer->gem = obj; + buffer->fb = fb;return buffer; +err_drm_mode_rmfb: +drm_mode_rmfb (dev, fb_req.fb_id, client->file); +err_drm_gem_object_put: +drm_gem_object_put (obj); err_delete:kfree (buffer);return ERR_PTR (ret);
@@ -319,51 +359,6 @@ void drm_client_buffer_vunmap(struct drm_client_buffer *buffer) }EXPORT_SYMBOL (drm_client_buffer_vunmap); -static void drm_client_buffer_rmfb (struct drm_client_buffer *buffer) -{ -int ret; - -if (!buffer->fb) -return ; - - ret =drm_mode_rmfb (buffer->client->dev, buffer->fb->base.id, buffer->client->file); -if (ret) -drm_err (buffer->client->dev, -"Error removing FB:%u (%d)\n" , buffer->fb->base.id, ret); - - buffer->fb =NULL ; -} - -static int drm_client_buffer_addfb (struct drm_client_buffer *buffer, -u32 width,u32 height,u32 format, -u32 handle,u32 pitch) -{ -struct drm_client_dev *client = buffer->client; -struct drm_mode_fb_cmd2 fb_req = { }; -int ret; - - fb_req.width = width; - fb_req.height = height; - fb_req.pixel_format = format; - fb_req.handles[0 ] = handle; - fb_req.pitches[0 ] = pitch; - - ret =drm_mode_addfb2 (client->dev, &fb_req, client->file); -if (ret) -return ret; - - buffer->fb =drm_framebuffer_lookup (client->dev, buffer->client->file, fb_req.fb_id); -if (WARN_ON (!buffer->fb)) -return -ENOENT ; - -/* drop the reference we picked up in framebuffer lookup */ -drm_framebuffer_put (buffer->fb); - -strscpy (buffer->fb->comm, client->name,TASK_COMM_LEN ); - -return 0 ; -} -/** * drm_client_framebuffer_create - Create a client framebuffer * @client: DRM client
@@ -401,11 +396,6 @@ drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 heig goto err_drm_mode_destroy_dumb; } - ret =drm_client_buffer_addfb (buffer, width, height, format, - dumb_args.handle, dumb_args.pitch); -if (ret) - goto err_drm_client_buffer_delete; -/* * The handle is only needed for creating the framebuffer, destroy it * again to solve a circular dependency should anybody export the GEM
@@ -416,8 +406,6 @@ drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 heigreturn buffer; -err_drm_client_buffer_delete: -drm_client_buffer_delete (buffer); err_drm_mode_destroy_dumb:drm_mode_destroy_dumb (client->dev, dumb_args.handle, client->file);return ERR_PTR (ret);
@@ -433,7 +421,6 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)if (!buffer)return ; -drm_client_buffer_rmfb (buffer);drm_client_buffer_delete (buffer); }EXPORT_SYMBOL (drm_client_framebuffer_delete);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 211/217 drm/client: Deprecate struct drm_client_buffer.gem
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit ea39f2e66e61035e203530977a3df428345d03e2 ]
The client buffer's framebuffer holds a reference and pointer on each of its GEM buffer objects. Thus the field gem in the client- buffer struct is not necessary. Deprecated the field and convert the client-buffer helpers to use the framebuffer's objects.
In drm_client_buffer_delete(), do a possible vunmap before releasing the framebuffer. Otherwise we'd eventually release the framebuffer before unmaping its buffer objects.
v2:
- avoid dependency on CONFIG_DRM_KMS_HELPER
Stable-dep-of: f4f2bba28df9 ("drm/log: Fix infinite loop when scale is too large for display")
--- drivers/gpu/drm/drm_client.c | 20 ++++++++++++-------- include/drm/drm_client.h | 9 +++------ 2 files changed, 15 insertions(+), 14 deletions(-)
drivers/gpu/drm/drm_client.c +12 -8
@@ -17,6 +17,7 @@#include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_mode.h> #include <drm/drm_print.h>
@@ -174,17 +175,17 @@ EXPORT_SYMBOL(drm_client_release);static void drm_client_buffer_delete (struct drm_client_buffer *buffer) { +struct drm_gem_object *gem = buffer->fb->obj[0 ];int ret; +drm_gem_vunmap (gem, &buffer->map); + ret =drm_mode_rmfb (buffer->client->dev, buffer->fb->base.id, buffer->client->file);if (ret)drm_err (buffer->client->dev,"Error removing FB:%u (%d)\n" , buffer->fb->base.id, ret); -if (buffer->gem) { -drm_gem_vunmap (buffer->gem, &buffer->map); -drm_gem_object_put (buffer->gem); - } +drm_gem_object_put (buffer->gem);kfree (buffer); }
@@ -274,7 +275,7 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,int drm_client_buffer_vmap_local (struct drm_client_buffer *buffer,struct iosys_map *map_copy) { -struct drm_gem_object *gem = buffer->gem; +struct drm_gem_object *gem = buffer->fb->obj[0 ];struct iosys_map *map = &buffer->map;int ret;
@@ -303,7 +304,7 @@ EXPORT_SYMBOL(drm_client_buffer_vmap_local);*/ void drm_client_buffer_vunmap_local (struct drm_client_buffer *buffer) { -struct drm_gem_object *gem = buffer->gem; +struct drm_gem_object *gem = buffer->fb->obj[0 ];struct iosys_map *map = &buffer->map;drm_gem_vunmap_locked (gem, map);
@@ -334,9 +335,10 @@ EXPORT_SYMBOL(drm_client_buffer_vunmap_local);int drm_client_buffer_vmap (struct drm_client_buffer *buffer,struct iosys_map *map_copy) { +struct drm_gem_object *gem = buffer->fb->obj[0 ];int ret; - ret =drm_gem_vmap (buffer->gem, &buffer->map); + ret =drm_gem_vmap (gem, &buffer->map);if (ret)return ret; *map_copy = buffer->map;
@@ -355,7 +357,9 @@ EXPORT_SYMBOL(drm_client_buffer_vmap);*/ void drm_client_buffer_vunmap (struct drm_client_buffer *buffer) { -drm_gem_vunmap (buffer->gem, &buffer->map); +struct drm_gem_object *gem = buffer->fb->obj[0 ]; + +drm_gem_vunmap (gem, &buffer->map); }EXPORT_SYMBOL (drm_client_buffer_vunmap);
include/drm/drm_client.h +3 -6
@@ -176,12 +176,9 @@ struct drm_client_buffer {/** * @gem: GEM object backing this buffer * -* FIXME: The dependency on GEM here isn't required, we could -* convert the driver handle to a dma-buf instead and use the -* backend-agnostic dma-buf vmap support instead. This would -* require that the handle2fd prime ioctl is reworked to pull the -* fd_install step out of the driver backend hooks, to make that -* final step optional for internal users. +* FIXME: The DRM framebuffer holds a reference on its GEM +* buffer objects. Do not use this field in new code and +* update existing users. */ struct drm_gem_object *gem;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/217 drm/client: Remove drm_client_framebuffer_delete()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit 3e3153325fd3693d0f9fe235c4afbcd68ef102e1 ]
Release client buffers with drm_client_buffer_delete() instead of drm_client_framebuffer_delete(). The latter is just a tiny wrapper around the former.
Move the test for !buffer into drm_client_buffer_delete(), although all callers appear to always have a valid pointer.
v2:
- test for !buffer before deref-ing pointer (Jocelyn, Dan)
Stable-dep-of: f4f2bba28df9 ("drm/log: Fix infinite loop when scale is too large for display")
--- drivers/gpu/drm/clients/drm_log.c | 4 ++-- drivers/gpu/drm/drm_client.c | 28 ++++++++++++---------------- drivers/gpu/drm/drm_fbdev_dma.c | 6 +++--- drivers/gpu/drm/drm_fbdev_shmem.c | 4 ++-- drivers/gpu/drm/drm_fbdev_ttm.c | 8 ++++---- include/drm/drm_client.h | 2 +- 6 files changed, 24 insertions(+), 28 deletions(-)
drivers/gpu/drm/clients/drm_log.c +2 -2
@@ -275,7 +275,7 @@ static void drm_log_init_client(struct drm_log *dlog) err_failed_commit:for (i =0 ; i < n_modeset; i++) -drm_client_framebuffer_delete (dlog->scanout[i].buffer); +drm_client_buffer_delete (dlog->scanout[i].buffer); err_nomodeset:kfree (dlog->scanout);
@@ -289,7 +289,7 @@ static void drm_log_free_scanout(struct drm_client_dev *client)if (dlog->n_scanout) {for (i =0 ; i < dlog->n_scanout; i++) -drm_client_framebuffer_delete (dlog->scanout[i].buffer); +drm_client_buffer_delete (dlog->scanout[i].buffer); dlog->n_scanout =0 ;kfree (dlog->scanout); dlog->scanout =NULL ;
drivers/gpu/drm/drm_client.c +12 -16
@@ -173,11 +173,19 @@ void drm_client_release(struct drm_client_dev *client) }EXPORT_SYMBOL (drm_client_release); -static void drm_client_buffer_delete (struct drm_client_buffer *buffer) +/** +* drm_client_buffer_delete - Delete a client buffer +* @buffer: DRM client buffer +*/ +void drm_client_buffer_delete (struct drm_client_buffer *buffer) { -struct drm_gem_object *gem = buffer->fb->obj[0 ]; +struct drm_gem_object *gem;int ret; +if (!buffer) +return ; + + gem = buffer->fb->obj[0 ];drm_gem_vunmap (gem, &buffer->map); ret =drm_mode_rmfb (buffer->client->dev, buffer->fb->base.id, buffer->client->file);
@@ -189,6 +197,7 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)kfree (buffer); } +EXPORT_SYMBOL (drm_client_buffer_delete);static struct drm_client_buffer * drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,
@@ -372,7 +381,7 @@ EXPORT_SYMBOL(drm_client_buffer_vunmap);* * This function creates a &drm_client_buffer which consists of a * &drm_framebuffer backed by a dumb buffer. -* Call drm_client_framebuffer_delete() to free the buffer. +* Call drm_client_buffer_delete() to free the buffer. * * Returns: * Pointer to a client buffer or an error pointer on failure.
@@ -416,19 +425,6 @@ drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 heig }EXPORT_SYMBOL (drm_client_framebuffer_create); -/** -* drm_client_framebuffer_delete - Delete a client framebuffer -* @buffer: DRM client buffer (can be NULL) -*/ -void drm_client_framebuffer_delete (struct drm_client_buffer *buffer) -{ -if (!buffer) -return ; - -drm_client_buffer_delete (buffer); -} -EXPORT_SYMBOL (drm_client_framebuffer_delete); -/** * drm_client_framebuffer_flush - Manually flush client framebuffer * @buffer: DRM client buffer (can be NULL)
drivers/gpu/drm/drm_fbdev_dma.c +3 -3
@@ -55,7 +55,7 @@ static void drm_fbdev_dma_fb_destroy(struct fb_info *info)drm_fb_helper_fini (fb_helper);drm_client_buffer_vunmap (fb_helper->buffer); -drm_client_framebuffer_delete (fb_helper->buffer); +drm_client_buffer_delete (fb_helper->buffer);drm_client_release (&fb_helper->client);drm_fb_helper_unprepare (fb_helper);kfree (fb_helper);
@@ -90,7 +90,7 @@ static void drm_fbdev_dma_shadowed_fb_destroy(struct fb_info *info)vfree (shadow);drm_client_buffer_vunmap (fb_helper->buffer); -drm_client_framebuffer_delete (fb_helper->buffer); +drm_client_buffer_delete (fb_helper->buffer);drm_client_release (&fb_helper->client);drm_fb_helper_unprepare (fb_helper);kfree (fb_helper);
@@ -320,7 +320,7 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper, fb_helper->buffer =NULL ;drm_client_buffer_vunmap (buffer); err_drm_client_buffer_delete: -drm_client_framebuffer_delete (buffer); +drm_client_buffer_delete (buffer);return ret; }EXPORT_SYMBOL (drm_fbdev_dma_driver_fbdev_probe);
drivers/gpu/drm/drm_fbdev_shmem.c +2 -2
@@ -63,7 +63,7 @@ static void drm_fbdev_shmem_fb_destroy(struct fb_info *info)drm_fb_helper_fini (fb_helper);drm_client_buffer_vunmap (fb_helper->buffer); -drm_client_framebuffer_delete (fb_helper->buffer); +drm_client_buffer_delete (fb_helper->buffer);drm_client_release (&fb_helper->client);drm_fb_helper_unprepare (fb_helper);kfree (fb_helper);
@@ -198,7 +198,7 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper, fb_helper->buffer =NULL ;drm_client_buffer_vunmap (buffer); err_drm_client_buffer_delete: -drm_client_framebuffer_delete (buffer); +drm_client_buffer_delete (buffer);return ret; }EXPORT_SYMBOL (drm_fbdev_shmem_driver_fbdev_probe);
drivers/gpu/drm/drm_fbdev_ttm.c +4 -4
@@ -50,7 +50,7 @@ static void drm_fbdev_ttm_fb_destroy(struct fb_info *info)fb_deferred_io_cleanup (info);drm_fb_helper_fini (fb_helper);vfree (shadow); -drm_client_framebuffer_delete (fb_helper->buffer); +drm_client_buffer_delete (fb_helper->buffer);drm_client_release (&fb_helper->client);drm_fb_helper_unprepare (fb_helper);
@@ -202,7 +202,7 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, screen_buffer =vzalloc (screen_size);if (!screen_buffer) { ret = -ENOMEM ; - goto err_drm_client_framebuffer_delete; + goto err_drm_client_buffer_delete; }drm_fb_helper_fill_info (info, fb_helper, sizes);
@@ -227,10 +227,10 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, err_vfree:vfree (screen_buffer); -err_drm_client_framebuffer_delete: +err_drm_client_buffer_delete: fb_helper->fb =NULL ; fb_helper->buffer =NULL ; -drm_client_framebuffer_delete (buffer); +drm_client_buffer_delete (buffer);return ret; }EXPORT_SYMBOL (drm_fbdev_ttm_driver_fbdev_probe);
include/drm/drm_client.h +1 -1
@@ -195,7 +195,7 @@ struct drm_client_buffer {struct drm_client_buffer *drm_client_framebuffer_create (struct drm_client_dev *client,u32 width,u32 height,u32 format); -void drm_client_framebuffer_delete (struct drm_client_buffer *buffer); +void drm_client_buffer_delete (struct drm_client_buffer *buffer);int drm_client_framebuffer_flush (struct drm_client_buffer *buffer,struct drm_rect *rect);int drm_client_buffer_vmap_local (struct drm_client_buffer *buffer,struct iosys_map *map_copy);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/217 drm/log: Fix infinite loop when scale is too large for display
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shixiong Ou <[email protected]>
[ Upstream commit f4f2bba28df9b9aaa00262a462139dbbcdc38d9f ]
When scale is large enough that scaled_font exceeds the display dimensions, rows or columns become 0. A columns value of 0 causes an infinite loop in drm_log_draw_kmsg_record() because the loop never decrements len.
Check for zero rows/columns in drm_log_setup_modeset() and return an error, cleaning up the already allocated buffer to avoid a leak.
--- drivers/gpu/drm/clients/drm_log.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/gpu/drm/clients/drm_log.c +6 -0
@@ -218,6 +218,12 @@ static int drm_log_setup_modeset(struct drm_client_dev *client, scanout->scaled_font_w = scanout->font->width * scale; scanout->rows = height / scanout->scaled_font_h; scanout->columns = width / scanout->scaled_font_w; +if (!scanout->rows || !scanout->columns) { +drm_client_buffer_delete (scanout->buffer); + scanout->buffer =NULL ; + mode_set->fb =NULL ; +return -EINVAL ; + } scanout->front_color =drm_draw_color_from_xrgb8888 (0xffffff , format); scanout->prefix_color =drm_draw_color_from_xrgb8888 (0x4e9a06 , format);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/217 spi: virtio: mark device ready before registering the controller
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jasper Wise <[email protected]>
[ Upstream commit 11058bd3d47d57eb3473935feae53868d6d168b7 ]
virtio_spi_probe() registers the SPI controller with devm_spi_register_controller(). spi_register_controller() binds a child inline unless its driver has asked for asynchronous probing, so a peripheral that performs a transfer during its own probe reaches virtio_spi_transfer_one(), which kicks the virtqueue before probe has returned.
The driver never calls virtio_device_ready(), so DRIVER_OK is set on its behalf by virtio_dev_probe(), only once probe has returned. The virtio spec is explicit about that ordering in 3.1 Device Initialization:
| The driver MUST NOT send any buffer available notifications to the | device before setting DRIVER_OK.
A device that waits for DRIVER_OK before servicing the queue therefore leaves the transfer unanswered, and virtio_spi_transfer_one() waits for its completion with no timeout, so probe never returns.
Mark the device ready before registering the controller, as done for the same reason in commit f5866db64f34 ("virtio_console: enable VQs early") and commit 1d774589f924 ("i2c: virtio: mark device ready before registering the adapter").
--- drivers/spi/spi-virtio.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/spi/spi-virtio.c +2 -0
@@ -361,6 +361,8 @@ static int virtio_spi_probe(struct virtio_device *vdev)if (ret)return dev_err_probe (&vdev->dev, ret,"Cannot register virtqueue cleanup\n" ); +virtio_device_ready (vdev); +/* Use devm version to register controller */ ret =devm_spi_register_controller (&vdev->dev, ctrl);if (ret)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 215/217 erofs: fix EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS on some UP platforms
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gao Xiang <[email protected]>
[ Upstream commit a64d500b0078e16e9abb25baca4dee1dbc9054fc ]
CONFIG_NR_CPUS doesn't define on some UP platforms (e.g. arm), so this can cause make oldconfig to loop indefinitely when CONFIG_SMP=n:
$ make ARCH=arm allmodconfig $ sed -i "/CONFIG_SMP=y/d" .config $ sed -i "/CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS.*/d" .config
EROFS LZMA default maximum decompression streams (EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS) [0] (NEW) EROFS LZMA default maximum decompression streams (EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS) [0] (NEW)
...
Let's guard NR_CPUS with SMP instead of using a hardcoded arbitrary CPU uplimit here, similar to commit a3344078101c ("mm: make SPLIT_PTE_PTLOCKS depend on SMP").
The initial report from SJ Park was for m68k [1] (m68k is the only arch without NR_CPUS in Kconfig), and that got fixed in commit 1fd495ef09ee ("m68k: Define NR_CPUS to 1")
--- fs/erofs/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/erofs/Kconfig +2 -1
@@ -123,7 +123,8 @@ config EROFS_FS_ZIP_LZMA config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS int "EROFS LZMA default maximum decompression streams" depends on EROFS_FS_ZIP_LZMA - range 1 NR_CPUS + range 1 NR_CPUS if SMP + range 1 1 if !SMP default 16 help By default EROFS allocates one LZMA decompression stream per CPU.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 216/217 drm/vmwgfx: Set surface-framebuffer GEM objects
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
commit 9942d36a73c2d46c52fdd6f37cf698f3cb09ce5c upstream.
Set struct drm_framebuffer.obj[0] to the allocated GEM buffer object for surface framebuffers. Avoids a NULL-pointer deref in the client's vmap helpers.
[ 22.640191] Console: switching to colour frame buffer device 160x50 [ 22.641788] Oops: general protection fault, probably for non-canonical address 0xdffffc000000001f: 0000 [#1] SMP KASAN NOPTI [ 22.641795] KASAN: null-ptr-deref in range [0x00000000000000f8-0x00000000000000ff] [...] [ 22.641809] Hardware name: VMware, Inc. VMware20,1/440BX Desktop Reference Platform, BIOS VMW201.00V.24928539.B64.2508260915 08/26/2025 [ 22.641812] Workqueue: events drm_fb_helper_damage_work [ 22.641824] RIP: 0010:drm_gem_lock+0x25/0x50 [ 22.641831] Code: 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 48 b8 00 00 00 00 00 fc ff df 53 48 89 fb 48 81 c7 f8 00 00 00 48 89 fa 48 c1 ea 03 <80> 3c 02 00 75 0f 48 8b bb f8 00 00 00 31 f6 5b e9 16 2e 15 01 e8 [...] [ 22.641889] Call Trace: [ 22.641891] <TASK> [ 22.641894] drm_client_buffer_vmap_local+0x78/0x140 [ 22.641903] drm_fbdev_ttm_helper_fb_dirty+0x20c/0x510 [drm_ttm_helper] [ 22.641913] ? __pfx_drm_fbdev_ttm_helper_fb_dirty+0x10/0x10 [drm_ttm_helper] [ 22.641918] ? __raw_spin_lock_irqsave+0x8c/0xf0 [ 22.641924] ? __pfx___raw_spin_lock_irqsave+0x10/0x10 [ 22.641928] ? __pfx_mutex_lock+0x10/0x10 [ 22.641936] drm_fb_helper_fb_dirty+0x29a/0x5e0 [ 22.641942] ? __pfx_drm_fb_helper_fb_dirty+0x10/0x10
[...]
--- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +3 -0
@@ -553,6 +553,9 @@ static int vmw_kms_new_framebuffer_surfamemcpy (&vfbs->uo, uo,sizeof (vfbs->uo));vmw_user_object_ref (&vfbs->uo); +if (vfbs->uo.buffer) + vfbs->base.base.obj[0 ] = &vfbs->uo.buffer->tbo.base; + *out = &vfbs->base; ret = drm_framebuffer_init(dev, &vfbs->base.base,
Greg Kroah-Hartman
maintainerPATCH 217/217 firewire: ohci: initialize page array to use alloc_pages_bulk() correctly
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
commit 9197e5949a41cfb5d44a6b8a860766266340d558 upstream.
The call of alloc_pages_bulk() skips to fill entries of page array when the entries already have values. While, 1394 OHCI PCI driver passes the page array without initializing. It could cause invalid state at PFN validation in vmap().
--- drivers/firewire/ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/firewire/ohci.c +1 -1
@@ -851,7 +851,7 @@ static int ar_context_init(struct ar_con {struct device *dev = ohci->card.device;unsigned int i; -struct page *pages[AR_BUFFERS +AR_WRAPAROUND_PAGES ]; +struct page *pages[AR_BUFFERS +AR_WRAPAROUND_PAGES ] = {NULL };void *vaddr;struct descriptor *d;
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
CIP testing did not find any problems here:
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-6.18.y
Best regards,
Pavel
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.18.46-rc1-g5025dd57f7da #1 SMP PREEMPT_DYNAMIC Thu Aug 20 19:57:29 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sat, 22 Aug 2026 14:51:11 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.46-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:
Signature · 1 line
Florian
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sat, 22 Aug 2026 14:51:11 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.46-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
Tested on an amd64 laptop (Lenovo ThinkPad T14 Gen 1). Working well, no regressions observed.
Signature · 1 line
-Barry K. Nathan <[email protected]>
Quoted 8 lines · On Thu, 20 Aug 2026 16:52:48 +0200 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sat, 22 Aug 2026 14:51:11 +0000. Anything received after that time might be too late.
Boot-tested under QEMU for Rust x86_64, arm64 and riscv64; built-tested for loongarch64 and arm32:
Thanks!
Cheers,
Miguel
Am 20.08.2026 um 16:52 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697 v2 server. No dmesg oddities or regressions found.
Beste Grüße,
Peter Schneider
Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge, enjoy the air and behold the view. Climb it so you can see the world, not so the world can see you. -- David McCullough Jr. OpenPGP: 0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244 Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc https://keys.mailvelope.com/pks/lookup?op=get&[email protected] https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sat, 22 Aug 2026 14:51:11 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 9 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.46-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sat, 22 Aug 2026 14:51:11 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.46-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Hi,
Build tested in our x86,arm64,loongarch,riscv config successfully without error.
Best Regards
Wentao Guan
Log:
Linux version 6.18.46-rc1-g5025dd57f7da (guanwentao@uos-PC) (aarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT_DYNAMIC Linux version 6.18.46-rc1-g5025dd57f7da (guanwentao@uos-PC) (aarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #2 SMP PREEMPT_DYNAMIC Sat Aug 22 03:39:19 CST 2026 Linux version 6.18.46-rc1-g5025dd57f7da (guanwentao@uos-PC) (loongarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT_DYNAMIC Linux version 6.18.46-rc1-g5025dd57f7da (guanwentao@uos-PC) (loongarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #3 SMP PREEMPT_DYNAMIC Sat Aug 22 03:57:08 CST 2026 Linux version 6.18.46-rc1+ (guanwentao@uos-PC) (riscv64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT
Linux version 6.18.46-rc1+ (guanwentao@uos-PC) (riscv64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #4 SMP PREEMPT Sat Aug 22 04:14:38 CST 2026 Linux version 6.18.46-rc1-g5025dd57f7da (guanwentao@uos-PC) (gcc (Deepin 12.3.0-17deepin15) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT_DYNAMIC
Linux version 6.18.46-rc1-g5025dd57f7da (guanwentao@uos-PC) (gcc (Deepin 12.3.0-17deepin15) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #1 SMP PREEMPT_DYNAMIC Sat Aug 22 03:18:44 CST 2026
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.46 release. There are 217 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.