6.12.64-rc1 review
Subsystems (4)
Greg Kroah-Hartman
maintainerPATCH 448/567 fjes: Add missing iounmap in fjes_hw_init()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
commit 15ef641a0c6728d25a400df73922e80ab2cf029c upstream.
In error paths, add fjes_hw_iounmap() to release the resource acquired by fjes_hw_iomap(). Add a goto label to do so.
--- drivers/net/fjes/fjes_hw.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/net/fjes/fjes_hw.c +9 -3
@@ -334,7 +334,7 @@ int fjes_hw_init(struct fjes_hw *hw) ret =fjes_hw_reset (hw);if (ret) -return ret; + goto err_iounmap;fjes_hw_set_irqmask (hw,REG_ICTL_MASK_ALL , true);
@@ -347,8 +347,10 @@ int fjes_hw_init(struct fjes_hw *hw) hw->max_epid =fjes_hw_get_max_epid (hw); hw->my_epid =fjes_hw_get_my_epid (hw); -if ((hw->max_epid ==0 ) || (hw->my_epid >= hw->max_epid)) -return -ENXIO ; +if ((hw->max_epid ==0 ) || (hw->my_epid >= hw->max_epid)) { + ret = -ENXIO ; + goto err_iounmap; + } ret =fjes_hw_setup (hw);
@@ -356,6 +358,10 @@ int fjes_hw_init(struct fjes_hw *hw) hw->hw_info.trace_size =FJES_DEBUG_BUFFER_SIZE ;return ret; + +err_iounmap: +fjes_hw_iounmap (hw); +return ret; }void fjes_hw_exit (struct fjes_hw *hw)
Greg Kroah-Hartman
maintainerPATCH 449/567 LoongArch: Refactor register restoration in ftrace_common_return
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenghao Duan <[email protected]>
commit 45cb47c628dfbd1994c619f3eac271a780602826 upstream.
Refactor the register restoration sequence in the ftrace_common_return function to clearly distinguish between the logic of normal returns and direct call returns in function tracing scenarios. The logic is as follows:
1. In the case of a normal return, the execution flow returns to the traced function, and ftrace must ensure that the register data is consistent with the state when the function was entered.
ra = parent return address; t0 = traced function return address.
2. In the case of a direct call return, the execution flow jumps to the custom trampoline function, and ftrace must ensure that the register data is consistent with the state when ftrace was entered.
ra = traced function return address; t0 = parent return address.
--- arch/loongarch/kernel/mcount_dyn.S | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
arch/loongarch/kernel/mcount_dyn.S +10 -4
@@ -94,7 +94,6 @@ SYM_INNER_LABEL(ftrace_graph_call, SYM_L * at the callsite, so there is no need to restore the T series regs. */ ftrace_common_return: - PTR_L ra, sp, PT_R1 PTR_L a0, sp, PT_R4 PTR_L a1, sp, PT_R5 PTR_L a2, sp, PT_R6
@@ -104,12 +103,17 @@ ftrace_common_return: PTR_L a6, sp, PT_R10 PTR_L a7, sp, PT_R11 PTR_L fp, sp, PT_R22 - PTR_L t0, sp, PT_ERA PTR_L t1, sp, PT_R13 - PTR_ADDI sp, sp, PT_SIZE bnez t1, .Ldirect + + PTR_L ra, sp, PT_R1 + PTR_L t0, sp, PT_ERA + PTR_ADDI sp, sp, PT_SIZE jr t0 .Ldirect: + PTR_L t0, sp, PT_R1 + PTR_L ra, sp, PT_ERA + PTR_ADDI sp, sp, PT_SIZE jr t1 SYM_CODE_END(ftrace_common)
@@ -161,6 +165,8 @@ SYM_CODE_END(return_to_handler) #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS SYM_CODE_START(ftrace_stub_direct_tramp) UNWIND_HINT_UNDEFINED - jr t0 + move t1, ra + move ra, t0 + jr t1 SYM_CODE_END(ftrace_stub_direct_tramp) #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
Greg Kroah-Hartman
maintainerPATCH 450/567 LoongArch: BPF: Zero-extend bpf_tail_call() index
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hengqi Chen <[email protected]>
commit eb71f5c433e1c6dff089b315881dec40a88a7baf upstream.
The bpf_tail_call() index should be treated as a u32 value. Let's zero-extend it to avoid calling wrong BPF progs. See similar fixes for x86 [1]) and arm64 ([2]) for more details.
[1]: https://github.com/torvalds/linux/commit/90caccdd8cc0215705f18b92771b449b01e2474a
[2]: https://github.com/torvalds/linux/commit/16338a9b3ac30740d49f5dfed81bac0ffa53b9c7
--- arch/loongarch/net/bpf_jit.c | 2 ++ 1 file changed, 2 insertions(+)
arch/loongarch/net/bpf_jit.c +2 -0
@@ -231,6 +231,8 @@ static int emit_bpf_tail_call(struct jit* goto out; */ tc_ninsn = insn ? ctx->offset[insn+1 ] - ctx->offset[insn] : ctx->offset[0 ]; +emit_zext_32 (ctx, a2, true); + off = offsetof(struct bpf_array, map.max_entries);emit_insn (ctx, ldwu, t1, a1, off);/* bgeu $a2, $t1, jmp_offset */
Greg Kroah-Hartman
maintainerPATCH 451/567 LoongArch: BPF: Sign extend kfunc call arguments
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hengqi Chen <[email protected]>
commit 3f5a238f24d7b75f9efe324d3539ad388f58536e upstream.
The kfunc calls are native calls so they should follow LoongArch calling conventions. Sign extend its arguments properly to avoid kernel panic. This is done by adding a new emit_abi_ext() helper. The emit_abi_ext() helper performs extension in place meaning a value already store in the target register (Note: this is different from the existing sign_extend() helper and thus we can't reuse it).
--- arch/loongarch/net/bpf_jit.c | 16 ++++++++++++++++ arch/loongarch/net/bpf_jit.h | 26 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+)
arch/loongarch/net/bpf_jit.c +16 -0
@@ -897,6 +897,22 @@ static int build_insn(const struct bpf_iif (ret <0 )return ret; +if (insn->src_reg ==BPF_PSEUDO_KFUNC_CALL ) { +const struct btf_func_model *m; +int i; + + m =bpf_jit_find_kfunc_model (ctx->prog, insn); +if (!m) +return -EINVAL ; + +for (i =0 ; i < m->nr_args; i++) { +u8 reg = regmap[BPF_REG_1 + i]; +bool sign = m->arg_flags[i] &BTF_FMODEL_SIGNED_ARG ; + +emit_abi_ext (ctx, reg, m->arg_size[i], sign); + } + } +move_addr (ctx, t1, func_addr);emit_insn (ctx, jirl,LOONGARCH_GPR_RA , t1,0 );
arch/loongarch/net/bpf_jit.h +26 -0
@@ -87,6 +87,32 @@ static inline void emit_sext_32(struct jemit_insn (ctx, addiw, reg, reg,0 ); } +/* Emit proper extension according to ABI requirements. +* Note that it requires a value of size `size` already resides in register `reg`. +*/ +static inline void emit_abi_ext (struct jit_ctx *ctx,int reg,u8 size,bool sign) +{ +/* ABI requires unsigned char/short to be zero-extended */ +if (!sign && (size ==1 || size ==2 )) +return ; + +switch (size) { +case 1 : +emit_insn (ctx, extwb, reg, reg); +break ; +case 2 : +emit_insn (ctx, extwh, reg, reg); +break ; +case 4 : +emit_insn (ctx, addiw, reg, reg,0 ); +break ; +case 8 : +break ; +default : +pr_warn ("bpf_jit: invalid size %d for extension\n" , size); + } +} +static inline void move_addr (struct jit_ctx *ctx,enum loongarch_gpr rd,u64 addr) {u64 imm_11_0, imm_31_12, imm_51_32, imm_63_52;
Greg Kroah-Hartman
maintainerPATCH 452/567 nfsd: Drop the client reference in client_states_open()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
commit 1f941b2c23fd34c6f3b76d36f9d0a2528fa92b8f upstream.
In error path, call drop_client() to drop the reference obtained by get_nfsdfs_clp().
--- fs/nfsd/nfs4state.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/nfsd/nfs4state.c +3 -1
@@ -2989,8 +2989,10 @@ static int client_states_open(struct inoreturn -ENXIO ; ret =seq_open (file, &states_seq_ops); -if (ret) +if (ret) { +drop_client (clp);return ret; + } s = file->private_data; s->private = clp;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 453/567 net: usb: sr9700: fix incorrect command used to write single register
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ethan Nelson-Moore <[email protected]>
commit fa0b198be1c6775bc7804731a43be5d899d19e7a upstream.
This fixes the device failing to initialize with "error reading MAC address" for me, probably because the incorrect write of NCR_RST to SR_NCR is not actually resetting the device.
--- drivers/net/usb/sr9700.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/usb/sr9700.c +2 -2
@@ -52,7 +52,7 @@ static int sr_read_reg(struct usbnet *destatic int sr_write_reg (struct usbnet *dev,u8 reg,u8 value) { -return usbnet_write_cmd (dev,SR_WR_REGS ,SR_REQ_WR_REG , +return usbnet_write_cmd (dev,SR_WR_REG ,SR_REQ_WR_REG , value, reg,NULL ,0 ); }
@@ -65,7 +65,7 @@ static void sr_write_async(struct usbnetstatic void sr_write_reg_async (struct usbnet *dev,u8 reg,u8 value) { -usbnet_write_cmd_async (dev,SR_WR_REGS ,SR_REQ_WR_REG , +usbnet_write_cmd_async (dev,SR_WR_REG ,SR_REQ_WR_REG , value, reg,NULL ,0 ); }
Greg Kroah-Hartman
maintainerPATCH 454/567 net: nfc: fix deadlock between nfc_unregister_device and rfkill_fop_write
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
commit 1ab526d97a57e44d26fadcc0e9adeb9c0c0182f5 upstream.
A deadlock can occur between nfc_unregister_device() and rfkill_fop_write() due to lock ordering inversion between device_lock and rfkill_global_mutex.
The problematic lock order is:
Thread A (rfkill_fop_write):
rfkill_fop_write()
mutex_lock(&rfkill_global_mutex)
rfkill_set_block()
nfc_rfkill_set_block()
nfc_dev_down()
device_lock(&dev->dev) <- waits for device_lockThread B (nfc_unregister_device):
nfc_unregister_device()
device_lock(&dev->dev)
rfkill_unregister()
mutex_lock(&rfkill_global_mutex) <- waits for rfkill_global_mutex
This creates a classic ABBA deadlock scenario.
Fix this by moving rfkill_unregister() and rfkill_destroy() outside the device_lock critical section. Store the rfkill pointer in a local variable before releasing the lock, then call rfkill_unregister() after releasing device_lock.
This change is safe because rfkill_fop_write() holds rfkill_global_mutex while calling the rfkill callbacks, and rfkill_unregister() also acquires rfkill_global_mutex before cleanup. Therefore, rfkill_unregister() will wait for any ongoing callback to complete before proceeding, and device_del() is only called after rfkill_unregister() returns, preventing any use-after-free.
The similar lock ordering in nfc_register_device() (device_lock -> rfkill_global_mutex via rfkill_register) is safe because during registration the device is not yet in rfkill_list, so no concurrent rfkill operations can occur on this device.
--- net/nfc/core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
net/nfc/core.c +7 -2
@@ -1154,6 +1154,7 @@ EXPORT_SYMBOL(nfc_register_device);void nfc_unregister_device (struct nfc_dev *dev) {int rc; +struct rfkill *rfk =NULL ;pr_debug ("dev_name=%s\n" ,dev_name (&dev->dev));
@@ -1164,13 +1165,17 @@ void nfc_unregister_device(struct nfc_dedevice_lock (&dev->dev);if (dev->rfkill) { -rfkill_unregister (dev->rfkill); -rfkill_destroy (dev->rfkill); + rfk = dev->rfkill; dev->rfkill =NULL ; } dev->shutting_down = true;device_unlock (&dev->dev); +if (rfk) { +rfkill_unregister (rfk); +rfkill_destroy (rfk); + } +if (dev->ops->check_presence) {del_timer_sync (&dev->check_pres_timer);cancel_work_sync (&dev->check_pres_work);
Greg Kroah-Hartman
maintainerPATCH 455/567 net: macb: Relocate mog_init_rings() callback from macb_mac_link_up() to macb_open()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiaolei Wang <[email protected]>
commit 99537d5c476cada9cf75aef9fa75579a31faadb9 upstream.
In the non-RT kernel, local_bh_disable() merely disables preemption, whereas it maps to an actual spin lock in the RT kernel. Consequently, when attempting to refill RX buffers via netdev_alloc_skb() in macb_mac_link_up(), a deadlock scenario arises as follows:
WARNING: possible circular locking dependency detected 6.18.0-08691-g2061f18ad76e #39 Not tainted
------------------------------------------------------
kworker/0:0/8 is trying to acquire lock:
ffff00080369bbe0 (&bp->lock){+.+.}-{3:3}, at: macb_start_xmit+0x808/0xb7c
but task is already holding lock:
ffff000803698e58 (&queue->tx_ptr_lock){+...}-{3:3}, at: macb_start_xmit +0x148/0xb7c
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 (&queue->tx_ptr_lock){+...}-{3:3}:
rt_spin_lock+0x50/0x1f0
macb_start_xmit+0x148/0xb7c
dev_hard_start_xmit+0x94/0x284
sch_direct_xmit+0x8c/0x37c
__dev_queue_xmit+0x708/0x1120
neigh_resolve_output+0x148/0x28c
ip6_finish_output2+0x2c0/0xb2c
__ip6_finish_output+0x114/0x308
ip6_output+0xc4/0x4a4
mld_sendpack+0x220/0x68c
mld_ifc_work+0x2a8/0x4f4
process_one_work+0x20c/0x5f8
worker_thread+0x1b0/0x35c
kthread+0x144/0x200
ret_from_fork+0x10/0x20-> #2 (_xmit_ETHER#2){+...}-{3:3}:
rt_spin_lock+0x50/0x1f0
sch_direct_xmit+0x11c/0x37c
__dev_queue_xmit+0x708/0x1120
neigh_resolve_output+0x148/0x28c
ip6_finish_output2+0x2c0/0xb2c
__ip6_finish_output+0x114/0x308
ip6_output+0xc4/0x4a4
mld_sendpack+0x220/0x68c
mld_ifc_work+0x2a8/0x4f4
process_one_work+0x20c/0x5f8
worker_thread+0x1b0/0x35c
kthread+0x144/0x200
ret_from_fork+0x10/0x20-> #1 ((softirq_ctrl.lock)){+.+.}-{3:3}:
lock_release+0x250/0x348
__local_bh_enable_ip+0x7c/0x240
__netdev_alloc_skb+0x1b4/0x1d8
gem_rx_refill+0xdc/0x240
gem_init_rings+0xb4/0x108
macb_mac_link_up+0x9c/0x2b4
phylink_resolve+0x170/0x614
process_one_work+0x20c/0x5f8
worker_thread+0x1b0/0x35c
kthread+0x144/0x200
ret_from_fork+0x10/0x20-> #0 (&bp->lock){+.+.}-{3:3}:
__lock_acquire+0x15a8/0x2084
lock_acquire+0x1cc/0x350
rt_spin_lock+0x50/0x1f0
macb_start_xmit+0x808/0xb7c
dev_hard_start_xmit+0x94/0x284
sch_direct_xmit+0x8c/0x37c
__dev_queue_xmit+0x708/0x1120
neigh_resolve_output+0x148/0x28c
ip6_finish_output2+0x2c0/0xb2c
__ip6_finish_output+0x114/0x308
ip6_output+0xc4/0x4a4
mld_sendpack+0x220/0x68c
mld_ifc_work+0x2a8/0x4f4
process_one_work+0x20c/0x5f8
worker_thread+0x1b0/0x35c
kthread+0x144/0x200
ret_from_fork+0x10/0x20other info that might help us debug this:
Chain exists of:
&bp->lock --> _xmit_ETHER#2 --> &queue->tx_ptr_lock
Possible unsafe locking scenario: CPU0 CPU1
---- ----
lock(&queue->tx_ptr_lock);
lock(_xmit_ETHER#2);
lock(&queue->tx_ptr_lock);
lock(&bp->lock);
*** DEADLOCK *** Call trace:
show_stack+0x18/0x24 (C)
dump_stack_lvl+0xa0/0xf0
dump_stack+0x18/0x24
print_circular_bug+0x28c/0x370
check_noncircular+0x198/0x1ac
__lock_acquire+0x15a8/0x2084
lock_acquire+0x1cc/0x350
rt_spin_lock+0x50/0x1f0
macb_start_xmit+0x808/0xb7c
dev_hard_start_xmit+0x94/0x284
sch_direct_xmit+0x8c/0x37c
__dev_queue_xmit+0x708/0x1120
neigh_resolve_output+0x148/0x28c
ip6_finish_output2+0x2c0/0xb2c
__ip6_finish_output+0x114/0x308
ip6_output+0xc4/0x4a4
mld_sendpack+0x220/0x68c
mld_ifc_work+0x2a8/0x4f4
process_one_work+0x20c/0x5f8
worker_thread+0x1b0/0x35c
kthread+0x144/0x200
ret_from_fork+0x10/0x20Notably, invoking the mog_init_rings() callback upon link establishment is unnecessary. Instead, we can exclusively call mog_init_rings() within the ndo_open() callback. This adjustment resolves the deadlock issue. Furthermore, since MACB_CAPS_MACB_IS_EMAC cases do not use mog_init_rings() when opening the network interface via at91ether_open(), moving mog_init_rings() to macb_open() also eliminates the MACB_CAPS_MACB_IS_EMAC check.
--- drivers/net/ethernet/cadence/macb_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/cadence/macb_main.c +2 -1
@@ -759,7 +759,6 @@ static void macb_mac_link_up(struct phyl/* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. */ - bp->macbgem_ops.mog_init_rings (bp);macb_init_buffers (bp);for (q =0 , queue = bp->queues; q < bp->num_queues; ++q, ++queue)
@@ -2985,6 +2984,8 @@ static int macb_open(struct net_device * goto pm_exit; } + bp->macbgem_ops.mog_init_rings (bp); +for (q =0 , queue = bp->queues; q < bp->num_queues; ++q, ++queue) {napi_enable (&queue->napi_rx);napi_enable (&queue->napi_tx);
Greg Kroah-Hartman
maintainerPATCH 456/567 Revert "drm/amd: Skip power ungate during suspend for VPE"
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello (AMD) <[email protected]>
commit 3925683515e93844be204381d2d5a1df5de34f31 upstream.
Skipping power ungate exposed some scenarios that will fail like below:
``` amdgpu: Register(0) [regVPEC_QUEUE_RESET_REQ] failed to reach value 0x00000000 != 0x00000001n amdgpu 0000:c1:00.0: amdgpu: VPE queue reset failed ... amdgpu: [drm] *ERROR* wait_for_completion_timeout timeout! ```
The underlying s2idle issue that prompted this commit is going to be fixed in BIOS.
This reverts commit 2a6c826cfeedd7714611ac115371a959ead55bda.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +1 -2
@@ -3092,11 +3092,10 @@ int amdgpu_device_set_pg_state(struct am (adev->ip_blocks[i].version->type ==AMD_IP_BLOCK_TYPE_GFX || adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_SDMA)) continue; -/* skip CG for VCE/UVD/VPE, it's handled specially */ +/* skip CG for VCE/UVD, it's handled specially */ if (adev->ip_blocks[i].version->type !=AMD_IP_BLOCK_TYPE_UVD && adev->ip_blocks[i].version->type !=AMD_IP_BLOCK_TYPE_VCE && adev->ip_blocks[i].version->type !=AMD_IP_BLOCK_TYPE_VCN && - adev->ip_blocks[i].version->type !=AMD_IP_BLOCK_TYPE_VPE && adev->ip_blocks[i].version->type !=AMD_IP_BLOCK_TYPE_JPEG && adev->ip_blocks[i].version->funcs->set_powergating_state) {/* enable powergating to save power */
Greg Kroah-Hartman
maintainerPATCH 457/567 drm/amdgpu/gmc12: add amdgpu_vm_handle_fault() handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit ff28ff98db6a8eeb469e02fb8bd1647b353232a9 upstream.
We need to call amdgpu_vm_handle_fault() on page fault on all gfx9 and newer parts to properly update the page tables, not just for recoverable page faults.
--- drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +27 -0
@@ -91,6 +91,8 @@ static int gmc_v12_0_process_interrupt(s struct amdgpu_iv_entry *entry) {struct amdgpu_vmhub *hub; +bool retry_fault = !!(entry->src_data[1 ] &0x80 ); +bool write_fault = !!(entry->src_data[1 ] &0x20 );uint32_t status =0 ;u64 addr;
@@ -102,6 +104,31 @@ static int gmc_v12_0_process_interrupt(selse hub = &adev->vmhub[AMDGPU_GFXHUB (0 )]; +if (retry_fault) { +/* Returning 1 here also prevents sending the IV to the KFD */ + +/* Process it only if it's the first fault for this address */ +if (entry->ih != &adev->irq.ih_soft && +amdgpu_gmc_filter_faults (adev, entry->ih, addr, entry->pasid, + entry->timestamp)) +return 1 ; + +/* Delegate it to a different ring if the hardware hasn't +* already done it. +*/ +if (entry->ih == &adev->irq.ih) { +amdgpu_irq_delegate (adev, entry,8 ); +return 1 ; + } + +/* Try to handle the recoverable page faults by filling page +* tables +*/ +if (amdgpu_vm_handle_fault (adev, entry->pasid,0 ,0 , addr, + entry->timestamp, write_fault)) +return 1 ; + } +if (!amdgpu_sriov_vf (adev)) {/* * Issue a dummy read to wait for the status register to
Greg Kroah-Hartman
maintainerPATCH 458/567 drm/amdgpu: add missing lock to amdgpu_ttm_access_memory_sdma
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pierre-Eric Pelloux-Prayer <[email protected]>
commit 4fa944255be521b1bbd9780383f77206303a3a5c upstream.
Users of ttm entities need to hold the gtt_window_lock before using them to guarantee proper ordering of jobs.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +2 -0
@@ -1513,6 +1513,7 @@ static int amdgpu_ttm_access_memory_sdmaif (r) goto out; +mutex_lock (&adev->mman.gtt_window_lock);amdgpu_res_first (abo->tbo.resource, offset, len, &src_mm); src_addr =amdgpu_ttm_domain_start (adev, bo->resource->mem_type) + src_mm.start;
@@ -1527,6 +1528,7 @@ static int amdgpu_ttm_access_memory_sdmaWARN_ON (job->ibs[0 ].length_dw > num_dw); fence =amdgpu_job_submit (job); +mutex_unlock (&adev->mman.gtt_window_lock);if (!dma_fence_wait_timeout (fence, false, adev->sdma_timeout)) r = -ETIMEDOUT ;
Greg Kroah-Hartman
maintainerPATCH 459/567 drm/amdgpu/gmc11: add amdgpu_vm_handle_fault() handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 3f2289b56cd98f5741056bdb6e521324eff07ce5 upstream.
We need to call amdgpu_vm_handle_fault() on page fault on all gfx9 and newer parts to properly update the page tables, not just for recoverable page faults.
--- drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +27 -0
@@ -103,12 +103,39 @@ static int gmc_v11_0_process_interrupt(suint32_t vmhub_index = entry->client_id ==SOC21_IH_CLIENTID_VMC ?AMDGPU_MMHUB0 (0 ) :AMDGPU_GFXHUB (0 );struct amdgpu_vmhub *hub = &adev->vmhub[vmhub_index]; +bool retry_fault = !!(entry->src_data[1 ] &0x80 ); +bool write_fault = !!(entry->src_data[1 ] &0x20 );uint32_t status =0 ;u64 addr; addr = (u64 )entry->src_data[0 ] <<12 ; addr |= ((u64 )entry->src_data[1 ] &0xf ) <<44 ; +if (retry_fault) { +/* Returning 1 here also prevents sending the IV to the KFD */ + +/* Process it only if it's the first fault for this address */ +if (entry->ih != &adev->irq.ih_soft && +amdgpu_gmc_filter_faults (adev, entry->ih, addr, entry->pasid, + entry->timestamp)) +return 1 ; + +/* Delegate it to a different ring if the hardware hasn't +* already done it. +*/ +if (entry->ih == &adev->irq.ih) { +amdgpu_irq_delegate (adev, entry,8 ); +return 1 ; + } + +/* Try to handle the recoverable page faults by filling page +* tables +*/ +if (amdgpu_vm_handle_fault (adev, entry->pasid,0 ,0 , addr, + entry->timestamp, write_fault)) +return 1 ; + } +if (!amdgpu_sriov_vf (adev)) {/* * Issue a dummy read to wait for the status register to
Greg Kroah-Hartman
maintainerPATCH 460/567 drm/msm/a6xx: Fix out of bound IO access in a6xx_get_gmu_registers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Akhil P Oommen <[email protected]>
commit 779b68a5bf2764c8ed3aa800e41ba0d5d007e1e7 upstream.
REG_A6XX_GMU_AO_AHB_FENCE_CTRL register falls under GMU's register range. So, use gmu_write() routines to write to this register.
Patchwork: https://patchwork.freedesktop.org/patch/688993/
Message-ID: <[email protected]>
--- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +1 -1
@@ -1231,7 +1231,7 @@ static void a6xx_get_gmu_registers(strucreturn ;/* Set the fence to ALLOW mode so we can access the registers */ -gpu_write (gpu,REG_A6XX_GMU_AO_AHB_FENCE_CTRL ,0 ); +gmu_write (&a6xx_gpu->gmu,REG_A6XX_GMU_AO_AHB_FENCE_CTRL ,0 );_a6xx_get_gmu_registers (gpu, a6xx_state, &a6xx_gmu_reglist[2 ], &a6xx_state->gmu_registers[2 ], false);
Greg Kroah-Hartman
maintainerPATCH 461/567 drm/buddy: Optimize free block management with RB tree
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arunpravin Paneer Selvam <[email protected]>
commit c178e534fff1d5a74da80ea03b20e2b948a00113 upstream.
Replace the freelist (O(n)) used for free block management with a red-black tree, providing more efficient O(log n) search, insert, and delete operations. This improves scalability and performance when managing large numbers of free blocks per order (e.g., hundreds or thousands).
In the VK-CTS memory stress subtest, the buddy manager merges fragmented memory and inserts freed blocks into the freelist. Since freelist insertion is O(n), this becomes a bottleneck as fragmentation increases. Benchmarking shows list_insert_sorted() consumes ~52.69% CPU with the freelist, compared to just 0.03% with the RB tree (rbtree_insert.isra.0), despite performing the same sorted insert.
This also improves performance in heavily fragmented workloads, such as games or graphics tests that stress memory.
As the buddy allocator evolves with new features such as clear-page tracking, the resulting fragmentation and complexity have grown. These RB-tree based design changes are introduced to address that growth and ensure the allocator continues to perform efficiently under fragmented conditions.
The RB tree implementation with separate clear/dirty trees provides:
- O(n log n) aggregate complexity for all operations instead of O(n^2)
- Elimination of soft lockups and system instability
- Improved code maintainability and clarity
- Better scalability for large memory systems
- Predictable performance under fragmentation
v3(Matthew):
- Remove RB_EMPTY_NODE check in force_merge function.
- Rename rb for loop macros to have less generic names and move to .c file.
- Make the rb node rb and link field as union.
v4(Jani Nikula):
- The kernel-doc comment should be "/**"
- Move all the rbtree macros to rbtree.h and add parens to ensure correct precedence.
v5:
- Remove the inline in a .c file (Jani Nikula).
v6(Peter Zijlstra):
- Add rb_add() function replacing the existing rbtree_insert() code.
v7:
- A full walk iteration in rbtree is slower than the list (Peter Zijlstra).
- The existing rbtree_postorder_for_each_entry_safe macro should be used in scenarios where traversal order is not a critical factor (Christian).
v8(Matthew):
- Remove the rbtree_is_empty() check in this patch as well.
--- drivers/gpu/drm/drm_buddy.c | 195 ++++++++++++++++++++++++++------------------ include/drm/drm_buddy.h | 11 +- 2 files changed, 126 insertions(+), 80 deletions(-)
drivers/gpu/drm/drm_buddy.c +118 -77
@@ -11,6 +11,8 @@static struct kmem_cache *slab_blocks; +#define rbtree_get_free_block (node) rb_entry((node), struct drm_buddy_block, rb) + static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, struct drm_buddy_block *parent, unsigned int order,
@@ -28,6 +30,8 @@ static struct drm_buddy_block *drm_block block->header |= order; block->parent = parent; +RB_CLEAR_NODE (&block->rb); +BUG_ON (block->header &DRM_BUDDY_HEADER_UNUSED );return block; }
@@ -38,23 +42,49 @@ static void drm_block_free(struct drm_bukmem_cache_free (slab_blocks, block); } -static void list_insert_sorted (struct drm_buddy *mm, -struct drm_buddy_block *block) +static bool drm_buddy_block_offset_less (const struct drm_buddy_block *block, +const struct drm_buddy_block *node) { -struct drm_buddy_block *node; -struct list_head *head; +return drm_buddy_block_offset (block) <drm_buddy_block_offset (node); +} - head = &mm->free_list[drm_buddy_block_order (block)]; -if (list_empty (head)) { -list_add (&block->link, head); -return ; - } +static bool rbtree_block_offset_less (struct rb_node *block, +const struct rb_node *node) +{ +return drm_buddy_block_offset_less (rbtree_get_free_block (block), +rbtree_get_free_block (node)); +} -list_for_each_entry (node, head, link) -if (drm_buddy_block_offset (block) <drm_buddy_block_offset (node)) -break ; +static void rbtree_insert (struct drm_buddy *mm, +struct drm_buddy_block *block) +{ +rb_add (&block->rb, + &mm->free_tree[drm_buddy_block_order (block)], + rbtree_block_offset_less); +} + +static void rbtree_remove (struct drm_buddy *mm, +struct drm_buddy_block *block) +{ +struct rb_root *root; + + root = &mm->free_tree[drm_buddy_block_order (block)]; +rb_erase (&block->rb, root); + +RB_CLEAR_NODE (&block->rb); +} + +static struct drm_buddy_block * +rbtree_last_entry (struct drm_buddy *mm,unsigned int order) +{ +struct rb_node *node =rb_last (&mm->free_tree[order]); + + return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; +} -__list_add (&block->link, node->link.prev, &node->link); +static bool rbtree_is_empty (struct drm_buddy *mm,unsigned int order) +{ +return RB_EMPTY_ROOT (&mm->free_tree[order]); }static void clear_reset (struct drm_buddy_block *block)
@@ -67,12 +97,13 @@ static void mark_cleared(struct drm_budd block->header |=DRM_BUDDY_HEADER_CLEAR ; } -static void mark_allocated (struct drm_buddy_block *block) +static void mark_allocated (struct drm_buddy *mm, +struct drm_buddy_block *block) { block->header &= ~DRM_BUDDY_HEADER_STATE ; block->header |=DRM_BUDDY_ALLOCATED ; -list_del (&block->link); +rbtree_remove (mm, block); } static void mark_free(struct drm_buddy *mm,
@@ -81,15 +112,16 @@ static void mark_free(struct drm_buddy * block->header &= ~DRM_BUDDY_HEADER_STATE ; block->header |=DRM_BUDDY_FREE ; -list_insert_sorted (mm, block); +rbtree_insert (mm, block); } -static void mark_split (struct drm_buddy_block *block) +static void mark_split (struct drm_buddy *mm, +struct drm_buddy_block *block) { block->header &= ~DRM_BUDDY_HEADER_STATE ; block->header |=DRM_BUDDY_SPLIT ; -list_del (&block->link); +rbtree_remove (mm, block); }static inline bool overlaps (u64 s1,u64 e1,u64 s2,u64 e2)
@@ -145,7 +177,7 @@ static unsigned int __drm_buddy_free(strmark_cleared (parent); } -list_del (&buddy->link); +rbtree_remove (mm, buddy);if (force_merge &&drm_buddy_block_is_clear (buddy)) mm->clear_avail -=drm_buddy_block_size (mm, buddy);
@@ -176,13 +208,19 @@ static int __force_merge(struct drm_buddreturn -EINVAL ;for (i = min_order -1 ; i >=0 ; i--) { -struct drm_buddy_block *block, *prev; +struct rb_root *root = &mm->free_tree[i]; +struct rb_node *iter; + + iter =rb_last (root); -list_for_each_entry_safe_reverse (block, prev, &mm->free_list[i], link) { -struct drm_buddy_block *buddy; +while (iter) { +struct drm_buddy_block *block, *buddy;u64 block_start, block_end; -if (!block->parent) + block =rbtree_get_free_block (iter); + iter =rb_prev (iter); + +if (!block || !block->parent)continue ; block_start =drm_buddy_block_offset (block);
@@ -198,15 +236,10 @@ static int __force_merge(struct drm_buddWARN_ON (drm_buddy_block_is_clear (block) ==drm_buddy_block_is_clear (buddy)); -/* -* If the prev block is same as buddy, don't access the -* block in the next iteration as we would free the -* buddy block as part of the free function. -*/ -if (prev == buddy) - prev =list_prev_entry (prev, link); +if (iter == &buddy->rb) + iter =rb_prev (iter); -list_del (&block->link); +rbtree_remove (mm, block);if (drm_buddy_block_is_clear (block)) mm->clear_avail -=drm_buddy_block_size (mm, block);
@@ -234,7 +267,7 @@ static int __force_merge(struct drm_buddint drm_buddy_init (struct drm_buddy *mm,u64 size,u64 chunk_size) {unsigned int i; -u64 offset; +u64 offset =0 ;if (size < chunk_size)return -EINVAL ;
@@ -255,14 +288,14 @@ int drm_buddy_init(struct drm_buddy *mm,BUG_ON (mm->max_order >DRM_BUDDY_MAX_ORDER ); - mm->free_list =kmalloc_array (mm->max_order +1 , -sizeof (struct list_head ), + mm->free_tree =kmalloc_array (mm->max_order +1 , +sizeof (struct rb_root ),GFP_KERNEL ); -if (!mm->free_list) +if (!mm->free_tree)return -ENOMEM ;for (i =0 ; i <= mm->max_order; ++i) -INIT_LIST_HEAD (&mm->free_list[i]); + mm->free_tree[i] =RB_ROOT ; mm->n_roots =hweight64 (size);
@@ -270,9 +303,8 @@ int drm_buddy_init(struct drm_buddy *mm,sizeof (struct drm_buddy_block *), GFP_KERNEL);if (!mm->roots) - goto out_free_list; + goto out_free_tree; - offset =0 ; i =0 ;/*
@@ -309,8 +341,8 @@ out_free_roots:while (i--)drm_block_free (mm, mm->roots[i]);kfree (mm->roots); -out_free_list: -kfree (mm->free_list); +out_free_tree: +kfree (mm->free_tree);return -ENOMEM ; }EXPORT_SYMBOL (drm_buddy_init);
@@ -320,7 +352,7 @@ EXPORT_SYMBOL(drm_buddy_init);* * @mm: DRM buddy manager to free * -* Cleanup memory manager resources and the freelist +* Cleanup memory manager resources and the freetree */ void drm_buddy_fini (struct drm_buddy *mm) {
@@ -345,7 +377,7 @@ void drm_buddy_fini(struct drm_buddy *mmWARN_ON (mm->avail != mm->size);kfree (mm->roots); -kfree (mm->free_list); +kfree (mm->free_tree); }EXPORT_SYMBOL (drm_buddy_fini);
@@ -378,7 +410,7 @@ static int split_block(struct drm_buddyclear_reset (block); } -mark_split (block); +mark_split (mm, block);return 0 ; }
@@ -407,7 +439,7 @@ EXPORT_SYMBOL(drm_get_buddy);* @is_clear: blocks clear state * * Reset the clear state based on @is_clear value for each block -* in the freelist. +* in the freetree. */ void drm_buddy_reset_clear (struct drm_buddy *mm,bool is_clear) {
@@ -426,9 +458,9 @@ void drm_buddy_reset_clear(struct drm_bu }for (i =0 ; i <= mm->max_order; ++i) { -struct drm_buddy_block *block; +struct drm_buddy_block *block, *tmp; -list_for_each_entry_reverse (block, &mm->free_list[i], link) { +rbtree_postorder_for_each_entry_safe (block, tmp, &mm->free_tree[i], rb) {if (is_clear !=drm_buddy_block_is_clear (block)) {if (is_clear) {mark_cleared (block);
@@ -634,14 +666,18 @@ get_maxblock(struct drm_buddy *mm, unsigunsigned int i;for (i = order; i <= mm->max_order; ++i) { +struct rb_node *iter =rb_last (&mm->free_tree[i]);struct drm_buddy_block *tmp_block; -list_for_each_entry_reverse (tmp_block, &mm->free_list[i], link) { -if (block_incompatible (tmp_block, flags)) -continue ; +while (iter) { + tmp_block =rbtree_get_free_block (iter); - block = tmp_block; -break ; +if (!block_incompatible (tmp_block, flags)) { + block = tmp_block; +break ; + } + + iter =rb_prev (iter); }if (!block)
@@ -662,7 +698,7 @@ get_maxblock(struct drm_buddy *mm, unsig }static struct drm_buddy_block * -alloc_from_freelist (struct drm_buddy *mm, +alloc_from_freetree (struct drm_buddy *mm,unsigned int order,unsigned long flags) {
@@ -677,14 +713,18 @@ alloc_from_freelist(struct drm_buddy *mm tmp =drm_buddy_block_order (block); }else {for (tmp = order; tmp <= mm->max_order; ++tmp) { +struct rb_node *iter =rb_last (&mm->free_tree[tmp]);struct drm_buddy_block *tmp_block; -list_for_each_entry_reverse (tmp_block, &mm->free_list[tmp], link) { -if (block_incompatible (tmp_block, flags)) -continue ; +while (iter) { + tmp_block =rbtree_get_free_block (iter); - block = tmp_block; -break ; +if (!block_incompatible (tmp_block, flags)) { + block = tmp_block; +break ; + } + + iter =rb_prev (iter); }if (block)
@@ -695,13 +735,9 @@ alloc_from_freelist(struct drm_buddy *mmif (!block) {/* Fallback method */ for (tmp = order; tmp <= mm->max_order; ++tmp) { -if (!list_empty (&mm->free_list[tmp])) { - block =list_last_entry (&mm->free_list[tmp], - struct drm_buddy_block, - link); -if (block) -break ; - } + block =rbtree_last_entry (mm, tmp); +if (block) +break ; }if (!block)
@@ -766,7 +802,7 @@ static int __alloc_range(struct drm_buddif (contains (start, end, block_start, block_end)) {if (drm_buddy_block_is_free (block)) { -mark_allocated (block); +mark_allocated (mm, block); total_allocated +=drm_buddy_block_size (mm, block); mm->avail -=drm_buddy_block_size (mm, block);if (drm_buddy_block_is_clear (block))
@@ -844,8 +880,8 @@ static int __alloc_contig_try_harder(str {u64 rhs_offset, lhs_offset, lhs_size, filled;struct drm_buddy_block *block; -struct list_head *list;LIST_HEAD (blocks_lhs); +struct rb_node *iter;unsigned long pages;unsigned int order;u64 modify_size;
@@ -857,11 +893,14 @@ static int __alloc_contig_try_harder(strif (order ==0 )return -ENOSPC ; - list = &mm->free_list[order]; -if (list_empty (list)) +if (rbtree_is_empty (mm, order))return -ENOSPC ; -list_for_each_entry_reverse (block, list, link) { + iter =rb_last (&mm->free_tree[order]); + +while (iter) { + block =rbtree_get_free_block (iter); +/* Allocate blocks traversing RHS */ rhs_offset =drm_buddy_block_offset (block); err = __drm_buddy_alloc_range(mm, rhs_offset, size,
@@ -886,6 +925,8 @@ static int __alloc_contig_try_harder(str }/* Free blocks for the next iteration */ drm_buddy_free_list_internal (mm, blocks); + + iter =rb_prev (iter); }return -ENOSPC ;
@@ -971,7 +1012,7 @@ int drm_buddy_block_trim(struct drm_buddlist_add (&block->tmp_link, &dfs); err =__alloc_range (mm, &dfs, new_start, new_size, blocks,NULL );if (err) { -mark_allocated (block); +mark_allocated (mm, block); mm->avail -=drm_buddy_block_size (mm, block);if (drm_buddy_block_is_clear (block)) mm->clear_avail -=drm_buddy_block_size (mm, block);
@@ -994,8 +1035,8 @@ __drm_buddy_alloc_blocks(struct drm_buddreturn __drm_buddy_alloc_range_bias (mm, start, end, order, flags); else -/* Allocate from freelist */ - return alloc_from_freelist(mm, order, flags); +/* Allocate from freetree */ + return alloc_from_freetree(mm, order, flags); }/**
@@ -1012,8 +1053,8 @@ __drm_buddy_alloc_blocks(struct drm_budd* alloc_range_bias() called on range limitations, which traverses * the tree and returns the desired block. * -* alloc_from_freelist() called when *no* range restrictions -* are enforced, which picks the block from the freelist. +* alloc_from_freetree() called when *no* range restrictions +* are enforced, which picks the block from the freetree. * * Returns: * 0 on success, error code on failure.
@@ -1115,7 +1156,7 @@ int drm_buddy_alloc_blocks(struct drm_bu } }while (1 ); -mark_allocated (block); +mark_allocated (mm, block); mm->avail -=drm_buddy_block_size (mm, block);if (drm_buddy_block_is_clear (block)) mm->clear_avail -=drm_buddy_block_size (mm, block);
@@ -1196,10 +1237,10 @@ void drm_buddy_print(struct drm_buddy *m mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);for (order = mm->max_order; order >=0 ; order--) { -struct drm_buddy_block *block; +struct drm_buddy_block *block, *tmp;u64 count =0 , free; -list_for_each_entry (block, &mm->free_list[order], link) { +rbtree_postorder_for_each_entry_safe (block, tmp, &mm->free_tree[order], rb) {BUG_ON (!drm_buddy_block_is_free (block)); count++; }
include/drm/drm_buddy.h +8 -3
@@ -10,6 +10,7 @@#include <linux/list.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/rbtree.h> #include <drm/drm_print.h>
@@ -53,7 +54,11 @@ struct drm_buddy_block {* a list, if so desired. As soon as the block is freed with * drm_buddy_free* ownership is given back to the mm. */ -struct list_head link; +union { +struct rb_node rb; +struct list_head link; + }; +struct list_head tmp_link; };
@@ -68,7 +73,7 @@ struct drm_buddy_block {*/ struct drm_buddy {/* Maintain a free list for each order. */ -struct list_head *free_list; +struct rb_root *free_tree;/* * Maintain explicit binary tree(s) to track the allocation of the
@@ -94,7 +99,7 @@ struct drm_buddy { };static inline u64 -drm_buddy_block_offset (struct drm_buddy_block *block) +drm_buddy_block_offset (const struct drm_buddy_block *block) {return block->header &DRM_BUDDY_HEADER_OFFSET ; }
Greg Kroah-Hartman
maintainerPATCH 462/567 drm/buddy: Separate clear and dirty free block trees
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arunpravin Paneer Selvam <[email protected]>
commit d4cd665c98c144dd6ad5d66d30396e13d23118c9 upstream.
Maintain two separate RB trees per order - one for clear (zeroed) blocks and another for dirty (uncleared) blocks. This separation improves code clarity and makes it more obvious which tree is being searched during allocation. It also improves scalability and efficiency when searching for a specific type of block, avoiding unnecessary checks and making the allocator more predictable under fragmentation.
The changes have been validated using the existing drm_buddy_test KUnit test cases, along with selected graphics workloads, to ensure correctness and avoid regressions.
v2: Missed adding the suggested-by tag. Added it in v2.
v3(Matthew):
- Remove the double underscores from the internal functions.
- Rename the internal functions to have less generic names.
- Fix the error handling code.
- Pass tree argument for the tree macro.
- Use the existing dirty/free bit instead of new tree field.
- Make free_trees[] instead of clear_tree and dirty_tree for more cleaner approach.
v4:
- A bug was reported by Intel CI and it is fixed by Matthew Auld.
- Replace the get_root function with
&mm->free_trees[tree][order] (Matthew)
- Remove the unnecessary rbtree_is_empty() check (Matthew)
- Remove the unnecessary get_tree_for_flags() function.
- Rename get_tree_for_block() name with get_block_tree() for more clarity.
v5(Jani Nikula):
- Don't use static inline in .c files.
- enum free_tree and enumerator names are quite generic for a header and usage and the whole enum should be an implementation detail.
v6:
- Rewrite the __force_merge() function using the rb_last() and rb_prev().
v7(Matthew):
- Replace the open-coded tree iteration for loops with the for_each_free_tree() macro throughout the code.
- Fixed out_free_roots to prevent double decrement of i, addressing potential crash.
- Replaced enum drm_buddy_free_tree with unsigned int in for_each_free_tree loops.
--- drivers/gpu/drm/drm_buddy.c | 329 ++++++++++++++++++++++++-------------------- include/drm/drm_buddy.h | 2 2 files changed, 186 insertions(+), 145 deletions(-)
drivers/gpu/drm/drm_buddy.c +185 -144
@@ -9,9 +9,16 @@#include <drm/drm_buddy.h> +enum drm_buddy_free_tree { +DRM_BUDDY_CLEAR_TREE =0 , +DRM_BUDDY_DIRTY_TREE , +DRM_BUDDY_MAX_FREE_TREES , +}; +static struct kmem_cache *slab_blocks; -#define rbtree_get_free_block (node) rb_entry((node), struct drm_buddy_block, rb) +#define for_each_free_tree (tree) \ + for ((tree) = 0; (tree) < DRM_BUDDY_MAX_FREE_TREES; (tree)++) static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, struct drm_buddy_block *parent,
@@ -42,6 +49,30 @@ static void drm_block_free(struct drm_bukmem_cache_free (slab_blocks, block); } +static enum drm_buddy_free_tree +get_block_tree (struct drm_buddy_block *block) +{ +return drm_buddy_block_is_clear (block) ? +DRM_BUDDY_CLEAR_TREE :DRM_BUDDY_DIRTY_TREE ; +} + +static struct drm_buddy_block * +rbtree_get_free_block (const struct rb_node *node) +{ + return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; +} + +static struct drm_buddy_block * +rbtree_last_free_block (struct rb_root *root) +{ +return rbtree_get_free_block (rb_last (root)); +} + +static bool rbtree_is_empty (struct rb_root *root) +{ +return RB_EMPTY_ROOT (root); +} +static bool drm_buddy_block_offset_less (const struct drm_buddy_block *block,const struct drm_buddy_block *node) {
@@ -56,37 +87,28 @@ static bool rbtree_block_offset_less(str }static void rbtree_insert (struct drm_buddy *mm, -struct drm_buddy_block *block) +struct drm_buddy_block *block, +enum drm_buddy_free_tree tree) {rb_add (&block->rb, - &mm->free_tree[drm_buddy_block_order (block)], + &mm->free_trees[tree][drm_buddy_block_order (block)], rbtree_block_offset_less); }static void rbtree_remove (struct drm_buddy *mm,struct drm_buddy_block *block) { +unsigned int order =drm_buddy_block_order (block); +enum drm_buddy_free_tree tree;struct rb_root *root; - root = &mm->free_tree[drm_buddy_block_order (block)]; -rb_erase (&block->rb, root); + tree =get_block_tree (block); + root = &mm->free_trees[tree][order]; +rb_erase (&block->rb, root);RB_CLEAR_NODE (&block->rb); } -static struct drm_buddy_block * -rbtree_last_entry (struct drm_buddy *mm,unsigned int order) -{ -struct rb_node *node =rb_last (&mm->free_tree[order]); - - return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; -} - -static bool rbtree_is_empty (struct drm_buddy *mm,unsigned int order) -{ -return RB_EMPTY_ROOT (&mm->free_tree[order]); -} -static void clear_reset (struct drm_buddy_block *block) { block->header &= ~DRM_BUDDY_HEADER_CLEAR ;
@@ -109,10 +131,13 @@ static void mark_allocated(struct drm_bustatic void mark_free (struct drm_buddy *mm,struct drm_buddy_block *block) { +enum drm_buddy_free_tree tree; + block->header &= ~DRM_BUDDY_HEADER_STATE ; block->header |=DRM_BUDDY_FREE ; -rbtree_insert (mm, block); + tree =get_block_tree (block); +rbtree_insert (mm, block, tree); } static void mark_split(struct drm_buddy *mm,
@@ -198,7 +223,7 @@ static int __force_merge(struct drm_buddu64 end, unsigned int min_order) { - unsigned int order; + unsigned int tree, order;int i;if (!min_order)
@@ -207,45 +232,48 @@ static int __force_merge(struct drm_buddif (min_order > mm->max_order)return -EINVAL ; -for (i = min_order -1 ; i >=0 ; i--) { -struct rb_root *root = &mm->free_tree[i]; -struct rb_node *iter; +for_each_free_tree (tree) { +for (i = min_order -1 ; i >=0 ; i--) { +struct rb_node *iter =rb_last (&mm->free_trees[tree][i]); - iter =rb_last (root); - -while (iter) { -struct drm_buddy_block *block, *buddy; -u64 block_start, block_end; +while (iter) { +struct drm_buddy_block *block, *buddy; +u64 block_start, block_end; - block =rbtree_get_free_block (iter); - iter =rb_prev (iter); + block =rbtree_get_free_block (iter); + iter =rb_prev (iter); -if (!block || !block->parent) -continue ; +if (!block || !block->parent) +continue ; - block_start =drm_buddy_block_offset (block); - block_end = block_start +drm_buddy_block_size (mm, block) -1 ; + block_start =drm_buddy_block_offset (block); + block_end = block_start +drm_buddy_block_size (mm, block) -1 ; -if (!contains (start, end, block_start, block_end)) -continue ; +if (!contains (start, end, block_start, block_end)) +continue ; - buddy =__get_buddy (block); -if (!drm_buddy_block_is_free (buddy)) -continue ; + buddy =__get_buddy (block); +if (!drm_buddy_block_is_free (buddy)) +continue ; -WARN_ON (drm_buddy_block_is_clear (block) == -drm_buddy_block_is_clear (buddy)); +WARN_ON (drm_buddy_block_is_clear (block) == +drm_buddy_block_is_clear (buddy)); -if (iter == &buddy->rb) - iter =rb_prev (iter); +/* +* Advance to the next node when the current node is the buddy, +* as freeing the block will also remove its buddy from the tree. +*/ +if (iter == &buddy->rb) + iter =rb_prev (iter); -rbtree_remove (mm, block); -if (drm_buddy_block_is_clear (block)) - mm->clear_avail -=drm_buddy_block_size (mm, block); +rbtree_remove (mm, block); +if (drm_buddy_block_is_clear (block)) + mm->clear_avail -=drm_buddy_block_size (mm, block); - order =__drm_buddy_free (mm, block, true); -if (order >= min_order) -return 0 ; + order =__drm_buddy_free (mm, block, true); +if (order >= min_order) +return 0 ; + } } }
@@ -266,7 +294,7 @@ static int __force_merge(struct drm_budd*/ int drm_buddy_init (struct drm_buddy *mm,u64 size,u64 chunk_size) { -unsigned int i; +unsigned int i, j, root_count =0 ;u64 offset =0 ;if (size < chunk_size)
@@ -288,14 +316,22 @@ int drm_buddy_init(struct drm_buddy *mm,BUG_ON (mm->max_order >DRM_BUDDY_MAX_ORDER ); - mm->free_tree =kmalloc_array (mm->max_order +1 , -sizeof (struct rb_root ), -GFP_KERNEL ); -if (!mm->free_tree) + mm->free_trees =kmalloc_array (DRM_BUDDY_MAX_FREE_TREES , +sizeof (*mm->free_trees), +GFP_KERNEL ); +if (!mm->free_trees)return -ENOMEM ; -for (i =0 ; i <= mm->max_order; ++i) - mm->free_tree[i] =RB_ROOT ; +for_each_free_tree (i) { + mm->free_trees[i] =kmalloc_array (mm->max_order +1 , +sizeof (struct rb_root ), +GFP_KERNEL ); +if (!mm->free_trees[i]) + goto out_free_tree; + +for (j =0 ; j <= mm->max_order; ++j) + mm->free_trees[i][j] =RB_ROOT ; + } mm->n_roots =hweight64 (size);
@@ -305,8 +341,6 @@ int drm_buddy_init(struct drm_buddy *mm,if (!mm->roots) goto out_free_tree; - i =0 ; -/* * Split into power-of-two blocks, in case we are given a size that is * not itself a power-of-two.
@@ -325,24 +359,26 @@ int drm_buddy_init(struct drm_buddy *mm,mark_free (mm, root); -BUG_ON (i > mm->max_order); +BUG_ON (root_count > mm->max_order);BUG_ON (drm_buddy_block_size (mm, root) < chunk_size); - mm->roots[i] = root; + mm->roots[root_count] = root; offset += root_size; size -= root_size; - i++; + root_count++; }while (size);return 0 ; out_free_roots: -while (i--) -drm_block_free (mm, mm->roots[i]); +while (root_count--) +drm_block_free (mm, mm->roots[root_count]);kfree (mm->roots); out_free_tree: -kfree (mm->free_tree); +while (i--) +kfree (mm->free_trees[i]); +kfree (mm->free_trees);return -ENOMEM ; }EXPORT_SYMBOL (drm_buddy_init);
@@ -376,8 +412,9 @@ void drm_buddy_fini(struct drm_buddy *mmWARN_ON (mm->avail != mm->size); +for_each_free_tree (i) +kfree (mm->free_trees[i]);kfree (mm->roots); -kfree (mm->free_tree); }EXPORT_SYMBOL (drm_buddy_fini);
@@ -401,8 +438,7 @@ static int split_block(struct drm_buddyreturn -ENOMEM ; } -mark_free (mm, block->left); -mark_free (mm, block->right); +mark_split (mm, block);if (drm_buddy_block_is_clear (block)) {mark_cleared (block->left);
@@ -410,7 +446,8 @@ static int split_block(struct drm_buddyclear_reset (block); } -mark_split (mm, block); +mark_free (mm, block->left); +mark_free (mm, block->right);return 0 ; }
@@ -443,6 +480,7 @@ EXPORT_SYMBOL(drm_get_buddy);*/ void drm_buddy_reset_clear (struct drm_buddy *mm,bool is_clear) { +enum drm_buddy_free_tree src_tree, dst_tree;u64 root_size, size, start;unsigned int order;int i;
@@ -457,19 +495,24 @@ void drm_buddy_reset_clear(struct drm_bu size -= root_size; } + src_tree = is_clear ?DRM_BUDDY_DIRTY_TREE :DRM_BUDDY_CLEAR_TREE ; + dst_tree = is_clear ?DRM_BUDDY_CLEAR_TREE :DRM_BUDDY_DIRTY_TREE ; +for (i =0 ; i <= mm->max_order; ++i) { +struct rb_root *root = &mm->free_trees[src_tree][i];struct drm_buddy_block *block, *tmp; -rbtree_postorder_for_each_entry_safe (block, tmp, &mm->free_tree[i], rb) { -if (is_clear !=drm_buddy_block_is_clear (block)) { -if (is_clear) { -mark_cleared (block); - mm->clear_avail +=drm_buddy_block_size (mm, block); - }else { -clear_reset (block); - mm->clear_avail -=drm_buddy_block_size (mm, block); - } +rbtree_postorder_for_each_entry_safe (block, tmp, root, rb) { +rbtree_remove (mm, block); +if (is_clear) { +mark_cleared (block); + mm->clear_avail +=drm_buddy_block_size (mm, block); + }else { +clear_reset (block); + mm->clear_avail -=drm_buddy_block_size (mm, block); } + +rbtree_insert (mm, block, dst_tree); } } }
@@ -659,27 +702,17 @@ __drm_buddy_alloc_range_bias(struct drm_ }static struct drm_buddy_block * -get_maxblock (struct drm_buddy *mm,unsigned int order, -unsigned long flags) +get_maxblock (struct drm_buddy *mm, +unsigned int order, +enum drm_buddy_free_tree tree) {struct drm_buddy_block *max_block =NULL , *block =NULL ; +struct rb_root *root;unsigned int i;for (i = order; i <= mm->max_order; ++i) { -struct rb_node *iter =rb_last (&mm->free_tree[i]); -struct drm_buddy_block *tmp_block; - -while (iter) { - tmp_block =rbtree_get_free_block (iter); - -if (!block_incompatible (tmp_block, flags)) { - block = tmp_block; -break ; - } - - iter =rb_prev (iter); - } - + root = &mm->free_trees[tree][i]; + block =rbtree_last_free_block (root);if (!block)continue ;
@@ -703,39 +736,37 @@ alloc_from_freetree(struct drm_buddy *mm unsigned long flags) {struct drm_buddy_block *block =NULL ; +struct rb_root *root; +enum drm_buddy_free_tree tree;unsigned int tmp;int err; + tree = (flags &DRM_BUDDY_CLEAR_ALLOCATION ) ? +DRM_BUDDY_CLEAR_TREE :DRM_BUDDY_DIRTY_TREE ; +if (flags &DRM_BUDDY_TOPDOWN_ALLOCATION ) { - block =get_maxblock (mm, order, flags); + block =get_maxblock (mm, order, tree);if (block)/* Store the obtained block order */ tmp =drm_buddy_block_order (block); }else {for (tmp = order; tmp <= mm->max_order; ++tmp) { -struct rb_node *iter =rb_last (&mm->free_tree[tmp]); -struct drm_buddy_block *tmp_block; - -while (iter) { - tmp_block =rbtree_get_free_block (iter); - -if (!block_incompatible (tmp_block, flags)) { - block = tmp_block; -break ; - } - - iter =rb_prev (iter); - } - +/* Get RB tree root for this order and tree */ + root = &mm->free_trees[tree][tmp]; + block =rbtree_last_free_block (root);if (block)break ; } }if (!block) { -/* Fallback method */ +/* Try allocating from the other tree */ + tree = (tree ==DRM_BUDDY_CLEAR_TREE ) ? +DRM_BUDDY_DIRTY_TREE :DRM_BUDDY_CLEAR_TREE ; +for (tmp = order; tmp <= mm->max_order; ++tmp) { - block =rbtree_last_entry (mm, tmp); + root = &mm->free_trees[tree][tmp]; + block =rbtree_last_free_block (root);if (block)break ; }
@@ -880,10 +911,9 @@ static int __alloc_contig_try_harder(str {u64 rhs_offset, lhs_offset, lhs_size, filled;struct drm_buddy_block *block; +unsigned int tree, order;LIST_HEAD (blocks_lhs); -struct rb_node *iter;unsigned long pages; -unsigned int order;u64 modify_size;int err;
@@ -893,40 +923,45 @@ static int __alloc_contig_try_harder(strif (order ==0 )return -ENOSPC ; -if (rbtree_is_empty (mm, order)) -return -ENOSPC ; +for_each_free_tree (tree) { +struct rb_root *root; +struct rb_node *iter; - iter =rb_last (&mm->free_tree[order]); + root = &mm->free_trees[tree][order]; +if (rbtree_is_empty (root)) +continue ; -while (iter) { - block =rbtree_get_free_block (iter); + iter =rb_last (root); +while (iter) { + block =rbtree_get_free_block (iter); -/* Allocate blocks traversing RHS */ - rhs_offset =drm_buddy_block_offset (block); - err =__drm_buddy_alloc_range (mm, rhs_offset, size, - &filled, blocks); -if (!err || err != -ENOSPC ) -return err; - - lhs_size =max ((size - filled), min_block_size); -if (!IS_ALIGNED (lhs_size, min_block_size)) - lhs_size =round_up (lhs_size, min_block_size); - -/* Allocate blocks traversing LHS */ - lhs_offset =drm_buddy_block_offset (block) - lhs_size; - err =__drm_buddy_alloc_range (mm, lhs_offset, lhs_size, -NULL , &blocks_lhs); -if (!err) { -list_splice (&blocks_lhs, blocks); -return 0 ; - }else if (err != -ENOSPC ) { +/* Allocate blocks traversing RHS */ + rhs_offset =drm_buddy_block_offset (block); + err =__drm_buddy_alloc_range (mm, rhs_offset, size, + &filled, blocks); +if (!err || err != -ENOSPC ) +return err; + + lhs_size =max ((size - filled), min_block_size); +if (!IS_ALIGNED (lhs_size, min_block_size)) + lhs_size =round_up (lhs_size, min_block_size); + +/* Allocate blocks traversing LHS */ + lhs_offset =drm_buddy_block_offset (block) - lhs_size; + err =__drm_buddy_alloc_range (mm, lhs_offset, lhs_size, +NULL , &blocks_lhs); +if (!err) { +list_splice (&blocks_lhs, blocks); +return 0 ; + }else if (err != -ENOSPC ) { +drm_buddy_free_list_internal (mm, blocks); +return err; + } +/* Free blocks for the next iteration */ drm_buddy_free_list_internal (mm, blocks); -return err; - } -/* Free blocks for the next iteration */ -drm_buddy_free_list_internal (mm, blocks); - iter =rb_prev (iter); + iter =rb_prev (iter); + } }return -ENOSPC ;
@@ -1238,11 +1273,17 @@ void drm_buddy_print(struct drm_buddy *mfor (order = mm->max_order; order >=0 ; order--) {struct drm_buddy_block *block, *tmp; +struct rb_root *root;u64 count =0 , free; +unsigned int tree; + +for_each_free_tree (tree) { + root = &mm->free_trees[tree][order]; -rbtree_postorder_for_each_entry_safe (block, tmp, &mm->free_tree[order], rb) { -BUG_ON (!drm_buddy_block_is_free (block)); - count++; +rbtree_postorder_for_each_entry_safe (block, tmp, root, rb) { +BUG_ON (!drm_buddy_block_is_free (block)); + count++; + } }drm_printf (p,"order-%2d " , order);
include/drm/drm_buddy.h +1 -1
@@ -73,7 +73,7 @@ struct drm_buddy_block {*/ struct drm_buddy {/* Maintain a free list for each order. */ -struct rb_root *free_tree; +struct rb_root **free_trees;/* * Maintain explicit binary tree(s) to track the allocation of the
Greg Kroah-Hartman
maintainerPATCH 463/567 drm/gma500: Remove unused helper psb_fbdev_fb_setcolreg()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
commit be729f9de6c64240645dc80a24162ac4d3fe00a8 upstream.
Remove psb_fbdev_fb_setcolreg(), which hasn't been called in almost a decade.
Gma500 commit 4d8d096e9ae8 ("gma500: introduce the framebuffer support code") added the helper psb_fbdev_fb_setcolreg() for setting the fbdev palette via fbdev's fb_setcolreg callback. Later
commit 3da6c2f3b730 ("drm/gma500: use DRM_FB_HELPER_DEFAULT_OPS for fb_ops") set several default helpers for fbdev emulation, including fb_setcmap.
The fbdev subsystem always prefers fb_setcmap over fb_setcolreg. [1] Hence, the gma500 code is no longer in use and gma500 has been using drm_fb_helper_setcmap() for several years without issues.
--- drivers/gpu/drm/gma500/fbdev.c | 43 ----------------------------------------- 1 file changed, 43 deletions(-)
drivers/gpu/drm/gma500/fbdev.c +0 -43
@@ -51,48 +51,6 @@ static const struct vm_operations_struct* struct fb_ops */ -#define CMAP_TOHW (_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) - -static int psb_fbdev_fb_setcolreg (unsigned int regno, -unsigned int red,unsigned int green, -unsigned int blue,unsigned int transp, -struct fb_info *info) -{ -struct drm_fb_helper *fb_helper = info->par; -struct drm_framebuffer *fb = fb_helper->fb; -uint32_t v; - -if (!fb) -return -ENOMEM ; - -if (regno >255 ) -return 1 ; - - red =CMAP_TOHW (red, info->var.red.length); - blue =CMAP_TOHW (blue, info->var.blue.length); - green =CMAP_TOHW (green, info->var.green.length); - transp =CMAP_TOHW (transp, info->var.transp.length); - - v = (red << info->var.red.offset) | - (green << info->var.green.offset) | - (blue << info->var.blue.offset) | - (transp << info->var.transp.offset); - -if (regno <16 ) { -switch (fb->format->cpp[0 ] *8 ) { -case 16 : - ((uint32_t *) info->pseudo_palette)[regno] = v; -break ; -case 24 : -case 32 : - ((uint32_t *) info->pseudo_palette)[regno] = v; -break ; - } - } - -return 0 ; -} -static int psb_fbdev_fb_mmap (struct fb_info *info,struct vm_area_struct *vma) {if (vma->vm_pgoff !=0 )
@@ -137,7 +95,6 @@ static const struct fb_ops psb_fbdev_fb_ .owner = THIS_MODULE, __FB_DEFAULT_IOMEM_OPS_RDWR,DRM_FB_HELPER_DEFAULT_OPS , - .fb_setcolreg = psb_fbdev_fb_setcolreg, __FB_DEFAULT_IOMEM_OPS_DRAW, .fb_mmap = psb_fbdev_fb_mmap, .fb_destroy = psb_fbdev_fb_destroy,
Greg Kroah-Hartman
maintainerPATCH 464/567 drm/edid: add DRM_EDID_IDENT_INIT() to initialize struct drm_edid_ident
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
commit 8b61583f993589a64c061aa91b44f5bd350d90a5 upstream.
Add a convenience helper for initializing struct drm_edid_ident.
--- include/drm/drm_edid.h | 6 ++++++ 1 file changed, 6 insertions(+)
include/drm/drm_edid.h +6 -0
@@ -333,6 +333,12 @@ struct drm_edid_ident {const char *name; }; +#define DRM_EDID_IDENT_INIT (_vend_chr_0, _vend_chr_1, _vend_chr_2, _product_id, _name) \ +{ \ + .panel_id = drm_edid_encode_panel_id(_vend_chr_0, _vend_chr_1, _vend_chr_2, _product_id), \ + .name = _name, \ +} +#define EDID_PRODUCT_ID (e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))/* Short Audio Descriptor */
Greg Kroah-Hartman
maintainerPATCH 465/567 drm/xe/oa: Fix potential UAF in xe_oa_add_config_ioctl()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanjay Yadav <[email protected]>
commit dcb171931954c51a1a7250d558f02b8f36570783 upstream.
In xe_oa_add_config_ioctl(), we accessed oa_config->id after dropping metrics_lock. Since this lock protects the lifetime of oa_config, an attacker could guess the id and call xe_oa_remove_config_ioctl() with perfect timing, freeing oa_config before we dereference it, leading to a potential use-after-free.
Fix this by caching the id in a local variable while holding the lock.
v2: (Matt A)
- Dropped mutex_unlock(&oa->metrics_lock) ordering change from xe_oa_remove_config_ioctl()
--- drivers/gpu/drm/xe/xe_oa.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/gpu/drm/xe/xe_oa.c +5 -3
@@ -2378,11 +2378,13 @@ int xe_oa_add_config_ioctl(struct drm_de goto sysfs_err; } -mutex_unlock (&oa->metrics_lock); + id = oa_config->id; + +drm_dbg (&oa->xe->drm,"Added config %s id=%i\n" , oa_config->uuid, id); -drm_dbg (&oa->xe->drm,"Added config %s id=%i\n" , oa_config->uuid, oa_config->id); +mutex_unlock (&oa->metrics_lock); -return oa_config->id; +return id; sysfs_err:mutex_unlock (&oa->metrics_lock);
Greg Kroah-Hartman
maintainerPATCH 466/567 drm/mediatek: Fix device node reference leak in mtk_dp_dt_parse()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miaoqian Lin <[email protected]>
commit a846505a193d7492ad3531e33cacfca31e4bcdd1 upstream.
The function mtk_dp_dt_parse() calls of_graph_get_endpoint_by_regs() to get the endpoint device node, but fails to call of_node_put() to release the reference when the function returns. This results in a device node reference leak.
Fix this by adding the missing of_node_put() call before returning from the function.
Found via static analysis and code review.
--- drivers/gpu/drm/mediatek/mtk_dp.c | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/mediatek/mtk_dp.c +1 -0
@@ -2067,6 +2067,7 @@ static int mtk_dp_dt_parse(struct mtk_dp endpoint =of_graph_get_endpoint_by_regs (pdev->dev.of_node,1 ,-1 ); len =of_property_count_elems_of_size (endpoint,"data-lanes" ,sizeof (u32)); +of_node_put (endpoint);if (len <0 || len >4 || len ==3 ) {dev_err (dev,"invalid data lane size: %d\n" , len);return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 467/567 drm/mediatek: Fix probe resource leaks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 07c7c640a8eb9e196f357d15d88a59602a947197 upstream.
Make sure to unmap and release the component iomap and clock on probe failure (e.g. probe deferral) and on driver unbind.
Note that unlike of_iomap(), devm_of_iomap() also checks whether the region is already mapped.
--- drivers/gpu/drm/mediatek/mtk_ddp_comp.c | 20 ++++++++++++++++---- drivers/gpu/drm/mediatek/mtk_ddp_comp.h | 2 +- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 4 ++-- 3 files changed, 19 insertions(+), 7 deletions(-)
drivers/gpu/drm/mediatek/mtk_ddp_comp.c +16 -4
@@ -621,15 +621,20 @@ int mtk_find_possible_crtcs(struct drm_dreturn ret; } -int mtk_ddp_comp_init (struct device_node *node,struct mtk_ddp_comp *comp, +static void mtk_ddp_comp_clk_put (void *_clk) +{ +struct clk *clk = _clk; + +clk_put (clk); +} + +int mtk_ddp_comp_init (struct device *dev,struct device_node *node,struct mtk_ddp_comp *comp,unsigned int comp_id) {struct platform_device *comp_pdev;enum mtk_ddp_comp_type type;struct mtk_ddp_comp_dev *priv; -#if IS_REACHABLE (CONFIG_MTK_CMDQ )int ret; -#endif if (comp_id >=DDP_COMPONENT_DRM_ID_MAX )return -EINVAL ;
@@ -670,11 +675,18 @@ int mtk_ddp_comp_init(struct device_nodeif (!priv)return -ENOMEM ; - priv->regs =of_iomap (node,0 ); + priv->regs =devm_of_iomap (dev, node,0 ,NULL ); +if (IS_ERR (priv->regs)) +return PTR_ERR (priv->regs); + priv->clk =of_clk_get (node,0 );if (IS_ERR (priv->clk))return PTR_ERR (priv->clk); + ret =devm_add_action_or_reset (dev, mtk_ddp_comp_clk_put, priv->clk); +if (ret) +return ret; +#if IS_REACHABLE (CONFIG_MTK_CMDQ ) ret =cmdq_dev_get_client_reg (comp->dev, &priv->cmdq_reg,0 );if (ret)
drivers/gpu/drm/mediatek/mtk_ddp_comp.h +1 -1
@@ -350,7 +350,7 @@ static inline void mtk_ddp_comp_encoder_int mtk_ddp_comp_get_id (struct device_node *node,enum mtk_ddp_comp_type comp_type);int mtk_find_possible_crtcs (struct drm_device *drm,struct device *dev); -int mtk_ddp_comp_init (struct device_node *comp_node,struct mtk_ddp_comp *comp, +int mtk_ddp_comp_init (struct device *dev,struct device_node *comp_node,struct mtk_ddp_comp *comp,unsigned int comp_id);enum mtk_ddp_comp_type mtk_ddp_comp_get_type (unsigned int comp_id); void mtk_ddp_write(struct cmdq_pkt *cmdq_pkt, unsigned int value,
drivers/gpu/drm/mediatek/mtk_drm_drv.c +2 -2
@@ -874,7 +874,7 @@ static int mtk_drm_probe(struct platform (void *)private->mmsys_dev, sizeof(*private->mmsys_dev)); private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR ].dev = &ovl_adaptor->dev; -mtk_ddp_comp_init (NULL , &private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR ], +mtk_ddp_comp_init (dev,NULL , &private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR ],DDP_COMPONENT_DRM_OVL_ADAPTOR );component_match_add (dev, &match, compare_dev, &ovl_adaptor->dev); }
@@ -943,7 +943,7 @@ static int mtk_drm_probe(struct platform node); } - ret =mtk_ddp_comp_init (node, &private->ddp_comp[comp_id], comp_id); + ret =mtk_ddp_comp_init (dev, node, &private->ddp_comp[comp_id], comp_id);if (ret) {of_node_put (node); goto err_node;
Greg Kroah-Hartman
maintainerPATCH 468/567 drm/mediatek: Fix probe memory leak
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 5e49200593f331cd0629b5376fab9192f698e8ef upstream.
The Mediatek DRM driver allocates private data for components without a platform driver but as the lifetime is tied to each component device, the memory is never freed.
Tie the allocation lifetime to the DRM platform device so that the memory is released on probe failure (e.g. probe deferral) and when the driver is unbound.
--- drivers/gpu/drm/mediatek/mtk_ddp_comp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/mediatek/mtk_ddp_comp.c +1 -1
@@ -671,7 +671,7 @@ int mtk_ddp_comp_init(struct device *dev type == MTK_DSI)return 0 ; - priv =devm_kzalloc (comp->dev,sizeof (*priv),GFP_KERNEL ); + priv =devm_kzalloc (dev,sizeof (*priv),GFP_KERNEL );if (!priv)return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 469/567 drm/mediatek: Fix probe device leaks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 2a2a04be8e869a19c9f950b89b1e05832a0f7ec7 upstream.
Make sure to drop the reference taken to each component device during probe on probe failure (e.g. probe deferral) and on driver unbind.
--- drivers/gpu/drm/mediatek/mtk_ddp_comp.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/gpu/drm/mediatek/mtk_ddp_comp.c +11 -0
@@ -621,6 +621,13 @@ int mtk_find_possible_crtcs(struct drm_dreturn ret; } +static void mtk_ddp_comp_put_device (void *_dev) +{ +struct device *dev = _dev; + +put_device (dev); +} +static void mtk_ddp_comp_clk_put (void *_clk) {struct clk *clk = _clk;
@@ -656,6 +663,10 @@ int mtk_ddp_comp_init(struct device *dev } comp->dev = &comp_pdev->dev; + ret =devm_add_action_or_reset (dev, mtk_ddp_comp_put_device, comp->dev); +if (ret) +return ret; + if (type == MTK_DISP_AAL || type == MTK_DISP_BLS || type == MTK_DISP_CCORR ||
Greg Kroah-Hartman
maintainerPATCH 470/567 drm/amdkfd: Export the cwsr_size and ctl_stack_size to userspace
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
commit 8fc2796dea6f1210e1a01573961d5836a7ce531e upstream.
This is important for userspace to avoid hardcoding VGPR size.
--- drivers/gpu/drm/amd/amdkfd/kfd_topology.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/gpu/drm/amd/amdkfd/kfd_topology.c +4 -0
@@ -509,6 +509,10 @@ static ssize_t node_show(struct kobject dev->node_props.num_sdma_queues_per_engine);sysfs_show_32bit_prop (buffer, offs,"num_cp_queues" , dev->node_props.num_cp_queues); +sysfs_show_32bit_prop (buffer, offs,"cwsr_size" , + dev->node_props.cwsr_size); +sysfs_show_32bit_prop (buffer, offs,"ctl_stack_size" , + dev->node_props.ctl_stack_size);if (dev->gpu) { log_max_watch_addr =
Greg Kroah-Hartman
maintainerPATCH 471/567 drm/amdkfd: bump minimum vgpr size for gfx1151
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jonathan Kim <[email protected]>
commit cf326449637a566ba98fb82c47d46cd479608c88 upstream.
GFX1151 has 1.5x the number of available physical VGPRs per SIMD. Bump total memory availability for acquire checks on queue creation.
--- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/amd/amdkfd/kfd_queue.c +1 -0
@@ -408,6 +408,7 @@ static u32 kfd_get_vgpr_size_per_cu(u32 vgpr_size =0x80000 ; else if (gfxv == 110000 || /* GFX_VERSION_PLUM_BONITO */ gfxv == 110001 || /* GFX_VERSION_WHEAT_NAS */ + gfxv == 110501 || /* GFX_VERSION_GFX1151 */ gfxv == 120000 || /* GFX_VERSION_GFX1200 */ gfxv == 120001) /* GFX_VERSION_GFX1201 */ vgpr_size =0x60000 ;
Greg Kroah-Hartman
maintainerPATCH 472/567 drm/amdkfd: Trap handler support for expert scheduling mode
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jay Cornwall <[email protected]>
commit b7851f8c66191cd23a0a08bd484465ad74bbbb7d upstream.
The trap may be entered with dependency checking disabled. Wait for dependency counters and save/restore scheduling mode.
v2:
Use ttmp1 instead of ttmp11. ttmp11 is not zero-initialized. While the trap handler does zero this field before use, a user-mode second-level trap handler could not rely on this being zero when using an older kernel mode driver.
v3:
Use ttmp11 primarily but copy to ttmp1 before jumping to the second level trap handler. ttmp1 is inspectable by a debugger. Unexpected bits in the unused space may regress existing software.
--- drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h | 62 +++++++++-------- drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx12.asm | 37 ++++++++++ 2 files changed, 73 insertions(+), 26 deletions(-)
drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h +36 -26
@@ -3640,14 +3640,18 @@ static const uint32_t cwsr_trap_gfx9_4_3 };static const uint32_t cwsr_trap_gfx12_hex[] = { -0xbfa00001 ,0xbfa002a2 , -0xb0804009 ,0xb8f8f804 , +0xbfa00001 ,0xbfa002b2 , +0xb0804009 ,0xb8eef81a , +0xbf880000 ,0xb980081a , +0x00000000 ,0xb8f8f804 , +0x9177ff77 ,0x0c000000 , +0x846e9a6e ,0x8c776e77 ,0x9178ff78 ,0x00008c00 ,0xb8fbf811 ,0x8b6eff78 ,0x00004000 ,0xbfa10008 ,0x8b6eff7b ,0x00000080 ,0xbfa20018 ,0x8b6ea07b , -0xbfa20042 ,0xbf830010 , +0xbfa2004a ,0xbf830010 ,0xb8fbf811 ,0xbfa0fffb ,0x8b6eff7b ,0x00000bd0 ,0xbfa20010 ,0xb8eef812 ,
@@ -3658,28 +3662,32 @@ static const uint32_t cwsr_trap_gfx12_he0xf0000000 ,0xbfa20005 ,0x8b6fff6f ,0x00000200 ,0xbfa20002 ,0x8b6ea07b , -0xbfa2002c ,0xbefa4d82 , +0xbfa20034 ,0xbefa4d82 ,0xbf8a0000 ,0x84fa887a ,0xbf0d8f7b ,0xbfa10002 ,0x8c7bff7b ,0xffff0000 , -0xf4601bbd ,0xf8000010 , -0xbf8a0000 ,0x846e976e , -0x9177ff77 ,0x00800000 , -0x8c776e77 ,0xf4603bbd , -0xf8000000 ,0xbf8a0000 , -0xf4603ebd ,0xf8000008 , -0xbf8a0000 ,0x8bee6e6e , -0xbfa10001 ,0xbe80486e , -0x8b6eff6d ,0xf0000000 , -0xbfa20009 ,0xb8eef811 , -0x8b6eff6e ,0x00000080 , -0xbfa20007 ,0x8c78ff78 , -0x00004000 ,0x80ec886c , -0x82ed806d ,0xbfa00002 , -0x806c846c ,0x826d806d , -0x8b6dff6d ,0x0000ffff , -0x8bfe7e7e ,0x8bea6a6a , -0x85788978 ,0xb9783244 , +0x8b6eff77 ,0x0c000000 , +0x916dff6d ,0x0c000000 , +0x8c6d6e6d ,0xf4601bbd , +0xf8000010 ,0xbf8a0000 , +0x846e976e ,0x9177ff77 , +0x00800000 ,0x8c776e77 , +0xf4603bbd ,0xf8000000 , +0xbf8a0000 ,0xf4603ebd , +0xf8000008 ,0xbf8a0000 , +0x8bee6e6e ,0xbfa10001 , +0xbe80486e ,0x8b6eff6d , +0xf0000000 ,0xbfa20009 , +0xb8eef811 ,0x8b6eff6e , +0x00000080 ,0xbfa20007 , +0x8c78ff78 ,0x00004000 , +0x80ec886c ,0x82ed806d , +0xbfa00002 ,0x806c846c , +0x826d806d ,0x8b6dff6d , +0x0000ffff ,0x8bfe7e7e , +0x8bea6a6a ,0x85788978 , +0x936eff77 ,0x0002001a , +0xb96ef81a ,0xb9783244 ,0xbe804a6c ,0xb8faf802 ,0xbf0d987a ,0xbfa10001 , 0xbfb00000, 0x8b6dff6d,
@@ -3977,7 +3985,7 @@ static const uint32_t cwsr_trap_gfx12_he0x008ce800 ,0x00000000 ,0x807d817d ,0x8070ff70 ,0x00000080 ,0xbf0a7b7d , -0xbfa2fff7 ,0xbfa0016e , +0xbfa2fff7 ,0xbfa00171 ,0xbef4007e ,0x8b75ff7f ,0x0000ffff ,0x8c75ff75 , 0x00040000, 0xbef60080,
@@ -4159,10 +4167,12 @@ static const uint32_t cwsr_trap_gfx12_he0xf8000074 ,0xbf8a0000 ,0x8b6dff6d ,0x0000ffff ,0x8bfe7e7e ,0x8bea6a6a , -0xb97af804 ,0xbe804ec2 , -0xbf94fffe ,0xbe804a6c , +0x936eff77 ,0x0002001a , +0xb96ef81a ,0xb97af804 ,0xbe804ec2 ,0xbf94fffe , -0xbfb10000 ,0xbf9f0000 , +0xbe804a6c ,0xbe804ec2 , +0xbf94fffe ,0xbfb10000 ,0xbf9f0000 ,0xbf9f0000 ,0xbf9f0000 ,0xbf9f0000 , + 0xbf9f0000, 0x00000000, };
drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx12.asm +37 -0
@@ -78,9 +78,16 @@ var SQ_WAVE_EXCP_FLAG_PRIV_RESTORE_PART_ var SQ_WAVE_EXCP_FLAG_PRIV_RESTORE_PART_2_SIZE = SQ_WAVE_EXCP_FLAG_PRIV_HOST_TRAP_SHIFT - SQ_WAVE_EXCP_FLAG_PRIV_ILLEGAL_INST_SHIFT var SQ_WAVE_EXCP_FLAG_PRIV_RESTORE_PART_3_SHIFT = SQ_WAVE_EXCP_FLAG_PRIV_WAVE_START_SHIFT var SQ_WAVE_EXCP_FLAG_PRIV_RESTORE_PART_3_SIZE = 32 - SQ_WAVE_EXCP_FLAG_PRIV_RESTORE_PART_3_SHIFT + +var SQ_WAVE_SCHED_MODE_DEP_MODE_SHIFT = 0 +var SQ_WAVE_SCHED_MODE_DEP_MODE_SIZE = 2 + var BARRIER_STATE_SIGNAL_OFFSET = 16 var BARRIER_STATE_VALID_OFFSET = 0 +var TTMP11_SCHED_MODE_SHIFT = 26 +var TTMP11_SCHED_MODE_SIZE = 2 +var TTMP11_SCHED_MODE_MASK = 0xC000000 var TTMP11_DEBUG_TRAP_ENABLED_SHIFT = 23 var TTMP11_DEBUG_TRAP_ENABLED_MASK = 0x800000
@@ -160,8 +167,19 @@ L_JUMP_TO_RESTORE: s_branch L_RESTORE L_SKIP_RESTORE: + // Assume most relaxed scheduling mode is set. Save and revert to normal mode. + s_getreg_b32 ttmp2, hwreg(HW_REG_WAVE_SCHED_MODE) + s_wait_alu 0 + s_setreg_imm32_b32 hwreg(HW_REG_WAVE_SCHED_MODE, \ + SQ_WAVE_SCHED_MODE_DEP_MODE_SHIFT, SQ_WAVE_SCHED_MODE_DEP_MODE_SIZE), 0 + s_getreg_b32 s_save_state_priv, hwreg(HW_REG_WAVE_STATE_PRIV) //save STATUS since we will change SCC + // Save SCHED_MODE[1:0] into ttmp11[27:26]. + s_andn2_b32 ttmp11, ttmp11, TTMP11_SCHED_MODE_MASK + s_lshl_b32 ttmp2, ttmp2, TTMP11_SCHED_MODE_SHIFT + s_or_b32 ttmp11, ttmp11, ttmp2 + // Clear SPI_PRIO: do not save with elevated priority. // Clear ECC_ERR: prevents SQC store and triggers FATAL_HALT if setreg'd. s_andn2_b32 s_save_state_priv, s_save_state_priv, SQ_WAVE_STATE_PRIV_ALWAYS_CLEAR_MASK
@@ -238,6 +256,13 @@ L_FETCH_2ND_TRAP: s_cbranch_scc0 L_NO_SIGN_EXTEND_TMA s_or_b32 ttmp15, ttmp15, 0xFFFF0000 L_NO_SIGN_EXTEND_TMA: +#if ASIC_FAMILY == CHIP_GFX12 + // Move SCHED_MODE[1:0] from ttmp11 to unused bits in ttmp1[27:26] (return PC_HI). + // The second-level trap will restore from ttmp1 for backwards compatibility. + s_and_b32 ttmp2, ttmp11, TTMP11_SCHED_MODE_MASK + s_andn2_b32 ttmp1, ttmp1, TTMP11_SCHED_MODE_MASK + s_or_b32 ttmp1, ttmp1, ttmp2 +#endif s_load_dword ttmp2, [ttmp14, ttmp15], 0x10 scope:SCOPE_SYS // debug trap enabled flag s_wait_idle
@@ -287,6 +312,10 @@ L_EXIT_TRAP: // STATE_PRIV.BARRIER_COMPLETE may have changed since we read it. // Only restore fields which the trap handler changes. s_lshr_b32 s_save_state_priv, s_save_state_priv, SQ_WAVE_STATE_PRIV_SCC_SHIFT + + // Assume relaxed scheduling mode after this point. + restore_sched_mode(ttmp2) + s_setreg_b32 hwreg(HW_REG_WAVE_STATE_PRIV, SQ_WAVE_STATE_PRIV_SCC_SHIFT, \ SQ_WAVE_STATE_PRIV_POISON_ERR_SHIFT - SQ_WAVE_STATE_PRIV_SCC_SHIFT + 1), s_save_state_priv
@@ -1043,6 +1072,9 @@ L_SKIP_BARRIER_RESTORE: s_and_b64 exec, exec, exec // Restore STATUS.EXECZ, not writable by s_setreg_b32 s_and_b64 vcc, vcc, vcc // Restore STATUS.VCCZ, not writable by s_setreg_b32 + // Assume relaxed scheduling mode after this point. + restore_sched_mode(s_restore_tmp) + s_setreg_b32 hwreg(HW_REG_WAVE_STATE_PRIV), s_restore_state_priv // SCC is included, which is changed by previous salu // Make barrier and LDS state visible to all waves in the group.
@@ -1134,3 +1166,8 @@ function valu_sgpr_hazard end #endif end + +function restore_sched_mode(s_tmp) + s_bfe_u32 s_tmp, ttmp11, (TTMP11_SCHED_MODE_SHIFT | (TTMP11_SCHED_MODE_SIZE << 0x10)) + s_setreg_b32 hwreg(HW_REG_WAVE_SCHED_MODE), s_tmp +end
Greg Kroah-Hartman
maintainerPATCH 473/567 drm/i915: Fix format string truncation warning
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ard Biesheuvel <[email protected]>
commit 1c7f9e528f8f488b060b786bfb90b40540854db3 upstream.
GCC notices that the 16-byte uabi_name field could theoretically be too small for the formatted string if the instance number exceeds 100.
So grow the field to 20 bytes.
drivers/gpu/drm/i915/intel_memory_region.c: In function ‘intel_memory_region_create’:drivers/gpu/drm/i915/intel_memory_region.c:273:61: error: ‘%u’ directive output may be truncated writing between 1 and 5 bytes into a region of size between 3 and 11 [-Werror=format-truncation=]
273 | snprintf(mem->uabi_name, sizeof(mem->uabi_name), "%s%u",
| ^~drivers/gpu/drm/i915/intel_memory_region.c:273:58: note: directive argument in the range [0, 65535]
273 | snprintf(mem->uabi_name, sizeof(mem->uabi_name), "%s%u",
| ^~~~~~drivers/gpu/drm/i915/intel_memory_region.c:273:9: note: ‘snprintf’ output between 7 and 19 bytes into a destination of size 16
273 | snprintf(mem->uabi_name, sizeof(mem->uabi_name), "%s%u",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
274 | intel_memory_type_str(type), instance);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- drivers/gpu/drm/i915/intel_memory_region.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/i915/intel_memory_region.h +1 -1
@@ -72,7 +72,7 @@ struct intel_memory_region {u16 instance;enum intel_region_id id;char name[16 ]; -char uabi_name[16 ]; +char uabi_name[20 ];bool private;/* not for userspace */ struct {
Greg Kroah-Hartman
maintainerPATCH 474/567 drm/ttm: Avoid NULL pointer deref for evicted BOs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Simon Richter <[email protected]>
commit 491adc6a0f9903c32b05f284df1148de39e8e644 upstream.
It is possible for a BO to exist that is not currently associated with a resource, e.g. because it has been evicted.
When devcoredump tries to read the contents of all BOs for dumping, we need to expect this as well -- in this case, ENODATA is recorded instead of the buffer contents.
--- drivers/gpu/drm/ttm/ttm_bo_vm.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/gpu/drm/ttm/ttm_bo_vm.c +6 -0
@@ -421,6 +421,11 @@ int ttm_bo_vm_access(struct vm_area_struif (ret)return ret; +if (!bo->resource) { + ret = -ENODATA ; + goto unlock; + } +switch (bo->resource->mem_type) {case TTM_PL_SYSTEM : fallthrough;
@@ -435,6 +440,7 @@ int ttm_bo_vm_access(struct vm_area_stru ret = -EIO ; } +unlock:ttm_bo_unreserve (bo);return ret;
Greg Kroah-Hartman
maintainerPATCH 475/567 drm/mgag200: Fix big-endian support
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: René Rebe <[email protected]>
commit 6cb31fba137d45e682ce455b8ea364f44d5d4f98 upstream.
Unlike the original, deleted Matrox mga driver, the new mgag200 driver has the XRGB frame-buffer byte swapped on big-endian "RISC" systems. Fix by enabling byte swapping "PowerPC" OPMODE for any __BIG_ENDIAN config.
--- drivers/gpu/drm/mgag200/mgag200_mode.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
drivers/gpu/drm/mgag200/mgag200_mode.c +25 -0
@@ -175,6 +175,30 @@ static void mgag200_set_startadd(structWREG_ECRT (0x00 , crtcext0); } +/* +* Set the opmode for the hardware swapper for Big-Endian processor +* support for the frame buffer aperture and DMAWIN space. +*/ +static void mgag200_set_datasiz (struct mga_device *mdev,u32 format) +{ +#if defined(__BIG_ENDIAN) +u32 opmode =RREG32 (MGAREG_OPMODE ); + + opmode &= ~(GENMASK (17 ,16 ) |GENMASK (9 ,8 ) |GENMASK (3 ,2 )); + +/* Big-endian byte-swapping */ +switch (format) { +case DRM_FORMAT_RGB565 : + opmode |=0x10100 ; +break ; +case DRM_FORMAT_XRGB8888 : + opmode |=0x20200 ; +break ; + } +WREG32 (MGAREG_OPMODE , opmode); +#endif +} +void mgag200_init_registers (struct mga_device *mdev) {u8 crtc11, misc;
@@ -510,6 +534,7 @@ void mgag200_primary_plane_helper_atomicstruct drm_atomic_helper_damage_iter iter;struct drm_rect damage; +mgag200_set_datasiz (mdev, fb->format->format);drm_atomic_helper_damage_iter_init (&iter, old_plane_state, plane_state);drm_atomic_for_each_plane_damage (&iter, &damage) {mgag200_handle_damage (mdev, shadow_plane_state->data, fb, &damage);
Greg Kroah-Hartman
maintainerPATCH 476/567 drm/xe/bo: Dont include the CCS metadata in the dma-buf sg-table
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Hellström <[email protected]>
commit 449bcd5d45eb4ce26740f11f8601082fe734bed2 upstream.
Some Xe bos are allocated with extra backing-store for the CCS metadata. It's never been the intention to share the CCS metadata when exporting such bos as dma-buf. Don't include it in the dma-buf sg-table.
--- drivers/gpu/drm/xe/xe_dma_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/xe/xe_dma_buf.c +1 -1
@@ -111,7 +111,7 @@ static struct sg_table *xe_dma_buf_map(scase XE_PL_TT : sgt =drm_prime_pages_to_sg (obj->dev, bo->ttm.ttm->pages, - bo->ttm.ttm->num_pages); + obj->size >>PAGE_SHIFT );if (IS_ERR (sgt))return sgt;
Greg Kroah-Hartman
maintainerPATCH 477/567 drm/xe/oa: Disallow 0 OA property values
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
commit 3595114bc31d1eb5e1996164c901485c1ffac6f7 upstream.
An OA property value of 0 is invalid and will cause a NPD.
--- drivers/gpu/drm/xe/xe_oa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/xe/xe_oa.c +1 -1
@@ -1266,7 +1266,7 @@ static int xe_oa_user_ext_set_property(s ARRAY_SIZE(xe_oa_set_property_funcs_config));if (XE_IOCTL_DBG (oa->xe, ext.property >=ARRAY_SIZE (xe_oa_set_property_funcs_open)) || -XE_IOCTL_DBG (oa->xe, ext.pad)) +XE_IOCTL_DBG (oa->xe, !ext.property) ||XE_IOCTL_DBG (oa->xe, ext.pad))return -EINVAL ; idx =array_index_nospec (ext.property,ARRAY_SIZE (xe_oa_set_property_funcs_open));
Greg Kroah-Hartman
maintainerPATCH 478/567 drm/xe: Adjust long-running workload timeslices to reasonable values
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthew Brost <[email protected]>
commit 6f0f404bd289d79a260b634c5b3f4d330b13472c upstream.
A 10ms timeslice for long-running workloads is far too long and causes significant jitter in benchmarks when the system is shared. Adjust the value to 5ms for preempt-fencing VMs, as the resume step there is quite costly as memory is moved around, and set it to zero for pagefault VMs, since switching back to pagefault mode after dma-fence mode is relatively fast.
Also change min_run_period_ms to 'unsiged int' type rather than 's64' as only positive values make sense.
--- drivers/gpu/drm/xe/xe_vm.c | 5 ++++- drivers/gpu/drm/xe/xe_vm_types.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_vm.c +4 -1
@@ -1468,7 +1468,10 @@ struct xe_vm *xe_vm_create(struct xe_devINIT_WORK (&vm->destroy_work, vm_destroy_work_func);INIT_LIST_HEAD (&vm->preempt.exec_queues); - vm->preempt.min_run_period_ms =10 ;/* FIXME: Wire up to uAPI */ +if (flags &XE_VM_FLAG_FAULT_MODE ) + vm->preempt.min_run_period_ms =0 ; +else + vm->preempt.min_run_period_ms =5 ;for_each_tile (tile, xe, id)xe_range_fence_tree_init (&vm->rftree[id]);
drivers/gpu/drm/xe/xe_vm_types.h +1 -1
@@ -243,7 +243,7 @@ struct xe_vm {* @min_run_period_ms: The minimum run period before preempting * an engine again */ -s64 min_run_period_ms; +unsigned int min_run_period_ms;/** @exec_queues: list of exec queues attached to this VM */ struct list_head exec_queues;/** @num_exec_queues: number exec queues attached to this VM */
Greg Kroah-Hartman
maintainerPATCH 479/567 drm/xe: Use usleep_range for accurate long-running workload timeslicing
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthew Brost <[email protected]>
commit 80f9c601d9c4d26f00356c0a9c461650e7089273 upstream.
msleep is not very accurate in terms of how long it actually sleeps, whereas usleep_range is precise. Replace the timeslice sleep for long-running workloads with the more accurate usleep_range to avoid jitter if the sleep period is less than 20ms.
--- drivers/gpu/drm/xe/xe_guc_submit.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
drivers/gpu/drm/xe/xe_guc_submit.c +19 -1
@@ -578,6 +578,24 @@ static u32 wq_space_until_wrap(struct xereturn (WQ_SIZE - q->guc->wqi_tail); } +static inline void relaxed_ms_sleep (unsigned int delay_ms) +{ +unsigned long min_us, max_us; + +if (!delay_ms) +return ; + +if (delay_ms >20 ) { +msleep (delay_ms); +return ; + } + + min_us =mul_u32_u32 (delay_ms,1000 ); + max_us = min_us +500 ; + +usleep_range (min_us, max_us); +} +static int wq_wait_for_space (struct xe_exec_queue *q,u32 wqi_size) {struct xe_guc *guc =exec_queue_to_guc (q);
@@ -1356,7 +1374,7 @@ static void __guc_exec_queue_process_msg since_resume_ms;if (wait_ms >0 && q->guc->resume_time) -msleep (wait_ms); +relaxed_ms_sleep (wait_ms);set_exec_queue_suspended (q);disable_scheduling (q, false);
Greg Kroah-Hartman
maintainerPATCH 480/567 drm/xe: Drop preempt-fences when destroying imported dma-bufs.
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Hellström <[email protected]>
commit fe3ccd24138fd391ae8e32289d492c85f67770fc upstream.
When imported dma-bufs are destroyed, TTM is not fully individualizing the dma-resv, but it *is* copying the fences that need to be waited for before declaring idle. So in the case where the bo->resv != bo->_resv we can still drop the preempt-fences, but make sure we do that on bo->_resv which contains the fence-pointer copy.
In the case where the copying fails, bo->_resv will typically not contain any fences pointers at all, so there will be nothing to drop. In that case, TTM would have ensured all fences that would have been copied are signaled, including any remaining preempt fences.
--- drivers/gpu/drm/xe/xe_bo.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-)
drivers/gpu/drm/xe/xe_bo.c +4 -11
@@ -1041,7 +1041,7 @@ static bool xe_ttm_bo_lock_in_destructor* always succeed here, as long as we hold the lru lock. */ spin_lock (&ttm_bo->bdev->lru_lock); - locked =dma_resv_trylock (ttm_bo->base.resv); + locked =dma_resv_trylock (&ttm_bo->base._resv);spin_unlock (&ttm_bo->bdev->lru_lock);xe_assert (xe, locked);
@@ -1061,13 +1061,6 @@ static void xe_ttm_bo_release_notify(str bo =ttm_to_xe_bo (ttm_bo);xe_assert (xe_bo_device (bo), !(bo->created &&kref_read (&ttm_bo->base.refcount))); -/* -* Corner case where TTM fails to allocate memory and this BOs resv -* still points the VMs resv -*/ -if (ttm_bo->base.resv != &ttm_bo->base._resv) -return ; -if (!xe_ttm_bo_lock_in_destructor (ttm_bo))return ;
@@ -1077,14 +1070,14 @@ static void xe_ttm_bo_release_notify(str* TODO: Don't do this for external bos once we scrub them after * unbind. */ -dma_resv_for_each_fence (&cursor, ttm_bo->base.resv, +dma_resv_for_each_fence (&cursor, &ttm_bo->base._resv,DMA_RESV_USAGE_BOOKKEEP , fence) {if (xe_fence_is_xe_preempt (fence) && !dma_fence_is_signaled (fence)) {if (!replacement) replacement =dma_fence_get_stub (); -dma_resv_replace_fences (ttm_bo->base.resv, +dma_resv_replace_fences (&ttm_bo->base._resv, fence->context, replacement,DMA_RESV_USAGE_BOOKKEEP );
@@ -1092,7 +1085,7 @@ static void xe_ttm_bo_release_notify(str }dma_fence_put (replacement); -dma_resv_unlock (ttm_bo->base.resv); +dma_resv_unlock (&ttm_bo->base._resv); }static void xe_ttm_bo_delete_mem_notify (struct ttm_buffer_object *ttm_bo)
Greg Kroah-Hartman
maintainerPATCH 481/567 drm/msm/dpu: Add missing NULL pointer check for pingpong interface
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Kuratov <[email protected]>
commit 88733a0b64872357e5ecd82b7488121503cb9cc6 upstream.
It is checked almost always in dpu_encoder_phys_wb_setup_ctl(), but in a single place the check is missing.
Also use convenient locals instead of phys_enc->* where available.
Patchwork: https://patchwork.freedesktop.org/patch/693860/
--- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +4 -6
@@ -243,14 +243,12 @@ static void dpu_encoder_phys_wb_setup_ctif (hw_cdm) intf_cfg.cdm = hw_cdm->idx; -if (phys_enc->hw_pp->merge_3d && phys_enc->hw_pp->merge_3d->ops.setup_3d_mode) - phys_enc->hw_pp->merge_3d->ops.setup_3d_mode (phys_enc->hw_pp->merge_3d, - mode_3d); +if (hw_pp && hw_pp->merge_3d && hw_pp->merge_3d->ops.setup_3d_mode) + hw_pp->merge_3d->ops.setup_3d_mode (hw_pp->merge_3d, mode_3d);/* setup which pp blk will connect to this wb */ -if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk) - phys_enc->hw_wb->ops.bind_pingpong_blk (phys_enc->hw_wb, - phys_enc->hw_pp->idx); +if (hw_pp && hw_wb->ops.bind_pingpong_blk) + hw_wb->ops.bind_pingpong_blk (hw_wb, hw_pp->idx); phys_enc->hw_ctl->ops.setup_intf_cfg (phys_enc->hw_ctl, &intf_cfg); }else if (phys_enc->hw_ctl && phys_enc->hw_ctl->ops.setup_intf_cfg) {
Greg Kroah-Hartman
maintainerPATCH 482/567 drm/i915/gem: Zero-initialize the eb.vma array in i915_gem_do_execbuffer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Niemiec <[email protected]>
commit 4fe2bd195435e71c117983d87f278112c5ab364c upstream.
Initialize the eb.vma array with values of 0 when the eb structure is first set up. In particular, this sets the eb->vma[i].vma pointers to NULL, simplifying cleanup and getting rid of the bug described below.
During the execution of eb_lookup_vmas(), the eb->vma array is successively filled up with struct eb_vma objects. This process includes calling eb_add_vma(), which might fail; however, even in the event of failure, eb->vma[i].vma is set for the currently processed buffer.
If eb_add_vma() fails, eb_lookup_vmas() returns with an error, which prompts a call to eb_release_vmas() to clean up the mess. Since eb_lookup_vmas() might fail during processing any (possibly not first) buffer, eb_release_vmas() checks whether a buffer's vma is NULL to know at what point did the lookup function fail.
In eb_lookup_vmas(), eb->vma[i].vma is set to NULL if either the helper function eb_lookup_vma() or eb_validate_vma() fails. eb->vma[i+1].vma is set to NULL in case i915_gem_object_userptr_submit_init() fails; the current one needs to be cleaned up by eb_release_vmas() at this point, so the next one is set. If eb_add_vma() fails, neither the current nor the next vma is set to NULL, which is a source of a NULL deref bug described in the issue linked in the Closes tag.
When entering eb_lookup_vmas(), the vma pointers are set to the slab poison value, instead of NULL. This doesn't matter for the actual lookup, since it gets overwritten anyway, however the eb_release_vmas() function only recognizes NULL as the stopping value, hence the pointers are being set to NULL as they go in case of intermediate failure. This patch changes the approach to filling them all with NULL at the start instead, rather than handling that manually during failure.
--- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 37 +++++++++++-------------- 1 file changed, 17 insertions(+), 20 deletions(-)
drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +17 -20
@@ -951,13 +951,13 @@ static int eb_lookup_vmas(struct i915_ex vma =eb_lookup_vma (eb, eb->exec[i].handle);if (IS_ERR (vma)) { err =PTR_ERR (vma); - goto err; +return err; } err =eb_validate_vma (eb, &eb->exec[i], vma);if (unlikely (err)) {i915_vma_put (vma); - goto err; +return err; } err =eb_add_vma (eb, ¤t_batch, i, vma);
@@ -966,19 +966,8 @@ static int eb_lookup_vmas(struct i915_exif (i915_gem_object_is_userptr (vma->obj)) { err =i915_gem_object_userptr_submit_init (vma->obj); -if (err) { -if (i +1 < eb->buffer_count) { -/* -* Execbuffer code expects last vma entry to be NULL, -* since we already initialized this entry, -* set the next value to NULL or we mess up -* cleanup handling. -*/ - eb->vma[i +1 ].vma =NULL ; - } - +if (err)return err; - } eb->vma[i].flags |= __EXEC_OBJECT_USERPTR_INIT; eb->args->flags |= __EXEC_USERPTR_USED;
@@ -986,10 +975,6 @@ static int eb_lookup_vmas(struct i915_ex }return 0 ; - -err: - eb->vma[i].vma =NULL ; -return err; }static int eb_lock_vmas (struct i915_execbuffer *eb)
@@ -3374,7 +3359,8 @@ i915_gem_do_execbuffer(struct drm_device eb.exec = exec; eb.vma = (struct eb_vma *)(exec + args->buffer_count +1 ); - eb.vma[0 ].vma =NULL ; +memset (eb.vma,0 , (args->buffer_count +1 ) *sizeof (struct eb_vma )); + eb.batch_pool =NULL ; eb.invalid_flags = __EXEC_OBJECT_UNKNOWN_FLAGS;
@@ -3583,7 +3569,18 @@ i915_gem_execbuffer2_ioctl(struct drm_deif (err)return err; -/* Allocate extra slots for use by the command parser */ +/* +* Allocate extra slots for use by the command parser. +* +* Note that this allocation handles two different arrays (the +* exec2_list array, and the eventual eb.vma array introduced in +* i915_gem_do_execbuffer()), that reside in virtually contiguous +* memory. Also note that the allocation intentionally doesn't fill the +* area with zeros, because the exec2_list part doesn't need to be, as +* it's immediately overwritten by user data a few lines below. +* However, the eb.vma part is explicitly zeroed later in +* i915_gem_do_execbuffer(). +*/ exec2_list =kvmalloc_array (count +2 ,eb_element_size (), __GFP_NOWARN |GFP_KERNEL );if (exec2_list ==NULL ) {
Greg Kroah-Hartman
maintainerPATCH 483/567 drm/nouveau/dispnv50: Dont call drm_atomic_get_crtc_state() in prepare_fb
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lyude Paul <[email protected]>
commit 560271e10b2c86e95ea35afa9e79822e4847f07a upstream.
Since we recently started warning about uses of this function after the atomic check phase completes, we've started getting warnings about this in nouveau. It appears a misplaced drm_atomic_get_crtc_state() call has been hiding in our .prepare_fb callback for a while.
So, fix this by adding a new nv50_head_atom_get_new() function and use that in our .prepare_fb callback instead.
--- drivers/gpu/drm/nouveau/dispnv50/atom.h | 13 +++++++++++++ drivers/gpu/drm/nouveau/dispnv50/wndw.c | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/dispnv50/atom.h +13 -0
@@ -152,8 +152,21 @@ static inline struct nv50_head_atom * nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc) { struct drm_crtc_state *statec = drm_atomic_get_crtc_state(state, crtc); +if (IS_ERR (statec))return (void *)statec; + +return nv50_head_atom (statec); +} + +static inline struct nv50_head_atom * +nv50_head_atom_get_new (struct drm_atomic_state *state,struct drm_crtc *crtc) +{ +struct drm_crtc_state *statec =drm_atomic_get_new_crtc_state (state, crtc); + +if (!statec) +return NULL ; +return nv50_head_atom (statec); }
drivers/gpu/drm/nouveau/dispnv50/wndw.c +1 -1
@@ -567,7 +567,7 @@ nv50_wndw_prepare_fb(struct drm_plane *p asyw->image.offset[0 ] = nvbo->offset;if (wndw->func->prepare) { - asyh =nv50_head_atom_get (asyw->state.state, asyw->state.crtc); + asyh =nv50_head_atom_get_new (asyw->state.state, asyw->state.crtc);if (IS_ERR (asyh))return PTR_ERR (asyh);
Greg Kroah-Hartman
maintainerPATCH 484/567 drm/imagination: Disallow exporting of PM/FW protected objects
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alessio Belle <[email protected]>
commit 6b991ad8dc3abfe5720fc2e9ee96be63ae43e362 upstream.
These objects are meant to be used by the GPU firmware or by the PM unit within the GPU, in which case they may contain physical addresses.
This adds a layer of protection against exposing potentially exploitable information outside of the driver.
--- drivers/gpu/drm/imagination/pvr_gem.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/gpu/drm/imagination/pvr_gem.c +11 -0
@@ -27,6 +27,16 @@ static void pvr_gem_object_free(struct ddrm_gem_shmem_object_free (obj); } +static struct dma_buf *pvr_gem_export (struct drm_gem_object *obj,int flags) +{ +struct pvr_gem_object *pvr_obj =gem_to_pvr_gem (obj); + +if (pvr_obj->flags &DRM_PVR_BO_PM_FW_PROTECT ) +return ERR_PTR (-EPERM ); + +return drm_gem_prime_export (obj, flags); +} +static int pvr_gem_mmap (struct drm_gem_object *gem_obj,struct vm_area_struct *vma) {struct pvr_gem_object *pvr_obj =gem_to_pvr_gem (gem_obj);
@@ -41,6 +51,7 @@ static int pvr_gem_mmap(struct drm_gem_ostatic const struct drm_gem_object_funcs pvr_gem_object_funcs = { .free = pvr_gem_object_free, .print_info = drm_gem_shmem_object_print_info, + .export = pvr_gem_export, .pin = drm_gem_shmem_object_pin, .unpin = drm_gem_shmem_object_unpin, .get_sg_table = drm_gem_shmem_object_get_sg_table,
Greg Kroah-Hartman
maintainerPATCH 485/567 lib/crypto: riscv/chacha: Avoid s0/fp register
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vivian Wang <[email protected]>
commit 43169328c7b4623b54b7713ec68479cebda5465f upstream.
In chacha_zvkb, avoid using the s0 register, which is the frame pointer, by reallocating KEY0 to t5. This makes stack traces available if e.g. a crash happens in chacha_zvkb.
No frame pointer maintenance is otherwise required since this is a leaf function.
--- arch/riscv/crypto/chacha-riscv64-zvkb.S | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
arch/riscv/crypto/chacha-riscv64-zvkb.S +2 -3
@@ -60,7 +60,8 @@ #define VL t2 #define STRIDE t3 #define NROUNDS t4 -#define KEY0 s0 +#define KEY0 t5 +// Avoid s0/fp to allow for unwinding #define KEY1 s1 #define KEY2 s2 #define KEY3 s3
@@ -141,7 +142,6 @@ SYM_FUNC_START(chacha20_zvkb) srli LEN, LEN, 6 // Bytes to blocks addi sp, sp, -96 - sd s0, 0(sp) sd s1, 8(sp) sd s2, 16(sp) sd s3, 24(sp)
@@ -277,7 +277,6 @@ SYM_FUNC_START(chacha20_zvkb) add INP, INP, TMP bnez LEN, .Lblock_loop - ld s0, 0(sp) ld s1, 8(sp) ld s2, 16(sp) ld s3, 24(sp)
Greg Kroah-Hartman
maintainerPATCH 486/567 gfs2: fix freeze error handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexey Velichayshiy <[email protected]>
[ Upstream commit 4cfc7d5a4a01d2133b278cdbb1371fba1b419174 ]
After commit b77b4a4815a9 ("gfs2: Rework freeze / thaw logic"), the freeze error handling is broken because gfs2_do_thaw() overwrites the 'error' variable, causing incorrect processing of the original freeze error.
Fix this by calling gfs2_do_thaw() when gfs2_lock_fs_check_clean() fails but ignoring its return value to preserve the original freeze error for proper reporting.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
--- fs/gfs2/super.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
fs/gfs2/super.c +1 -3
@@ -759,9 +759,7 @@ static int gfs2_freeze_super(struct supebreak ; } - error =gfs2_do_thaw (sdp, who); -if (error) - goto out; + (void )gfs2_do_thaw (sdp, who);if (error == -EBUSY )fs_err (sdp,"waiting for recovery before freeze\n" );
Greg Kroah-Hartman
maintainerPATCH 487/567 btrfs: dont rewrite ret from inode_permission
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <[email protected]>
[ Upstream commit 0185c2292c600993199bc6b1f342ad47a9e8c678 ]
In our user safe ino resolve ioctl we'll just turn any ret into -EACCES from inode_permission(). This is redundant, and could potentially be wrong if we had an ENOMEM in the security layer or some such other error, so simply return the actual return value.
Note: The patch was taken from v5 of fscrypt patchset
(https://lore.kernel.org/linux-btrfs/[email protected]/)
which was handled over time by various people: Omar Sandoval, Sweet Tea Dorminy, Josef Bacik.
--- fs/btrfs/ioctl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
fs/btrfs/ioctl.c +1 -3
@@ -2012,10 +2012,8 @@ static int btrfs_search_path_in_tree_use ret =inode_permission (idmap, temp_inode,MAY_READ |MAY_EXEC );iput (temp_inode); -if (ret) { - ret = -EACCES ; +if (ret) goto out_put; - }if (key.offset == upper_limit)break ;
Greg Kroah-Hartman
maintainerPATCH 488/567 sched/eevdf: Fix min_vruntime vs avg_vruntime
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit 79f3f9bedd149ea438aaeb0fb6a083637affe205 ]
Basically, from the constraint that the sum of lag is zero, you can infer that the 0-lag point is the weighted average of the individual vruntime, which is what we're trying to compute:
\Sum w_i * v_i
avg = --------------
\Sum w_i
Now, since vruntime takes the whole u64 (worse, it wraps), this multiplication term in the numerator is not something we can compute; instead we do the min_vruntime (v0 henceforth) thing like:
v_i = (v_i - v0) + v0
This does two things:
- it keeps the key: (v_i - v0) 'small';
- it creates a relative 0-point in the modular space.
If you do that subtitution and work it all out, you end up with:
\Sum w_i * (v_i - v0)
avg = --------------------- + v0
\Sum w_i
Since you cannot very well track a ratio like that (and not suffer terrible numerical problems) we simpy track the numerator and denominator individually and only perform the division when strictly needed.
Notably, the numerator lives in cfs_rq->avg_vruntime and the denominator lives in cfs_rq->avg_load.
The one extra 'funny' is that these numbers track the entities in the tree, and current is typically outside of the tree, so avg_vruntime() adds current when needed before doing the division.
(vruntime_eligible() elides the division by cross-wise multiplication)
Anyway, as mentioned above, we currently use the CFS era min_vruntime for this purpose. However, this thing can only move forward, while the above avg can in fact move backward (when a non-eligible task leaves, the average becomes smaller), this can cause trouble when through happenstance (or construction) these values drift far enough apart to wreck the game.
Replace cfs_rq::min_vruntime with cfs_rq::zero_vruntime which is kept near/at avg_vruntime, following its motion.
The down-side is that this requires computing the avg more often.
--- kernel/sched/debug.c | 8 ++-- kernel/sched/fair.c | 84 +++++++++++---------------------------------------- kernel/sched/sched.h | 4 +- 3 files changed, 25 insertions(+), 71 deletions(-)
kernel/sched/debug.c +4 -4
@@ -804,7 +804,7 @@ static void print_rq(struct seq_file *m,void print_cfs_rq (struct seq_file *m,int cpu,struct cfs_rq *cfs_rq) { -s64 left_vruntime =-1 , min_vruntime, right_vruntime =-1 , left_deadline =-1 , spread; +s64 left_vruntime =-1 , zero_vruntime, right_vruntime =-1 , left_deadline =-1 , spread;struct sched_entity *last, *first, *root;struct rq *rq =cpu_rq (cpu);unsigned long flags;
@@ -827,15 +827,15 @@ void print_cfs_rq(struct seq_file *m, in last =__pick_last_entity (cfs_rq);if (last) right_vruntime = last->vruntime; - min_vruntime = cfs_rq->min_vruntime; + zero_vruntime = cfs_rq->zero_vruntime;raw_spin_rq_unlock_irqrestore (rq, flags);SEQ_printf (m," .%-30s: %Ld.%06ld\n" ,"left_deadline" ,SPLIT_NS (left_deadline));SEQ_printf (m," .%-30s: %Ld.%06ld\n" ,"left_vruntime" ,SPLIT_NS (left_vruntime)); -SEQ_printf (m," .%-30s: %Ld.%06ld\n" ,"min_vruntime" , -SPLIT_NS (min_vruntime)); +SEQ_printf (m," .%-30s: %Ld.%06ld\n" ,"zero_vruntime" , +SPLIT_NS (zero_vruntime));SEQ_printf (m," .%-30s: %Ld.%06ld\n" ,"avg_vruntime" ,SPLIT_NS (avg_vruntime (cfs_rq))); SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "right_vruntime",
kernel/sched/fair.c +19 -65
183 lines not on this page · raw message
kernel/sched/sched.h +2 -2
@@ -660,10 +660,10 @@ struct cfs_rq {s64 avg_vruntime;u64 avg_load; -u64 min_vruntime; +u64 zero_vruntime;#ifdef CONFIG_SCHED_CORE unsigned int forceidle_seq; -u64 min_vruntime_fi; +u64 zero_vruntime_fi;#endif struct rb_root_cached tasks_timeline;
Greg Kroah-Hartman
maintainerPATCH 489/567 erofs: fix unexpected EIO under memory pressure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junbeom Yeom <[email protected]>
[ Upstream commit 4012d78562193ef5eb613bad4b0c0fa187637cfe ]
erofs readahead could fail with ENOMEM under the memory pressure because it tries to alloc_page with GFP_NOWAIT | GFP_NORETRY, while GFP_KERNEL for a regular read. And if readahead fails (with non-uptodate folios), the original request will then fall back to synchronous read, and .read_folio() should return appropriate errnos.
However, in scenarios where readahead and read operations compete, read operation could return an unintended EIO because of an incorrect error propagation.
To resolve this, this patch modifies the behavior so that, when the PCL is for read(which means pcl.besteffort is true), it attempts actual decompression instead of propagating the privios error except initial EIO.
- Page size: 4K
- The original size of FileA: 16K
- Compress-ratio per PCL: 50% (Uncompressed 8K -> Compressed 4K) [page0, page1] [page2, page3]
[PCL0]---------[PCL1]
- functions declaration:
. pread(fd, buf, count, offset)
. readahead(fd, offset, count)
- Thread A tries to read the last 4K
- Thread B tries to do readahead 8K from 4K
- RA, besteffort == false
- R, besteffort == true
<process A> <process B>
pread(FileA, buf, 4K, 12K)
do readahead(page3) // failed with ENOMEM
wait_lock(page3)
if (!uptodate(page3))
goto do_read
readahead(FileA, 4K, 8K)
// Here create PCL-chain like below:
// [null, page1] [page2, null]
// [PCL0:RA]-----[PCL1:RA]
...
do read(page3) // found [PCL1:RA] and add page3 into it,
// and then, change PCL1 from RA to R
...
// Now, PCL-chain is as below:
// [null, page1] [page2, page3]
// [PCL0:RA]-----[PCL1:R]
// try to decompress PCL-chain...
z_erofs_decompress_queue
err = 0;
// failed with ENOMEM, so page 1
// only for RA will not be uptodated.
// it's okay.
err = decompress([PCL0:RA], err)
// However, ENOMEM propagated to next
// PCL, even though PCL is not only
// for RA but also for R. As a result,
// it just failed with ENOMEM without
// trying any decompression, so page2
// and page3 will not be uptodated.
** BUG HERE ** --> err = decompress([PCL1:R], err)
return err as ENOMEM...
wait_lock(page3)
if (!uptodate(page3))
return EIO <-- Return an unexpected EIO!
...
--- fs/erofs/zdata.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
fs/erofs/zdata.c +4 -4
@@ -1244,14 +1244,14 @@ static int z_erofs_parse_in_bvecs(structreturn err; } -static int z_erofs_decompress_pcluster (struct z_erofs_backend *be,int err) +static int z_erofs_decompress_pcluster (struct z_erofs_backend *be,bool eio) {struct erofs_sb_info *const sbi =EROFS_SB (be->sb);struct z_erofs_pcluster *pcl = be->pcl;unsigned int pclusterpages =z_erofs_pclusterpages (pcl);const struct z_erofs_decompressor *decomp = z_erofs_decomp[pcl->algorithmformat]; -int i, j, jtop, err2; +int i, j, jtop, err2, err = eio ? -EIO :0 ;struct page *page;bool overlapped;bool try_free = true;
@@ -1381,12 +1381,12 @@ static int z_erofs_decompress_queue(cons .pcl = io->head, };struct z_erofs_pcluster *next; -int err = io->eio ? -EIO :0 ; +int err =0 ;for (; be.pcl !=Z_EROFS_PCLUSTER_TAIL ; be.pcl = next) {DBG_BUGON (!be.pcl); next =READ_ONCE (be.pcl->next); - err =z_erofs_decompress_pcluster (&be, err) ?: err; + err =z_erofs_decompress_pcluster (&be, io->eio) ?: err; }return err; }
Greg Kroah-Hartman
maintainerPATCH 490/567 sched_ext: Fix incorrect sched_class settings for per-cpu migration tasks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zqiang <[email protected]>
[ Upstream commit 1dd6c84f1c544e552848a8968599220bd464e338 ]
When loading the ebpf scheduler, the tasks in the scx_tasks list will be traversed and invoke __setscheduler_class() to get new sched_class. however, this would also incorrectly set the per-cpu migration task's->sched_class to rt_sched_class, even after unload, the per-cpu migration task's->sched_class remains sched_rt_class.
The log for this issue is as follows:
./scx_rustland --stats 1
[ 199.245639][ T630] sched_ext: "rustland" does not implement cgroup cpu.weight [ 199.269213][ T630] sched_ext: BPF scheduler "rustland" enabled
04:25:09 [INFO] RustLand scheduler attached
bpftrace -e 'iter:task /strcontains(ctx->task->comm, "migration")/
{ printf("%s:%d->%pS\n", ctx->task->comm, ctx->task->pid, ctx->task->sched_class); }' Attaching 1 probe...
migration/0:24->rt_sched_class+0x0/0xe0 migration/1:27->rt_sched_class+0x0/0xe0 migration/2:33->rt_sched_class+0x0/0xe0 migration/3:39->rt_sched_class+0x0/0xe0 migration/4:45->rt_sched_class+0x0/0xe0 migration/5:52->rt_sched_class+0x0/0xe0 migration/6:58->rt_sched_class+0x0/0xe0 migration/7:64->rt_sched_class+0x0/0xe0
sched_ext: BPF scheduler "rustland" disabled (unregistered from user space)
EXIT: unregistered from user space
04:25:21 [INFO] Unregister RustLand scheduler
bpftrace -e 'iter:task /strcontains(ctx->task->comm, "migration")/
{ printf("%s:%d->%pS\n", ctx->task->comm, ctx->task->pid, ctx->task->sched_class); }' Attaching 1 probe...
migration/0:24->rt_sched_class+0x0/0xe0 migration/1:27->rt_sched_class+0x0/0xe0 migration/2:33->rt_sched_class+0x0/0xe0 migration/3:39->rt_sched_class+0x0/0xe0 migration/4:45->rt_sched_class+0x0/0xe0 migration/5:52->rt_sched_class+0x0/0xe0 migration/6:58->rt_sched_class+0x0/0xe0 migration/7:64->rt_sched_class+0x0/0xe0
This commit therefore generate a new scx_setscheduler_class() and add check for stop_sched_class to replace __setscheduler_class().
--- kernel/sched/ext.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
kernel/sched/ext.c +10 -4
@@ -1057,6 +1057,14 @@ static struct scx_dispatch_q *find_user_return rhashtable_lookup_fast (&dsq_hash, &dsq_id, dsq_hash_params); } +static const struct sched_class *scx_setscheduler_class (struct task_struct *p) +{ +if (p->sched_class == &stop_sched_class) +return &stop_sched_class; + +return __setscheduler_class (p->policy, p->prio); +} +/* * scx_kf_mask enforcement. Some kfuncs can only be called from specific SCX * ops. When invoking SCX ops, SCX_CALL_OP[_RET]() should be used to indicate
@@ -4653,8 +4661,7 @@ static void scx_ops_disable_workfn(strucscx_task_iter_start (&sti);while ((p =scx_task_iter_next_locked (&sti))) {const struct sched_class *old_class = p->sched_class; -const struct sched_class *new_class = -__setscheduler_class (p->policy, p->prio); +const struct sched_class *new_class =scx_setscheduler_class (p);struct sched_enq_and_set_ctx ctx;if (old_class != new_class && p->se.sched_delayed)
@@ -5368,8 +5375,7 @@ static int scx_ops_enable(struct sched_escx_task_iter_start (&sti);while ((p =scx_task_iter_next_locked (&sti))) {const struct sched_class *old_class = p->sched_class; -const struct sched_class *new_class = -__setscheduler_class (p->policy, p->prio); +const struct sched_class *new_class =scx_setscheduler_class (p);struct sched_enq_and_set_ctx ctx;if (!tryget_task_struct (p))
Greg Kroah-Hartman
maintainerPATCH 491/567 jbd2: fix the inconsistency between checksum and data in memory for journal sb
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ye Bin <[email protected]>
[ Upstream commit 6abfe107894af7e8ce3a2e120c619d81ee764ad5 ]
Copying the file system while it is mounted as read-only results in a mount failure:
[~]# mkfs.ext4 -F /dev/sdc
[~]# mount /dev/sdc -o ro /mnt/test
[~]# dd if=/dev/sdc of=/dev/sda bs=1M
[~]# mount /dev/sda /mnt/test1
[ 1094.849826] JBD2: journal checksum error [ 1094.850927] EXT4-fs (sda): Could not load journal inode
mount: mount /dev/sda on /mnt/test1 failed: Bad message
The process described above is just an abstracted way I came up with to reproduce the issue. In the actual scenario, the file system was mounted read-only and then copied while it was still mounted. It was found that the mount operation failed. The user intended to verify the data or use it as a backup, and this action was performed during a version upgrade. Above issue may happen as follows:ext4_fill_super
set_journal_csum_feature_set(sb)
if (ext4_has_metadata_csum(sb))
incompat = JBD2_FEATURE_INCOMPAT_CSUM_V3;
if (test_opt(sb, JOURNAL_CHECKSUM)
jbd2_journal_set_features(sbi->s_journal, compat, 0, incompat);
lock_buffer(journal->j_sb_buffer);
sb->s_feature_incompat |= cpu_to_be32(incompat);
//The data in the journal sb was modified, but the checksum was not
updated, so the data remaining in memory has a mismatch between the
data and the checksum.
unlock_buffer(journal->j_sb_buffer);In this case, the journal sb copied over is in a state where the checksum and data are inconsistent, so mounting fails.
To solve the above issue, update the checksum in memory after modifying the journal sb.
Message-ID: <[email protected]>
--- fs/jbd2/journal.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
fs/jbd2/journal.c +14 -0
@@ -2375,6 +2375,12 @@ int jbd2_journal_set_features(journal_t sb->s_feature_compat |=cpu_to_be32 (compat); sb->s_feature_ro_compat |=cpu_to_be32 (ro); sb->s_feature_incompat |=cpu_to_be32 (incompat); +/* +* Update the checksum now so that it is valid even for read-only +* filesystems where jbd2_write_superblock() doesn't get called. +*/ +if (jbd2_journal_has_csum_v2or3 (journal)) + sb->s_checksum =jbd2_superblock_csum (journal, sb);unlock_buffer (journal->j_sb_buffer);jbd2_journal_init_transaction_limits (journal);
@@ -2404,9 +2410,17 @@ void jbd2_journal_clear_features(journal sb = journal->j_superblock; +lock_buffer (journal->j_sb_buffer); sb->s_feature_compat &= ~cpu_to_be32 (compat); sb->s_feature_ro_compat &= ~cpu_to_be32 (ro); sb->s_feature_incompat &= ~cpu_to_be32 (incompat); +/* +* Update the checksum now so that it is valid even for read-only +* filesystems where jbd2_write_superblock() doesn't get called. +*/ +if (jbd2_journal_has_csum_v2or3 (journal)) + sb->s_checksum =jbd2_superblock_csum (journal, sb); +unlock_buffer (journal->j_sb_buffer);jbd2_journal_init_transaction_limits (journal); }EXPORT_SYMBOL (jbd2_journal_clear_features);
Greg Kroah-Hartman
maintainerPATCH 492/567 tty: introduce and use tty_port_tty_vhangup() helper
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Jiri Slaby (SUSE)" <[email protected]>
[ Upstream commit 2b5eac0f8c6e79bc152c8804f9f88d16717013ab ]
This code (tty_get -> vhangup -> tty_put) is repeated on few places. Introduce a helper similar to tty_port_tty_hangup() (asynchronous) to handle even vhangup (synchronous).
And use it on those places.
In fact, reuse the tty_port_tty_hangup()'s code and call tty_vhangup() depending on a new bool parameter.
Stable-dep-of: 74098cc06e75 ("xhci: dbgtty: fix device unregister: fixup")
--- drivers/isdn/capi/capi.c | 8 +------- drivers/staging/greybus/uart.c | 7 +------ drivers/tty/serial/serial_core.c | 7 +------ drivers/tty/tty_port.c | 12 ++++++++---- drivers/usb/class/cdc-acm.c | 7 +------ drivers/usb/serial/usb-serial.c | 7 +------ include/linux/tty_port.h | 12 +++++++++++- net/bluetooth/rfcomm/tty.c | 7 +------ 8 files changed, 25 insertions(+), 42 deletions(-)
drivers/isdn/capi/capi.c +1 -7
@@ -306,15 +306,9 @@ static void capincci_alloc_minor(structstatic void capincci_free_minor (struct capincci *np) {struct capiminor *mp = np->minorp; -struct tty_struct *tty;if (mp) { - tty =tty_port_tty_get (&mp->port); -if (tty) { -tty_vhangup (tty); -tty_kref_put (tty); - } - +tty_port_tty_vhangup (&mp->port);capiminor_free (mp); } }
drivers/staging/greybus/uart.c +1 -6
@@ -914,7 +914,6 @@ static void gb_uart_remove(struct gbphy_ {struct gb_tty *gb_tty =gb_gbphy_get_data (gbphy_dev);struct gb_connection *connection = gb_tty->connection; -struct tty_struct *tty;int ret; ret =gbphy_runtime_get_sync (gbphy_dev);
@@ -927,11 +926,7 @@ static void gb_uart_remove(struct gbphy_wake_up_all (&gb_tty->wioctl);mutex_unlock (&gb_tty->mutex); - tty =tty_port_tty_get (&gb_tty->port); -if (tty) { -tty_vhangup (tty); -tty_kref_put (tty); - } +tty_port_tty_vhangup (&gb_tty->port);gb_connection_disable_rx (connection);tty_unregister_device (gb_tty_driver, gb_tty->minor);
drivers/tty/serial/serial_core.c +1 -6
@@ -3238,7 +3238,6 @@ static void serial_core_remove_one_port(struct uart_state *state = drv->state + uport->line;struct tty_port *port = &state->port;struct uart_port *uart_port; -struct tty_struct *tty;mutex_lock (&port->mutex); uart_port =uart_port_check (state);
@@ -3257,11 +3256,7 @@ static void serial_core_remove_one_port(*/ tty_port_unregister_device (port, drv->tty_driver, uport->line); - tty =tty_port_tty_get (port); -if (tty) { -tty_vhangup (port->tty); -tty_kref_put (tty); - } +tty_port_tty_vhangup (port);/* * If the port is used as a console, unregister it
drivers/tty/tty_port.c +8 -4
@@ -416,15 +416,19 @@ EXPORT_SYMBOL(tty_port_hangup);* @port: tty port * @check_clocal: hang only ttys with %CLOCAL unset? */ -void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) +void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async) {struct tty_struct *tty =tty_port_tty_get (port); -if (tty && (!check_clocal || !C_CLOCAL (tty))) -tty_hangup (tty); +if (tty && (!check_clocal || !C_CLOCAL (tty))) { +if (async) +tty_hangup (tty); +else +tty_vhangup (tty); + }tty_kref_put (tty); } -EXPORT_SYMBOL_GPL (tty_port_tty_hangup); +EXPORT_SYMBOL_GPL (__tty_port_tty_hangup);/** * tty_port_tty_wakeup - helper to wake up a tty
drivers/usb/class/cdc-acm.c +1 -6
@@ -1572,7 +1572,6 @@ err_put_port:static void acm_disconnect (struct usb_interface *intf) {struct acm *acm =usb_get_intfdata (intf); -struct tty_struct *tty;int i;/* sibling interface is already cleaning up */
@@ -1599,11 +1598,7 @@ static void acm_disconnect(struct usb_inusb_set_intfdata (acm->data,NULL );mutex_unlock (&acm->mutex); - tty =tty_port_tty_get (&acm->port); -if (tty) { -tty_vhangup (tty); -tty_kref_put (tty); - } +tty_port_tty_vhangup (&acm->port);cancel_delayed_work_sync (&acm->dwork);
drivers/usb/serial/usb-serial.c +1 -6
@@ -1178,7 +1178,6 @@ static void usb_serial_disconnect(structstruct usb_serial *serial =usb_get_intfdata (interface);struct device *dev = &interface->dev;struct usb_serial_port *port; -struct tty_struct *tty;/* sibling interface is cleaning up */ if (!serial)
@@ -1193,11 +1192,7 @@ static void usb_serial_disconnect(structfor (i =0 ; i < serial->num_ports; ++i) { port = serial->port[i]; - tty =tty_port_tty_get (&port->port); -if (tty) { -tty_vhangup (tty); -tty_kref_put (tty); - } +tty_port_tty_vhangup (&port->port);usb_serial_port_poison_urbs (port);wake_up_interruptible (&port->port.delta_msr_wait);cancel_work_sync (&port->work);
include/linux/tty_port.h +11 -1
@@ -235,7 +235,7 @@ bool tty_port_carrier_raised(struct tty_void tty_port_raise_dtr_rts (struct tty_port *port);void tty_port_lower_dtr_rts (struct tty_port *port);void tty_port_hangup (struct tty_port *port); -void tty_port_tty_hangup (struct tty_port *port,bool check_clocal); +void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async);void tty_port_tty_wakeup (struct tty_port *port);int tty_port_block_til_ready (struct tty_port *port,struct tty_struct *tty,struct file *filp);
@@ -254,4 +254,14 @@ static inline int tty_port_users(struct return port->count + port->blocked_open; } +static inline void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) +{ +__tty_port_tty_hangup (port, check_clocal, true); +} + +static inline void tty_port_tty_vhangup (struct tty_port *port) +{ +__tty_port_tty_hangup (port, false, false); +} +#endif
net/bluetooth/rfcomm/tty.c +1 -6
@@ -438,7 +438,6 @@ static int __rfcomm_release_dev(void __u {struct rfcomm_dev_req req;struct rfcomm_dev *dev; -struct tty_struct *tty;if (copy_from_user (&req, arg,sizeof (req)))return -EFAULT ;
@@ -464,11 +463,7 @@ static int __rfcomm_release_dev(void __urfcomm_dlc_close (dev->dlc,0 );/* Shut down TTY synchronously before freeing rfcomm_dev */ - tty =tty_port_tty_get (&dev->port); -if (tty) { -tty_vhangup (tty); -tty_kref_put (tty); - } +tty_port_tty_vhangup (&dev->port);if (!test_bit (RFCOMM_TTY_OWNED , &dev->status))tty_port_put (&dev->port);
Greg Kroah-Hartman
maintainerPATCH 493/567 xhci: dbgtty: fix device unregister: fixup
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Łukasz Bartosik <[email protected]>
[ Upstream commit 74098cc06e753d3ffd8398b040a3a1dfb65260c0 ]
This fixup replaces tty_vhangup() call with call to tty_port_tty_vhangup(). Both calls hangup tty device synchronously however tty_port_tty_vhangup() increases reference count during the hangup operation using scoped_guard(tty_port_tty).
--- drivers/usb/host/xhci-dbgtty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/host/xhci-dbgtty.c +1 -1
@@ -522,7 +522,7 @@ static void xhci_dbc_tty_unregister_devi* Hang up the TTY. This wakes up any blocked * writers and causes subsequent writes to fail. */ -tty_vhangup (port->port.tty); +tty_port_tty_vhangup (&port->port);tty_unregister_device (dbc_tty_driver, port->minor);xhci_dbc_tty_exit_port (port);
Re: [PATCH 6.12 493/567] xhci: dbgtty: fix device unregister: fixup
Quoted 29 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Łukasz Bartosik <[email protected]>
[ Upstream commit
74098cc06e753d3ffd8398b040a3a1dfb65260c0]This fixup replaces
tty_vhangup()call with call totty_port_tty_vhangup(). Both calls hangup tty device synchronously howevertty_port_tty_vhangup()increases reference count during the hangup operation usingscoped_guard(tty_port_tty).Cc stable <[email protected]>Fixes 1f73b8b56cf3 ("xhci: dbgtty: fix device unregister")Signed-off-by Łukasz Bartosik <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>Signed-off-by Sasha Levin <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/usb/host/xhci-dbgtty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/host/xhci-dbgtty.c static void xhci_dbc_tty_unregister_devi · line 525
Quoted 5 lines
drivers/usb/host/xhci-dbgtty.c +1
-1@@ -522,7 +522,7 @@ static void xhci_dbc_tty_unregister_devi* Hang up the TTY. This wakes up any blocked * writers and causes subsequent writes to fail. */ -tty_vhangup (port->port.tty); +tty_port_tty_vhangup (&port->port);
The function tty_port_tty_vhangup does not exist in the 6.12 kernel. It was added later.
I sent updated patch
https://lore.kernel.org/stable/[email protected]/T/#mb46d870145474d04aaabeccc76aaf949b34bbf86
Thanks,
Łukasz
Quoted 5 lines
drivers/usb/host/xhci-dbgtty.c +0
-0tty_unregister_device (dbc_tty_driver, port->minor);xhci_dbc_tty_exit_port (port);
Greg Kroah-Hartman
maintainerRe: [PATCH 6.12 493/567] xhci: dbgtty: fix device unregister: fixup
Quoted 40 lines · Łukasz Bartosik
Quoted 29 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Łukasz Bartosik <[email protected]>
[ Upstream commit
74098cc06e753d3ffd8398b040a3a1dfb65260c0]This fixup replaces
tty_vhangup()call with call totty_port_tty_vhangup(). Both calls hangup tty device synchronously howevertty_port_tty_vhangup()increases reference count during the hangup operation usingscoped_guard(tty_port_tty).Cc stable <[email protected]>Fixes 1f73b8b56cf3 ("xhci: dbgtty: fix device unregister")Signed-off-by Łukasz Bartosik <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>Signed-off-by Sasha Levin <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/usb/host/xhci-dbgtty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/usb/host/xhci-dbgtty.c static void xhci_dbc_tty_unregister_devi · line 525
Quoted 5 lines
drivers/usb/host/xhci-dbgtty.c +1
-1@@ -522,7 +522,7 @@ static void xhci_dbc_tty_unregister_devi* Hang up the TTY. This wakes up any blocked * writers and causes subsequent writes to fail. */ -tty_vhangup (port->port.tty); +tty_port_tty_vhangup (&port->port);The function
tty_port_tty_vhangupdoes not exist in the 6.12 kernel. It was added later.
Quoted 2 lines
I sent updated patch
https://lore.kernel.org/stable/[email protected]/T/#mb46d870145474d04aaabeccc76aaf949b34bbf86
The patch before this one added that new api, so all is fine here.
thanks,
greg k-h
Re: [PATCH 6.12 493/567] xhci: dbgtty: fix device unregister: fixup
Quoted 42 lines · Greg Kroah-Hartman
Quoted 40 lines · Łukasz Bartosik
Quoted 29 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Łukasz Bartosik <[email protected]>
[ Upstream commit
74098cc06e753d3ffd8398b040a3a1dfb65260c0]This fixup replaces
tty_vhangup()call with call totty_port_tty_vhangup(). Both calls hangup tty device synchronously howevertty_port_tty_vhangup()increases reference count during the hangup operation usingscoped_guard(tty_port_tty).Cc stable <[email protected]>Fixes 1f73b8b56cf3 ("xhci: dbgtty: fix device unregister")Signed-off-by Łukasz Bartosik <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>Signed-off-by Sasha Levin <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/usb/host/xhci-dbgtty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/usb/host/xhci-dbgtty.c static void xhci_dbc_tty_unregister_devi · line 525
Quoted 5 lines
drivers/usb/host/xhci-dbgtty.c +1
-1@@ -522,7 +522,7 @@ static void xhci_dbc_tty_unregister_devi* Hang up the TTY. This wakes up any blocked * writers and causes subsequent writes to fail. */ -tty_vhangup (port->port.tty); +tty_port_tty_vhangup (&port->port);The function
tty_port_tty_vhangupdoes not exist in the 6.12 kernel. It was added later.
Quoted 5 lines
Quoted 2 lines
I sent updated patch
https://lore.kernel.org/stable/[email protected]/T/#mb46d870145474d04aaabeccc76aaf949b34bbf86The patch before this one added that new api, so all is fine here.
Thank you for pointing that out.
Thanks,
Łukasz
Quoted 3 lines
thanks,
greg k-h
Greg Kroah-Hartman
maintainerPATCH 494/567 f2fs: fix to detect recoverable inode during dryrun of find_fsync_dnodes()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 68d05693f8c031257a0822464366e1c2a239a512 ]
mkfs.f2fs -f /dev/vdd
mount /dev/vdd /mnt/f2fs
touch /mnt/f2fs/foo
sync # avoid CP_UMOUNT_FLAG in last f2fs_checkpoint.ckpt_flags
touch /mnt/f2fs/barf2fs_io fsync /mnt/f2fs/barf2fs_io shutdown 2 /mnt/f2fs
umount /mnt/f2fs
blockdev --setro /dev/vdd
mount /dev/vdd /mnt/f2fs
mount: /mnt/f2fs: WARNING: source write-protected, mounted read-only.
For the case if we create and fsync a new inode before sudden power-cut, without norecovery or disable_roll_forward mount option, the following mount will succeed w/o recovering last fsynced inode.
The problem here is that we only check inode_list list after find_fsync_dnodes() in f2fs_recover_fsync_data() to find out whether there is recoverable data in the iamge, but there is a missed case, if last fsynced inode is not existing in last checkpoint, then, we will fail to get its inode due to nat of inode node is not existing in last checkpoint, so the inode won't be linked in inode_list.
Let's detect such case in dyrun mode to fix this issue.
After this change, mount will fail as expected below:
mount: /mnt/f2fs: cannot mount /dev/vdd read-only.
dmesg(1) may have more information after failed mount system call.
demsg:
F2FS-fs (vdd): Need to recover fsync data, but write access unavailable, please try mount w/ disable_roll_forward or norecovery
--- fs/f2fs/recovery.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
fs/f2fs/recovery.c +12 -8
@@ -398,7 +398,7 @@ static int sanity_check_node_chain(struc }static int find_fsync_dnodes (struct f2fs_sb_info *sbi,struct list_head *head, -bool check_only) +bool check_only,bool *new_inode) {struct curseg_info *curseg;struct page *page =NULL ;
@@ -445,16 +445,19 @@ static int find_fsync_dnodes(struct f2fs quota_inode = true; } -/* -* CP | dnode(F) | inode(DF) -* For this case, we should not give up now. -*/ entry =add_fsync_inode (sbi, head,ino_of_node (page), quota_inode);if (IS_ERR (entry)) { err =PTR_ERR (entry); -if (err == -ENOENT ) +/* +* CP | dnode(F) | inode(DF) +* For this case, we should not give up now. +*/ +if (err == -ENOENT ) { +if (check_only) + *new_inode = true; goto next; + }f2fs_put_page (page,1 );break ; }
@@ -852,6 +855,7 @@ int f2fs_recover_fsync_data(struct f2fs_int ret =0 ;unsigned long s_flags = sbi->sb->s_flags;bool need_writecp = false; +bool new_inode = false;if (is_sbi_flag_set (sbi,SBI_IS_WRITABLE ))f2fs_info (sbi,"recover fsync data on readonly fs" );
@@ -864,8 +868,8 @@ int f2fs_recover_fsync_data(struct f2fs_f2fs_down_write (&sbi->cp_global_sem);/* step #1: find fsynced inode numbers */ - err =find_fsync_dnodes (sbi, &inode_list, check_only); -if (err ||list_empty (&inode_list)) + err =find_fsync_dnodes (sbi, &inode_list, check_only, &new_inode); +if (err <0 || (list_empty (&inode_list) && (!check_only || !new_inode))) goto skip;if (check_only) {
Greg Kroah-Hartman
maintainerPATCH 495/567 f2fs: use global inline_xattr_slab instead of per-sb slab cache
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 1f27ef42bb0b7c0740c5616ec577ec188b8a1d05 ]
As Hong Yun reported in mailing list:
loop7: detected capacity change from 0 to 131072
------------[ cut here ]------------ kmem_cache of name 'f2fs_xattr_entry-7:7' already exists WARNING: CPU: 0 PID: 24426 at mm/slab_common.c:110 kmem_cache_sanity_check mm/slab_common.c:109 [inline] WARNING: CPU: 0 PID: 24426 at mm/slab_common.c:110 __kmem_cache_create_args+0xa6/0x320 mm/slab_common.c:307 CPU: 0 UID: 0 PID: 24426 Comm: syz.7.1370 Not tainted 6.17.0-rc4 #1 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014 RIP: 0010:kmem_cache_sanity_check mm/slab_common.c:109 [inline] RIP: 0010:__kmem_cache_create_args+0xa6/0x320 mm/slab_common.c:307 Call Trace: __kmem_cache_create include/linux/slab.h:353 [inline] f2fs_kmem_cache_create fs/f2fs/f2fs.h:2943 [inline] f2fs_init_xattr_caches+0xa5/0xe0 fs/f2fs/xattr.c:843 f2fs_fill_super+0x1645/0x2620 fs/f2fs/super.c:4918 get_tree_bdev_flags+0x1fb/0x260 fs/super.c:1692 vfs_get_tree+0x43/0x140 fs/super.c:1815 do_new_mount+0x201/0x550 fs/namespace.c:3808 do_mount fs/namespace.c:4136 [inline] __do_sys_mount fs/namespace.c:4347 [inline] __se_sys_mount+0x298/0x2f0 fs/namespace.c:4324 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x8e/0x3a0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x76/0x7e
The bug can be reproduced w/ below scripts:
- mount /dev/vdb /mnt1
- mount /dev/vdc /mnt2
- umount /mnt1
- mounnt /dev/vdb /mnt1
The reason is if we created two slab caches, named f2fs_xattr_entry-7:3 and f2fs_xattr_entry-7:7, and they have the same slab size. Actually, slab system will only create one slab cache core structure which has slab name of "f2fs_xattr_entry-7:3", and two slab caches share the same structure and cache address.
So, if we destroy f2fs_xattr_entry-7:3 cache w/ cache address, it will decrease reference count of slab cache, rather than release slab cache entirely, since there is one more user has referenced the cache.
Then, if we try to create slab cache w/ name "f2fs_xattr_entry-7:3" again, slab system will find that there is existed cache which has the same name and trigger the warning.
Let's changes to use global inline_xattr_slab instead of per-sb slab cache for fixing.
--- fs/f2fs/f2fs.h | 3 --- fs/f2fs/super.c | 17 ++++++++--------- fs/f2fs/xattr.c | 30 ++++++++++-------------------- fs/f2fs/xattr.h | 10 ++++++---- 4 files changed, 24 insertions(+), 36 deletions(-)
fs/f2fs/f2fs.h +0 -3
@@ -1804,9 +1804,6 @@ struct f2fs_sb_info {spinlock_t error_lock;/* protect errors/stop_reason array */ bool error_dirty;/* errors of sb is dirty */ -struct kmem_cache *inline_xattr_slab;/* inline xattr entry */ -unsigned int inline_xattr_slab_size;/* default inline xattr slab size */ -/* For reclaimed segs statistics per each GC mode */ unsigned int gc_segment_mode;/* GC state for reclaimed segments */ unsigned int gc_reclaimed_segs[MAX_GC_MODE ];/* Reclaimed segs for each mode */
fs/f2fs/super.c +8 -9
@@ -1695,7 +1695,6 @@ static void f2fs_put_super(struct super_kfree (sbi->raw_super);f2fs_destroy_page_array_cache (sbi); -f2fs_destroy_xattr_caches (sbi);#ifdef CONFIG_QUOTA for (i =0 ; i <MAXQUOTAS ; i++)kfree (F2FS_OPTION (sbi).s_qf_names[i]);
@@ -4568,13 +4567,9 @@ try_onemore:if (err) goto free_iostat; -/* init per sbi slab cache */ - err =f2fs_init_xattr_caches (sbi); -if (err) - goto free_percpu; err =f2fs_init_page_array_cache (sbi);if (err) - goto free_xattr_cache; + goto free_percpu;/* get an inode for meta space */ sbi->meta_inode =f2fs_iget (sb,F2FS_META_INO (sbi));
@@ -4906,8 +4901,6 @@ free_meta_inode: sbi->meta_inode =NULL ; free_page_array_cache:f2fs_destroy_page_array_cache (sbi); -free_xattr_cache: -f2fs_destroy_xattr_caches (sbi); free_percpu:destroy_percpu_info (sbi); free_iostat:
@@ -5069,10 +5062,15 @@ static int __init init_f2fs_fs(void) err =f2fs_create_casefold_cache ();if (err) goto free_compress_cache; - err =register_filesystem (&f2fs_fs_type); + err =f2fs_init_xattr_cache ();if (err) goto free_casefold_cache; + err =register_filesystem (&f2fs_fs_type); +if (err) + goto free_xattr_cache;return 0 ; +free_xattr_cache: +f2fs_destroy_xattr_cache (); free_casefold_cache:f2fs_destroy_casefold_cache (); free_compress_cache:
@@ -5113,6 +5111,7 @@ fail: static void __exit exit_f2fs_fs(void) {unregister_filesystem (&f2fs_fs_type); +f2fs_destroy_xattr_cache ();f2fs_destroy_casefold_cache ();f2fs_destroy_compress_cache ();f2fs_destroy_compress_mempool ();
fs/f2fs/xattr.c +10 -20
@@ -23,11 +23,12 @@#include "xattr.h" #include "segment.h" +static struct kmem_cache *inline_xattr_slab;static void *xattr_alloc (struct f2fs_sb_info *sbi,int size,bool *is_inline) { -if (likely (size == sbi->inline_xattr_slab_size)) { +if (likely (size ==DEFAULT_XATTR_SLAB_SIZE )) { *is_inline = true; -return f2fs_kmem_cache_alloc (sbi->inline_xattr_slab, +return f2fs_kmem_cache_alloc (inline_xattr_slab,GFP_F2FS_ZERO , false, sbi); } *is_inline = false;
@@ -38,7 +39,7 @@ static void xattr_free(struct f2fs_sb_in bool is_inline) {if (is_inline) -kmem_cache_free (sbi->inline_xattr_slab, xattr_addr); +kmem_cache_free (inline_xattr_slab, xattr_addr);else kfree (xattr_addr); }
@@ -830,25 +831,14 @@ int f2fs_setxattr(struct inode *inode, ireturn err; } -int f2fs_init_xattr_caches (struct f2fs_sb_info *sbi) +int __init f2fs_init_xattr_cache (void ) { -dev_t dev = sbi->sb->s_bdev->bd_dev; -char slab_name[32 ]; - -sprintf (slab_name,"f2fs_xattr_entry-%u:%u" ,MAJOR (dev),MINOR (dev)); - - sbi->inline_xattr_slab_size =F2FS_OPTION (sbi).inline_xattr_size * -sizeof (__le32) +XATTR_PADDING_SIZE ; - - sbi->inline_xattr_slab =f2fs_kmem_cache_create (slab_name, - sbi->inline_xattr_slab_size); -if (!sbi->inline_xattr_slab) -return -ENOMEM ; - -return 0 ; + inline_xattr_slab =f2fs_kmem_cache_create ("f2fs_xattr_entry" , +DEFAULT_XATTR_SLAB_SIZE ); +return inline_xattr_slab ?0 : -ENOMEM ; } -void f2fs_destroy_xattr_caches (struct f2fs_sb_info *sbi) +void f2fs_destroy_xattr_cache (void ) { -kmem_cache_destroy (sbi->inline_xattr_slab); +kmem_cache_destroy (inline_xattr_slab); }
fs/f2fs/xattr.h +6 -4
@@ -89,6 +89,8 @@ struct f2fs_xattr_entry { F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \ DEF_INLINE_RESERVED_SIZE - \ MIN_INLINE_DENTRY_SIZE / sizeof(__le32)) +#define DEFAULT_XATTR_SLAB_SIZE (DEFAULT_INLINE_XATTR_ADDRS * \ + sizeof(__le32) + XATTR_PADDING_SIZE)/* * On-disk structure of f2fs_xattr
@@ -132,8 +134,8 @@ extern int f2fs_setxattr(struct inode *,extern int f2fs_getxattr (struct inode *,int ,const char *,void *,size_t ,struct page *);extern ssize_t f2fs_listxattr (struct dentry *,char *,size_t ); -extern int f2fs_init_xattr_caches (struct f2fs_sb_info *); -extern void f2fs_destroy_xattr_caches (struct f2fs_sb_info *); +extern int __init f2fs_init_xattr_cache (void ); +extern void f2fs_destroy_xattr_cache (void );#else #define f2fs_xattr_handlers NULL
@@ -150,8 +152,8 @@ static inline int f2fs_getxattr(struct i {return -EOPNOTSUPP ; } -static inline int f2fs_init_xattr_caches (struct f2fs_sb_info *sbi) {return 0 ; } -static inline void f2fs_destroy_xattr_caches (struct f2fs_sb_info *sbi) { } +static inline int __init f2fs_init_xattr_cache (void ) {return 0 ; } +static inline void f2fs_destroy_xattr_cache (void ) { }#endif #ifdef CONFIG_F2FS_FS_SECURITY
Greg Kroah-Hartman
maintainerPATCH 496/567 f2fs: drop inode from the donation list when the last file is closed
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jaegeuk Kim <[email protected]>
[ Upstream commit 078cad8212ce4f4ebbafcc0936475b8215e1ca2a ]
Let's drop the inode from the donation list when there is no other open file.
Stable-dep-of: 10b591e7fb7c ("f2fs: fix to avoid updating compression context during writeback")
--- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 8 +++++++- fs/f2fs/inode.c | 2 +- fs/f2fs/super.c | 1 + 4 files changed, 11 insertions(+), 2 deletions(-)
fs/f2fs/f2fs.h +2 -0
@@ -859,6 +859,7 @@ struct f2fs_inode_info {/* linked in global inode list for cache donation */ struct list_head gdonate_list;pgoff_t donate_start, donate_end;/* inclusive */ +atomic_t open_count;/* # of open files */ struct task_struct *atomic_write_task;/* store atomic write task */ struct extent_tree *extent_tree[NR_EXTENT_CACHES ];
@@ -3600,6 +3601,7 @@ int f2fs_try_to_free_nats(struct f2fs_sbvoid f2fs_update_inode (struct inode *inode,struct page *node_page);void f2fs_update_inode_page (struct inode *inode);int f2fs_write_inode (struct inode *inode,struct writeback_control *wbc); +void f2fs_remove_donate_inode (struct inode *inode);void f2fs_evict_inode (struct inode *inode);void f2fs_handle_failed_inode (struct inode *inode);
fs/f2fs/file.c +7 -1
@@ -631,7 +631,10 @@ static int f2fs_file_open(struct inode *if (err)return err; -return finish_preallocate_blocks (inode); + err =finish_preallocate_blocks (inode); +if (!err) +atomic_inc (&F2FS_I (inode)->open_count); +return err; }void f2fs_truncate_data_blocks_range (struct dnode_of_data *dn,int count)
@@ -1992,6 +1995,9 @@ out:static int f2fs_release_file (struct inode *inode,struct file *filp) { +if (atomic_dec_and_test (&F2FS_I (inode)->open_count)) +f2fs_remove_donate_inode (inode); +/* * f2fs_release_file is called at every close calls. So we should * not drop any inmemory pages by close called by other process.
fs/f2fs/inode.c +1 -1
@@ -807,7 +807,7 @@ int f2fs_write_inode(struct inode *inodereturn 0 ; } -static void f2fs_remove_donate_inode (struct inode *inode) +void f2fs_remove_donate_inode (struct inode *inode) {struct f2fs_sb_info *sbi =F2FS_I_SB (inode);
fs/f2fs/super.c +1 -0
@@ -1425,6 +1425,7 @@ static struct inode *f2fs_alloc_inode(st/* Initialize f2fs-specific inode info */ atomic_set (&fi->dirty_pages,0 );atomic_set (&fi->i_compr_blocks,0 ); +atomic_set (&fi->open_count,0 );init_f2fs_rwsem (&fi->i_sem);spin_lock_init (&fi->i_size_lock);INIT_LIST_HEAD (&fi->dirty_list);
Greg Kroah-Hartman
maintainerPATCH 497/567 f2fs: fix to avoid updating compression context during writeback
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 10b591e7fb7cdc8c1e53e9c000dc0ef7069aaa76 ]
Bai, Shuangpeng <[email protected]> reported a bug as below:
Oops: divide error: 0000 [#1] SMP KASAN PTI CPU: 0 UID: 0 PID: 11441 Comm: syz.0.46 Not tainted 6.17.0 #1 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:f2fs_all_cluster_page_ready+0x106/0x550 fs/f2fs/compress.c:857 Call Trace: <TASK> f2fs_write_cache_pages fs/f2fs/data.c:3078 [inline] __f2fs_write_data_pages fs/f2fs/data.c:3290 [inline] f2fs_write_data_pages+0x1c19/0x3600 fs/f2fs/data.c:3317 do_writepages+0x38e/0x640 mm/page-writeback.c:2634 filemap_fdatawrite_wbc mm/filemap.c:386 [inline] __filemap_fdatawrite_range mm/filemap.c:419 [inline] file_write_and_wait_range+0x2ba/0x3e0 mm/filemap.c:794 f2fs_do_sync_file+0x6e6/0x1b00 fs/f2fs/file.c:294 generic_write_sync include/linux/fs.h:3043 [inline] f2fs_file_write_iter+0x76e/0x2700 fs/f2fs/file.c:5259 new_sync_write fs/read_write.c:593 [inline] vfs_write+0x7e9/0xe00 fs/read_write.c:686 ksys_write+0x19d/0x2d0 fs/read_write.c:738 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xf7/0x470 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f
The bug was triggered w/ below race condition:
fsync setattr ioctl
- f2fs_do_sync_file
- file_write_and_wait_range
- f2fs_write_cache_pages
: inode is non-compressed
: cc.cluster_size =
F2FS_I(inode)->i_cluster_size = 0
- tag_pages_for_writeback
- f2fs_setattr
- truncate_setsize
- f2fs_truncate
- f2fs_fileattr_set
- f2fs_setflags_common
- set_compress_context
: F2FS_I(inode)->i_cluster_size = 4 : set_inode_flag(inode, FI_COMPRESSED_FILE)
- f2fs_compressed_file
: return true
- f2fs_all_cluster_page_ready
: "pgidx % cc->cluster_size" trigger dividing 0 issue
Let's change as below to fix this issue:
- introduce a new atomic type variable .writeback in structure f2fs_inode_info to track the number of threads which calling f2fs_write_cache_pages().
- use .i_sem lock to protect .writeback update.
- check .writeback before update compression context in f2fs_setflags_common() to avoid race w/ ->writepages.
--- fs/f2fs/data.c | 17 +++++++++++++++++ fs/f2fs/f2fs.h | 3 ++- fs/f2fs/file.c | 5 +++-- fs/f2fs/super.c | 1 + 4 files changed, 23 insertions(+), 3 deletions(-)
fs/f2fs/data.c +17 -0
@@ -3272,6 +3272,19 @@ static inline bool __should_serialize_ioreturn false; } +static inline void account_writeback (struct inode *inode,bool inc) +{ +if (!f2fs_sb_has_compression (F2FS_I_SB (inode))) +return ; + +f2fs_down_read (&F2FS_I (inode)->i_sem); +if (inc) +atomic_inc (&F2FS_I (inode)->writeback); +else +atomic_dec (&F2FS_I (inode)->writeback); +f2fs_up_read (&F2FS_I (inode)->i_sem); +} +static int __f2fs_write_data_pages (struct address_space *mapping,struct writeback_control *wbc,enum iostat_type io_type)
@@ -3321,10 +3334,14 @@ static int __f2fs_write_data_pages(struc locked = true; } +account_writeback (inode, true); +blk_start_plug (&plug); ret =f2fs_write_cache_pages (mapping, wbc, io_type);blk_finish_plug (&plug); +account_writeback (inode, false); +if (locked)mutex_unlock (&sbi->writepages);
fs/f2fs/f2fs.h +2 -1
@@ -887,6 +887,7 @@ struct f2fs_inode_info {unsigned char i_compress_level;/* compress level (lz4hc,zstd) */ unsigned char i_compress_flag;/* compress flag */ unsigned int i_cluster_size;/* cluster size */ +atomic_t writeback;/* count # of writeback thread */ unsigned int atomic_write_cnt;loff_t original_i_size;/* original i_size before atomic write */
@@ -4540,7 +4541,7 @@ static inline bool f2fs_disable_compressf2fs_up_write (&fi->i_sem);return true; } -if (f2fs_is_mmap_file (inode) || +if (f2fs_is_mmap_file (inode) ||atomic_read (&fi->writeback) || (S_ISREG (inode->i_mode) &&F2FS_HAS_BLOCKS (inode))) {f2fs_up_write (&fi->i_sem);return false;
fs/f2fs/file.c +3 -2
@@ -2071,8 +2071,9 @@ static int f2fs_setflags_common(struct if2fs_down_write (&fi->i_sem);if (!f2fs_may_compress (inode) || - (S_ISREG (inode->i_mode) && -F2FS_HAS_BLOCKS (inode))) { +atomic_read (&fi->writeback) || + (S_ISREG (inode->i_mode) && +F2FS_HAS_BLOCKS (inode))) {f2fs_up_write (&fi->i_sem);return -EINVAL ; }
fs/f2fs/super.c +1 -0
@@ -1426,6 +1426,7 @@ static struct inode *f2fs_alloc_inode(statomic_set (&fi->dirty_pages,0 );atomic_set (&fi->i_compr_blocks,0 );atomic_set (&fi->open_count,0 ); +atomic_set (&fi->writeback,0 );init_f2fs_rwsem (&fi->i_sem);spin_lock_init (&fi->i_size_lock);INIT_LIST_HEAD (&fi->dirty_list);
Greg Kroah-Hartman
maintainerPATCH 498/567 serial: core: fix OF node leak
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 273cc3406c8d4e830ed45967c70d08d20ca1380e ]
Make sure to drop the OF node reference taken when initialising the control and port devices when the devices are later released.
Stable-dep-of: 24ec03cc5512 ("serial: core: Restore sysfs fwnode information")
--- drivers/tty/serial/serial_base_bus.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/tty/serial/serial_base_bus.c +3 -0
@@ -13,6 +13,7 @@#include <linux/device.h> #include <linux/idr.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/serial_core.h> #include <linux/slab.h> #include <linux/spinlock.h>
@@ -93,6 +94,7 @@ static void serial_base_ctrl_release(str {struct serial_ctrl_device *ctrl_dev =to_serial_base_ctrl_device (dev); +of_node_put (dev->of_node);kfree (ctrl_dev); }
@@ -140,6 +142,7 @@ static void serial_base_port_release(str {struct serial_port_device *port_dev =to_serial_base_port_device (dev); +of_node_put (dev->of_node);kfree (port_dev); }
Greg Kroah-Hartman
maintainerPATCH 499/567 serial: core: Restore sysfs fwnode information
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 24ec03cc55126b7b3adf102f4b3d9f716532b329 ]
The change that restores sysfs fwnode information does it only for OF cases. Update the fix to cover all possible types of fwnodes.
--- drivers/tty/serial/serial_base_bus.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/tty/serial/serial_base_bus.c +6 -4
@@ -13,7 +13,7 @@#include <linux/device.h> #include <linux/idr.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/property.h> #include <linux/serial_core.h> #include <linux/slab.h> #include <linux/spinlock.h>
@@ -60,6 +60,7 @@ void serial_base_driver_unregister(strucdriver_unregister (driver); } +/* On failure the caller must put device @dev with put_device() */ static int serial_base_device_init(struct uart_port *port, struct device *dev, struct device *parent_dev,
@@ -73,7 +74,8 @@ static int serial_base_device_init(struc dev->parent = parent_dev; dev->bus = &serial_base_bus_type; dev->release = release; -device_set_of_node_from_dev (dev, parent_dev); + +device_set_node (dev,fwnode_handle_get (dev_fwnode (parent_dev)));if (!serial_base_initialized) {dev_dbg (port->dev,"uart_add_one_port() called before arch_initcall()?\n" );
@@ -94,7 +96,7 @@ static void serial_base_ctrl_release(str {struct serial_ctrl_device *ctrl_dev =to_serial_base_ctrl_device (dev); -of_node_put (dev->of_node); +fwnode_handle_put (dev_fwnode (dev));kfree (ctrl_dev); }
@@ -142,7 +144,7 @@ static void serial_base_port_release(str {struct serial_port_device *port_dev =to_serial_base_port_device (dev); -of_node_put (dev->of_node); +fwnode_handle_put (dev_fwnode (dev));kfree (port_dev); }
Greg Kroah-Hartman
maintainerPATCH 500/567 mptcp: pm: ignore unknown endpoint flags
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Matthieu Baerts (NGI0)" <[email protected]>
[ Upstream commit 0ace3297a7301911e52d8195cb1006414897c859 ]
Before this patch, the kernel was saving any flags set by the userspace, even unknown ones. This doesn't cause critical issues because the kernel is only looking at specific ones. But on the other hand, endpoints dumps could tell the userspace some recent flags seem to be supported on older kernel versions.
Instead, ignore all unknown flags when parsing them. By doing that, the userspace can continue to set unsupported flags, but it has a way to verify what is supported by the kernel.
Note that it sounds better to continue accepting unsupported flags not to change the behaviour, but also that eases things on the userspace side by adding "optional" endpoint types only supported by newer kernel versions without having to deal with the different kernel versions.
A note for the backports: there will be conflicts in mptcp.h on older versions not having the mentioned flags, the new line should still be added last, and the '5' needs to be adapted to have the same value as the last entry.
--- include/uapi/linux/mptcp.h | 1 + net/mptcp/pm_netlink.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
include/uapi/linux/mptcp.h +1 -0
@@ -38,6 +38,7 @@#define MPTCP_PM_ADDR_FLAG_BACKUP (1 << 2)#define MPTCP_PM_ADDR_FLAG_FULLMESH (1 << 3)#define MPTCP_PM_ADDR_FLAG_IMPLICIT (1 << 4) +#define MPTCP_PM_ADDR_FLAGS_MASK GENMASK(4, 0)struct mptcp_info {__u8 mptcpi_subflows;
net/mptcp/pm_netlink.c +2 -1
@@ -1409,7 +1409,8 @@ int mptcp_pm_parse_entry(struct nlattr * }if (tb[MPTCP_PM_ADDR_ATTR_FLAGS ]) - entry->flags =nla_get_u32 (tb[MPTCP_PM_ADDR_ATTR_FLAGS ]); + entry->flags =nla_get_u32 (tb[MPTCP_PM_ADDR_ATTR_FLAGS ]) & +MPTCP_PM_ADDR_FLAGS_MASK ;if (tb[MPTCP_PM_ADDR_ATTR_PORT ]) entry->addr.port =htons (nla_get_u16 (tb[MPTCP_PM_ADDR_ATTR_PORT ]));
Greg Kroah-Hartman
maintainerPATCH 501/567 mm/ksm: fix exec/fork inheritance support for prctl
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: xu xin <[email protected]>
[ Upstream commit 590c03ca6a3fbb114396673314e2aa483839608b ]
Patch series "ksm: fix exec/fork inheritance", v2.
This series fixes exec/fork inheritance. See the detailed description of the issue below.
This patch (of 2):
Background
==========
commit d7597f59d1d33 ("mm: add new api to enable ksm per process") introduced MMF_VM_MERGE_ANY for mm->flags, and allowed user to set it by prctl() so that the process's VMAs are forcibly scanned by ksmd.
Subsequently, the 3c6f33b7273a ("mm/ksm: support fork/exec for prctl") supported inheriting the MMF_VM_MERGE_ANY flag when a task calls execve().
Finally, commit 3a9e567ca45fb ("mm/ksm: fix ksm exec support for prctl") fixed the issue that ksmd doesn't scan the mm_struct with MMF_VM_MERGE_ANY by adding the mm_slot to ksm_mm_head in __bprm_mm_init().
Problem
=======
In some extreme scenarios, however, this inheritance of MMF_VM_MERGE_ANY during exec/fork can fail. For example, when the scanning frequency of ksmd is tuned extremely high, a process carrying MMF_VM_MERGE_ANY may still fail to pass it to the newly exec'd process. This happens because ksm_execve() is executed too early in the do_execve flow (prematurely adding the new mm_struct to the ksm_mm_slot list).
As a result, before do_execve completes, ksmd may have already performed a scan and found that this new mm_struct has no VM_MERGEABLE VMAs, thus clearing its MMF_VM_MERGE_ANY flag. Consequently, when the new program executes, the flag MMF_VM_MERGE_ANY inheritance missed.
Root reason
===========
commit d7597f59d1d33 ("mm: add new api to enable ksm per process") clear the flag MMF_VM_MERGE_ANY when ksmd found no VM_MERGEABLE VMAs.
Solution
========
Firstly, Don't clear MMF_VM_MERGE_ANY when ksmd found no VM_MERGEABLE VMAs, because perhaps their mm_struct has just been added to ksm_mm_slot list, and its process has not yet officially started running or has not yet performed mmap/brk to allocate anonymous VMAS.
Secondly, recheck MMF_VM_MERGEABLE again if a process takes MMF_VM_MERGE_ANY, and create a mm_slot and join it into ksm_scan_list again.
--- mm/ksm.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
mm/ksm.c +16 -2
@@ -2704,8 +2704,14 @@ no_vmas:spin_unlock (&ksm_mmlist_lock);mm_slot_free (mm_slot_cache, mm_slot); +/* +* Only clear MMF_VM_MERGEABLE. We must not clear +* MMF_VM_MERGE_ANY, because for those MMF_VM_MERGE_ANY process, +* perhaps their mm_struct has just been added to ksm_mm_slot +* list, and its process has not yet officially started running +* or has not yet performed mmap/brk to allocate anonymous VMAS. +*/ clear_bit (MMF_VM_MERGEABLE , &mm->flags); -clear_bit (MMF_VM_MERGE_ANY , &mm->flags);mmap_read_unlock (mm);mmdrop (mm); }else {
@@ -2820,8 +2826,16 @@ void ksm_add_vma(struct vm_area_struct * {struct mm_struct *mm = vma->vm_mm; -if (test_bit (MMF_VM_MERGE_ANY , &mm->flags)) +if (test_bit (MMF_VM_MERGE_ANY , &mm->flags)) {__ksm_add_vma (vma); +/* +* Generally, the flags here always include MMF_VM_MERGEABLE. +* However, in rare cases, this flag may be cleared by ksmd who +* scans a cycle without finding any mergeable vma. +*/ +if (unlikely (!test_bit (MMF_VM_MERGEABLE , &mm->flags))) +__ksm_enter (mm); + } }static void ksm_add_vmas (struct mm_struct *mm)
Greg Kroah-Hartman
maintainerPATCH 502/567 svcrdma: bound check rq_pages index in inline path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Rogers <[email protected]>
[ Upstream commit d1bea0ce35b6095544ee82bb54156fc62c067e58 ]
svc_rdma_copy_inline_range indexed rqstp->rq_pages[rc_curpage] without verifying rc_curpage stays within the allocated page array. Add guards before the first use and after advancing to a new page.
--- net/sunrpc/xprtrdma/svc_rdma_rw.c | 3 +++ 1 file changed, 3 insertions(+)
net/sunrpc/xprtrdma/svc_rdma_rw.c +3 -0
@@ -841,6 +841,9 @@ static int svc_rdma_copy_inline_range(stfor (page_no =0 ; page_no < numpages; page_no++) {unsigned int page_len; +if (head->rc_curpage >=ARRAY_SIZE (rqstp->rq_pages)) +return -EINVAL ; + page_len = min_t(unsigned int, remaining,PAGE_SIZE - head->rc_pageoff);
Greg Kroah-Hartman
maintainerPATCH 503/567 ARM: dts: microchip: sama7g5: fix uart fifo size to 32
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Ferre <[email protected]>
[ Upstream commit 5654889a94b0de5ad6ceae3793e7f5e0b61b50b6 ]
On some flexcom nodes related to uart, the fifo sizes were wrong: fix them to 32 data.
--- arch/arm/boot/dts/microchip/sama7g5.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm/boot/dts/microchip/sama7g5.dtsi +2 -2
@@ -811,7 +811,7 @@ dma-names = "tx", "rx"; atmel,use-dma-rx; atmel,use-dma-tx; - atmel,fifo-size = <16>; + atmel,fifo-size = <32>; status = "disabled"; }; };
@@ -837,7 +837,7 @@ dma-names = "tx", "rx"; atmel,use-dma-rx; atmel,use-dma-tx; - atmel,fifo-size = <16>; + atmel,fifo-size = <32>; status = "disabled"; }; };
Greg Kroah-Hartman
maintainerPATCH 504/567 block: freeze queue when updating zone resources
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
[ Upstream commit bba4322e3f303b2d656e748be758320b567f046f ]
Modify disk_update_zone_resources() to freeze the device queue before updating the number of zones, zone capacity and other zone related resources. The locking order resulting from the call to queue_limits_commit_update_frozen() is preserved, that is, the queue limits lock is first taken by calling queue_limits_start_update() before freezing the queue, and the queue is unfrozen after executing queue_limits_commit_update(), which replaces the call to queue_limits_commit_update_frozen().
This change ensures that there are no in-flights I/Os when the zone resources are updated due to a zone revalidation. In case of error when the limits are applied, directly call disk_free_zone_resources() from disk_update_zone_resources() while the disk queue is still frozen to avoid needing to freeze & unfreeze the queue again in blk_revalidate_disk_zones(), thus simplifying that function code a little.
--- block/blk-zoned.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-)
block/blk-zoned.c +23 -16
@@ -1514,6 +1514,11 @@ static int disk_update_zone_resources(stunsigned int nr_seq_zones, nr_conv_zones;unsigned int pool_size;struct queue_limits lim; +int ret =0 ; + + lim =queue_limits_start_update (q); + +blk_mq_freeze_queue (q); disk->nr_zones = args->nr_zones; disk->zone_capacity = args->zone_capacity;
@@ -1523,11 +1528,10 @@ static int disk_update_zone_resources(stif (nr_conv_zones >= disk->nr_zones) {pr_warn ("%s: Invalid number of conventional zones %u / %u\n" , disk->disk_name, nr_conv_zones, disk->nr_zones); -return -ENODEV ; + ret = -ENODEV ; + goto unfreeze; } - lim =queue_limits_start_update (q); -/* * Some devices can advertize zone resource limits that are larger than * the number of sequential zones of the zoned block device, e.g. a
@@ -1564,7 +1568,15 @@ static int disk_update_zone_resources(st } commit: -return queue_limits_commit_update_frozen (q, &lim); + ret =queue_limits_commit_update (q, &lim); + +unfreeze: +if (ret) +disk_free_zone_resources (disk); + +blk_mq_unfreeze_queue (q); + +return ret; } static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
@@ -1785,19 +1797,14 @@ int blk_revalidate_disk_zones(struct gen ret = -ENODEV ; } -/* -* Set the new disk zone parameters only once the queue is frozen and -* all I/Os are completed. -*/ if (ret >0 ) - ret =disk_update_zone_resources (disk, &args); -else -pr_warn ("%s: failed to revalidate zones\n" , disk->disk_name); -if (ret) { -blk_mq_freeze_queue (q); -disk_free_zone_resources (disk); -blk_mq_unfreeze_queue (q); - } +return disk_update_zone_resources (disk, &args); + +pr_warn ("%s: failed to revalidate zones\n" , disk->disk_name); + +blk_mq_freeze_queue (q); +disk_free_zone_resources (disk); +blk_mq_unfreeze_queue (q);return ret; }
Greg Kroah-Hartman
maintainerPATCH 505/567 tpm2-sessions: Fix tpm2_read_public range checks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jarkko Sakkinen <[email protected]>
[ Upstream commit bda1cbf73c6e241267c286427f2ed52b5735d872 ]
tpm2_read_public() has some rudimentary range checks but the function does not ensure that the response buffer has enough bytes for the full TPMT_HA payload.
Re-implement the function with necessary checks and validation, and return name and name size for all handle types back to the caller.
--- drivers/char/tpm/tpm2-cmd.c | 3 + drivers/char/tpm/tpm2-sessions.c | 85 ++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 32 deletions(-)
drivers/char/tpm/tpm2-cmd.c +3 -0
@@ -11,8 +11,11 @@* used by the kernel internally. */ +#include "linux/dev_printk.h" +#include "linux/tpm.h" #include "tpm.h" #include <crypto/hash_info.h> +#include <linux/unaligned.h> static bool disable_pcr_integrity;module_param (disable_pcr_integrity, bool,0444 );
drivers/char/tpm/tpm2-sessions.c +53 -32
120 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 506/567 sched_ext: Factor out local_dsq_post_enq() from dispatch_enqueue()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit 530b6637c79e728d58f1d9b66bd4acf4b735b86d ]
Factor out local_dsq_post_enq() which performs post-enqueue handling for local DSQs - triggering resched_curr() if SCX_ENQ_PREEMPT is specified or if the current CPU is idle. No functional change.
This will be used by the next patch to fix move_local_task_to_local_dsq().
--- kernel/sched/ext.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-)
kernel/sched/ext.c +19 -15
@@ -1676,6 +1676,22 @@ static void dsq_mod_nr(struct scx_dispatWRITE_ONCE (dsq->nr, dsq->nr + delta); } +static void local_dsq_post_enq (struct scx_dispatch_q *dsq,struct task_struct *p, +u64 enq_flags) +{ + struct rq *rq = container_of(dsq, struct rq, scx.local_dsq); +bool preempt = false; + +if ((enq_flags &SCX_ENQ_PREEMPT ) && p != rq->curr && + rq->curr->sched_class == &ext_sched_class) { + rq->curr->scx.slice =0 ; + preempt = true; + } + +if (preempt ||sched_class_above (&ext_sched_class, rq->curr->sched_class)) +resched_curr (rq); +} +static void dispatch_enqueue (struct scx_dispatch_q *dsq,struct task_struct *p,u64 enq_flags) {
@@ -1773,22 +1789,10 @@ static void dispatch_enqueue(struct scx_if (enq_flags &SCX_ENQ_CLEAR_OPSS )atomic_long_set_release (&p->scx.ops_state,SCX_OPSS_NONE ); -if (is_local) { - struct rq *rq = container_of(dsq, struct rq, scx.local_dsq); -bool preempt = false; - -if ((enq_flags &SCX_ENQ_PREEMPT ) && p != rq->curr && - rq->curr->sched_class == &ext_sched_class) { - rq->curr->scx.slice =0 ; - preempt = true; - } - -if (preempt ||sched_class_above (&ext_sched_class, - rq->curr->sched_class)) -resched_curr (rq); - }else { +if (is_local) +local_dsq_post_enq (dsq, p, enq_flags); +else raw_spin_unlock (&dsq->lock); - } } static void task_unlink_from_dsq(struct task_struct *p,
Greg Kroah-Hartman
maintainerPATCH 507/567 sched_ext: Fix missing post-enqueue handling in move_local_task_to_local_dsq()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit f5e1e5ec204da11fa87fdf006d451d80ce06e118 ]
move_local_task_to_local_dsq() is used when moving a task from a non-local DSQ to a local DSQ on the same CPU. It directly manipulates the local DSQ without going through dispatch_enqueue() and was missing the post-enqueue handling that triggers preemption when SCX_ENQ_PREEMPT is set or the idle task is running.
The function is used by move_task_between_dsqs() which backs scx_bpf_dsq_move() and may be called while the CPU is busy.
Add local_dsq_post_enq() call to move_local_task_to_local_dsq(). As the dispatch path doesn't need post-enqueue handling, add SCX_RQ_IN_BALANCE early exit to keep consume_dispatch_q() behavior unchanged and avoid triggering unnecessary resched when scx_bpf_dsq_move() is used from the dispatch path.
--- kernel/sched/ext.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/sched/ext.c +10 -0
@@ -1682,6 +1682,14 @@ static void local_dsq_post_enq(struct sc struct rq *rq = container_of(dsq, struct rq, scx.local_dsq);bool preempt = false; +/* +* If @rq is in balance, the CPU is already vacant and looking for the +* next task to run. No need to preempt or trigger resched after moving +* @p into its local DSQ. +*/ +if (rq->scx.flags &SCX_RQ_IN_BALANCE ) +return ; +if ((enq_flags &SCX_ENQ_PREEMPT ) && p != rq->curr && rq->curr->sched_class == &ext_sched_class) { rq->curr->scx.slice =0 ;
@@ -2259,6 +2267,8 @@ static void move_local_task_to_local_dsqdsq_mod_nr (dst_dsq,1 ); p->scx.dsq = dst_dsq; + +local_dsq_post_enq (dst_dsq, p, enq_flags); }#ifdef CONFIG_SMP
Greg Kroah-Hartman
maintainerPATCH 508/567 drm/displayid: add quirk to ignore DisplayID checksum errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit 83cbb4d33dc22b0ca1a4e85c6e892c9b729e28d4 ]
Add a mechanism for DisplayID specific quirks, and add the first quirk to ignore DisplayID section checksum errors.
It would be quite inconvenient to pass existing EDID quirks from drm_edid.c for DisplayID parsing. Not all places doing DisplayID iteration have the quirks readily available, and would have to pass it in all places. Simply add a separate array of DisplayID specific EDID quirks. We do end up checking it every time we iterate DisplayID blocks, but hopefully the number of quirks remains small.
There are a few laptop models with DisplayID checksum failures, leading to higher refresh rates only present in the DisplayID blocks being ignored. Add a quirk for the panel in the machines.
--- drivers/gpu/drm/drm_displayid.c | 41 +++++++++++++++++++++++++++---- drivers/gpu/drm/drm_displayid_internal.h | 2 + 2 files changed, 39 insertions(+), 4 deletions(-)
drivers/gpu/drm/drm_displayid.c +37 -4
@@ -9,6 +9,34 @@#include "drm_crtc_internal.h" #include "drm_displayid_internal.h" +enum { +QUIRK_IGNORE_CHECKSUM , +}; + +struct displayid_quirk { +const struct drm_edid_ident ident; +u8 quirks; +}; + +static const struct displayid_quirk quirks[] = { + { + .ident =DRM_EDID_IDENT_INIT ('C' ,'S' ,'O' ,5142 ,"MNE007ZA1-5" ), + .quirks =BIT (QUIRK_IGNORE_CHECKSUM ), + }, +}; + +static u8 get_quirks (const struct drm_edid *drm_edid) +{ +int i; + +for (i =0 ; i <ARRAY_SIZE (quirks); i++) { +if (drm_edid_match (drm_edid, &quirks[i].ident)) +return quirks[i].quirks; + } + +return 0 ; +} +static const struct displayid_header *displayid_get_header (const u8 *displayid,int length,int index) {
@@ -23,7 +51,7 @@ displayid_get_header(const u8 *displayid }static const struct displayid_header * -validate_displayid (const u8 *displayid,int length,int idx) +validate_displayid (const u8 *displayid,int length,int idx,bool ignore_checksum) {int i, dispid_length;u8 csum =0 ;
@@ -41,8 +69,11 @@ validate_displayid(const u8 *displayid,for (i =0 ; i < dispid_length; i++) csum += displayid[idx + i];if (csum) { -DRM_NOTE ("DisplayID checksum invalid, remainder is %d\n" , csum); -return ERR_PTR (-EINVAL ); +DRM_NOTE ("DisplayID checksum invalid, remainder is %d%s\n" , csum, + ignore_checksum ?" (ignoring)" :"" ); + +if (!ignore_checksum) +return ERR_PTR (-EINVAL ); }return base;
@@ -52,6 +83,7 @@ static const u8 *find_next_displayid_ext {const struct displayid_header *base;const u8 *displayid; +bool ignore_checksum = iter->quirks &BIT (QUIRK_IGNORE_CHECKSUM ); displayid =drm_edid_find_extension (iter->drm_edid,DISPLAYID_EXT , &iter->ext_index);if (!displayid)
@@ -61,7 +93,7 @@ static const u8 *find_next_displayid_ext iter->length =EDID_LENGTH -1 ; iter->idx =1 ; - base =validate_displayid (displayid, iter->length, iter->idx); + base =validate_displayid (displayid, iter->length, iter->idx, ignore_checksum);if (IS_ERR (base))return NULL ;
@@ -76,6 +108,7 @@ void displayid_iter_edid_begin(const strmemset (iter,0 ,sizeof (*iter)); iter->drm_edid = drm_edid; + iter->quirks =get_quirks (drm_edid); } static const struct displayid_block *
drivers/gpu/drm/drm_displayid_internal.h +2 -0
@@ -154,6 +154,8 @@ struct displayid_iter {u8 version;u8 primary_use; + +u8 quirks; }; void displayid_iter_edid_begin(const struct drm_edid *drm_edid,
Greg Kroah-Hartman
maintainerPATCH 509/567 hrtimers: Introduce hrtimer_update_function()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nam Cao <[email protected]>
[ Upstream commit 8f02e3563bb5824eb01c94f2c75f1dcee2d05625 ]
Some users of hrtimer need to change the callback function after the initial setup. They write to hrtimer::function directly.
That's not safe under all circumstances as the write is lockless and a concurrent timer expiry might end up using the wrong function pointer.
Introduce hrtimer_update_function(), which also performs runtime checks whether it is safe to modify the callback.
This allows to make hrtimer::function private once all users are converted.
Stable-dep-of: 267ee93c417e ("serial: xilinx_uartps: fix rs485 delay_rts_after_send")
--- include/linux/hrtimer.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
include/linux/hrtimer.h +22 -0
@@ -337,6 +337,28 @@ static inline int hrtimer_callback_runnireturn timer->base->running == timer; } +/** +* hrtimer_update_function - Update the timer's callback function +* @timer: Timer to update +* @function: New callback function +* +* Only safe to call if the timer is not enqueued. Can be called in the callback function if the +* timer is not enqueued at the same time (see the comments above HRTIMER_STATE_ENQUEUED). +*/ +static inline void hrtimer_update_function (struct hrtimer *timer, +enum hrtimer_restart (*function)(struct hrtimer *)) +{ +guard (raw_spinlock_irqsave)(&timer->base->cpu_base->lock); + +if (WARN_ON_ONCE (hrtimer_is_queued (timer))) +return ; + +if (WARN_ON_ONCE (!function)) +return ; + + timer->function = function; +} +/* Forward a hrtimer so it expires after now: */ extern u64 hrtimer_forward (struct hrtimer *timer,ktime_t now,ktime_t interval);
Greg Kroah-Hartman
maintainerPATCH 510/567 serial: xilinx_uartps: Use helper function hrtimer_update_function()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nam Cao <[email protected]>
[ Upstream commit eee00df8e1f1f5648ed8f9e40e2bb54c2877344a ]
The field 'function' of struct hrtimer should not be changed directly, as the write is lockless and a concurrent timer expiry might end up using the wrong function pointer.
Switch to use hrtimer_update_function() which also performs runtime checks that it is safe to modify the callback.
Stable-dep-of: 267ee93c417e ("serial: xilinx_uartps: fix rs485 delay_rts_after_send")
--- drivers/tty/serial/xilinx_uartps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/tty/serial/xilinx_uartps.c +2 -2
@@ -454,7 +454,7 @@ static void cdns_uart_handle_tx(void *deif (cdns_uart->port->rs485.flags &SER_RS485_ENABLED && (kfifo_is_empty (&tport->xmit_fifo) ||uart_tx_stopped (port))) { - cdns_uart->tx_timer.function = &cdns_rs485_rx_callback; +hrtimer_update_function (&cdns_uart->tx_timer, cdns_rs485_rx_callback);hrtimer_start (&cdns_uart->tx_timer,ns_to_ktime (cdns_calc_after_tx_delay (cdns_uart)),HRTIMER_MODE_REL ); }
@@ -734,7 +734,7 @@ static void cdns_uart_start_tx(struct uaif (cdns_uart->port->rs485.flags &SER_RS485_ENABLED ) {if (!cdns_uart->rs485_tx_started) { - cdns_uart->tx_timer.function = &cdns_rs485_tx_callback; +hrtimer_update_function (&cdns_uart->tx_timer, cdns_rs485_tx_callback);cdns_rs485_tx_setup (cdns_uart); return hrtimer_start(&cdns_uart->tx_timer, ms_to_ktime(port->rs485.delay_rts_before_send),
Greg Kroah-Hartman
maintainerPATCH 511/567 serial: xilinx_uartps: fix rs485 delay_rts_after_send
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: "j.turek" <[email protected]>
[ Upstream commit 267ee93c417e685d9f8e079e41c70ba6ee4df5a5 ]
RTS line control with delay should be triggered when there is no more bytes in kfifo and hardware buffer is empty. Without this patch RTS control is scheduled right after feeding hardware buffer and this is too early.
RTS line may change state before hardware buffer is empty.
With this patch delayed RTS state change is triggered when function cdns_uart_handle_tx is called from cdns_uart_isr on CDNS_UART_IXR_TXEMPTY exactly when hardware completed transmission
--- drivers/tty/serial/xilinx_uartps.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/tty/serial/xilinx_uartps.c +7 -7
@@ -431,10 +431,17 @@ static void cdns_uart_handle_tx(void *destruct tty_port *tport = &port->state->port;unsigned int numbytes;unsigned char ch; +ktime_t rts_delay;if (kfifo_is_empty (&tport->xmit_fifo) ||uart_tx_stopped (port)) {/* Disable the TX Empty interrupt */ writel (CDNS_UART_IXR_TXEMPTY , port->membase +CDNS_UART_IDR ); +/* Set RTS line after delay */ +if (cdns_uart->port->rs485.flags &SER_RS485_ENABLED ) { + cdns_uart->tx_timer.function = &cdns_rs485_rx_callback; + rts_delay =ns_to_ktime (cdns_calc_after_tx_delay (cdns_uart)); +hrtimer_start (&cdns_uart->tx_timer, rts_delay,HRTIMER_MODE_REL ); + }return ; }
@@ -451,13 +458,6 @@ static void cdns_uart_handle_tx(void *de/* Enable the TX Empty interrupt */ writel (CDNS_UART_IXR_TXEMPTY , cdns_uart->port->membase +CDNS_UART_IER ); - -if (cdns_uart->port->rs485.flags &SER_RS485_ENABLED && - (kfifo_is_empty (&tport->xmit_fifo) ||uart_tx_stopped (port))) { -hrtimer_update_function (&cdns_uart->tx_timer, cdns_rs485_rx_callback); -hrtimer_start (&cdns_uart->tx_timer, -ns_to_ktime (cdns_calc_after_tx_delay (cdns_uart)),HRTIMER_MODE_REL ); - } }/**
Greg Kroah-Hartman
maintainerPATCH 512/567 f2fs: clear SBI_POR_DOING before initing inmem curseg
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sheng Yong <[email protected]>
[ Upstream commit f88c7904b5c7e35ab8037e2a59e10d80adf6fd7e ]
SBI_POR_DOING can be cleared after recovery is completed, so that changes made before recovery can be persistent, and subsequent errors can be recorded into cp/sb.
Stable-dep-of: be112e7449a6 ("f2fs: fix to propagate error from f2fs_enable_checkpoint()")
--- fs/f2fs/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/f2fs/super.c +3 -3
@@ -4804,13 +4804,13 @@ reset_checkpoint:if (err) goto free_meta; +/* f2fs_recover_fsync_data() cleared this already */ +clear_sbi_flag (sbi,SBI_POR_DOING ); + err =f2fs_init_inmem_curseg (sbi);if (err) goto sync_free_meta; -/* f2fs_recover_fsync_data() cleared this already */ -clear_sbi_flag (sbi,SBI_POR_DOING ); -if (test_opt (sbi,DISABLE_CHECKPOINT )) { err =f2fs_disable_checkpoint (sbi);if (err)
Greg Kroah-Hartman
maintainerPATCH 513/567 f2fs: add timeout in f2fs_enable_checkpoint()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 4bc347779698b5e67e1514bab105c2c083e55502 ]
During f2fs_enable_checkpoint() in remount(), if we flush a large amount of dirty pages into slow device, it may take long time which will block write IO, let's add a timeout machanism during dirty pages flush to avoid long time block in f2fs_enable_checkpoint().
Stable-dep-of: be112e7449a6 ("f2fs: fix to propagate error from f2fs_enable_checkpoint()")
--- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/super.c | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-)
fs/f2fs/f2fs.h +2 -0
@@ -249,6 +249,7 @@ enum {#define DEF_CP_INTERVAL 60/* 60 secs */ #define DEF_IDLE_INTERVAL 5/* 5 secs */ #define DEF_DISABLE_INTERVAL 5/* 5 secs */ +#define DEF_ENABLE_INTERVAL 16/* 16 secs */ #define DEF_DISABLE_QUICK_INTERVAL 1/* 1 secs */ #define DEF_UMOUNT_DISCARD_TIMEOUT 5/* 5 secs */
@@ -1351,6 +1352,7 @@ enum {DISCARD_TIME ,GC_TIME ,DISABLE_TIME , +ENABLE_TIME ,UMOUNT_DISCARD_TIMEOUT , MAX_TIME, };
fs/f2fs/super.c +15 -6
@@ -2283,16 +2283,24 @@ restore_flag:static void f2fs_enable_checkpoint (struct f2fs_sb_info *sbi) { -int retry =DEFAULT_RETRY_IO_COUNT ; +unsigned int nr_pages =get_pages (sbi,F2FS_DIRTY_DATA ) /16 ; + +f2fs_update_time (sbi,ENABLE_TIME );/* we should flush all the data to keep data consistency */ -do { -sync_inodes_sb (sbi->sb); +while (get_pages (sbi,F2FS_DIRTY_DATA )) { +writeback_inodes_sb_nr (sbi->sb, nr_pages,WB_REASON_SYNC );f2fs_io_schedule_timeout (DEFAULT_IO_TIMEOUT ); - }while (get_pages (sbi,F2FS_DIRTY_DATA ) && retry--); -if (unlikely (retry <0 )) -f2fs_warn (sbi,"checkpoint=enable has some unwritten data." ); +if (f2fs_time_over (sbi,ENABLE_TIME )) +break ; + } + +sync_inodes_sb (sbi->sb); + +if (unlikely (get_pages (sbi,F2FS_DIRTY_DATA ))) +f2fs_warn (sbi,"checkpoint=enable has some unwritten data: %lld" , +get_pages (sbi,F2FS_DIRTY_DATA ));f2fs_down_write (&sbi->gc_lock);f2fs_dirty_to_prefree (sbi);
@@ -3868,6 +3876,7 @@ static void init_sb_info(struct f2fs_sb_ sbi->interval_time[DISCARD_TIME ] =DEF_IDLE_INTERVAL ; sbi->interval_time[GC_TIME ] =DEF_IDLE_INTERVAL ; sbi->interval_time[DISABLE_TIME ] =DEF_DISABLE_INTERVAL ; + sbi->interval_time[ENABLE_TIME ] =DEF_ENABLE_INTERVAL ; sbi->interval_time[UMOUNT_DISCARD_TIMEOUT ] =DEF_UMOUNT_DISCARD_TIMEOUT ;clear_sbi_flag (sbi,SBI_NEED_FSCK );
Greg Kroah-Hartman
maintainerPATCH 514/567 f2fs: dump more information for f2fs_{enable,disable}_checkpoint()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit 80b6d1d2535a343e43d658777a46f1ebce8f3413 ]
Changes as below:
- print more logs for f2fs_{enable,disable}_checkpoint()
- account and dump time stats for f2fs_enable_checkpoint()
Stable-dep-of: be112e7449a6 ("f2fs: fix to propagate error from f2fs_enable_checkpoint()")
--- fs/f2fs/super.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
fs/f2fs/super.c +16 -0
@@ -2278,15 +2278,24 @@ out_unlock: restore_flag: sbi->gc_mode = gc_mode; sbi->sb->s_flags = s_flags;/* Restore SB_RDONLY status */ +f2fs_info (sbi,"f2fs_disable_checkpoint() finish, err:%d" , err);return err; }static void f2fs_enable_checkpoint (struct f2fs_sb_info *sbi) {unsigned int nr_pages =get_pages (sbi,F2FS_DIRTY_DATA ) /16 ; +long long start, writeback, end; + +f2fs_info (sbi,"f2fs_enable_checkpoint() starts, meta: %lld, node: %lld, data: %lld" , +get_pages (sbi,F2FS_DIRTY_META ), +get_pages (sbi,F2FS_DIRTY_NODES ), +get_pages (sbi,F2FS_DIRTY_DATA ));f2fs_update_time (sbi,ENABLE_TIME ); + start =ktime_get (); +/* we should flush all the data to keep data consistency */ while (get_pages (sbi,F2FS_DIRTY_DATA )) {writeback_inodes_sb_nr (sbi->sb, nr_pages,WB_REASON_SYNC );
@@ -2295,6 +2304,7 @@ static void f2fs_enable_checkpoint(strucif (f2fs_time_over (sbi,ENABLE_TIME ))break ; } + writeback =ktime_get ();sync_inodes_sb (sbi->sb);
@@ -2313,6 +2323,12 @@ static void f2fs_enable_checkpoint(struc/* Let's ensure there's no pending checkpoint anymore */ f2fs_flush_ckpt_thread (sbi); + + end =ktime_get (); + +f2fs_info (sbi,"f2fs_enable_checkpoint() finishes, writeback:%llu, sync:%llu" , +ktime_ms_delta (writeback, start), +ktime_ms_delta (end, writeback)); }static int f2fs_remount (struct super_block *sb,int *flags,char *data)
Greg Kroah-Hartman
maintainerPATCH 515/567 f2fs: fix to propagate error from f2fs_enable_checkpoint()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Yu <[email protected]>
[ Upstream commit be112e7449a6e1b54aa9feac618825d154b3a5c7 ]
In order to let userspace detect such error rather than suffering silent failure.
--- fs/f2fs/super.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-)
fs/f2fs/super.c +16 -10
@@ -2282,10 +2282,11 @@ restore_flag:return err; } -static void f2fs_enable_checkpoint (struct f2fs_sb_info *sbi) +static int f2fs_enable_checkpoint (struct f2fs_sb_info *sbi) {unsigned int nr_pages =get_pages (sbi,F2FS_DIRTY_DATA ) /16 ;long long start, writeback, end; +int ret; f2fs_info(sbi, "f2fs_enable_checkpoint() starts, meta: %lld, node: %lld, data: %lld", get_pages(sbi, F2FS_DIRTY_META),
@@ -2319,7 +2320,9 @@ static void f2fs_enable_checkpoint(strucset_sbi_flag (sbi,SBI_IS_DIRTY );f2fs_up_write (&sbi->gc_lock); -f2fs_sync_fs (sbi->sb,1 ); + ret =f2fs_sync_fs (sbi->sb,1 ); +if (ret) +f2fs_err (sbi,"%s sync_fs failed, ret: %d" , __func__, ret);/* Let's ensure there's no pending checkpoint anymore */ f2fs_flush_ckpt_thread (sbi);
@@ -2329,6 +2332,7 @@ static void f2fs_enable_checkpoint(strucf2fs_info (sbi,"f2fs_enable_checkpoint() finishes, writeback:%llu, sync:%llu" ,ktime_ms_delta (writeback, start),ktime_ms_delta (end, writeback)); +return ret; }static int f2fs_remount (struct super_block *sb,int *flags,char *data)
@@ -2543,7 +2547,9 @@ static int f2fs_remount(struct super_blo goto restore_discard; need_enable_checkpoint = true; }else { -f2fs_enable_checkpoint (sbi); + err =f2fs_enable_checkpoint (sbi); +if (err) + goto restore_discard; need_disable_checkpoint = true; } }
@@ -2585,7 +2591,8 @@ skip:return 0 ; restore_checkpoint:if (need_enable_checkpoint) { -f2fs_enable_checkpoint (sbi); +if (f2fs_enable_checkpoint (sbi)) +f2fs_warn (sbi,"checkpoint has not been enabled" ); }else if (need_disable_checkpoint) {if (f2fs_disable_checkpoint (sbi))f2fs_warn (sbi,"checkpoint has not been disabled" );
@@ -4836,13 +4843,12 @@ reset_checkpoint:if (err) goto sync_free_meta; -if (test_opt (sbi,DISABLE_CHECKPOINT )) { +if (test_opt (sbi,DISABLE_CHECKPOINT )) err =f2fs_disable_checkpoint (sbi); -if (err) - goto sync_free_meta; - }else if (is_set_ckpt_flags (sbi,CP_DISABLED_FLAG )) { -f2fs_enable_checkpoint (sbi); - } +else if (is_set_ckpt_flags (sbi,CP_DISABLED_FLAG )) + err =f2fs_enable_checkpoint (sbi); +if (err) + goto sync_free_meta;/* * If filesystem is not mounted as read-only then
Greg Kroah-Hartman
maintainerPATCH 516/567 gpiolib: acpi: Switch to use enum in acpi_gpio_in_ignore_list()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit b24fd5bc8e6d6b6006db65b5956c2c2cd0ee5a7b ]
Switch to use enum instead of pointers in acpi_gpio_in_ignore_list() which moves towards isolating the GPIO ACPI and quirk APIs. It will helps splitting them completely in the next changes.
No functional changes.
Stable-dep-of: 2d967310c49e ("gpiolib: acpi: Add quirk for Dell Precision 7780")
--- drivers/gpio/gpiolib-acpi.c | 21 ++++++++++++++++----- drivers/gpio/gpiolib-acpi.h | 8 ++++++++ 2 files changed, 24 insertions(+), 5 deletions(-)
drivers/gpio/gpiolib-acpi.c +16 -5
@@ -350,14 +350,25 @@ static struct gpio_desc *acpi_request_owreturn desc; } -static bool acpi_gpio_in_ignore_list (const char *ignore_list,const char *controller_in, -unsigned int pin_in) +bool acpi_gpio_in_ignore_list (enum acpi_gpio_ignore_list list,const char *controller_in, +unsigned int pin_in) { -const char *controller, *pin_str; +const char *ignore_list, *controller, *pin_str;unsigned int pin;char *endp;int len; +switch (list) { +case ACPI_GPIO_IGNORE_WAKE : + ignore_list = ignore_wake; +break ; +case ACPI_GPIO_IGNORE_INTERRUPT : + ignore_list = ignore_interrupt; +break ; +default : +return false; + } + controller = ignore_list;while (controller) { pin_str =strchr (controller,'@' );
@@ -394,7 +405,7 @@ static bool acpi_gpio_irq_is_wake(structif (agpio->wake_capable !=ACPI_WAKE_CAPABLE )return false; -if (acpi_gpio_in_ignore_list (ignore_wake,dev_name (parent), pin)) { +if (acpi_gpio_in_ignore_list (ACPI_GPIO_IGNORE_WAKE ,dev_name (parent), pin)) {dev_info (parent,"Ignoring wakeup on pin %u\n" , pin);return false; }
@@ -437,7 +448,7 @@ static acpi_status acpi_gpiochip_alloc_eif (!handler)return AE_OK ; -if (acpi_gpio_in_ignore_list (ignore_interrupt,dev_name (chip->parent), pin)) { +if (acpi_gpio_in_ignore_list (ACPI_GPIO_IGNORE_INTERRUPT ,dev_name (chip->parent), pin)) {dev_info (chip->parent,"Ignoring interrupt on pin %u\n" , pin);return AE_OK ; }
drivers/gpio/gpiolib-acpi.h +8 -0
@@ -58,4 +58,12 @@ static inline int acpi_gpio_count(const }#endif +enum acpi_gpio_ignore_list { +ACPI_GPIO_IGNORE_WAKE , +ACPI_GPIO_IGNORE_INTERRUPT , +}; + +bool acpi_gpio_in_ignore_list (enum acpi_gpio_ignore_list list, +const char *controller_in,unsigned int pin_in); +#endif /* GPIOLIB_ACPI_H */
Greg Kroah-Hartman
maintainerPATCH 517/567 gpiolib: acpi: Handle deferred list via new API
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit a594877663d1e3d5cf57ec8af739582fc5c47cec ]
Introduce a new API and handle deferred list via it which moves towards isolating the GPIO ACPI and quirk APIs. It will helps splitting them completely in the next changes.
No functional changes.
Stable-dep-of: 2d967310c49e ("gpiolib: acpi: Add quirk for Dell Precision 7780")
--- drivers/gpio/gpiolib-acpi.c | 52 +++++++++++++++++++++++++++----------------- drivers/gpio/gpiolib-acpi.h | 5 ++++ 2 files changed, 37 insertions(+), 20 deletions(-)
drivers/gpio/gpiolib-acpi.c +32 -20
@@ -350,6 +350,27 @@ static struct gpio_desc *acpi_request_owreturn desc; } +bool acpi_gpio_add_to_deferred_list (struct list_head *list) +{ +bool defer; + +mutex_lock (&acpi_gpio_deferred_req_irqs_lock); + defer = !acpi_gpio_deferred_req_irqs_done; +if (defer) +list_add (list, &acpi_gpio_deferred_req_irqs_list); +mutex_unlock (&acpi_gpio_deferred_req_irqs_lock); + +return defer; +} + +void acpi_gpio_remove_from_deferred_list (struct list_head *list) +{ +mutex_lock (&acpi_gpio_deferred_req_irqs_lock); +if (!list_empty (list)) +list_del_init (list); +mutex_unlock (&acpi_gpio_deferred_req_irqs_lock); +} +bool acpi_gpio_in_ignore_list (enum acpi_gpio_ignore_list list,const char *controller_in,unsigned int pin_in) {
@@ -536,7 +557,6 @@ void acpi_gpiochip_request_interrupts(ststruct acpi_gpio_chip *acpi_gpio;acpi_handle handle;acpi_status status; -bool defer;if (!chip->parent || !chip->to_irq)return ;
@@ -555,14 +575,7 @@ void acpi_gpiochip_request_interrupts(stacpi_walk_resources (handle,METHOD_NAME__AEI , acpi_gpiochip_alloc_event, acpi_gpio); -mutex_lock (&acpi_gpio_deferred_req_irqs_lock); - defer = !acpi_gpio_deferred_req_irqs_done; -if (defer) -list_add (&acpi_gpio->deferred_req_irqs_list_entry, - &acpi_gpio_deferred_req_irqs_list); -mutex_unlock (&acpi_gpio_deferred_req_irqs_lock); - -if (defer) +if (acpi_gpio_add_to_deferred_list (&acpi_gpio->deferred_req_irqs_list_entry))return ;acpi_gpiochip_request_irqs (acpi_gpio);
@@ -594,10 +607,7 @@ void acpi_gpiochip_free_interrupts(strucif (ACPI_FAILURE (status))return ; -mutex_lock (&acpi_gpio_deferred_req_irqs_lock); -if (!list_empty (&acpi_gpio->deferred_req_irqs_list_entry)) -list_del_init (&acpi_gpio->deferred_req_irqs_list_entry); -mutex_unlock (&acpi_gpio_deferred_req_irqs_lock); +acpi_gpio_remove_from_deferred_list (&acpi_gpio->deferred_req_irqs_list_entry);list_for_each_entry_safe_reverse (event, ep, &acpi_gpio->events, node) {if (event->irq_requested) {
@@ -615,6 +625,14 @@ void acpi_gpiochip_free_interrupts(struc }EXPORT_SYMBOL_GPL (acpi_gpiochip_free_interrupts); +void __init acpi_gpio_process_deferred_list (struct list_head *list) +{ +struct acpi_gpio_chip *acpi_gpio, *tmp; + +list_for_each_entry_safe (acpi_gpio, tmp, list, deferred_req_irqs_list_entry) +acpi_gpiochip_request_irqs (acpi_gpio); +} +int acpi_dev_add_driver_gpios (struct acpi_device *adev,const struct acpi_gpio_mapping *gpios) {
@@ -1505,14 +1523,8 @@ int acpi_gpio_count(const struct fwnode_/* Run deferred acpi_gpiochip_request_irqs() */ static int __init acpi_gpio_handle_deferred_request_irqs (void ) { -struct acpi_gpio_chip *acpi_gpio, *tmp; -mutex_lock (&acpi_gpio_deferred_req_irqs_lock); -list_for_each_entry_safe (acpi_gpio, tmp, - &acpi_gpio_deferred_req_irqs_list, - deferred_req_irqs_list_entry) -acpi_gpiochip_request_irqs (acpi_gpio); - +acpi_gpio_process_deferred_list (&acpi_gpio_deferred_req_irqs_list); acpi_gpio_deferred_req_irqs_done = true;mutex_unlock (&acpi_gpio_deferred_req_irqs_lock);
drivers/gpio/gpiolib-acpi.h +5 -0
@@ -58,6 +58,11 @@ static inline int acpi_gpio_count(const }#endif +void acpi_gpio_process_deferred_list (struct list_head *list); + +bool acpi_gpio_add_to_deferred_list (struct list_head *list); +void acpi_gpio_remove_from_deferred_list (struct list_head *list); +enum acpi_gpio_ignore_list {ACPI_GPIO_IGNORE_WAKE ,ACPI_GPIO_IGNORE_INTERRUPT ,
Greg Kroah-Hartman
maintainerPATCH 518/567 gpiolib: acpi: Add acpi_gpio_need_run_edge_events_on_boot() getter
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 5666a8777add09d1167de308df2147983486a0af ]
Add acpi_gpio_need_run_edge_events_on_boot() getter which moves towards isolating the GPIO ACPI and quirk APIs. It will helps splitting them completely in the next changes.
No functional changes.
Stable-dep-of: 2d967310c49e ("gpiolib: acpi: Add quirk for Dell Precision 7780")
--- drivers/gpio/gpiolib-acpi.c | 7 ++++++- drivers/gpio/gpiolib-acpi.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-)
drivers/gpio/gpiolib-acpi.c +6 -1
@@ -268,7 +268,7 @@ static void acpi_gpiochip_request_irq(st event->irq_requested = true;/* Make sure we trigger the initial state of edge-triggered IRQs */ -if (run_edge_events_on_boot && +if (acpi_gpio_need_run_edge_events_on_boot () && (event->irqflags & (IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING ))) { value =gpiod_get_raw_value_cansleep (event->desc); if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) ||
@@ -371,6 +371,11 @@ void acpi_gpio_remove_from_deferred_listmutex_unlock (&acpi_gpio_deferred_req_irqs_lock); } +int acpi_gpio_need_run_edge_events_on_boot (void ) +{ +return run_edge_events_on_boot; +} +bool acpi_gpio_in_ignore_list (enum acpi_gpio_ignore_list list,const char *controller_in,unsigned int pin_in) {
drivers/gpio/gpiolib-acpi.h +2 -0
@@ -63,6 +63,8 @@ void acpi_gpio_process_deferred_list(strbool acpi_gpio_add_to_deferred_list (struct list_head *list);void acpi_gpio_remove_from_deferred_list (struct list_head *list); +int acpi_gpio_need_run_edge_events_on_boot (void ); +enum acpi_gpio_ignore_list {ACPI_GPIO_IGNORE_WAKE ,ACPI_GPIO_IGNORE_INTERRUPT ,
Greg Kroah-Hartman
maintainerPATCH 519/567 gpiolib: acpi: Move quirks to a separate file
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 92dc572852ddcae687590cb159189004d58e382e ]
The gpiolib-acpi.c is huge enough even without DMI quirks. Move them to a separate file for a better maintenance.
No functional change intended.
Stable-dep-of: 2d967310c49e ("gpiolib: acpi: Add quirk for Dell Precision 7780")
---
drivers/gpio/Makefile | 1
drivers/gpio/gpiolib-acpi-core.c | 1419 +++++++++++++++++++++++++++++
drivers/gpio/gpiolib-acpi-quirks.c | 363 +++++++
drivers/gpio/gpiolib-acpi.c | 1765 -------------------------------------
4 files changed, 1783 insertions(+), 1765 deletions(-)
rename drivers/gpio/{gpiolib-acpi.c => gpiolib-acpi-core.c} (79%)
create mode 100644 drivers/gpio/gpiolib-acpi-quirks.cdrivers/gpio/Makefile +1 -0
@@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o +gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o # Device drivers. Generally keep list sorted alphabetically
drivers/gpio/gpiolib-acpi-core.c new file +1419 -0
1,419 lines not on this page · raw message
drivers/gpio/gpiolib-acpi-quirks.c new file +363 -0
363 lines not on this page · raw message
drivers/gpio/gpiolib-acpi.c deleted +0 -1765
1,211 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
Re: [PATCH 6.12 519/567] gpiolib: acpi: Move quirks to a separate file
Quoted 4 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
Quoted 8 lines
From: Andy Shevchenko <[email protected]>
[ Upstream commit
92dc572852ddcae687590cb159189004d58e382e]The
gpiolib-acpi.cis huge enough even without DMI quirks. Move them to a separate file for a better maintenance.No functional change intended.
[...]
However, this did cause some documentation breakage. Please cherry-pick commit ec0c0aab1524 ("gpiolib-acpi: Update file references in the Documentation and MAINTAINERS") to fix that.
Ben.
Signature · 2 lines
Ben Hutchings It's easier to fight for one's principles than to live up to them.
Greg Kroah-Hartman
maintainerRe: [PATCH 6.12 519/567] gpiolib: acpi: Move quirks to a separate file
Quoted 12 lines · Ben Hutchings
Quoted 11 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit
92dc572852ddcae687590cb159189004d58e382e]The
gpiolib-acpi.cis huge enough even without DMI quirks. Move them to a separate file for a better maintenance.
Quoted 6 lines
Quoted 1 line
No functional change intended.
[...]
However, this did cause some documentation breakage. Please cherry-pick commit
ec0c0aab1524("gpiolib-acpi: Update file references in the Documentation andMAINTAINERS") to fix that.
Thanks, the Fixes: tag in that commit is invalid, which is why I didn't see it to pick this up, nice catch!
greg k-h
Greg Kroah-Hartman
maintainerPATCH 520/567 gpiolib: acpi: Add a quirk for Acer Nitro V15
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
[ Upstream commit 9ab29ed505557bd106e292184fa4917955eb8e6e ]
It is reported that on Acer Nitro V15 suspend only works properly if the keyboard backlight is turned off. In looking through the issue Acer Nitro V15 has a GPIO (#8) specified in _AEI but it has no matching notify device in _EVT. The values for GPIO #8 change as keyboard backlight is turned on and off.
This makes it seem that GPIO #8 is actually supposed to be solely for keyboard backlight. Turning off the interrupt for this GPIO fixes the issue. Add a quirk that does just that.
Stable-dep-of: 2d967310c49e ("gpiolib: acpi: Add quirk for Dell Precision 7780")
--- drivers/gpio/gpiolib-acpi-quirks.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/gpio/gpiolib-acpi-quirks.c +13 -0
@@ -331,6 +331,19 @@ static const struct dmi_system_id gpioli .ignore_interrupt = "AMDI0030:00@11", }, }, + { + /* + * Wakeup only works when keyboard backlight is turned off + * https://gitlab.freedesktop.org/drm/amd/-/issues/4169 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 15"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "AMDI0030:00@8", + }, + }, {} /* Terminating entry */ };
Greg Kroah-Hartman
maintainerPATCH 521/567 gpiolib: acpi: Add quirk for ASUS ProArt PX13
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Mario Limonciello (AMD)" <[email protected]>
[ Upstream commit 23800ad1265f10c2bc6f42154ce4d20e59f2900e ]
The ASUS ProArt PX13 has a spurious wakeup event from the touchpad a few moments after entering hardware sleep. This can be avoided by preventing the touchpad from being a wake source.
Add to the wakeup ignore list.
Stable-dep-of: 2d967310c49e ("gpiolib: acpi: Add quirk for Dell Precision 7780")
--- drivers/gpio/gpiolib-acpi-quirks.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
drivers/gpio/gpiolib-acpi-quirks.c +14 -0
@@ -344,6 +344,20 @@ static const struct dmi_system_id gpioli .ignore_interrupt = "AMDI0030:00@8", }, }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 5.35 + * https://gitlab.freedesktop.org/drm/amd/-/issues/4482 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt PX13"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "ASCP1A00:00@8", + }, + }, {} /* Terminating entry */ };
Greg Kroah-Hartman
maintainerPATCH 522/567 gpiolib: acpi: Add quirk for Dell Precision 7780
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Askar Safin <[email protected]>
[ Upstream commit 2d967310c49ed93ac11cef408a55ddf15c3dd52e ]
Dell Precision 7780 often wakes up on its own from suspend. Sometimes wake up happens immediately (i. e. within 7 seconds), sometimes it happens after, say, 30 minutes.
--- drivers/gpio/gpiolib-acpi-quirks.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
drivers/gpio/gpiolib-acpi-quirks.c +22 -0
@@ -358,6 +358,28 @@ static const struct dmi_system_id gpioli .ignore_wake = "ASCP1A00:00@8", }, }, + { + /* + * Spurious wakeups, likely from touchpad controller + * Dell Precision 7780 + * Found in BIOS 1.24.1 + * + * Found in touchpad firmware, installed by Dell Touchpad Firmware Update Utility version 1160.4196.9, A01 + * ( Dell-Touchpad-Firmware-Update-Utility_VYGNN_WIN64_1160.4196.9_A00.EXE ), + * released on 11 Jul 2024 + * + * https://lore.kernel.org/linux-i2c/[email protected]/ + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Precision"), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7780"), + DMI_MATCH(DMI_BOARD_NAME, "0C6JVW"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "VEN_0488:00@355", + }, + }, {} /* Terminating entry */ };
Greg Kroah-Hartman
maintainerPATCH 523/567 netfilter: nft_ct: add seqadj extension for natted connections
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrii Melnychenko <[email protected]>
[ Upstream commit 90918e3b6404c2a37837b8f11692471b4c512de2 ]
Sequence adjustment may be required for FTP traffic with PASV/EPSV modes. due to need to re-write packet payload (IP, port) on the ftp control connection. This can require changes to the TCP length and expected seq / ack_seq.
The easiest way to reproduce this issue is with PASV mode. Example ruleset:
table inet ftp_nat {
ct helper ftp_helper {
type "ftp" protocol tcp
l3proto inet
}
chain prerouting {
type filter hook prerouting priority 0; policy accept;
tcp dport 21 ct state new ct helper set "ftp_helper"
}
}
table ip nat {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
tcp dport 21 dnat ip prefix to ip daddr map {
192.168.100.1 : 192.168.13.2/32 }
}
chain postrouting {
type nat hook postrouting priority 100 ; policy accept;
tcp sport 21 snat ip prefix to ip saddr map {
192.168.13.2 : 192.168.100.1/32 }
}
}Note that the ftp helper gets assigned *after* the dnat setup.
The inverse (nat after helper assign) is handled by an existing check in nf_nat_setup_info() and will not show the problem.
Topoloy:
+-------------------+ +----------------------------------+
| FTP: 192.168.13.2 | <-> | NAT: 192.168.13.3, 192.168.100.1 |
+-------------------+ +----------------------------------+
|
+-----------------------+
| Client: 192.168.100.2 |
+-----------------------+ftp nat changes do not work as expected in this case:
Connected to 192.168.100.1.
[..]
ftp> epsv
EPSV/EPRT on IPv4 off.
ftp> ls
227 Entering passive mode (192,168,100,1,209,129). 421 Service not available, remote server has closed connection.
Kernel logs:
Missing nfct_seqadj_ext_add() setup call
WARNING: CPU: 1 PID: 0 at net/netfilter/nf_conntrack_seqadj.c:41 [..] __nf_nat_mangle_tcp_packet+0x100/0x160 [nf_nat] nf_nat_ftp+0x142/0x280 [nf_nat_ftp] help+0x4d1/0x880 [nf_conntrack_ftp] nf_confirm+0x122/0x2e0 [nf_conntrack] nf_hook_slow+0x3c/0xb0 ..
Fix this by adding the required extension when a conntrack helper is assigned to a connection that has a nat binding.
--- net/netfilter/nft_ct.c | 5 +++++ 1 file changed, 5 insertions(+)
net/netfilter/nft_ct.c +5 -0
@@ -22,6 +22,7 @@#include <net/netfilter/nf_conntrack_timeout.h> #include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_expect.h> +#include <net/netfilter/nf_conntrack_seqadj.h> struct nft_ct_helper_obj {struct nf_conntrack_helper *helper4;
@@ -1173,6 +1174,10 @@ static void nft_ct_helper_obj_eval(strucif (help) {rcu_assign_pointer (help->helper, to_assign);set_bit (IPS_HELPER_BIT , &ct->status); + +if ((ct->status &IPS_NAT_MASK ) && !nfct_seqadj (ct)) +if (!nfct_seqadj_ext_add (ct)) + regs->verdict.code =NF_DROP ; } }
Greg Kroah-Hartman
maintainerPATCH 524/567 md/raid10: wait barrier before returning discard request with REQ_NOWAIT
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiao Ni <[email protected]>
[ Upstream commit 3db4404435397a345431b45f57876a3df133f3b4 ]
raid10_handle_discard should wait barrier before returning a discard bio which has REQ_NOWAIT. And there is no need to print warning calltrace if a discard bio has REQ_NOWAIT flag. Quality engineer usually checks dmesg and reports error if dmesg has warning/error calltrace.
--- drivers/md/raid10.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/md/raid10.c +1 -2
@@ -1626,11 +1626,10 @@ static int raid10_handle_discard(structif (test_bit (MD_RECOVERY_RESHAPE , &mddev->recovery))return -EAGAIN ; -if (WARN_ON_ONCE (bio->bi_opf &REQ_NOWAIT )) { +if (!wait_barrier (conf, bio->bi_opf &REQ_NOWAIT )) {bio_wouldblock_error (bio);return 0 ; } -wait_barrier (conf, false);/* * Check reshape again to avoid reshape happens after checking
Greg Kroah-Hartman
maintainerPATCH 525/567 drm/panthor: Flush shmem writes before mapping buffers CPU-uncached
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Boris Brezillon <[email protected]>
[ Upstream commit 576c930e5e7dcb937648490611a83f1bf0171048 ]
The shmem layer zeroes out the new pages using cached mappings, and if we don't CPU-flush we might leave dirty cachelines behind, leading to potential data leaks and/or asynchronous buffer corruption when dirty cachelines are evicted.
[Harshit: Resolve conflicts due to missing commit: fe69a3918084 ("drm/panthor: Fix UAF in panthor_gem_create_with_handle() debugfs code") in 6.12.y]
--- drivers/gpu/drm/panthor/panthor_gem.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
drivers/gpu/drm/panthor/panthor_gem.c +18 -0
@@ -214,6 +214,23 @@ panthor_gem_create_with_handle(struct dr bo->base.base.resv = bo->exclusive_vm_root_gem->resv; } +/* If this is a write-combine mapping, we query the sgt to force a CPU +* cache flush (dma_map_sgtable() is called when the sgt is created). +* This ensures the zero-ing is visible to any uncached mapping created +* by vmap/mmap. +* FIXME: Ideally this should be done when pages are allocated, not at +* BO creation time. +*/ +if (shmem->map_wc) { +struct sg_table *sgt; + + sgt =drm_gem_shmem_get_pages_sgt (shmem); +if (IS_ERR (sgt)) { + ret =PTR_ERR (sgt); + goto out_put_gem; + } + } +/* * Allocate an id of idr table where the obj is registered * and handle has the id what user can see.
@@ -222,6 +239,7 @@ panthor_gem_create_with_handle(struct drif (!ret) *size = bo->base.base.size; +out_put_gem:/* drop reference from allocate - handle holds it now. */ drm_gem_object_put (&shmem->base);
Greg Kroah-Hartman
maintainerPATCH 526/567 net: ipv6: ioam6: use consistent dst names
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Iurman <[email protected]>
[ Upstream commit d55acb9732d981c7a8e07dd63089a77d2938e382 ]
Be consistent and use the same terminology as other lwt users: orig_dst is the dst_entry before the transformation, while dst is either the dst_entry in the cache or the dst_entry after the transformation
Stable-dep-of: 99a2ace61b21 ("net: use dst_dev_rcu() in sk_setup_caps()")
--- net/ipv6/ioam6_iptunnel.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-)
net/ipv6/ioam6_iptunnel.c +18 -17
@@ -338,7 +338,8 @@ static int ioam6_do_encap(struct net *nestatic int ioam6_output (struct net *net,struct sock *sk,struct sk_buff *skb) { -struct dst_entry *dst =skb_dst (skb), *cache_dst =NULL ; +struct dst_entry *orig_dst =skb_dst (skb); +struct dst_entry *dst =NULL ;struct ioam6_lwt *ilwt;int err = -EINVAL ;u32 pkt_cnt;
@@ -346,7 +347,7 @@ static int ioam6_output(struct net *net,if (skb->protocol !=htons (ETH_P_IPV6 )) goto drop; - ilwt =ioam6_lwt_state (dst->lwtstate); + ilwt =ioam6_lwt_state (orig_dst->lwtstate);/* Check for insertion frequency (i.e., "k over n" insertions) */ pkt_cnt =atomic_fetch_inc (&ilwt->pkt_cnt);
@@ -354,7 +355,7 @@ static int ioam6_output(struct net *net, goto out;local_bh_disable (); - cache_dst =dst_cache_get (&ilwt->cache); + dst =dst_cache_get (&ilwt->cache);local_bh_enable ();switch (ilwt->mode) {
@@ -364,7 +365,7 @@ do_inline:if (ipv6_hdr (skb)->nexthdr ==NEXTHDR_HOP ) goto out; - err =ioam6_do_inline (net, skb, &ilwt->tuninfo, cache_dst); + err =ioam6_do_inline (net, skb, &ilwt->tuninfo, dst);if (unlikely (err)) goto drop;
@@ -374,7 +375,7 @@ do_encap:/* Encapsulation (ip6ip6) */ err =ioam6_do_encap (net, skb, &ilwt->tuninfo, ilwt->has_tunsrc, &ilwt->tunsrc, - &ilwt->tundst, cache_dst); + &ilwt->tundst, dst);if (unlikely (err)) goto drop;
@@ -392,7 +393,7 @@ do_encap: goto drop; } -if (unlikely (!cache_dst)) { +if (unlikely (!dst)) {struct ipv6hdr *hdr =ipv6_hdr (skb);struct flowi6 fl6;
@@ -403,20 +404,20 @@ do_encap: fl6.flowi6_mark = skb->mark; fl6.flowi6_proto = hdr->nexthdr; - cache_dst =ip6_route_output (net,NULL , &fl6); -if (cache_dst->error) { - err = cache_dst->error; + dst =ip6_route_output (net,NULL , &fl6); +if (dst->error) { + err = dst->error; goto drop; }/* cache only if we don't create a dst reference loop */ -if (dst->lwtstate != cache_dst->lwtstate) { +if (orig_dst->lwtstate != dst->lwtstate) {local_bh_disable (); -dst_cache_set_ip6 (&ilwt->cache, cache_dst, &fl6.saddr); +dst_cache_set_ip6 (&ilwt->cache, dst, &fl6.saddr);local_bh_enable (); } - err =skb_cow_head (skb,LL_RESERVED_SPACE (cache_dst->dev)); + err =skb_cow_head (skb,LL_RESERVED_SPACE (dst->dev));if (unlikely (err)) goto drop; }
@@ -424,16 +425,16 @@ do_encap:/* avoid lwtunnel_output() reentry loop when destination is the same * after transformation (e.g., with the inline mode) */ -if (dst->lwtstate != cache_dst->lwtstate) { +if (orig_dst->lwtstate != dst->lwtstate) {skb_dst_drop (skb); -skb_dst_set (skb, cache_dst); +skb_dst_set (skb, dst);return dst_output (net, sk, skb); } out: -dst_release (cache_dst); -return dst->lwtstate->orig_output (net, sk, skb); +dst_release (dst); +return orig_dst->lwtstate->orig_output (net, sk, skb); drop: -dst_release (cache_dst); +dst_release (dst);kfree_skb (skb);return err; }
Greg Kroah-Hartman
maintainerPATCH 527/567 ipv6: adopt dst_dev() helper
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 1caf27297215a5241f9bfc9c07336349d9034ee3 ]
Use the new helper as a step to deal with potential dst->dev races.
Stable-dep-of: 99a2ace61b21 ("net: use dst_dev_rcu() in sk_setup_caps()")
--- include/net/ip6_route.h | 4 ++-- net/ipv6/exthdrs.c | 2 +- net/ipv6/icmp.c | 4 +++- net/ipv6/ila/ila_lwt.c | 2 +- net/ipv6/ioam6_iptunnel.c | 4 ++-- net/ipv6/ip6_gre.c | 8 +++++--- net/ipv6/ip6_output.c | 19 ++++++++++--------- net/ipv6/ip6_tunnel.c | 4 ++-- net/ipv6/ip6_udp_tunnel.c | 2 +- net/ipv6/ip6_vti.c | 2 +- net/ipv6/ndisc.c | 6 ++++-- net/ipv6/netfilter/nf_dup_ipv6.c | 2 +- net/ipv6/output_core.c | 2 +- net/ipv6/route.c | 20 ++++++++++++-------- net/ipv6/rpl_iptunnel.c | 4 ++-- net/ipv6/seg6_iptunnel.c | 20 +++++++++++--------- net/ipv6/seg6_local.c | 2 +- 17 files changed, 60 insertions(+), 47 deletions(-)
include/net/ip6_route.h +2 -2
@@ -274,7 +274,7 @@ static inline unsigned int ip6_skb_dst_munsigned int mtu;if (np &&READ_ONCE (np->pmtudisc) >=IPV6_PMTUDISC_PROBE ) { - mtu =READ_ONCE (dst->dev->mtu); + mtu =READ_ONCE (dst_dev (dst)->mtu); mtu -=lwtunnel_headroom (dst->lwtstate, mtu); }else { mtu =dst_mtu (dst);
@@ -337,7 +337,7 @@ static inline unsigned int ip6_dst_mtu_m mtu =IPV6_MIN_MTU ;rcu_read_lock (); - idev =__in6_dev_get (dst->dev); + idev =__in6_dev_get (dst_dev (dst));if (idev) mtu =READ_ONCE (idev->cnf.mtu6);rcu_read_unlock ();
net/ipv6/exthdrs.c +1 -1
@@ -306,7 +306,7 @@ static int ipv6_destopt_rcv(struct sk_buif (!pskb_may_pull (skb,skb_transport_offset (skb) +8 ) || !pskb_may_pull (skb, (skb_transport_offset (skb) + ((skb_transport_header (skb)[1 ] +1 ) <<3 )))) { -__IP6_INC_STATS (dev_net (dst->dev), idev, +__IP6_INC_STATS (dev_net (dst_dev (dst)), idev,IPSTATS_MIB_INHDRERRORS ); fail_and_free:kfree_skb (skb);
net/ipv6/icmp.c +3 -1
@@ -196,6 +196,7 @@ static bool icmpv6_xrlim_allow(struct so struct flowi6 *fl6, bool apply_ratelimit) {struct net *net =sock_net (sk); +struct net_device *dev;struct dst_entry *dst;bool res = false;
@@ -208,10 +209,11 @@ static bool icmpv6_xrlim_allow(struct so* this lookup should be more aggressive (not longer than timeout). */ dst =ip6_route_output (net, sk, fl6); + dev =dst_dev (dst);if (dst->error) {IP6_INC_STATS (net,ip6_dst_idev (dst),IPSTATS_MIB_OUTNOROUTES ); - }else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK )) { + }else if (dev && (dev->flags &IFF_LOOPBACK )) { res = true; }else {struct rt6_info *rt =dst_rt6_info (dst);
net/ipv6/ila/ila_lwt.c +1 -1
@@ -70,7 +70,7 @@ static int ila_output(struct net *net, s*/ memset (&fl6,0 ,sizeof (fl6)); - fl6.flowi6_oif = orig_dst->dev->ifindex; + fl6.flowi6_oif =dst_dev (orig_dst)->ifindex; fl6.flowi6_iif =LOOPBACK_IFINDEX ; fl6.daddr = *rt6_nexthop (dst_rt6_info (orig_dst), &ip6h->daddr);
net/ipv6/ioam6_iptunnel.c +2 -2
@@ -328,7 +328,7 @@ static int ioam6_do_encap(struct net *neif (has_tunsrc)memcpy (&hdr->saddr, tunsrc,sizeof (*tunsrc));else -ipv6_dev_get_saddr (net, dst->dev, &hdr->daddr, +ipv6_dev_get_saddr (net,dst_dev (dst), &hdr->daddr,IPV6_PREFER_SRC_PUBLIC , &hdr->saddr);skb_postpush_rcsum (skb, hdr, len);
@@ -417,7 +417,7 @@ do_encap:local_bh_enable (); } - err =skb_cow_head (skb,LL_RESERVED_SPACE (dst->dev)); + err =skb_cow_head (skb,LL_RESERVED_SPACE (dst_dev (dst)));if (unlikely (err)) goto drop; }
net/ipv6/ip6_gre.c +5 -3
@@ -1084,9 +1084,11 @@ static netdev_tx_t ip6erspan_tunnel_xmit htonl(atomic_fetch_inc(&t->o_seqno)));/* TooBig packet may have updated dst->dev's mtu */ -if (!t->parms.collect_md && dst &&dst_mtu (dst) > dst->dev->mtu) - dst->ops->update_pmtu (dst,NULL , skb, dst->dev->mtu, false); - +if (!t->parms.collect_md && dst) { + mtu =READ_ONCE (dst_dev (dst)->mtu); +if (dst_mtu (dst) > mtu) + dst->ops->update_pmtu (dst,NULL , skb, mtu, false); + } err =ip6_tnl_xmit (skb, dev, dsfield, &fl6, encap_limit, &mtu,NEXTHDR_GRE );if (err !=0 ) {
net/ipv6/ip6_output.c +10 -9
@@ -60,7 +60,7 @@static int ip6_finish_output2 (struct net *net,struct sock *sk,struct sk_buff *skb) {struct dst_entry *dst =skb_dst (skb); -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst);struct inet6_dev *idev =ip6_dst_idev (dst);unsigned int hh_len =LL_RESERVED_SPACE (dev);const struct in6_addr *daddr, *nexthop;
@@ -271,7 +271,7 @@ int ip6_xmit(const struct sock *sk, struconst struct ipv6_pinfo *np =inet6_sk (sk);struct in6_addr *first_hop = &fl6->daddr;struct dst_entry *dst =skb_dst (skb); -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst);struct inet6_dev *idev =ip6_dst_idev (dst);struct hop_jumbo_hdr *hop_jumbo;int hoplen =sizeof (*hop_jumbo);
@@ -503,7 +503,8 @@ int ip6_forward(struct sk_buff *skb)struct dst_entry *dst =skb_dst (skb);struct ipv6hdr *hdr =ipv6_hdr (skb);struct inet6_skb_parm *opt =IP6CB (skb); -struct net *net =dev_net (dst->dev); +struct net *net =dev_net (dst_dev (dst)); +struct net_device *dev;struct inet6_dev *idev;SKB_DR (reason);u32 mtu;
@@ -591,12 +592,12 @@ int ip6_forward(struct sk_buff *skb) goto drop; } dst =skb_dst (skb); - + dev =dst_dev (dst);/* IPv6 specs say nothing about it, but it is clear that we cannot send redirects to source routed frames. We don't send redirects to frames decapsulated from IPsec. */ -if (IP6CB (skb)->iif == dst->dev->ifindex && +if (IP6CB (skb)->iif == dev->ifindex && opt->srcrt ==0 && !skb_sec_path (skb)) {struct in6_addr *target =NULL ;struct inet_peer *peer;
@@ -644,7 +645,7 @@ int ip6_forward(struct sk_buff *skb)if (ip6_pkt_too_big (skb, mtu)) {/* Again, force OUTPUT device used as source address */ - skb->dev = dst->dev; + skb->dev = dev;icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu);__IP6_INC_STATS (net, idev,IPSTATS_MIB_INTOOBIGERRORS ); __IP6_INC_STATS(net, ip6_dst_idev(dst),
@@ -653,7 +654,7 @@ int ip6_forward(struct sk_buff *skb)return -EMSGSIZE ; } -if (skb_cow (skb, dst->dev->hard_header_len)) { +if (skb_cow (skb, dev->hard_header_len)) {__IP6_INC_STATS (net,ip6_dst_idev (dst),IPSTATS_MIB_OUTDISCARDS ); goto drop;
@@ -666,7 +667,7 @@ int ip6_forward(struct sk_buff *skb) hdr->hop_limit--;return NF_HOOK (NFPROTO_IPV6 ,NF_INET_FORWARD , - net,NULL , skb, skb->dev, dst->dev, + net,NULL , skb, skb->dev, dev, ip6_forward_finish); error:
@@ -1093,7 +1094,7 @@ static struct dst_entry *ip6_sk_dst_chec#ifdef CONFIG_IPV6_SUBTREES ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) || #endif - (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) { + (fl6->flowi6_oif && fl6->flowi6_oif != dst_dev(dst)->ifindex)) {dst_release (dst); dst =NULL ; }
net/ipv6/ip6_tunnel.c +2 -2
@@ -1179,7 +1179,7 @@ route_lookup: ndst = dst; } - tdev = dst->dev; + tdev =dst_dev (dst);if (tdev == dev) {DEV_STATS_INC (dev, collisions);
@@ -1255,7 +1255,7 @@ route_lookup:/* Calculate max headroom for all the headers and adjust * needed_headroom if necessary. */ - max_headroom =LL_RESERVED_SPACE (dst->dev) +sizeof (struct ipv6hdr ) + max_headroom =LL_RESERVED_SPACE (tdev) +sizeof (struct ipv6hdr ) + dst->header_len + t->hlen;ip_tunnel_adj_headroom (dev, max_headroom);
net/ipv6/ip6_udp_tunnel.c +1 -1
@@ -168,7 +168,7 @@ struct dst_entry *udp_tunnel6_dst_lookupnetdev_dbg (dev,"no route to %pI6\n" , &fl6.daddr);return ERR_PTR (-ENETUNREACH ); } -if (dst->dev == dev) {/* is this necessary? */ +if (dst_dev (dst) == dev) {/* is this necessary? */ netdev_dbg (dev,"circular route to %pI6\n" , &fl6.daddr);dst_release (dst);return ERR_PTR (-ELOOP );
net/ipv6/ip6_vti.c +1 -1
@@ -497,7 +497,7 @@ vti6_xmit(struct sk_buff *skb, struct ne (const struct in6_addr *)&x->id.daddr)) goto tx_err_link_failure; - tdev = dst->dev; + tdev =dst_dev (dst);if (tdev == dev) {DEV_STATS_INC (dev, collisions);
net/ipv6/ndisc.c +4 -2
@@ -473,6 +473,7 @@ void ndisc_send_skb(struct sk_buff *skb, {struct icmp6hdr *icmp6h =icmp6_hdr (skb);struct dst_entry *dst =skb_dst (skb); +struct net_device *dev;struct inet6_dev *idev;struct net *net;struct sock *sk;
@@ -507,11 +508,12 @@ void ndisc_send_skb(struct sk_buff *skb,ip6_nd_hdr (skb, saddr, daddr,READ_ONCE (inet6_sk (sk)->hop_limit), skb->len); - idev =__in6_dev_get (dst->dev); + dev =dst_dev (dst); + idev =__in6_dev_get (dev);IP6_INC_STATS (net, idev,IPSTATS_MIB_OUTREQUESTS ); err =NF_HOOK (NFPROTO_IPV6 ,NF_INET_LOCAL_OUT , - net, sk, skb,NULL , dst->dev, + net, sk, skb,NULL , dev, dst_output);if (!err) {ICMP6MSGOUT_INC_STATS (net, idev, type);
net/ipv6/netfilter/nf_dup_ipv6.c +1 -1
@@ -38,7 +38,7 @@ static bool nf_dup_ipv6_route(struct net }skb_dst_drop (skb);skb_dst_set (skb, dst); - skb->dev = dst->dev; + skb->dev =dst_dev (dst); skb->protocol =htons (ETH_P_IPV6 );return true;
net/ipv6/output_core.c +1 -1
@@ -105,7 +105,7 @@ int ip6_dst_hoplimit(struct dst_entry *d {int hoplimit =dst_metric_raw (dst,RTAX_HOPLIMIT );if (hoplimit ==0 ) { -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst);struct inet6_dev *idev;rcu_read_lock ();
net/ipv6/route.c +12 -8
@@ -228,13 +228,13 @@ static struct neighbour *ip6_dst_neigh_lconst struct rt6_info *rt =dst_rt6_info (dst);return ip6_neigh_lookup (rt6_nexthop (rt, &in6addr_any), - dst->dev, skb, daddr); +dst_dev (dst), skb, daddr); }static void ip6_confirm_neigh (const struct dst_entry *dst,const void *daddr) {const struct rt6_info *rt =dst_rt6_info (dst); -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst); daddr =choose_neigh_daddr (rt6_nexthop (rt, &in6addr_any),NULL , daddr);if (!daddr)
@@ -2945,7 +2945,7 @@ static void __ip6_rt_update_pmtu(structif (res.f6i->nh) {struct fib6_nh_match_arg arg = { - .dev = dst->dev, + .dev =dst_dev (dst), .gw = &rt6->rt6i_gateway, };
@@ -3240,7 +3240,7 @@ EXPORT_SYMBOL_GPL(ip6_sk_redirect);static unsigned int ip6_default_advmss (const struct dst_entry *dst) { -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst);unsigned int mtu =dst_mtu (dst);struct net *net;
@@ -4264,7 +4264,7 @@ static void rt6_do_redirect(struct dst_eif (res.f6i->nh) {struct fib6_nh_match_arg arg = { - .dev = dst->dev, + .dev =dst_dev (dst), .gw = &rt->rt6i_gateway, };
@@ -4551,13 +4551,14 @@ int ipv6_route_ioctl(struct net *net, unstatic int ip6_pkt_drop (struct sk_buff *skb,u8 code,int ipstats_mib_noroutes) {struct dst_entry *dst =skb_dst (skb); -struct net *net =dev_net (dst->dev); +struct net_device *dev =dst_dev (dst); +struct net *net =dev_net (dev);struct inet6_dev *idev;SKB_DR (reason);int type;if (netif_is_l3_master (skb->dev) || - dst->dev == net->loopback_dev) + dev == net->loopback_dev) idev =__in6_dev_get_safely (dev_get_by_index_rcu (net,IP6CB (skb)->iif));else idev =ip6_dst_idev (dst);
@@ -5775,11 +5776,14 @@ static int rt6_fill_node(struct net *net* each as a nexthop within RTA_MULTIPATH. */ if (rt6) { +struct net_device *dev; +if (rt6_flags &RTF_GATEWAY &&nla_put_in6_addr (skb,RTA_GATEWAY , &rt6->rt6i_gateway)) goto nla_put_failure; -if (dst->dev &&nla_put_u32 (skb,RTA_OIF , dst->dev->ifindex)) + dev =dst_dev (dst); +if (dev &&nla_put_u32 (skb,RTA_OIF , dev->ifindex)) goto nla_put_failure; if (dst->lwtstate &&
net/ipv6/rpl_iptunnel.c +2 -2
@@ -242,7 +242,7 @@ static int rpl_output(struct net *net, slocal_bh_enable (); } - err =skb_cow_head (skb,LL_RESERVED_SPACE (dst->dev)); + err =skb_cow_head (skb,LL_RESERVED_SPACE (dst_dev (dst)));if (unlikely (err)) goto drop; }
@@ -297,7 +297,7 @@ static int rpl_input(struct sk_buff *skblocal_bh_enable (); } - err =skb_cow_head (skb,LL_RESERVED_SPACE (dst->dev)); + err =skb_cow_head (skb,LL_RESERVED_SPACE (dst_dev (dst)));if (unlikely (err)) goto drop; }else {
net/ipv6/seg6_iptunnel.c +11 -9
@@ -128,7 +128,8 @@ static int __seg6_do_srh_encap(struct sk int proto, struct dst_entry *cache_dst) { struct dst_entry *dst = skb_dst(skb); -struct net *net =dev_net (dst->dev); +struct net_device *dev =dst_dev (dst); +struct net *net =dev_net (dev);struct ipv6hdr *hdr, *inner_hdr;struct ipv6_sr_hdr *isrh;int hdrlen, tot_len, err;
@@ -181,7 +182,7 @@ static int __seg6_do_srh_encap(struct sk isrh->nexthdr = proto; hdr->daddr = isrh->segments[isrh->first_segment]; -set_tun_src (net, dst->dev, &hdr->daddr, &hdr->saddr); +set_tun_src (net, dev, &hdr->daddr, &hdr->saddr);#ifdef CONFIG_IPV6_SEG6_HMAC if (sr_has_hmac (isrh)) {
@@ -212,7 +213,8 @@ static int seg6_do_srh_encap_red(struct {__u8 first_seg = osrh->first_segment;struct dst_entry *dst =skb_dst (skb); -struct net *net =dev_net (dst->dev); +struct net_device *dev =dst_dev (dst); +struct net *net =dev_net (dev);struct ipv6hdr *hdr, *inner_hdr;int hdrlen =ipv6_optlen (osrh);int red_tlv_offset, tlv_offset;
@@ -270,7 +272,7 @@ static int seg6_do_srh_encap_red(structif (skip_srh) { hdr->nexthdr = proto; -set_tun_src (net, dst->dev, &hdr->daddr, &hdr->saddr); +set_tun_src (net, dev, &hdr->daddr, &hdr->saddr); goto out; }
@@ -306,7 +308,7 @@ static int seg6_do_srh_encap_red(struct srcaddr: isrh->nexthdr = proto; -set_tun_src (net, dst->dev, &hdr->daddr, &hdr->saddr); +set_tun_src (net, dev, &hdr->daddr, &hdr->saddr);#ifdef CONFIG_IPV6_SEG6_HMAC if (unlikely (!skip_srh &&sr_has_hmac (isrh))) {
@@ -507,7 +509,7 @@ static int seg6_input_core(struct net *nlocal_bh_enable (); } - err =skb_cow_head (skb,LL_RESERVED_SPACE (dst->dev)); + err =skb_cow_head (skb,LL_RESERVED_SPACE (dst_dev (dst)));if (unlikely (err)) goto drop; }else {
@@ -518,7 +520,7 @@ static int seg6_input_core(struct net *nif (static_branch_unlikely (&nf_hooks_lwtunnel_enabled))return NF_HOOK (NFPROTO_IPV6 ,NF_INET_LOCAL_OUT ,dev_net (skb->dev),NULL , skb,NULL , -skb_dst (skb)->dev, seg6_input_finish); +skb_dst_dev (skb), seg6_input_finish);return seg6_input_finish (dev_net (skb->dev),NULL , skb); drop:
@@ -593,7 +595,7 @@ static int seg6_output_core(struct net *local_bh_enable (); } - err =skb_cow_head (skb,LL_RESERVED_SPACE (dst->dev)); + err =skb_cow_head (skb,LL_RESERVED_SPACE (dst_dev (dst)));if (unlikely (err)) goto drop; }
@@ -603,7 +605,7 @@ static int seg6_output_core(struct net *if (static_branch_unlikely (&nf_hooks_lwtunnel_enabled))return NF_HOOK (NFPROTO_IPV6 ,NF_INET_LOCAL_OUT , net, sk, skb, -NULL ,skb_dst (skb)->dev, dst_output); +NULL ,dst_dev (dst), dst_output);return dst_output (net, sk, skb); drop:
net/ipv6/seg6_local.c +1 -1
@@ -310,7 +310,7 @@ seg6_lookup_any_nexthop(struct sk_buff *if (!local_delivery) dev_flags |=IFF_LOOPBACK ; -if (dst && (dst->dev->flags & dev_flags) && !dst->error) { +if (dst && (dst_dev (dst)->flags & dev_flags) && !dst->error) {dst_release (dst); dst =NULL ; }
Greg Kroah-Hartman
maintainerPATCH 528/567 net: use dst_dev_rcu() in sk_setup_caps()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 99a2ace61b211b0be861b07fbaa062fca4b58879 ]
Use RCU to protect accesses to dst->dev from sk_setup_caps() and sk_dst_gso_max_size().
Also use dst_dev_rcu() in ip6_dst_mtu_maybe_forward(), and ip_dst_mtu_maybe_forward().
ip4_dst_hoplimit() can use dst_dev_net_rcu().
[Harshit: Backport to 6.12.y, resolve conflict due to missing commit:22d6c9eebf2e ("net: Unexport shared functions for DCCP.") in 6.12.y]
--- include/net/ip.h | 6 ++++-- include/net/ip6_route.h | 2 +- include/net/route.h | 2 +- net/core/sock.c | 16 ++++++++++------ 4 files changed, 16 insertions(+), 10 deletions(-)
include/net/ip.h +4 -2
@@ -470,12 +470,14 @@ static inline unsigned int ip_dst_mtu_ma bool forwarding) {const struct rtable *rt =dst_rtable (dst); +const struct net_device *dev;unsigned int mtu, res;struct net *net;rcu_read_lock (); - net =dev_net_rcu (dst_dev (dst)); + dev =dst_dev_rcu (dst); + net =dev_net_rcu (dev);if (READ_ONCE (net->ipv4.sysctl_ip_fwd_use_pmtu) ||ip_mtu_locked (dst) || !forwarding) {
@@ -489,7 +491,7 @@ static inline unsigned int ip_dst_mtu_maif (mtu) goto out; - mtu =READ_ONCE (dst_dev (dst)->mtu); + mtu =READ_ONCE (dev->mtu);if (unlikely (ip_mtu_locked (dst))) {if (rt->rt_uses_gateway && mtu >576 )
include/net/ip6_route.h +1 -1
@@ -337,7 +337,7 @@ static inline unsigned int ip6_dst_mtu_m mtu =IPV6_MIN_MTU ;rcu_read_lock (); - idev =__in6_dev_get (dst_dev (dst)); + idev =__in6_dev_get (dst_dev_rcu (dst));if (idev) mtu =READ_ONCE (idev->cnf.mtu6);rcu_read_unlock ();
include/net/route.h +1 -1
@@ -369,7 +369,7 @@ static inline int ip4_dst_hoplimit(constconst struct net *net;rcu_read_lock (); - net =dev_net_rcu (dst_dev (dst)); + net =dst_dev_net_rcu (dst); hoplimit =READ_ONCE (net->ipv4.sysctl_ip_default_ttl);rcu_read_unlock (); }
net/core/sock.c +10 -6
@@ -2524,7 +2524,7 @@ void sk_free_unlock_clone(struct sock *s }EXPORT_SYMBOL_GPL (sk_free_unlock_clone); -static u32 sk_dst_gso_max_size (struct sock *sk,struct dst_entry *dst) +static u32 sk_dst_gso_max_size (struct sock *sk,const struct net_device *dev) {bool is_ipv6 = false;u32 max_size;
@@ -2534,8 +2534,8 @@ static u32 sk_dst_gso_max_size(struct so !ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr));#endif /* pairs with the WRITE_ONCE() in netif_set_gso(_ipv4)_max_size() */ - max_size = is_ipv6 ?READ_ONCE (dst_dev (dst)->gso_max_size) : -READ_ONCE (dst_dev (dst)->gso_ipv4_max_size); + max_size = is_ipv6 ?READ_ONCE (dev->gso_max_size) : +READ_ONCE (dev->gso_ipv4_max_size);if (max_size >GSO_LEGACY_MAX_SIZE && !sk_is_tcp (sk)) max_size =GSO_LEGACY_MAX_SIZE ;
@@ -2544,9 +2544,12 @@ static u32 sk_dst_gso_max_size(struct sovoid sk_setup_caps (struct sock *sk,struct dst_entry *dst) { +const struct net_device *dev;u32 max_segs =1 ; - sk->sk_route_caps =dst_dev (dst)->features; +rcu_read_lock (); + dev =dst_dev_rcu (dst); + sk->sk_route_caps = dev->features;if (sk_is_tcp (sk)) {struct inet_connection_sock *icsk =inet_csk (sk);
@@ -2562,13 +2565,14 @@ void sk_setup_caps(struct sock *sk, stru sk->sk_route_caps &= ~NETIF_F_GSO_MASK ; }else { sk->sk_route_caps |=NETIF_F_SG |NETIF_F_HW_CSUM ; - sk->sk_gso_max_size =sk_dst_gso_max_size (sk, dst); + sk->sk_gso_max_size =sk_dst_gso_max_size (sk, dev);/* pairs with the WRITE_ONCE() in netif_set_gso_max_segs() */ - max_segs =max_t (u32,READ_ONCE (dst_dev (dst)->gso_max_segs),1 ); + max_segs =max_t (u32,READ_ONCE (dev->gso_max_segs),1 ); } } sk->sk_gso_max_segs = max_segs;sk_dst_set (sk, dst); +rcu_read_unlock (); }EXPORT_SYMBOL_GPL (sk_setup_caps);
Greg Kroah-Hartman
maintainerPATCH 529/567 usbnet: Fix using smp_processor_id() in preemptible code warnings
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zqiang <[email protected]>
[ Upstream commit 327cd4b68b4398b6c24f10eb2b2533ffbfc10185 ]
Syzbot reported the following warning:
BUG: using smp_processor_id() in preemptible [00000000] code: dhcpcd/2879 caller is usbnet_skb_return+0x74/0x490 drivers/net/usb/usbnet.c:331 CPU: 1 UID: 0 PID: 2879 Comm: dhcpcd Not tainted 6.15.0-rc4-syzkaller-00098-g615dca38c2ea #0 PREEMPT(voluntary) Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x16c/0x1f0 lib/dump_stack.c:120 check_preemption_disabled+0xd0/0xe0 lib/smp_processor_id.c:49 usbnet_skb_return+0x74/0x490 drivers/net/usb/usbnet.c:331 usbnet_resume_rx+0x4b/0x170 drivers/net/usb/usbnet.c:708 usbnet_change_mtu+0x1be/0x220 drivers/net/usb/usbnet.c:417 __dev_set_mtu net/core/dev.c:9443 [inline] netif_set_mtu_ext+0x369/0x5c0 net/core/dev.c:9496 netif_set_mtu+0xb0/0x160 net/core/dev.c:9520 dev_set_mtu+0xae/0x170 net/core/dev_api.c:247 dev_ifsioc+0xa31/0x18d0 net/core/dev_ioctl.c:572 dev_ioctl+0x223/0x10e0 net/core/dev_ioctl.c:821 sock_do_ioctl+0x19d/0x280 net/socket.c:1204 sock_ioctl+0x42f/0x6a0 net/socket.c:1311 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:906 [inline] __se_sys_ioctl fs/ioctl.c:892 [inline] __x64_sys_ioctl+0x190/0x200 fs/ioctl.c:892 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xcd/0x260 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f
For historical and portability reasons, the netif_rx() is usually run in the softirq or interrupt context, this commit therefore add local_bh_disable/enable() protection in the usbnet_resume_rx().
[Harshit: Resolved conflicts due to missing commit: 2c04d279e857 ("net:
usb: Convert tasklet API to new bottom half workqueue mechanism") in 6.12.y]
--- drivers/net/usb/usbnet.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/usb/usbnet.c +2 -0
@@ -702,6 +702,7 @@ void usbnet_resume_rx(struct usbnet *devstruct sk_buff *skb;int num =0 ; +local_bh_disable ();clear_bit (EVENT_RX_PAUSED , &dev->flags);while ((skb =skb_dequeue (&dev->rxq_pause)) !=NULL ) {
@@ -710,6 +711,7 @@ void usbnet_resume_rx(struct usbnet *dev }tasklet_schedule (&dev->bh); +local_bh_enable ();netif_dbg (dev, rx_status, dev->net,"paused rx queue disabled, %d skbs requeued\n" , num);
Greg Kroah-Hartman
maintainerPATCH 530/567 serial: core: Fix serial device initialization
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Stein <[email protected]>
commit f54151148b969fb4b62bec8093d255306d20df30 upstream.
During restoring sysfs fwnode information the information of_node_reused was dropped. This was previously set by device_set_of_node_from_dev(). Add it back manually
--- drivers/tty/serial/serial_base_bus.c | 1 + 1 file changed, 1 insertion(+)
drivers/tty/serial/serial_base_bus.c +1 -0
@@ -74,6 +74,7 @@ static int serial_base_device_init(struc dev->parent = parent_dev; dev->bus = &serial_base_bus_type; dev->release = release; + dev->of_node_reused = true;device_set_node (dev,fwnode_handle_get (dev_fwnode (parent_dev)));
Greg Kroah-Hartman
maintainerPATCH 531/567 tty: fix tty_port_tty_*hangup() kernel-doc
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Slaby (SUSE) <[email protected]>
commit 6241b49540a65a6d5274fa938fd3eb4cbfe2e076 upstream.
The commit below added a new helper, but omitted to move (and add) the corressponding kernel-doc. Do it now.
--- Documentation/driver-api/tty/tty_port.rst | 5 +++-- drivers/tty/tty_port.c | 5 ----- include/linux/tty_port.h | 9 +++++++++ 3 files changed, 12 insertions(+), 7 deletions(-)
Documentation/driver-api/tty/tty_port.rst +3 -2
@@ -42,9 +42,10 @@ TTY Refcounting TTY Helpers ----------- +.. kernel-doc:: include/linux/tty_port.h + :identifiers: tty_port_tty_hangup tty_port_tty_vhangup .. kernel-doc:: drivers/tty/tty_port.c - :identifiers: tty_port_tty_hangup tty_port_tty_wakeup - + :identifiers: tty_port_tty_wakeup Modem Signals -------------
drivers/tty/tty_port.c +0 -5
@@ -411,11 +411,6 @@ void tty_port_hangup(struct tty_port *po }EXPORT_SYMBOL (tty_port_hangup); -/** -* tty_port_tty_hangup - helper to hang up a tty -* @port: tty port -* @check_clocal: hang only ttys with %CLOCAL unset? -*/ void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async) {struct tty_struct *tty =tty_port_tty_get (port);
include/linux/tty_port.h +9 -0
@@ -254,11 +254,20 @@ static inline int tty_port_users(structreturn port->count + port->blocked_open; } +/** +* tty_port_tty_hangup - helper to hang up a tty asynchronously +* @port: tty port +* @check_clocal: hang only ttys with %CLOCAL unset? +*/ static inline void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) {__tty_port_tty_hangup (port, check_clocal, true); } +/** +* tty_port_tty_vhangup - helper to hang up a tty synchronously +* @port: tty port +*/ static inline void tty_port_tty_vhangup (struct tty_port *port) {__tty_port_tty_hangup (port, false, false);
Greg Kroah-Hartman
maintainerPATCH 532/567 x86/microcode/AMD: Select which microcode patch to load
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Borislav Petkov (AMD) <[email protected]>
commit 8d171045069c804e5ffaa18be590c42c6af0cf3f upstream.
All microcode patches up to the proper BIOS Entrysign fix are loaded only after the sha256 signature carried in the driver has been verified.
Microcode patches after the Entrysign fix has been applied, do not need that signature verification anymore.
In order to not abandon machines which haven't received the BIOS update yet, add the capability to select which microcode patch to load.
The corresponding microcode container supplied through firmware-linux has been modified to carry two patches per CPU type (family/model/stepping) so that the proper one gets selected.
--- arch/x86/kernel/cpu/microcode/amd.c | 104 +++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 37 deletions(-)
arch/x86/kernel/cpu/microcode/amd.c +67 -37
@@ -174,50 +174,61 @@ static u32 cpuid_to_ucode_rev(unsigned ireturn p.ucode_rev; } +static u32 get_cutoff_revision (u32 rev) +{ +switch (rev >>8 ) { +case 0x80012 :return 0x8001277 ;break ; +case 0x80082 :return 0x800820f ;break ; +case 0x83010 :return 0x830107c ;break ; +case 0x86001 :return 0x860010e ;break ; +case 0x86081 :return 0x8608108 ;break ; +case 0x87010 :return 0x8701034 ;break ; +case 0x8a000 :return 0x8a0000a ;break ; +case 0xa0010 :return 0xa00107a ;break ; +case 0xa0011 :return 0xa0011da ;break ; +case 0xa0012 :return 0xa001243 ;break ; +case 0xa0082 :return 0xa00820e ;break ; +case 0xa1011 :return 0xa101153 ;break ; +case 0xa1012 :return 0xa10124e ;break ; +case 0xa1081 :return 0xa108109 ;break ; +case 0xa2010 :return 0xa20102f ;break ; +case 0xa2012 :return 0xa201212 ;break ; +case 0xa4041 :return 0xa404109 ;break ; +case 0xa5000 :return 0xa500013 ;break ; +case 0xa6012 :return 0xa60120a ;break ; +case 0xa7041 :return 0xa704109 ;break ; +case 0xa7052 :return 0xa705208 ;break ; +case 0xa7080 :return 0xa708009 ;break ; +case 0xa70c0 :return 0xa70C009 ;break ; +case 0xaa001 :return 0xaa00116 ;break ; +case 0xaa002 :return 0xaa00218 ;break ; +case 0xb0021 :return 0xb002146 ;break ; +case 0xb0081 :return 0xb008111 ;break ; +case 0xb1010 :return 0xb101046 ;break ; +case 0xb2040 :return 0xb204031 ;break ; +case 0xb4040 :return 0xb404031 ;break ; +case 0xb4041 :return 0xb404101 ;break ; +case 0xb6000 :return 0xb600031 ;break ; +case 0xb6080 :return 0xb608031 ;break ; +case 0xb7000 :return 0xb700031 ;break ; +default :break ; + + } +return 0 ; +} +static bool need_sha_check (u32 cur_rev) { +u32 cutoff; +if (!cur_rev) { cur_rev =cpuid_to_ucode_rev (bsp_cpuid_1_eax);pr_info_once ("No current revision, generating the lowest one: 0x%x\n" , cur_rev); } -switch (cur_rev >>8 ) { -case 0x80012 :return cur_rev <=0x8001277 ;break ; -case 0x80082 :return cur_rev <=0x800820f ;break ; -case 0x83010 :return cur_rev <=0x830107c ;break ; -case 0x86001 :return cur_rev <=0x860010e ;break ; -case 0x86081 :return cur_rev <=0x8608108 ;break ; -case 0x87010 :return cur_rev <=0x8701034 ;break ; -case 0x8a000 :return cur_rev <=0x8a0000a ;break ; -case 0xa0010 :return cur_rev <=0xa00107a ;break ; -case 0xa0011 :return cur_rev <=0xa0011da ;break ; -case 0xa0012 :return cur_rev <=0xa001243 ;break ; -case 0xa0082 :return cur_rev <=0xa00820e ;break ; -case 0xa1011 :return cur_rev <=0xa101153 ;break ; -case 0xa1012 :return cur_rev <=0xa10124e ;break ; -case 0xa1081 :return cur_rev <=0xa108109 ;break ; -case 0xa2010 :return cur_rev <=0xa20102f ;break ; -case 0xa2012 :return cur_rev <=0xa201212 ;break ; -case 0xa4041 :return cur_rev <=0xa404109 ;break ; -case 0xa5000 :return cur_rev <=0xa500013 ;break ; -case 0xa6012 :return cur_rev <=0xa60120a ;break ; -case 0xa7041 :return cur_rev <=0xa704109 ;break ; -case 0xa7052 :return cur_rev <=0xa705208 ;break ; -case 0xa7080 :return cur_rev <=0xa708009 ;break ; -case 0xa70c0 :return cur_rev <=0xa70C009 ;break ; -case 0xaa001 :return cur_rev <=0xaa00116 ;break ; -case 0xaa002 :return cur_rev <=0xaa00218 ;break ; -case 0xb0021 :return cur_rev <=0xb002146 ;break ; -case 0xb0081 :return cur_rev <=0xb008111 ;break ; -case 0xb1010 :return cur_rev <=0xb101046 ;break ; -case 0xb2040 :return cur_rev <=0xb204031 ;break ; -case 0xb4040 :return cur_rev <=0xb404031 ;break ; -case 0xb4041 :return cur_rev <=0xb404101 ;break ; -case 0xb6000 :return cur_rev <=0xb600031 ;break ; -case 0xb6080 :return cur_rev <=0xb608031 ;break ; -case 0xb7000 :return cur_rev <=0xb700031 ;break ; -default :break ; - } + cutoff =get_cutoff_revision (cur_rev); +if (cutoff) +return cur_rev <= cutoff;pr_info ("You should not be seeing this. Please send the following couple of lines to x86-<at>-kernel.org\n" );pr_info ("CPUID(1).EAX: 0x%x, current revision: 0x%x\n" , bsp_cpuid_1_eax, cur_rev);
@@ -468,6 +479,7 @@ static int verify_patch(const u8 *buf, s {u8 family =x86_family (bsp_cpuid_1_eax);struct microcode_header_amd *mc_hdr; +u32 cur_rev, cutoff, patch_rev;unsigned int ret;u32 sh_psize;u16 proc_id;
@@ -511,6 +523,24 @@ static int verify_patch(const u8 *buf, sif (patch_fam != family)return 1 ; + cur_rev =get_patch_level (); + +/* No cutoff revision means old/unaffected by signing algorithm weakness => matches */ + cutoff =get_cutoff_revision (cur_rev); +if (!cutoff) + goto ok; + + patch_rev = mc_hdr->patch_id; + +if (cur_rev <= cutoff && patch_rev <= cutoff) + goto ok; + +if (cur_rev > cutoff && patch_rev > cutoff) + goto ok; + +return 1 ; +ok: +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 533/567 media: i2c: imx219: Fix 1920x1080 mode to use 1:1 pixel aspect ratio
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Stevenson <[email protected]>
commit 9ef6e4db152c34580cc52792f32485c193945395 upstream.
Commit 0af46fbc333d ("media: i2c: imx219: Calculate crop rectangle dynamically") meant that the 1920x1080 mode switched from using no binning to using vertical binning but no horizontal binning, which resulted in stretched pixels.
Until proper controls are available to independently select horizontal and vertical binning, restore the original 1:1 pixel aspect ratio by forcing binning to be uniform in both directions.
--- drivers/media/i2c/imx219.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/media/i2c/imx219.c +6 -3
@@ -843,7 +843,7 @@ static int imx219_set_pad_format(structconst struct imx219_mode *mode;struct v4l2_mbus_framefmt *format;struct v4l2_rect *crop; -unsigned int bin_h, bin_v; +unsigned int bin_h, bin_v, binning; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes),
@@ -862,9 +862,12 @@ static int imx219_set_pad_format(struct bin_h =min (IMX219_PIXEL_ARRAY_WIDTH / format->width,2U ); bin_v =min (IMX219_PIXEL_ARRAY_HEIGHT / format->height,2U ); +/* Ensure bin_h and bin_v are same to avoid 1:2 or 2:1 stretching */ + binning =min (bin_h, bin_v); + crop =v4l2_subdev_state_get_crop (state,0 ); - crop->width = format->width * bin_h; - crop->height = format->height * bin_v; + crop->width = format->width * binning; + crop->height = format->height * binning; crop->left = (IMX219_NATIVE_WIDTH - crop->width) /2 ; crop->top = (IMX219_NATIVE_HEIGHT - crop->height) /2 ;
Greg Kroah-Hartman
maintainerPATCH 534/567 wifi: mt76: mt7925: fix the unfinished command of regd_notifier before suspend
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quan Zhou <[email protected]>
[ Upstream commit 1b97fc8443aea01922560de9f24a6383e6eb6ae8 ]
Before entering suspend, we need to ensure that all MCU command are completed. In some cases, such as with regd_notifier, there is a chance that CLC commands, will be executed before suspend.
--- drivers/net/wireless/mediatek/mt76/mt7925/init.c | 4 ++++ drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 3 +++ 2 files changed, 7 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/init.c +4 -0
@@ -81,11 +81,14 @@ mt7925_regd_notifier(struct wiphy *wiphy mdev->region = req->dfs_region; dev->country_ie_env = req->country_ie_env; + dev->regd_in_progress = true;mt792x_mutex_acquire (dev);mt7925_mcu_set_clc (dev, req->alpha2, req->country_ie_env);mt7925_mcu_set_channel_domain (hw->priv);mt7925_set_tx_sar_pwr (hw,NULL );mt792x_mutex_release (dev); + dev->regd_in_progress = false; +wake_up (&dev->wait); }static void mt7925_mac_init_basic_rates (struct mt792x_dev *dev)
@@ -235,6 +238,7 @@ int mt7925_register_device(struct mt792xspin_lock_init (&dev->pm.wake.lock);mutex_init (&dev->pm.mutex);init_waitqueue_head (&dev->pm.wait); +init_waitqueue_head (&dev->wait);spin_lock_init (&dev->pm.txq_lock);INIT_DELAYED_WORK (&dev->mphy.mac_work, mt792x_mac_work);INIT_DELAYED_WORK (&dev->phy.scan_work, mt7925_scan_work);
drivers/net/wireless/mediatek/mt76/mt7925/pci.c +3 -0
@@ -455,6 +455,9 @@ static int mt7925_pci_suspend(struct devif (err <0 ) goto restore_suspend; +wait_event_timeout (dev->wait, + !dev->regd_in_progress,5 *HZ ); +/* always enable deep sleep during suspend to reduce * power consumption */
Greg Kroah-Hartman
maintainerPATCH 535/567 wifi: mt76: mt7925: fix CLC command timeout when suspend/resume
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quan Zhou <[email protected]>
[ Upstream commit a0f721b8d986b62b4de316444f2b2e356d17e3b5 ]
When enter suspend/resume while in a connected state, the upper layer will trigger disconnection before entering suspend, and at the same time, it will trigger regd_notifier() and update CLC, causing the CLC event to not be received due to suspend, resulting in a command timeout.
Therefore, the update of CLC is postponed until resume, to ensure data consistency and avoid the occurrence of command timeout.
--- drivers/net/wireless/mediatek/mt76/mt7925/init.c | 20 +++++++++++++++++--- drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 1 + drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 3 +++ 3 files changed, 21 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/init.c +17 -3
@@ -59,6 +59,18 @@ static int mt7925_thermal_init(struct mt mt7925_hwmon_groups);return PTR_ERR_OR_ZERO (hwmon); } + +void mt7925_regd_update (struct mt792x_dev *dev) +{ +struct mt76_dev *mdev = &dev->mt76; +struct ieee80211_hw *hw = mdev->hw; + +mt7925_mcu_set_clc (dev, mdev->alpha2, dev->country_ie_env); +mt7925_mcu_set_channel_domain (hw->priv); +mt7925_set_tx_sar_pwr (hw,NULL ); +} +EXPORT_SYMBOL_GPL (mt7925_regd_update); +static void mt7925_regd_notifier (struct wiphy *wiphy,struct regulatory_request *req)
@@ -66,6 +78,7 @@ mt7925_regd_notifier(struct wiphy *wiphystruct ieee80211_hw *hw =wiphy_to_ieee80211_hw (wiphy);struct mt792x_dev *dev =mt792x_hw_dev (hw);struct mt76_dev *mdev = &dev->mt76; +struct mt76_connac_pm *pm = &dev->pm;/* allow world regdom at the first boot only */ if (!memcmp(req->alpha2, "00", 2) &&
@@ -81,11 +94,12 @@ mt7925_regd_notifier(struct wiphy *wiphy mdev->region = req->dfs_region; dev->country_ie_env = req->country_ie_env; +if (pm->suspended) +return ; + dev->regd_in_progress = true;mt792x_mutex_acquire (dev); -mt7925_mcu_set_clc (dev, req->alpha2, req->country_ie_env); -mt7925_mcu_set_channel_domain (hw->priv); -mt7925_set_tx_sar_pwr (hw,NULL ); +mt7925_regd_update (dev);mt792x_mutex_release (dev); dev->regd_in_progress = false;wake_up (&dev->wait);
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +1 -0
@@ -218,6 +218,7 @@ int mt7925_mcu_chip_config(struct mt792xint mt7925_mcu_set_rxfilter (struct mt792x_dev *dev,u32 fif,u8 bit_op,u32 bit_map); +void mt7925_regd_update (struct mt792x_dev *dev);int mt7925_mac_init (struct mt792x_dev *dev);int mt7925_mac_sta_add (struct mt76_dev *mdev,struct ieee80211_vif *vif,struct ieee80211_sta *sta);
drivers/net/wireless/mediatek/mt76/mt7925/pci.c +3 -0
@@ -554,11 +554,14 @@ static int mt7925_pci_resume(struct devilocal_bh_enable (); err =mt76_connac_mcu_set_hif_suspend (mdev, false); +if (err <0 ) + goto failed;/* restore previous ds setting */ if (!pm->ds_enable)mt7925_mcu_set_deep_sleep (dev, false); +mt7925_regd_update (dev); failed: pm->suspended = false;
Greg Kroah-Hartman
maintainerPATCH 536/567 wifi: mt76: mt7925: add handler to hif suspend/resume event
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quan Zhou <[email protected]>
[ Upstream commit 8f6571ad470feb242dcef36e53f7cf1bba03780f ]
When the system suspend or resume, the WiFi driver sends an hif_ctrl command to the firmware and waits for an event. Due to changes in the event format reported by the chip, the current mt7925's driver does not account for these changes, resulting in command timeout. Add flow to handle hif_ctrl event to avoid command timeout. We also exented API mt76_connac_mcu_set_hif_suspend for connac3 this time.
--- drivers/net/wireless/mediatek/mt76/mt7615/main.c | 4 - drivers/net/wireless/mediatek/mt76/mt7615/pci.c | 6 +- drivers/net/wireless/mediatek/mt76/mt7615/sdio.c | 4 - drivers/net/wireless/mediatek/mt76/mt7615/usb.c | 4 - drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 4 - drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 3 - drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 6 +- drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 6 +- drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 4 - drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 49 ++++++++++++++++++- drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 20 +++++++ drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 29 ++++++++--- drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 20 +++++-- drivers/net/wireless/mediatek/mt76/mt792x.h | 2 14 files changed, 127 insertions(+), 34 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7615/main.c +2 -2
@@ -1249,7 +1249,7 @@ static int mt7615_suspend(struct ieee802 phy->mt76);if (!mt7615_dev_running (dev)) - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, true); + err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, true, true);mt7615_mutex_release (dev);
@@ -1271,7 +1271,7 @@ static int mt7615_resume(struct ieee8021if (!running) {int err; - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, false); + err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, false, true);if (err <0 ) {mt7615_mutex_release (dev);return err;
drivers/net/wireless/mediatek/mt76/mt7615/pci.c +3 -3
@@ -83,7 +83,7 @@ static int mt7615_pci_suspend(struct pci hif_suspend = !test_bit (MT76_STATE_SUSPEND , &dev->mphy.state) &&mt7615_firmware_offload (dev);if (hif_suspend) { - err =mt76_connac_mcu_set_hif_suspend (mdev, true); + err =mt76_connac_mcu_set_hif_suspend (mdev, true, true);if (err)return err; }
@@ -131,7 +131,7 @@ restore: }napi_enable (&mdev->tx_napi);if (hif_suspend) -mt76_connac_mcu_set_hif_suspend (mdev, false); +mt76_connac_mcu_set_hif_suspend (mdev, false, true);return err; }
@@ -175,7 +175,7 @@ static int mt7615_pci_resume(struct pci_if (!test_bit (MT76_STATE_SUSPEND , &dev->mphy.state) &&mt7615_firmware_offload (dev)) - err =mt76_connac_mcu_set_hif_suspend (mdev, false); + err =mt76_connac_mcu_set_hif_suspend (mdev, false, true);return err; }
drivers/net/wireless/mediatek/mt76/mt7615/sdio.c +2 -2
@@ -191,7 +191,7 @@ static int mt7663s_suspend(struct device mt7615_firmware_offload(mdev)) {int err; - err =mt76_connac_mcu_set_hif_suspend (&mdev->mt76, true); + err =mt76_connac_mcu_set_hif_suspend (&mdev->mt76, true, true);if (err <0 )return err; }
@@ -230,7 +230,7 @@ static int mt7663s_resume(struct deviceif (!test_bit (MT76_STATE_SUSPEND , &mdev->mphy.state) &&mt7615_firmware_offload (mdev)) - err =mt76_connac_mcu_set_hif_suspend (&mdev->mt76, false); + err =mt76_connac_mcu_set_hif_suspend (&mdev->mt76, false, true);return err; }
drivers/net/wireless/mediatek/mt76/mt7615/usb.c +2 -2
@@ -225,7 +225,7 @@ static int mt7663u_suspend(struct usb_in mt7615_firmware_offload(dev)) {int err; - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, true); + err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, true, true);if (err <0 )return err; }
@@ -253,7 +253,7 @@ static int mt7663u_resume(struct usb_intif (!test_bit (MT76_STATE_SUSPEND , &dev->mphy.state) &&mt7615_firmware_offload (dev)) - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, false); + err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, false, true);return err; }
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +2 -2
@@ -2527,7 +2527,7 @@ mt76_connac_mcu_set_wow_ctrl(struct mt76 }EXPORT_SYMBOL_GPL (mt76_connac_mcu_set_wow_ctrl); -int mt76_connac_mcu_set_hif_suspend (struct mt76_dev *dev,bool suspend) +int mt76_connac_mcu_set_hif_suspend (struct mt76_dev *dev,bool suspend,bool wait_resp) { struct { struct {
@@ -2559,7 +2559,7 @@ int mt76_connac_mcu_set_hif_suspend(stru req.hdr.hif_type =0 ;return mt76_mcu_send_msg (dev,MCU_UNI_CMD (HIF_CTRL ), &req, -sizeof (req), true); +sizeof (req), wait_resp); }EXPORT_SYMBOL_GPL (mt76_connac_mcu_set_hif_suspend);
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +2 -1
@@ -1049,6 +1049,7 @@ enum {/* unified event table */ enum {MCU_UNI_EVENT_RESULT =0x01 , +MCU_UNI_EVENT_HIF_CTRL =0x03 ,MCU_UNI_EVENT_FW_LOG_2_HOST =0x04 ,MCU_UNI_EVENT_ACCESS_REG =0x6 ,MCU_UNI_EVENT_IE_COUNTDOWN =0x09 ,
@@ -1989,7 +1990,7 @@ int mt76_connac_mcu_set_suspend_mode(strstruct ieee80211_vif *vif, bool enable, u8 mdtim, bool wow_suspend); -int mt76_connac_mcu_set_hif_suspend (struct mt76_dev *dev,bool suspend); +int mt76_connac_mcu_set_hif_suspend (struct mt76_dev *dev,bool suspend,bool wait_resp);void mt76_connac_mcu_set_suspend_iter (void *priv,u8 *mac,struct ieee80211_vif *vif); int mt76_connac_sta_state_dp(struct mt76_dev *dev,
drivers/net/wireless/mediatek/mt76/mt7921/pci.c +3 -3
@@ -435,7 +435,7 @@ static int mt7921_pci_suspend(struct devif (err <0 ) goto restore_suspend; - err =mt76_connac_mcu_set_hif_suspend (mdev, true); + err =mt76_connac_mcu_set_hif_suspend (mdev, true, true);if (err) goto restore_suspend;
@@ -481,7 +481,7 @@ restore_napi:if (!pm->ds_enable)mt76_connac_mcu_set_deep_sleep (&dev->mt76, false); -mt76_connac_mcu_set_hif_suspend (mdev, false); +mt76_connac_mcu_set_hif_suspend (mdev, false, true); restore_suspend: pm->suspended = false;
@@ -532,7 +532,7 @@ static int mt7921_pci_resume(struct deviif (!pm->ds_enable)mt76_connac_mcu_set_deep_sleep (&dev->mt76, false); - err =mt76_connac_mcu_set_hif_suspend (mdev, false); + err =mt76_connac_mcu_set_hif_suspend (mdev, false, true);if (err <0 ) goto failed;
drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +3 -3
@@ -240,7 +240,7 @@ static int mt7921s_suspend(struct device mt76s_txqs_empty(&dev->mt76), 5 * HZ);/* It is supposed that SDIO bus is idle at the point */ - err =mt76_connac_mcu_set_hif_suspend (mdev, true); + err =mt76_connac_mcu_set_hif_suspend (mdev, true, true);if (err) goto restore_worker;
@@ -258,7 +258,7 @@ static int mt7921s_suspend(struct device restore_txrx_worker:mt76_worker_enable (&mdev->sdio.net_worker);mt76_worker_enable (&mdev->sdio.txrx_worker); -mt76_connac_mcu_set_hif_suspend (mdev, false); +mt76_connac_mcu_set_hif_suspend (mdev, false, true); restore_worker:mt76_worker_enable (&mdev->tx_worker);
@@ -302,7 +302,7 @@ static int mt7921s_resume(struct deviceif (!pm->ds_enable)mt76_connac_mcu_set_deep_sleep (mdev, false); - err =mt76_connac_mcu_set_hif_suspend (mdev, false); + err =mt76_connac_mcu_set_hif_suspend (mdev, false, true); failed: pm->suspended = false;
drivers/net/wireless/mediatek/mt76/mt7921/usb.c +2 -2
@@ -263,7 +263,7 @@ static int mt7921u_suspend(struct usb_in pm->suspended = true;flush_work (&dev->reset_work); - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, true); + err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, true, true);if (err) goto failed;
@@ -313,7 +313,7 @@ static int mt7921u_resume(struct usb_intif (err <0 ) goto failed; - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, false); + err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, false, true); failed: pm->suspended = false;
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +48 -1
67 lines not on this page · raw message
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +20 -0
@@ -27,6 +27,26 @@#define MCU_UNI_EVENT_ROC 0x27 +#define HIF_TRAFFIC_IDLE 0x2 + +enum { +UNI_EVENT_HIF_CTRL_BASIC =0 , +UNI_EVENT_HIF_CTRL_TAG_NUM +}; + +struct mt7925_mcu_hif_ctrl_basic_tlv { +__le16 tag; +__le16 len; +u8 cid; +u8 pad[3 ]; +u32 status; +u8 hif_type; +u8 hif_tx_traffic_status; +u8 hif_rx_traffic_status; +u8 hifsuspend; +u8 rsv[4 ]; +} __packed; +enum {UNI_ROC_ACQUIRE ,UNI_ROC_ABORT ,
drivers/net/wireless/mediatek/mt76/mt7925/pci.c +21 -8
@@ -442,9 +442,10 @@ static int mt7925_pci_suspend(struct devstruct mt76_dev *mdev =pci_get_drvdata (pdev); struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);struct mt76_connac_pm *pm = &dev->pm; -int i, err; +int i, err, ret; pm->suspended = true; + dev->hif_resumed = false;flush_work (&dev->reset_work);cancel_delayed_work_sync (&pm->ps_work);cancel_work_sync (&pm->wake_work);
@@ -463,9 +464,13 @@ static int mt7925_pci_suspend(struct dev*/ mt7925_mcu_set_deep_sleep (dev, true); - err =mt76_connac_mcu_set_hif_suspend (mdev, true); -if (err) +mt76_connac_mcu_set_hif_suspend (mdev, true, false); + ret =wait_event_timeout (dev->wait, + dev->hif_idle,3 *HZ ); +if (!ret) { + err = -ETIMEDOUT ; goto restore_suspend; + }napi_disable (&mdev->tx_napi);mt76_worker_disable (&mdev->tx_worker);
@@ -506,8 +511,11 @@ restore_napi:if (!pm->ds_enable)mt7925_mcu_set_deep_sleep (dev, false); -mt76_connac_mcu_set_hif_suspend (mdev, false); - +mt76_connac_mcu_set_hif_suspend (mdev, false, false); + ret =wait_event_timeout (dev->wait, + dev->hif_resumed,3 *HZ ); +if (!ret) + err = -ETIMEDOUT ; restore_suspend: pm->suspended = false;
@@ -523,8 +531,9 @@ static int mt7925_pci_resume(struct devistruct mt76_dev *mdev =pci_get_drvdata (pdev); struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);struct mt76_connac_pm *pm = &dev->pm; -int i, err; +int i, err, ret; + dev->hif_idle = false; err =mt792x_mcu_drv_pmctrl (dev);if (err <0 ) goto failed;
@@ -553,9 +562,13 @@ static int mt7925_pci_resume(struct devinapi_schedule (&mdev->tx_napi);local_bh_enable (); - err =mt76_connac_mcu_set_hif_suspend (mdev, false); -if (err <0 ) +mt76_connac_mcu_set_hif_suspend (mdev, false, false); + ret =wait_event_timeout (dev->wait, + dev->hif_resumed,3 *HZ ); +if (!ret) { + err = -ETIMEDOUT ; goto failed; + }/* restore previous ds setting */ if (!pm->ds_enable)
drivers/net/wireless/mediatek/mt76/mt7925/usb.c +15 -5
@@ -246,14 +246,19 @@ static int mt7925u_suspend(struct usb_in {struct mt792x_dev *dev =usb_get_intfdata (intf);struct mt76_connac_pm *pm = &dev->pm; -int err; +int err, ret; pm->suspended = true; + dev->hif_resumed = false;flush_work (&dev->reset_work); - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, true); -if (err) +mt76_connac_mcu_set_hif_suspend (&dev->mt76, true, false); + ret =wait_event_timeout (dev->wait, + dev->hif_idle,3 *HZ ); +if (!ret) { + err = -ETIMEDOUT ; goto failed; + }mt76u_stop_rx (&dev->mt76);mt76u_stop_tx (&dev->mt76);
@@ -274,8 +279,9 @@ static int mt7925u_resume(struct usb_intstruct mt792x_dev *dev =usb_get_intfdata (intf);struct mt76_connac_pm *pm = &dev->pm;bool reinit = true; -int err, i; +int err, i, ret; + dev->hif_idle = false;for (i =0 ; i <10 ; i++) {u32 val =mt76_rr (dev,MT_WF_SW_DEF_CR_USB_MCU_EVENT );
@@ -301,7 +307,11 @@ static int mt7925u_resume(struct usb_intif (err <0 ) goto failed; - err =mt76_connac_mcu_set_hif_suspend (&dev->mt76, false); +mt76_connac_mcu_set_hif_suspend (&dev->mt76, false, false); + ret =wait_event_timeout (dev->wait, + dev->hif_resumed,3 *HZ ); +if (!ret) + err = -ETIMEDOUT ; failed: pm->suspended = false;
drivers/net/wireless/mediatek/mt76/mt792x.h +2 -0
@@ -216,6 +216,8 @@ struct mt792x_dev { bool has_eht:1; bool regd_in_progress:1; bool aspm_supported:1; + bool hif_idle:1; + bool hif_resumed:1;wait_queue_head_t wait;struct work_struct init_work;
Greg Kroah-Hartman
maintainerPATCH 537/567 idpf: add support for SW triggered interrupts
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit 93433c1d919775f8ac0f7893692f42e6731a5373 ]
SW triggered interrupts are guaranteed to fire after their timer expires, unlike Tx and Rx interrupts which will only fire after the timer expires _and_ a descriptor write back is available to be processed by the driver.
Add the necessary fields, defines, and initializations to enable a SW triggered interrupt in the vector's dyn_ctl register.
--- drivers/net/ethernet/intel/idpf/idpf_dev.c | 3 +++ drivers/net/ethernet/intel/idpf/idpf_txrx.h | 8 +++++++- drivers/net/ethernet/intel/idpf/idpf_vf_dev.c | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-)
drivers/net/ethernet/intel/idpf/idpf_dev.c +3 -0
@@ -101,6 +101,9 @@ static int idpf_intr_reg_init(struct idp intr->dyn_ctl_itridx_s =PF_GLINT_DYN_CTL_ITR_INDX_S ; intr->dyn_ctl_intrvl_s =PF_GLINT_DYN_CTL_INTERVAL_S ; intr->dyn_ctl_wb_on_itr_m =PF_GLINT_DYN_CTL_WB_ON_ITR_M ; + intr->dyn_ctl_swint_trig_m =PF_GLINT_DYN_CTL_SWINT_TRIG_M ; + intr->dyn_ctl_sw_itridx_ena_m = +PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_M ; spacing =IDPF_ITR_IDX_SPACING (reg_vals[vec_id].itrn_index_spacing,IDPF_PF_ITR_IDX_SPACING );
drivers/net/ethernet/intel/idpf/idpf_txrx.h +7 -1
@@ -354,6 +354,8 @@ struct idpf_vec_regs {* @dyn_ctl_itridx_m: Mask for ITR index * @dyn_ctl_intrvl_s: Register bit offset for ITR interval * @dyn_ctl_wb_on_itr_m: Mask for WB on ITR feature +* @dyn_ctl_sw_itridx_ena_m: Mask for SW ITR index +* @dyn_ctl_swint_trig_m: Mask for dyn_ctl SW triggered interrupt enable * @rx_itr: RX ITR register * @tx_itr: TX ITR register * @icr_ena: Interrupt cause register offset
@@ -367,6 +369,8 @@ struct idpf_intr_reg {u32 dyn_ctl_itridx_m;u32 dyn_ctl_intrvl_s;u32 dyn_ctl_wb_on_itr_m; +u32 dyn_ctl_sw_itridx_ena_m; +u32 dyn_ctl_swint_trig_m;void __iomem *rx_itr;void __iomem *tx_itr;void __iomem *icr_ena;
@@ -437,7 +441,7 @@ struct idpf_q_vector {cpumask_var_t affinity_mask;__cacheline_group_end_aligned (cold); }; -libeth_cacheline_set_assert(struct idpf_q_vector, 112, +libeth_cacheline_set_assert(struct idpf_q_vector, 120,24 +sizeof (struct napi_struct ) +2 *sizeof (struct dim ), 8 + sizeof(cpumask_var_t));
@@ -471,6 +475,8 @@ struct idpf_tx_queue_stats {#define IDPF_ITR_IS_DYNAMIC (itr_mode) (itr_mode)#define IDPF_ITR_TX_DEF IDPF_ITR_20K#define IDPF_ITR_RX_DEF IDPF_ITR_20K +/* Index used for 'SW ITR' update in DYN_CTL register */ +#define IDPF_SW_ITR_UPDATE_IDX 2/* Index used for 'No ITR' update in DYN_CTL register */ #define IDPF_NO_ITR_UPDATE_IDX 3#define IDPF_ITR_IDX_SPACING (spacing, dflt) (spacing ? spacing : dflt)
drivers/net/ethernet/intel/idpf/idpf_vf_dev.c +3 -0
@@ -101,6 +101,9 @@ static int idpf_vf_intr_reg_init(struct intr->dyn_ctl_itridx_s =VF_INT_DYN_CTLN_ITR_INDX_S ; intr->dyn_ctl_intrvl_s =VF_INT_DYN_CTLN_INTERVAL_S ; intr->dyn_ctl_wb_on_itr_m =VF_INT_DYN_CTLN_WB_ON_ITR_M ; + intr->dyn_ctl_swint_trig_m =VF_INT_DYN_CTLN_SWINT_TRIG_M ; + intr->dyn_ctl_sw_itridx_ena_m = +VF_INT_DYN_CTLN_SW_ITR_INDX_ENA_M ; spacing =IDPF_ITR_IDX_SPACING (reg_vals[vec_id].itrn_index_spacing,IDPF_VF_ITR_IDX_SPACING );
Greg Kroah-Hartman
maintainerPATCH 538/567 idpf: trigger SW interrupt when exiting wb_on_itr mode
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit 0c1683c681681c14f4389e3bfa8de10baf242ba8 ]
There is a race condition between exiting wb_on_itr and completion write backs. For example, we are in wb_on_itr mode and a Tx completion is generated by HW, ready to be written back, as we are re-enabling interrupts:
HW SW | | | | idpf_tx_splitq_clean_all | | napi_complete_done | | | tx_completion_wb | idpf_vport_intr_update_itr_ena_irq
That tx_completion_wb happens before the vector is fully re-enabled. Continuing with this example, it is a UDP stream and the tx_completion_wb is the last one in the flow (there are no rx packets). Because the HW generated the completion before the interrupt is fully enabled, the HW will not fire the interrupt once the timer expires and the write back will not happen. NAPI poll won't be called. We have indicated we're back in interrupt mode but nothing else will trigger the interrupt. Therefore, the completion goes unprocessed, triggering a Tx timeout.
To mitigate this, fire a SW triggered interrupt upon exiting wb_on_itr. This interrupt will catch the rogue completion and avoid the timeout. Add logic to set the appropriate bits in the vector's dyn_ctl register.
--- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 29 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_txrx.c +19 -10
@@ -3502,21 +3502,31 @@ static void idpf_vport_intr_dis_irq_all(/** * idpf_vport_intr_buildreg_itr - Enable default interrupt generation settings * @q_vector: pointer to q_vector -* @type: itr index -* @itr: itr value */ -static u32 idpf_vport_intr_buildreg_itr (struct idpf_q_vector *q_vector, -const int type,u16 itr) +static u32 idpf_vport_intr_buildreg_itr (struct idpf_q_vector *q_vector) { -u32 itr_val; +u32 itr_val = q_vector->intr_reg.dyn_ctl_intena_m; +int type =IDPF_NO_ITR_UPDATE_IDX ; +u16 itr =0 ; + +if (q_vector->wb_on_itr) { +/* +* Trigger a software interrupt when exiting wb_on_itr, to make +* sure we catch any pending write backs that might have been +* missed due to interrupt state transition. +*/ + itr_val |= q_vector->intr_reg.dyn_ctl_swint_trig_m | + q_vector->intr_reg.dyn_ctl_sw_itridx_ena_m; + type =IDPF_SW_ITR_UPDATE_IDX ; + itr =IDPF_ITR_20K ; + } itr &=IDPF_ITR_MASK ;/* Don't clear PBA because that can cause lost interrupts that * came in while we were cleaning/polling */ - itr_val = q_vector->intr_reg.dyn_ctl_intena_m | - (type << q_vector->intr_reg.dyn_ctl_itridx_s) | - (itr << (q_vector->intr_reg.dyn_ctl_intrvl_s -1 )); + itr_val |= (type << q_vector->intr_reg.dyn_ctl_itridx_s) | + (itr << (q_vector->intr_reg.dyn_ctl_intrvl_s -1 ));return itr_val; }
@@ -3614,9 +3624,8 @@ void idpf_vport_intr_update_itr_ena_irq(/* net_dim() updates ITR out-of-band using a work item */ idpf_net_dim (q_vector); + intval =idpf_vport_intr_buildreg_itr (q_vector); q_vector->wb_on_itr = false; - intval =idpf_vport_intr_buildreg_itr (q_vector, -IDPF_NO_ITR_UPDATE_IDX ,0 );writel (intval, q_vector->intr_reg.dyn_ctl); }
Greg Kroah-Hartman
maintainerPATCH 539/567 idpf: add support for Tx refillqs in flow scheduling mode
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit cb83b559bea39f207ee214ee2972657e8576ed18 ]
Changes from original commit:
- Adjusted idpf_tx_queue assert size to align with 6.12 struct definition
In certain production environments, it is possible for completion tags to collide, meaning N packets with the same completion tag are in flight at the same time. In this environment, any given Tx queue is effectively used to send both slower traffic and higher throughput traffic simultaneously. This is the result of a customer's specific configuration in the device pipeline, the details of which Intel cannot provide. This configuration results in a small number of out-of-order completions, i.e., a small number of packets in flight. The existing guardrails in the driver only protect against a large number of packets in flight. The slower flow completions are delayed which causes the out-of-order completions. The fast flow will continue sending traffic and generating tags. Because tags are generated on the fly, the fast flow eventually uses the same tag for a packet that is still in flight from the slower flow. The driver has no idea which packet it should clean when it processes the completion with that tag, but it will look for the packet on the buffer ring before the hash table. If the slower flow packet completion is processed first, it will end up cleaning the fast flow packet on the ring prematurely. This leaves the descriptor ring in a bad state resulting in a crash or Tx timeout.
In summary, generating a tag when a packet is sent can lead to the same tag being associated with multiple packets. This can lead to resource leaks, crashes, and/or Tx timeouts.
Before we can replace the tag generation, we need a new mechanism for the send path to know what tag to use next. The driver will allocate and initialize a refillq for each TxQ with all of the possible free tag values. During send, the driver grabs the next free tag from the refillq from next_to_clean. While cleaning the packet, the clean routine posts the tag back to the refillq's next_to_use to indicate that it is now free to use.
This mechanism works exactly the same way as the existing Rx refill queues, which post the cleaned buffer IDs back to the buffer queue to be reposted to HW. Since we're using the refillqs for both Rx and Tx now, genericize some of the existing refillq support.
Note: the refillqs will not be used yet. This is only demonstrating how they will be used to pass free tags back to the send path.
--- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 93 +++++++++++++++++++++++++--- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 8 +- 2 files changed, 91 insertions(+), 10 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_txrx.c +86 -7
@@ -158,6 +158,9 @@ static void idpf_tx_desc_rel(struct idpfif (!txq->desc_ring)return ; +if (txq->refillq) +kfree (txq->refillq->ring); +dmam_free_coherent (txq->dev, txq->size, txq->desc_ring, txq->dma); txq->desc_ring =NULL ; txq->next_to_use =0 ;
@@ -263,6 +266,7 @@ static int idpf_tx_desc_alloc(const stru struct idpf_tx_queue *tx_q) {struct device *dev = tx_q->dev; +struct idpf_sw_queue *refillq;int err; err =idpf_tx_buf_alloc_all (tx_q);
@@ -286,6 +290,29 @@ static int idpf_tx_desc_alloc(const stru tx_q->next_to_clean =0 ;idpf_queue_set (GEN_CHK , tx_q); +if (!idpf_queue_has (FLOW_SCH_EN , tx_q)) +return 0 ; + + refillq = tx_q->refillq; + refillq->desc_count = tx_q->desc_count; + refillq->ring =kcalloc (refillq->desc_count,sizeof (u32), +GFP_KERNEL ); +if (!refillq->ring) { + err = -ENOMEM ; + goto err_alloc; + } + +for (unsigned int i =0 ; i < refillq->desc_count; i++) + refillq->ring[i] = +FIELD_PREP (IDPF_RFL_BI_BUFID_M , i) | +FIELD_PREP (IDPF_RFL_BI_GEN_M , +idpf_queue_has (GEN_CHK , refillq)); + +/* Go ahead and flip the GEN bit since this counts as filling +* up the ring, i.e. we already ring wrapped. +*/ +idpf_queue_change (GEN_CHK , refillq); +return 0 ; err_alloc:
@@ -622,18 +649,18 @@ static int idpf_rx_hdr_buf_alloc_all(str }/** -* idpf_rx_post_buf_refill - Post buffer id to refill queue +* idpf_post_buf_refill - Post buffer id to refill queue * @refillq: refill queue to post to * @buf_id: buffer id to post */ -static void idpf_rx_post_buf_refill (struct idpf_sw_queue *refillq,u16 buf_id) +static void idpf_post_buf_refill (struct idpf_sw_queue *refillq,u16 buf_id) {u32 nta = refillq->next_to_use;/* store the buffer ID and the SW maintained GEN bit to the refillq */ refillq->ring[nta] = -FIELD_PREP (IDPF_RX_BI_BUFID_M , buf_id) | -FIELD_PREP (IDPF_RX_BI_GEN_M , +FIELD_PREP (IDPF_RFL_BI_BUFID_M , buf_id) | +FIELD_PREP (IDPF_RFL_BI_GEN_M ,idpf_queue_has (GEN_CHK , refillq));if (unlikely (++nta == refillq->desc_count)) {
@@ -1014,6 +1041,11 @@ static void idpf_txq_group_rel(struct idstruct idpf_txq_group *txq_grp = &vport->txq_grps[i];for (j =0 ; j < txq_grp->num_txq; j++) { +if (flow_sch_en) { +kfree (txq_grp->txqs[j]->refillq); + txq_grp->txqs[j]->refillq =NULL ; + } +kfree (txq_grp->txqs[j]); txq_grp->txqs[j] =NULL ; }
@@ -1425,6 +1457,13 @@ static int idpf_txq_group_alloc(struct i }idpf_queue_set (FLOW_SCH_EN , q); + + q->refillq =kzalloc (sizeof (*q->refillq),GFP_KERNEL ); +if (!q->refillq) + goto err_alloc; + +idpf_queue_set (GEN_CHK , q->refillq); +idpf_queue_set (RFL_GEN_CHK , q->refillq); }if (!split)
@@ -1973,6 +2012,8 @@ static void idpf_tx_handle_rs_completion compl_tag =le16_to_cpu (desc->q_head_compl_tag.compl_tag); +idpf_post_buf_refill (txq->refillq, compl_tag); +/* If we didn't clean anything on the ring, this packet must be * in the hash table. Go clean it there. */
@@ -2333,6 +2374,37 @@ static unsigned int idpf_tx_splitq_bump_ }/** +* idpf_tx_get_free_buf_id - get a free buffer ID from the refill queue +* @refillq: refill queue to get buffer ID from +* @buf_id: return buffer ID +* +* Return: true if a buffer ID was found, false if not +*/ +static bool idpf_tx_get_free_buf_id (struct idpf_sw_queue *refillq, +u16 *buf_id) +{ +u32 ntc = refillq->next_to_clean; +u32 refill_desc; + + refill_desc = refillq->ring[ntc]; + +if (unlikely (idpf_queue_has (RFL_GEN_CHK , refillq) != + !!(refill_desc &IDPF_RFL_BI_GEN_M ))) +return false; + + *buf_id =FIELD_GET (IDPF_RFL_BI_BUFID_M , refill_desc); + +if (unlikely (++ntc == refillq->desc_count)) { +idpf_queue_change (RFL_GEN_CHK , refillq); + ntc =0 ; + } + + refillq->next_to_clean = ntc; + +return true; +} + +/** * idpf_tx_splitq_map - Build the Tx flex descriptor * @tx_q: queue to send buffer on * @params: pointer to splitq params struct
@@ -2702,6 +2774,13 @@ static netdev_tx_t idpf_tx_splitq_frame( }if (idpf_queue_has (FLOW_SCH_EN , tx_q)) { +if (unlikely (!idpf_tx_get_free_buf_id (tx_q->refillq, + &tx_params.compl_tag))) { +u64_stats_update_begin (&tx_q->stats_sync); +u64_stats_inc (&tx_q->q_stats.q_busy); +u64_stats_update_end (&tx_q->stats_sync); + } + tx_params.dtype =IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE ; tx_params.eop_cmd =IDPF_TXD_FLEX_FLOW_CMD_EOP ;/* Set the RE bit to catch any packets that may have not been
@@ -3220,7 +3299,7 @@ payload: skip_data: rx_buf->page =NULL ; -idpf_rx_post_buf_refill (refillq, buf_id); +idpf_post_buf_refill (refillq, buf_id);IDPF_RX_BUMP_NTC (rxq, ntc);/* skip if it is non EOP desc */
@@ -3328,10 +3407,10 @@ static void idpf_rx_clean_refillq(structbool failure;if (idpf_queue_has (RFL_GEN_CHK , refillq) != - !!(refill_desc &IDPF_RX_BI_GEN_M )) + !!(refill_desc &IDPF_RFL_BI_GEN_M ))break ; - buf_id =FIELD_GET (IDPF_RX_BI_BUFID_M , refill_desc); + buf_id =FIELD_GET (IDPF_RFL_BI_BUFID_M , refill_desc); failure =idpf_rx_update_bufq_desc (bufq, buf_id, buf_desc);if (failure)break ;
drivers/net/ethernet/intel/idpf/idpf_txrx.h +5 -3
@@ -107,8 +107,8 @@ do { \*/ #define IDPF_TX_SPLITQ_RE_MIN_GAP 64 -#define IDPF_RX_BI_GEN_M BIT(16) -#define IDPF_RX_BI_BUFID_M GENMASK(15, 0) +#define IDPF_RFL_BI_GEN_M BIT(16) +#define IDPF_RFL_BI_BUFID_M GENMASK(15, 0)#define IDPF_RXD_EOF_SPLITQ VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_EOF_M#define IDPF_RXD_EOF_SINGLEQ VIRTCHNL2_RX_BASE_DESC_STATUS_EOF_M
@@ -635,6 +635,7 @@ libeth_cacheline_set_assert(struct idpf_* @cleaned_pkts: Number of packets cleaned for the above said case * @tx_max_bufs: Max buffers that can be transmitted with scatter-gather * @stash: Tx buffer stash for Flow-based scheduling mode +* @refillq: Pointer to refill queue * @compl_tag_bufid_m: Completion tag buffer id mask * @compl_tag_cur_gen: Used to keep track of current completion tag generation * @compl_tag_gen_max: To determine when compl_tag_cur_gen should be reset
@@ -682,6 +683,7 @@ struct idpf_tx_queue {u16 tx_max_bufs;struct idpf_txq_stash *stash; +struct idpf_sw_queue *refillq;u16 compl_tag_bufid_m;u16 compl_tag_cur_gen;
@@ -700,7 +702,7 @@ struct idpf_tx_queue {__cacheline_group_end_aligned (cold); }; libeth_cacheline_set_assert(struct idpf_tx_queue, 64, -88 +sizeof (struct u64_stats_sync ), +96 +sizeof (struct u64_stats_sync ), 24);/**
Greg Kroah-Hartman
maintainerPATCH 540/567 idpf: improve when to set RE bit logic
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit f2d18e16479cac7a708d77cbfb4220a9114a71fc ]
Track the gap between next_to_use and the last RE index. Set RE again if the gap is large enough to ensure RE bit is set frequently. This is critical before removing the stashing mechanisms because the opportunistic descriptor ring cleaning from the out-of-order completions will go away. Previously the descriptors would be "cleaned" by both the descriptor (RE) completion and the out-of-order completions. Without the latter, we must ensure the RE bit is set more frequently. Otherwise, it's theoretically possible for the descriptor ring next_to_clean to never advance. The previous implementation was dependent on the start of a packet falling on a 64th index in the descriptor ring, which is not guaranteed with large packets.
--- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 20 +++++++++++++++++++- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 6 ++++-- 2 files changed, 23 insertions(+), 3 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_txrx.c +19 -1
@@ -313,6 +313,8 @@ static int idpf_tx_desc_alloc(const stru*/ idpf_queue_change (GEN_CHK , refillq); + tx_q->last_re = tx_q->desc_count -IDPF_TX_SPLITQ_RE_MIN_GAP ; +return 0 ; err_alloc:
@@ -2709,6 +2711,21 @@ netdev_tx_t idpf_tx_drop_skb(struct idpf }/** +* idpf_tx_splitq_need_re - check whether RE bit needs to be set +* @tx_q: pointer to Tx queue +* +* Return: true if RE bit needs to be set, false otherwise +*/ +static bool idpf_tx_splitq_need_re (struct idpf_tx_queue *tx_q) +{ +int gap = tx_q->next_to_use - tx_q->last_re; + + gap += (gap <0 ) ? tx_q->desc_count :0 ; + +return gap >=IDPF_TX_SPLITQ_RE_MIN_GAP ; +} + +/** * idpf_tx_splitq_frame - Sends buffer on Tx ring using flex descriptors * @skb: send buffer * @tx_q: queue to send buffer on
@@ -2788,9 +2805,10 @@ static netdev_tx_t idpf_tx_splitq_frame(* MIN_RING size to ensure it will be set at least once each * time around the ring. */ -if (!(tx_q->next_to_use %IDPF_TX_SPLITQ_RE_MIN_GAP )) { +if (idpf_tx_splitq_need_re (tx_q)) { tx_params.eop_cmd |=IDPF_TXD_FLEX_FLOW_CMD_RE ; tx_q->txq_grp->num_completions_pending++; + tx_q->last_re = tx_q->next_to_use; }if (skb->ip_summed ==CHECKSUM_PARTIAL )
drivers/net/ethernet/intel/idpf/idpf_txrx.h +4 -2
@@ -623,6 +623,8 @@ libeth_cacheline_set_assert(struct idpf_* @netdev: &net_device corresponding to this queue * @next_to_use: Next descriptor to use * @next_to_clean: Next descriptor to clean +* @last_re: last descriptor index that RE bit was set +* @tx_max_bufs: Max buffers that can be transmitted with scatter-gather * @cleaned_bytes: Splitq only, TXQ only: When a TX completion is received on * the TX completion queue, it can be for any TXQ associated * with that completion queue. This means we can clean up to
@@ -633,7 +635,6 @@ libeth_cacheline_set_assert(struct idpf_* only once at the end of the cleaning routine. * @clean_budget: singleq only, queue cleaning budget * @cleaned_pkts: Number of packets cleaned for the above said case -* @tx_max_bufs: Max buffers that can be transmitted with scatter-gather * @stash: Tx buffer stash for Flow-based scheduling mode * @refillq: Pointer to refill queue * @compl_tag_bufid_m: Completion tag buffer id mask
@@ -674,6 +675,8 @@ struct idpf_tx_queue {__cacheline_group_begin_aligned (read_write);u16 next_to_use;u16 next_to_clean; +u16 last_re; +u16 tx_max_bufs;union {u32 cleaned_bytes;
@@ -681,7 +684,6 @@ struct idpf_tx_queue { };u16 cleaned_pkts; -u16 tx_max_bufs;struct idpf_txq_stash *stash;struct idpf_sw_queue *refillq;
Greg Kroah-Hartman
maintainerPATCH 541/567 idpf: simplify and fix splitq Tx packet rollback error path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit b61dfa9bc4430ad82b96d3a7c1c485350f91b467 ]
Move (and rename) the existing rollback logic to singleq.c since that will be the only consumer. Create a simplified splitq specific rollback function to loop through and unmap tx_bufs based on the completion tag. This is critical before replacing the Tx buffer ring with the buffer pool since the previous rollback indexing will not work to unmap the chained buffers from the pool.
Cache the next_to_use index before any portion of the packet is put on the descriptor ring. In case of an error, the rollback will bump tail to the correct next_to_use value. Because the splitq path now supports different types of context descriptors (and potentially multiple in the future), this will take care of rolling back any and all context descriptors encoded on the ring for the erroneous packet. The previous rollback logic was broken for PTP packets since it would not account for the PTP context descriptor.
--- drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c | 57 ++++++++++++ drivers/net/ethernet/intel/idpf/idpf_txrx.c | 91 ++++++++------------ drivers/net/ethernet/intel/idpf/idpf_txrx.h | 5 - 3 files changed, 95 insertions(+), 58 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c +55 -2
@@ -180,6 +180,58 @@ static int idpf_tx_singleq_csum(struct s }/** +* idpf_tx_singleq_dma_map_error - handle TX DMA map errors +* @txq: queue to send buffer on +* @skb: send buffer +* @first: original first buffer info buffer for packet +* @idx: starting point on ring to unwind +*/ +static void idpf_tx_singleq_dma_map_error (struct idpf_tx_queue *txq, +struct sk_buff *skb, +struct idpf_tx_buf *first,u16 idx) +{ +struct libeth_sq_napi_stats ss = { }; +struct libeth_cq_pp cp = { + .dev = txq->dev, + .ss = &ss, + }; + +u64_stats_update_begin (&txq->stats_sync); +u64_stats_inc (&txq->q_stats.dma_map_errs); +u64_stats_update_end (&txq->stats_sync); + +/* clear dma mappings for failed tx_buf map */ +for (;;) { +struct idpf_tx_buf *tx_buf; + + tx_buf = &txq->tx_buf[idx]; +libeth_tx_complete (tx_buf, &cp); +if (tx_buf == first) +break ; +if (idx ==0 ) + idx = txq->desc_count; + idx--; + } + +if (skb_is_gso (skb)) { +union idpf_tx_flex_desc *tx_desc; + +/* If we failed a DMA mapping for a TSO packet, we will have +* used one additional descriptor for a context +* descriptor. Reset that here. +*/ + tx_desc = &txq->flex_tx[idx]; +memset (tx_desc,0 ,sizeof (*tx_desc)); +if (idx ==0 ) + idx = txq->desc_count; + idx--; + } + +/* Update tail in case netdev_xmit_more was previously true */ +idpf_tx_buf_hw_update (txq, idx, false); +} + +/** * idpf_tx_singleq_map - Build the Tx base descriptor * @tx_q: queue to send buffer on * @first: first buffer info buffer to use
@@ -219,8 +271,9 @@ static void idpf_tx_singleq_map(struct ifor (frag = &skb_shinfo (skb)->frags[0 ];; frag++) {unsigned int max_data =IDPF_TX_MAX_DESC_DATA_ALIGNED ; -if (dma_mapping_error (tx_q->dev, dma)) -return idpf_tx_dma_map_error (tx_q, skb, first, i); +if (unlikely (dma_mapping_error (tx_q->dev, dma))) +return idpf_tx_singleq_dma_map_error (tx_q, skb, + first, i);/* record length, and DMA address */ dma_unmap_len_set (tx_buf, len, size);
drivers/net/ethernet/intel/idpf/idpf_txrx.c +37 -54
115 lines not on this page · raw message
drivers/net/ethernet/intel/idpf/idpf_txrx.h +3 -2
@@ -194,6 +194,7 @@ struct idpf_tx_offload_params {* @compl_tag: Associated tag for completion * @td_tag: Descriptor tunneling tag * @offload: Offload parameters +* @prev_ntu: stored TxQ next_to_use in case of rollback */ struct idpf_tx_splitq_params {enum idpf_tx_desc_dtype_value dtype;
@@ -204,6 +205,8 @@ struct idpf_tx_splitq_params { };struct idpf_tx_offload_params offload; + +u16 prev_ntu; };enum idpf_tx_ctx_desc_eipt_offload {
@@ -1050,8 +1053,6 @@ void idpf_tx_buf_hw_update(struct idpf_t bool xmit_more);unsigned int idpf_size_to_txd_count (unsigned int size);netdev_tx_t idpf_tx_drop_skb (struct idpf_tx_queue *tx_q,struct sk_buff *skb); -void idpf_tx_dma_map_error (struct idpf_tx_queue *txq,struct sk_buff *skb, -struct idpf_tx_buf *first,u16 ring_idx);unsigned int idpf_tx_desc_count_required (struct idpf_tx_queue *txq,struct sk_buff *skb);void idpf_tx_timeout (struct net_device *netdev,unsigned int txqueue);
Greg Kroah-Hartman
maintainerPATCH 542/567 idpf: replace flow scheduling buffer ring with buffer pool
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit 5f417d551324d2894168b362f2429d120ab06243 ]
Replace the TxQ buffer ring with one large pool/array of buffers (only for flow scheduling). This eliminates the tag generation and makes it impossible for a tag to be associated with more than one packet.
The completion tag passed to HW through the descriptor is the index into the array. That same completion tag is posted back to the driver in the completion descriptor, and used to index into the array to quickly retrieve the buffer during cleaning. In this way, the tags are treated as a fix sized resource. If all tags are in use, no more packets can be sent on that particular queue (until some are freed up). The tag pool size is 64K since the completion tag width is 16 bits.
For each packet, the driver pulls a free tag from the refillq to get the next free buffer index. When cleaning is complete, the tag is posted back to the refillq. A multi-frag packet spans multiple buffers in the driver, therefore it uses multiple buffer indexes/tags from the pool. Each frag pulls from the refillq to get the next free buffer index. These are tracked in a next_buf field that replaces the completion tag field in the buffer struct. This chains the buffers together so that the packet can be cleaned from the starting completion tag taken from the completion descriptor, then from the next_buf field for each subsequent buffer.
In case of a dma_mapping_error occurs or the refillq runs out of free buf_ids, the packet will execute the rollback error path. This unmaps any buffers previously mapped for the packet. Since several free buf_ids could have already been pulled from the refillq, we need to restore its original state as well. Otherwise, the buf_ids/tags will be leaked and not used again until the queue is reallocated.
Descriptor completions only advance the descriptor ring index to "clean" the descriptors. The packet completions only clean the buffers associated with the given packet completion tag and do not update the descriptor ring index.
When operating in queue based scheduling mode, the array still acts as a ring and will only have TxQ descriptor count entries. The tx_bufs are still associated 1:1 with the descriptor ring entries and we can use the conventional indexing mechanisms.
--- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 209 ++++++++++++---------------- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 10 + 2 files changed, 106 insertions(+), 113 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_txrx.c +97 -112
@@ -12,6 +12,7 @@ struct idpf_tx_stash {struct libeth_sqe buf; }; +#define idpf_tx_buf_next (buf) (*(u32 *)&(buf)->priv)#define idpf_tx_buf_compl_tag (buf) (*(u32 *)&(buf)->priv)LIBETH_SQE_CHECK_PRIV (u32);
@@ -110,7 +111,7 @@ static void idpf_tx_buf_rel_all(struct ireturn ;/* Free all the Tx buffer sk_buffs */ -for (i =0 ; i < txq->desc_count; i++) +for (i =0 ; i < txq->buf_pool_size; i++)libeth_tx_complete (&txq->tx_buf[i], &cp);kfree (txq->tx_buf);
@@ -218,14 +219,17 @@ static void idpf_tx_desc_rel_all(structstatic int idpf_tx_buf_alloc_all (struct idpf_tx_queue *tx_q) {struct idpf_buf_lifo *buf_stack; -int buf_size;int i;/* Allocate book keeping buffers only. Buffers to be supplied to HW * are allocated by kernel network stack and received as part of skb */ - buf_size =sizeof (struct idpf_tx_buf ) * tx_q->desc_count; - tx_q->tx_buf =kzalloc (buf_size,GFP_KERNEL ); +if (idpf_queue_has (FLOW_SCH_EN , tx_q)) + tx_q->buf_pool_size =U16_MAX ; +else + tx_q->buf_pool_size = tx_q->desc_count; + tx_q->tx_buf =kcalloc (tx_q->buf_pool_size,sizeof (*tx_q->tx_buf), +GFP_KERNEL );if (!tx_q->tx_buf)return -ENOMEM ;
@@ -294,7 +298,7 @@ static int idpf_tx_desc_alloc(const strureturn 0 ; refillq = tx_q->refillq; - refillq->desc_count = tx_q->desc_count; + refillq->desc_count = tx_q->buf_pool_size; refillq->ring =kcalloc (refillq->desc_count,sizeof (u32),GFP_KERNEL );if (!refillq->ring) {
@@ -1841,6 +1845,12 @@ static bool idpf_tx_splitq_clean(structstruct idpf_tx_buf *tx_buf;bool clean_complete = true; +if (descs_only) { +/* Bump ring index to mark as cleaned. */ + tx_q->next_to_clean = end; +return true; + } + tx_desc = &tx_q->flex_tx[ntc]; next_pending_desc = &tx_q->flex_tx[end]; tx_buf = &tx_q->tx_buf[ntc];
@@ -1907,83 +1917,40 @@ do { \ }while (0 )/** -* idpf_tx_clean_buf_ring - clean flow scheduling TX queue buffers +* idpf_tx_clean_bufs - clean flow scheduling TX queue buffers * @txq: queue to clean -* @compl_tag: completion tag of packet to clean (from completion descriptor) +* @buf_id: packet's starting buffer ID, from completion descriptor * @cleaned: pointer to stats struct to track cleaned packets/bytes * @budget: Used to determine if we are in netpoll * -* Cleans all buffers associated with the input completion tag either from the -* TX buffer ring or from the hash table if the buffers were previously -* stashed. Returns the byte/segment count for the cleaned packet associated -* this completion tag. -*/ -static bool idpf_tx_clean_buf_ring(struct idpf_tx_queue *txq, u16 compl_tag, -struct libeth_sq_napi_stats *cleaned, -int budget) +* Clean all buffers associated with the packet starting at buf_id. Returns the +* byte/segment count for the cleaned packet. +*/ +static bool idpf_tx_clean_bufs(struct idpf_tx_queue *txq, u32 buf_id, +struct libeth_sq_napi_stats *cleaned, +int budget) { -u16 idx = compl_tag & txq->compl_tag_bufid_m;struct idpf_tx_buf *tx_buf =NULL ;struct libeth_cq_pp cp = { .dev = txq->dev, .ss = cleaned, .napi = budget, }; -u16 ntc, orig_idx = idx; - - tx_buf = &txq->tx_buf[idx]; - -if (unlikely (tx_buf->type <=LIBETH_SQE_CTX || -idpf_tx_buf_compl_tag (tx_buf) != compl_tag)) -return false; -if (tx_buf->type ==LIBETH_SQE_SKB ) + tx_buf = &txq->tx_buf[buf_id]; +if (tx_buf->type ==LIBETH_SQE_SKB ) {libeth_tx_complete (tx_buf, &cp); +idpf_post_buf_refill (txq->refillq, buf_id); + } -idpf_tx_clean_buf_ring_bump_ntc (txq, idx, tx_buf); +while (idpf_tx_buf_next (tx_buf) !=IDPF_TXBUF_NULL ) { + buf_id =idpf_tx_buf_next (tx_buf); -while (idpf_tx_buf_compl_tag (tx_buf) == compl_tag) { + tx_buf = &txq->tx_buf[buf_id];libeth_tx_complete (tx_buf, &cp); -idpf_tx_clean_buf_ring_bump_ntc (txq, idx, tx_buf); +idpf_post_buf_refill (txq->refillq, buf_id); } -/* -* It's possible the packet we just cleaned was an out of order -* completion, which means we can stash the buffers starting from -* the original next_to_clean and reuse the descriptors. We need -* to compare the descriptor ring next_to_clean packet's "first" buffer -* to the "first" buffer of the packet we just cleaned to determine if -* this is the case. Howevever, next_to_clean can point to either a -* reserved buffer that corresponds to a context descriptor used for the -* next_to_clean packet (TSO packet) or the "first" buffer (single -* packet). The orig_idx from the packet we just cleaned will always -* point to the "first" buffer. If next_to_clean points to a reserved -* buffer, let's bump ntc once and start the comparison from there. -*/ - ntc = txq->next_to_clean; - tx_buf = &txq->tx_buf[ntc]; - -if (tx_buf->type ==LIBETH_SQE_CTX ) -idpf_tx_clean_buf_ring_bump_ntc (txq, ntc, tx_buf); - -/* -* If ntc still points to a different "first" buffer, clean the -* descriptor ring and stash all of the buffers for later cleaning. If -* we cannot stash all of the buffers, next_to_clean will point to the -* "first" buffer of the packet that could not be stashed and cleaning -* will start there next time. -*/ -if (unlikely (tx_buf != &txq->tx_buf[orig_idx] && - !idpf_tx_splitq_clean (txq, orig_idx, budget, cleaned, - true))) -return true; - -/* -* Otherwise, update next_to_clean to reflect the cleaning that was -* done above. -*/ - txq->next_to_clean = idx; -return true; }
@@ -2014,12 +1981,10 @@ static void idpf_tx_handle_rs_completion compl_tag =le16_to_cpu (desc->q_head_compl_tag.compl_tag); -idpf_post_buf_refill (txq->refillq, compl_tag); -/* If we didn't clean anything on the ring, this packet must be * in the hash table. Go clean it there. */ -if (!idpf_tx_clean_buf_ring (txq, compl_tag, cleaned, budget)) +if (!idpf_tx_clean_bufs (txq, compl_tag, cleaned, budget))idpf_tx_clean_stashed_bufs (txq, compl_tag, cleaned, budget); }
@@ -2332,7 +2297,7 @@ static unsigned int idpf_tx_splitq_bump_* Return: true if a buffer ID was found, false if not */ static bool idpf_tx_get_free_buf_id (struct idpf_sw_queue *refillq, -u16 *buf_id) +u32 *buf_id) {u32 ntc = refillq->next_to_clean;u32 refill_desc;
@@ -2365,25 +2330,34 @@ static void idpf_tx_splitq_pkt_err_unmapstruct idpf_tx_splitq_params *params, struct idpf_tx_buf *first) { + struct idpf_sw_queue *refillq = txq->refillq;struct libeth_sq_napi_stats ss = { };struct idpf_tx_buf *tx_buf = first;struct libeth_cq_pp cp = { .dev = txq->dev, .ss = &ss, }; -u32 idx =0 ;u64_stats_update_begin (&txq->stats_sync);u64_stats_inc (&txq->q_stats.dma_map_errs);u64_stats_update_end (&txq->stats_sync); -do { +libeth_tx_complete (tx_buf, &cp); +while (idpf_tx_buf_next (tx_buf) !=IDPF_TXBUF_NULL ) { + tx_buf = &txq->tx_buf[idpf_tx_buf_next (tx_buf)];libeth_tx_complete (tx_buf, &cp); -idpf_tx_clean_buf_ring_bump_ntc (txq, idx, tx_buf); - }while (idpf_tx_buf_compl_tag (tx_buf) == params->compl_tag); + }/* Update tail in case netdev_xmit_more was previously true. */ idpf_tx_buf_hw_update (txq, params->prev_ntu, false); + +if (!refillq) +return ; + +/* Restore refillq state to avoid leaking tags. */ +if (params->prev_refill_gen !=idpf_queue_has (RFL_GEN_CHK , refillq)) +idpf_queue_change (RFL_GEN_CHK , refillq); + refillq->next_to_clean = params->prev_refill_ntc; }/**
@@ -2407,6 +2381,7 @@ static void idpf_tx_splitq_map(struct idstruct netdev_queue *nq;struct sk_buff *skb;skb_frag_t *frag; +u32 next_buf_id;u16 td_cmd =0 ;dma_addr_t dma;
@@ -2424,18 +2399,16 @@ static void idpf_tx_splitq_map(struct id tx_buf = first; first->nr_frags =0 ; - params->compl_tag = - (tx_q->compl_tag_cur_gen << tx_q->compl_tag_gen_s) | i; -for (frag = &skb_shinfo (skb)->frags[0 ];; frag++) {unsigned int max_data =IDPF_TX_MAX_DESC_DATA_ALIGNED ; -if (unlikely (dma_mapping_error (tx_q->dev, dma))) +if (unlikely (dma_mapping_error (tx_q->dev, dma))) { +idpf_tx_buf_next (tx_buf) =IDPF_TXBUF_NULL ;return idpf_tx_splitq_pkt_err_unmap (tx_q, params, first); + } first->nr_frags++; -idpf_tx_buf_compl_tag (tx_buf) = params->compl_tag; tx_buf->type =LIBETH_SQE_FRAG ;/* record length, and DMA address */
@@ -2491,29 +2464,14 @@ static void idpf_tx_splitq_map(struct id max_data);if (unlikely (++i == tx_q->desc_count)) { - tx_buf = tx_q->tx_buf; tx_desc = &tx_q->flex_tx[0 ]; i =0 ; tx_q->compl_tag_cur_gen =IDPF_TX_ADJ_COMPL_TAG_GEN (tx_q); }else { - tx_buf++; tx_desc++; } -/* Since this packet has a buffer that is going to span -* multiple descriptors, it's going to leave holes in -* to the TX buffer ring. To ensure these holes do not -* cause issues in the cleaning routines, we will clear -* them of any stale data and assign them the same -* completion tag as the current packet. Then when the -* packet is being cleaned, the cleaning routines will -* simply pass over these holes and finish cleaning the -* rest of the packet. -*/ - tx_buf->type =LIBETH_SQE_EMPTY ; -idpf_tx_buf_compl_tag (tx_buf) = params->compl_tag; -/* Adjust the DMA offset and the remaining size of the * fragment. On the first iteration of this loop, * max_data will be >= 12K and <= 16K-1. On any
@@ -2538,15 +2496,26 @@ static void idpf_tx_splitq_map(struct ididpf_tx_splitq_build_desc (tx_desc, params, td_cmd, size);if (unlikely (++i == tx_q->desc_count)) { - tx_buf = tx_q->tx_buf; tx_desc = &tx_q->flex_tx[0 ]; i =0 ; tx_q->compl_tag_cur_gen =IDPF_TX_ADJ_COMPL_TAG_GEN (tx_q); }else { - tx_buf++; tx_desc++; } +if (idpf_queue_has (FLOW_SCH_EN , tx_q)) { +if (unlikely (!idpf_tx_get_free_buf_id (tx_q->refillq, + &next_buf_id))) { +idpf_tx_buf_next (tx_buf) =IDPF_TXBUF_NULL ; +return idpf_tx_splitq_pkt_err_unmap (tx_q, params, + first); + } + }else { + next_buf_id = i; + } +idpf_tx_buf_next (tx_buf) = next_buf_id; + tx_buf = &tx_q->tx_buf[next_buf_id]; + size =skb_frag_size (frag); data_len -= size;
@@ -2561,6 +2530,7 @@ static void idpf_tx_splitq_map(struct id/* write last descriptor with RS and EOP bits */ first->rs_idx = i; +idpf_tx_buf_next (tx_buf) =IDPF_TXBUF_NULL ; td_cmd |= params->eop_cmd;idpf_tx_splitq_build_desc (tx_desc, params, td_cmd, size); i =idpf_tx_splitq_bump_ntu (tx_q, i);
@@ -2664,8 +2634,6 @@ idpf_tx_splitq_get_ctx_desc(struct idpf_struct idpf_flex_tx_ctx_desc *desc;int i = txq->next_to_use; - txq->tx_buf[i].type =LIBETH_SQE_CTX ; -/* grab the next descriptor */ desc = &txq->flex_ctx[i]; txq->next_to_use =idpf_tx_splitq_bump_ntu (txq, i);
@@ -2721,6 +2689,7 @@ static netdev_tx_t idpf_tx_splitq_frame( };struct idpf_tx_buf *first;unsigned int count; +u32 buf_id;int tso; count =idpf_tx_desc_count_required (tx_q, skb);
@@ -2760,26 +2729,28 @@ static netdev_tx_t idpf_tx_splitq_frame(u64_stats_update_end (&tx_q->stats_sync); } -/* record the location of the first descriptor for this packet */ - first = &tx_q->tx_buf[tx_q->next_to_use]; - first->skb = skb; +if (idpf_queue_has (FLOW_SCH_EN , tx_q)) { +struct idpf_sw_queue *refillq = tx_q->refillq; -if (tso) { - first->packets = tx_params.offload.tso_segs; - first->bytes = skb->len + - ((first->packets -1 ) * tx_params.offload.tso_hdr_len); - }else { - first->packets =1 ; - first->bytes = max_t(unsigned int, skb->len, ETH_ZLEN); - } +/* Save refillq state in case of a packet rollback. Otherwise, +* the tags will be leaked since they will be popped from the +* refillq but never reposted during cleaning. +*/ + tx_params.prev_refill_gen = +idpf_queue_has (RFL_GEN_CHK , refillq); + tx_params.prev_refill_ntc = refillq->next_to_clean; -if (idpf_queue_has (FLOW_SCH_EN , tx_q)) {if (unlikely (!idpf_tx_get_free_buf_id (tx_q->refillq, - &tx_params.compl_tag))) { -u64_stats_update_begin (&tx_q->stats_sync); -u64_stats_inc (&tx_q->q_stats.q_busy); -u64_stats_update_end (&tx_q->stats_sync); + &buf_id))) { +if (tx_params.prev_refill_gen != +idpf_queue_has (RFL_GEN_CHK , refillq)) +idpf_queue_change (RFL_GEN_CHK , refillq); + refillq->next_to_clean = tx_params.prev_refill_ntc; + + tx_q->next_to_use = tx_params.prev_ntu; +return idpf_tx_drop_skb (tx_q, skb); } + tx_params.compl_tag = buf_id; tx_params.dtype =IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE ; tx_params.eop_cmd =IDPF_TXD_FLEX_FLOW_CMD_EOP ;
@@ -2798,6 +2769,8 @@ static netdev_tx_t idpf_tx_splitq_frame( tx_params.offload.td_cmd |=IDPF_TXD_FLEX_FLOW_CMD_CS_EN ; }else { + buf_id = tx_q->next_to_use; + tx_params.dtype =IDPF_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2 ; tx_params.eop_cmd =IDPF_TXD_LAST_DESC_CMD ;
@@ -2805,6 +2778,18 @@ static netdev_tx_t idpf_tx_splitq_frame( tx_params.offload.td_cmd |=IDPF_TX_FLEX_DESC_CMD_CS_EN ; } + first = &tx_q->tx_buf[buf_id]; + first->skb = skb; + +if (tso) { + first->packets = tx_params.offload.tso_segs; + first->bytes = skb->len + + ((first->packets -1 ) * tx_params.offload.tso_hdr_len); + }else { + first->packets =1 ; + first->bytes = max_t(unsigned int, skb->len, ETH_ZLEN); + } +idpf_tx_splitq_map (tx_q, &tx_params, first);return NETDEV_TX_OK ;
drivers/net/ethernet/intel/idpf/idpf_txrx.h +9 -1
@@ -136,6 +136,8 @@ do { \ ((++(txq)->compl_tag_cur_gen) >= (txq)->compl_tag_gen_max ? \ 0 : (txq)->compl_tag_cur_gen) +#define IDPF_TXBUF_NULL U32_MAX +#define IDPF_TXD_LAST_DESC_CMD (IDPF_TX_DESC_CMD_EOP | IDPF_TX_DESC_CMD_RS)#define IDPF_TX_FLAGS_TSO BIT(0)
@@ -195,6 +197,8 @@ struct idpf_tx_offload_params {* @td_tag: Descriptor tunneling tag * @offload: Offload parameters * @prev_ntu: stored TxQ next_to_use in case of rollback +* @prev_refill_ntc: stored refillq next_to_clean in case of packet rollback +* @prev_refill_gen: stored refillq generation bit in case of packet rollback */ struct idpf_tx_splitq_params {enum idpf_tx_desc_dtype_value dtype;
@@ -207,6 +211,8 @@ struct idpf_tx_splitq_params {struct idpf_tx_offload_params offload;u16 prev_ntu; +u16 prev_refill_ntc; +bool prev_refill_gen; };enum idpf_tx_ctx_desc_eipt_offload {
@@ -649,6 +655,7 @@ libeth_cacheline_set_assert(struct idpf_* @size: Length of descriptor ring in bytes * @dma: Physical address of ring * @q_vector: Backreference to associated vector +* @buf_pool_size: Total number of idpf_tx_buf */ struct idpf_tx_queue {__cacheline_group_begin_aligned (read_mostly);
@@ -704,11 +711,12 @@ struct idpf_tx_queue {dma_addr_t dma;struct idpf_q_vector *q_vector; +u32 buf_pool_size;__cacheline_group_end_aligned (cold); }; libeth_cacheline_set_assert(struct idpf_tx_queue, 64,96 +sizeof (struct u64_stats_sync ), - 24); + 32);/** * struct idpf_buf_queue - software structure representing a buffer queue
Greg Kroah-Hartman
maintainerPATCH 543/567 idpf: stop Tx if there are insufficient buffer resources
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit 0c3f135e840d4a2ba4253e15d530ec61bc30718e ]
The Tx refillq logic will cause packets to be silently dropped if there are not enough buffer resources available to send a packet in flow scheduling mode. Instead, determine how many buffers are needed along with number of descriptors. Make sure there are enough of both resources to send the packet, and stop the queue if not.
--- drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c | 4 - drivers/net/ethernet/intel/idpf/idpf_txrx.c | 47 +++++++++++++------- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 15 +++++- 3 files changed, 47 insertions(+), 19 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c +2 -2
@@ -415,11 +415,11 @@ netdev_tx_t idpf_tx_singleq_frame(struct {struct idpf_tx_offload_params offload = { };struct idpf_tx_buf *first; +u32 count, buf_count =1 ;int csum, tso, needed; -unsigned int count;__be16 protocol; - count =idpf_tx_desc_count_required (tx_q, skb); + count =idpf_tx_res_count_required (tx_q, skb, &buf_count);if (unlikely (!count))return idpf_tx_drop_skb (tx_q, skb);
drivers/net/ethernet/intel/idpf/idpf_txrx.c +32 -15
99 lines not on this page · raw message
drivers/net/ethernet/intel/idpf/idpf_txrx.h +13 -2
@@ -1034,6 +1034,17 @@ static inline void idpf_vport_intr_set_w reg->dyn_ctl); } +/** +* idpf_tx_splitq_get_free_bufs - get number of free buf_ids in refillq +* @refillq: pointer to refillq containing buf_ids +*/ +static inline u32 idpf_tx_splitq_get_free_bufs (struct idpf_sw_queue *refillq) +{ +return (refillq->next_to_use > refillq->next_to_clean ? +0 : refillq->desc_count) + + refillq->next_to_use - refillq->next_to_clean -1 ; +} +int idpf_vport_singleq_napi_poll (struct napi_struct *napi,int budget);void idpf_vport_init_num_qs (struct idpf_vport *vport,struct virtchnl2_create_vport *vport_msg);
@@ -1061,8 +1072,8 @@ void idpf_tx_buf_hw_update(struct idpf_t bool xmit_more);unsigned int idpf_size_to_txd_count (unsigned int size);netdev_tx_t idpf_tx_drop_skb (struct idpf_tx_queue *tx_q,struct sk_buff *skb); -unsigned int idpf_tx_desc_count_required (struct idpf_tx_queue *txq, -struct sk_buff *skb); +unsigned int idpf_tx_res_count_required (struct idpf_tx_queue *txq, +struct sk_buff *skb,u32 *buf_count);void idpf_tx_timeout (struct net_device *netdev,unsigned int txqueue);netdev_tx_t idpf_tx_singleq_frame (struct sk_buff *skb,struct idpf_tx_queue *tx_q);
Greg Kroah-Hartman
maintainerPATCH 544/567 idpf: remove obsolete stashing code
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Hay <[email protected]>
[ Upstream commit 6c4e68480238274f84aa50d54da0d9e262df6284 ]
Changes from original commit:
- Adjusted idpf_tx_queue assert size to align with 6.12 struct definition
With the new Tx buffer management scheme, there is no need for all of the stashing mechanisms, the hash table, the reserve buffer stack, etc. Remove all of that.
--- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 309 +--------------------------- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 47 ---- 2 files changed, 22 insertions(+), 334 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_txrx.c +21 -288
@@ -7,13 +7,7 @@#include "idpf.h" #include "idpf_virtchnl.h" -struct idpf_tx_stash { -struct hlist_node hlist; -struct libeth_sqe buf; -}; -#define idpf_tx_buf_next (buf) (*(u32 *)&(buf)->priv) -#define idpf_tx_buf_compl_tag (buf) (*(u32 *)&(buf)->priv)LIBETH_SQE_CHECK_PRIV (u32);/**
@@ -40,36 +34,6 @@ static bool idpf_chk_linearize(const str }/** -* idpf_buf_lifo_push - push a buffer pointer onto stack -* @stack: pointer to stack struct -* @buf: pointer to buf to push -* -* Returns 0 on success, negative on failure -**/ -static int idpf_buf_lifo_push (struct idpf_buf_lifo *stack, -struct idpf_tx_stash *buf) -{ -if (unlikely (stack->top == stack->size)) -return -ENOSPC ; - - stack->bufs[stack->top++] = buf; - -return 0 ; -} - -/** -* idpf_buf_lifo_pop - pop a buffer pointer from stack -* @stack: pointer to stack struct -**/ -static struct idpf_tx_stash *idpf_buf_lifo_pop (struct idpf_buf_lifo *stack) -{ -if (unlikely (!stack->top)) -return NULL ; - -return stack->bufs[--stack->top]; -} - -/** * idpf_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure * @txqueue: TX queue
@@ -97,14 +61,11 @@ void idpf_tx_timeout(struct net_device *static void idpf_tx_buf_rel_all (struct idpf_tx_queue *txq) {struct libeth_sq_napi_stats ss = { }; -struct idpf_buf_lifo *buf_stack; -struct idpf_tx_stash *stash;struct libeth_cq_pp cp = { .dev = txq->dev, .ss = &ss, }; -struct hlist_node *tmp; -u32 i, tag; +u32 i;/* Buffers already cleared, nothing to do */ if (!txq->tx_buf)
@@ -116,33 +77,6 @@ static void idpf_tx_buf_rel_all(struct ikfree (txq->tx_buf); txq->tx_buf =NULL ; - -if (!idpf_queue_has (FLOW_SCH_EN , txq)) -return ; - - buf_stack = &txq->stash->buf_stack; -if (!buf_stack->bufs) -return ; - -/* -* If a Tx timeout occurred, there are potentially still bufs in the -* hash table, free them here. -*/ -hash_for_each_safe (txq->stash->sched_buf_hash, tag, tmp, stash, - hlist) { -if (!stash) -continue ; - -libeth_tx_complete (&stash->buf, &cp); -hash_del (&stash->hlist); -idpf_buf_lifo_push (buf_stack, stash); - } - -for (i =0 ; i < buf_stack->size; i++) -kfree (buf_stack->bufs[i]); - -kfree (buf_stack->bufs); - buf_stack->bufs =NULL ; }/**
@@ -218,9 +152,6 @@ static void idpf_tx_desc_rel_all(struct*/ static int idpf_tx_buf_alloc_all (struct idpf_tx_queue *tx_q) { -struct idpf_buf_lifo *buf_stack; -int i; -/* Allocate book keeping buffers only. Buffers to be supplied to HW * are allocated by kernel network stack and received as part of skb */
@@ -233,29 +164,6 @@ static int idpf_tx_buf_alloc_all(structif (!tx_q->tx_buf)return -ENOMEM ; -if (!idpf_queue_has (FLOW_SCH_EN , tx_q)) -return 0 ; - - buf_stack = &tx_q->stash->buf_stack; - -/* Initialize tx buf stack for out-of-order completions if -* flow scheduling offload is enabled -*/ - buf_stack->bufs =kcalloc (tx_q->desc_count,sizeof (*buf_stack->bufs), -GFP_KERNEL ); -if (!buf_stack->bufs) -return -ENOMEM ; - - buf_stack->size = tx_q->desc_count; - buf_stack->top = tx_q->desc_count; - -for (i =0 ; i < tx_q->desc_count; i++) { - buf_stack->bufs[i] =kzalloc (sizeof (*buf_stack->bufs[i]), -GFP_KERNEL ); -if (!buf_stack->bufs[i]) -return -ENOMEM ; - } -return 0 ; }
@@ -369,8 +277,6 @@ static int idpf_tx_desc_alloc_all(structfor (i =0 ; i < vport->num_txq_grp; i++) {for (j =0 ; j < vport->txq_grps[i].num_txq; j++) {struct idpf_tx_queue *txq = vport->txq_grps[i].txqs[j]; -u8 gen_bits =0 ; -u16 bufidx_mask; err =idpf_tx_desc_alloc (vport, txq);if (err) {
@@ -379,34 +285,6 @@ static int idpf_tx_desc_alloc_all(struct i); goto err_out; } - -if (!idpf_is_queue_model_split (vport->txq_model)) -continue ; - - txq->compl_tag_cur_gen =0 ; - -/* Determine the number of bits in the bufid -* mask and add one to get the start of the -* generation bits -*/ - bufidx_mask = txq->desc_count -1 ; -while (bufidx_mask >>1 ) { - txq->compl_tag_gen_s++; - bufidx_mask = bufidx_mask >>1 ; - } - txq->compl_tag_gen_s++; - - gen_bits =IDPF_TX_SPLITQ_COMPL_TAG_WIDTH - - txq->compl_tag_gen_s; - txq->compl_tag_gen_max =GETMAXVAL (gen_bits); - -/* Set bufid mask based on location of first -* gen bit; it cannot simply be the descriptor -* ring size-1 since we can have size values -* where not all of those bits are set. -*/ - txq->compl_tag_bufid_m = -GETMAXVAL (txq->compl_tag_gen_s); }if (!idpf_is_queue_model_split (vport->txq_model))
@@ -1061,9 +939,6 @@ static void idpf_txq_group_rel(struct idkfree (txq_grp->complq); txq_grp->complq =NULL ; - -if (flow_sch_en) -kfree (txq_grp->stashes); }kfree (vport->txq_grps); vport->txq_grps =NULL ;
@@ -1416,7 +1291,6 @@ static int idpf_txq_group_alloc(struct ifor (i =0 ; i < vport->num_txq_grp; i++) {struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];struct idpf_adapter *adapter = vport->adapter; -struct idpf_txq_stash *stashes;int j; tx_qgrp->vport = vport;
@@ -1429,15 +1303,6 @@ static int idpf_txq_group_alloc(struct i goto err_alloc; } -if (split && flow_sch_en) { - stashes =kcalloc (num_txq,sizeof (*stashes), -GFP_KERNEL ); -if (!stashes) - goto err_alloc; - - tx_qgrp->stashes = stashes; - } -for (j =0 ; j < tx_qgrp->num_txq; j++) {struct idpf_tx_queue *q = tx_qgrp->txqs[j];
@@ -1457,11 +1322,6 @@ static int idpf_txq_group_alloc(struct iif (!flow_sch_en)continue ; -if (split) { - q->stash = &stashes[j]; -hash_init (q->stash->sched_buf_hash); - } -idpf_queue_set (FLOW_SCH_EN , q); q->refillq =kzalloc (sizeof (*q->refillq),GFP_KERNEL );
@@ -1719,82 +1579,6 @@ static void idpf_tx_handle_sw_marker(strwake_up (&vport->sw_marker_wq); } -/** -* idpf_tx_clean_stashed_bufs - clean bufs that were stored for -* out of order completions -* @txq: queue to clean -* @compl_tag: completion tag of packet to clean (from completion descriptor) -* @cleaned: pointer to stats struct to track cleaned packets/bytes -* @budget: Used to determine if we are in netpoll -*/ -static void idpf_tx_clean_stashed_bufs (struct idpf_tx_queue *txq, -u16 compl_tag, -struct libeth_sq_napi_stats *cleaned, -int budget) -{ -struct idpf_tx_stash *stash; -struct hlist_node *tmp_buf; -struct libeth_cq_pp cp = { - .dev = txq->dev, - .ss = cleaned, - .napi = budget, - }; - -/* Buffer completion */ -hash_for_each_possible_safe (txq->stash->sched_buf_hash, stash, tmp_buf, - hlist, compl_tag) { -if (unlikely (idpf_tx_buf_compl_tag (&stash->buf) != compl_tag)) -continue ; - -hash_del (&stash->hlist); -libeth_tx_complete (&stash->buf, &cp); - -/* Push shadow buf back onto stack */ -idpf_buf_lifo_push (&txq->stash->buf_stack, stash); - } -} - -/** -* idpf_stash_flow_sch_buffers - store buffer parameters info to be freed at a -* later time (only relevant for flow scheduling mode) -* @txq: Tx queue to clean -* @tx_buf: buffer to store -*/ -static int idpf_stash_flow_sch_buffers (struct idpf_tx_queue *txq, -struct idpf_tx_buf *tx_buf) -{ -struct idpf_tx_stash *stash; - -if (unlikely (tx_buf->type <=LIBETH_SQE_CTX )) -return 0 ; - - stash =idpf_buf_lifo_pop (&txq->stash->buf_stack); -if (unlikely (!stash)) { -net_err_ratelimited ("%s: No out-of-order TX buffers left!\n" , -netdev_name (txq->netdev)); - -return -ENOMEM ; - } - -/* Store buffer params in shadow buffer */ - stash->buf.skb = tx_buf->skb; - stash->buf.bytes = tx_buf->bytes; - stash->buf.packets = tx_buf->packets; - stash->buf.type = tx_buf->type; - stash->buf.nr_frags = tx_buf->nr_frags; -dma_unmap_addr_set (&stash->buf, dma,dma_unmap_addr (tx_buf, dma)); -dma_unmap_len_set (&stash->buf, len,dma_unmap_len (tx_buf, len)); -idpf_tx_buf_compl_tag (&stash->buf) =idpf_tx_buf_compl_tag (tx_buf); - -/* Add buffer to buf_hash table to be freed later */ -hash_add (txq->stash->sched_buf_hash, &stash->hlist, -idpf_tx_buf_compl_tag (&stash->buf)); - - tx_buf->type =LIBETH_SQE_EMPTY ; - -return 0 ; -} -#define idpf_tx_splitq_clean_bump_ntc (txq, ntc, desc, buf) \ do { \ if (unlikely(++(ntc) == (txq)->desc_count)) { \
@@ -1822,14 +1606,8 @@ do { \* Separate packet completion events will be reported on the completion queue, * and the buffers will be cleaned separately. The stats are not updated from * this function when using flow-based scheduling. -* -* Furthermore, in flow scheduling mode, check to make sure there are enough -* reserve buffers to stash the packet. If there are not, return early, which -* will leave next_to_clean pointing to the packet that failed to be stashed. -* -* Return: false in the scenario above, true otherwise. */ -static bool idpf_tx_splitq_clean (struct idpf_tx_queue *tx_q,u16 end, +static void idpf_tx_splitq_clean (struct idpf_tx_queue *tx_q,u16 end,int napi_budget,struct libeth_sq_napi_stats *cleaned,bool descs_only)
@@ -1843,12 +1621,11 @@ static bool idpf_tx_splitq_clean(struct .napi = napi_budget, };struct idpf_tx_buf *tx_buf; -bool clean_complete = true;if (descs_only) {/* Bump ring index to mark as cleaned. */ tx_q->next_to_clean = end; -return true; +return ; } tx_desc = &tx_q->flex_tx[ntc];
@@ -1869,53 +1646,24 @@ static bool idpf_tx_splitq_clean(structbreak ; eop_idx = tx_buf->rs_idx; +libeth_tx_complete (tx_buf, &cp); -if (descs_only) { -if (IDPF_TX_BUF_RSV_UNUSED (tx_q) < tx_buf->nr_frags) { - clean_complete = false; - goto tx_splitq_clean_out; - } - -idpf_stash_flow_sch_buffers (tx_q, tx_buf); +/* unmap remaining buffers */ +while (ntc != eop_idx) { +idpf_tx_splitq_clean_bump_ntc (tx_q, ntc, + tx_desc, tx_buf); -while (ntc != eop_idx) { -idpf_tx_splitq_clean_bump_ntc (tx_q, ntc, - tx_desc, tx_buf); -idpf_stash_flow_sch_buffers (tx_q, tx_buf); - } - }else { +/* unmap any remaining paged data */ libeth_tx_complete (tx_buf, &cp); - -/* unmap remaining buffers */ -while (ntc != eop_idx) { -idpf_tx_splitq_clean_bump_ntc (tx_q, ntc, - tx_desc, tx_buf); - -/* unmap any remaining paged data */ -libeth_tx_complete (tx_buf, &cp); - } } fetch_next_txq_desc:idpf_tx_splitq_clean_bump_ntc (tx_q, ntc, tx_desc, tx_buf); } -tx_splitq_clean_out: tx_q->next_to_clean = ntc; - -return clean_complete; } -#define idpf_tx_clean_buf_ring_bump_ntc (txq, ntc, buf) \ -do { \ - (buf)++; \ - (ntc)++; \ - if (unlikely((ntc) == (txq)->desc_count)) { \ - buf = (txq)->tx_buf; \ - ntc = 0; \ - } \ -} while (0) -/** * idpf_tx_clean_bufs - clean flow scheduling TX queue buffers * @txq: queue to clean
@@ -1926,7 +1674,7 @@ do { \* Clean all buffers associated with the packet starting at buf_id. Returns the * byte/segment count for the cleaned packet. */ -static bool idpf_tx_clean_bufs (struct idpf_tx_queue *txq,u32 buf_id, +static void idpf_tx_clean_bufs (struct idpf_tx_queue *txq,u32 buf_id,struct libeth_sq_napi_stats *cleaned,int budget) {
@@ -1950,8 +1698,6 @@ static bool idpf_tx_clean_bufs(struct idlibeth_tx_complete (tx_buf, &cp);idpf_post_buf_refill (txq->refillq, buf_id); } - -return true; }/**
@@ -1970,22 +1716,17 @@ static void idpf_tx_handle_rs_completion struct libeth_sq_napi_stats *cleaned, int budget) { -u16 compl_tag; +/* RS completion contains queue head for queue based scheduling or +* completion tag for flow based scheduling. +*/ +u16 rs_compl_val =le16_to_cpu (desc->q_head_compl_tag.q_head);if (!idpf_queue_has (FLOW_SCH_EN , txq)) { -u16 head =le16_to_cpu (desc->q_head_compl_tag.q_head); - -idpf_tx_splitq_clean (txq, head, budget, cleaned, false); +idpf_tx_splitq_clean (txq, rs_compl_val, budget, cleaned, false);return ; } - compl_tag =le16_to_cpu (desc->q_head_compl_tag.compl_tag); - -/* If we didn't clean anything on the ring, this packet must be -* in the hash table. Go clean it there. -*/ -if (!idpf_tx_clean_bufs (txq, compl_tag, cleaned, budget)) -idpf_tx_clean_stashed_bufs (txq, compl_tag, cleaned, budget); +idpf_tx_clean_bufs (txq, rs_compl_val, cleaned, budget); }/**
@@ -2102,8 +1843,7 @@ fetch_next_desc:/* Update BQL */ nq =netdev_get_tx_queue (tx_q->netdev, tx_q->idx); - dont_wake = !complq_ok ||IDPF_TX_BUF_RSV_LOW (tx_q) || - np->state != __IDPF_VPORT_UP || + dont_wake = !complq_ok || np->state != __IDPF_VPORT_UP || !netif_carrier_ok (tx_q->netdev);/* Check if the TXQ needs to and can be restarted */ __netif_txq_completed_wake(nq, tx_q->cleaned_pkts, tx_q->cleaned_bytes,
@@ -2174,7 +1914,6 @@ static int idpf_txq_has_room(struct idpfif (IDPF_DESC_UNUSED (tx_q) < descs_needed ||IDPF_TX_COMPLQ_PENDING (tx_q->txq_grp) >IDPF_TX_COMPLQ_OVERFLOW_THRESH (tx_q->txq_grp->complq) || -IDPF_TX_BUF_RSV_LOW (tx_q) ||idpf_tx_splitq_get_free_bufs (tx_q->refillq) < bufs_needed)return 0 ;return 1 ;
@@ -2298,10 +2037,8 @@ static unsigned int idpf_tx_splitq_bump_ { ntu++; -if (ntu == txq->desc_count) { +if (ntu == txq->desc_count) ntu =0 ; - txq->compl_tag_cur_gen =IDPF_TX_ADJ_COMPL_TAG_GEN (txq); - }return ntu; }
@@ -2483,8 +2220,6 @@ static void idpf_tx_splitq_map(struct idif (unlikely (++i == tx_q->desc_count)) { tx_desc = &tx_q->flex_tx[0 ]; i =0 ; - tx_q->compl_tag_cur_gen = -IDPF_TX_ADJ_COMPL_TAG_GEN (tx_q); }else { tx_desc++; }
@@ -2515,7 +2250,6 @@ static void idpf_tx_splitq_map(struct idif (unlikely (++i == tx_q->desc_count)) { tx_desc = &tx_q->flex_tx[0 ]; i =0 ; - tx_q->compl_tag_cur_gen =IDPF_TX_ADJ_COMPL_TAG_GEN (tx_q); }else { tx_desc++; }
@@ -2771,10 +2505,9 @@ static netdev_tx_t idpf_tx_splitq_frame( tx_params.dtype =IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE ; tx_params.eop_cmd =IDPF_TXD_FLEX_FLOW_CMD_EOP ; -/* Set the RE bit to catch any packets that may have not been -* stashed during RS completion cleaning. MIN_GAP is set to -* MIN_RING size to ensure it will be set at least once each -* time around the ring. +/* Set the RE bit to periodically "clean" the descriptor ring. +* MIN_GAP is set to MIN_RING size to ensure it will be set at +* least once each time around the ring. */ if (idpf_tx_splitq_need_re (tx_q)) { tx_params.eop_cmd |=IDPF_TXD_FLEX_FLOW_CMD_RE ;
drivers/net/ethernet/intel/idpf/idpf_txrx.h +1 -46
110 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 545/567 hrtimers: Make hrtimer_update_function() less expensive
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Gleixner <[email protected]>
commit 2ea97b76d6712bfb0408e5b81ffd7bc4551d3153 upstream.
The sanity checks in hrtimer_update_function() are expensive for high frequency usage like in the io/uring code due to locking.
Hide the sanity checks behind CONFIG_PROVE_LOCKING, which has a decent chance to be enabled on a regular basis for testing.
--- include/linux/hrtimer.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
include/linux/hrtimer.h +2 -1
@@ -348,6 +348,7 @@ static inline int hrtimer_callback_runnistatic inline void hrtimer_update_function (struct hrtimer *timer,enum hrtimer_restart (*function)(struct hrtimer *)) { +#ifdef CONFIG_PROVE_LOCKING guard (raw_spinlock_irqsave)(&timer->base->cpu_base->lock);if (WARN_ON_ONCE (hrtimer_is_queued (timer)))
@@ -355,7 +356,7 @@ static inline void hrtimer_update_functiif (WARN_ON_ONCE (!function))return ; - +#endif timer->function = function; }
Greg Kroah-Hartman
maintainerPATCH 546/567 gve: defer interrupt enabling until NAPI registration
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ankit Garg <[email protected]>
commit 3d970eda003441f66551a91fda16478ac0711617 upstream.
Currently, interrupts are automatically enabled immediately upon request. This allows interrupt to fire before the associated NAPI context is fully initialized and cause failures like below:
[ 0.946369] Call Trace: [ 0.946369] <IRQ> [ 0.946369] __napi_poll+0x2a/0x1e0 [ 0.946369] net_rx_action+0x2f9/0x3f0 [ 0.946369] handle_softirqs+0xd6/0x2c0 [ 0.946369] ? handle_edge_irq+0xc1/0x1b0 [ 0.946369] __irq_exit_rcu+0xc3/0xe0 [ 0.946369] common_interrupt+0x81/0xa0 [ 0.946369] </IRQ> [ 0.946369] <TASK> [ 0.946369] asm_common_interrupt+0x22/0x40 [ 0.946369] RIP: 0010:pv_native_safe_halt+0xb/0x10
Use the IRQF_NO_AUTOEN flag when requesting interrupts to prevent auto enablement and explicitly enable the interrupt in NAPI initialization path (and disable it during NAPI teardown).
This ensures that interrupt lifecycle is strictly coupled with readiness of NAPI context.
--- drivers/net/ethernet/google/gve/gve_main.c | 2 +- drivers/net/ethernet/google/gve/gve_utils.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/google/gve/gve_main.c +1 -1
@@ -500,7 +500,7 @@ static int gve_alloc_notify_blocks(struc block->priv = priv; err =request_irq (priv->msix_vectors[msix_idx].vector,gve_is_gqi (priv) ? gve_intr : gve_intr_dqo, -0 , block->name, block); +IRQF_NO_AUTOEN , block->name, block);if (err) {dev_err (&priv->pdev->dev,"Failed to receive msix vector %d\n" , i);
drivers/net/ethernet/google/gve/gve_utils.c +2 -0
@@ -111,11 +111,13 @@ void gve_add_napi(struct gve_priv *priv,struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx];netif_napi_add (priv->dev, &block->napi, gve_poll); +enable_irq (block->irq); }void gve_remove_napi (struct gve_priv *priv,int ntfy_idx) {struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; +disable_irq (block->irq);netif_napi_del (&block->napi); }
Greg Kroah-Hartman
maintainerPATCH 547/567 ASoC: renesas: rz-ssi: Fix channel swap issue in full duplex mode
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Biju Das <[email protected]>
[ Upstream commit 52a525011cb8e293799a085436f026f2958403f9 ]
The full duplex audio starts with half duplex mode and then switch to full duplex mode (another FIFO reset) when both playback/capture streams available leading to random audio left/right channel swap issue. Fix this channel swap issue by detecting the full duplex condition by populating struct dup variable in startup() callback and synchronize starting both the play and capture at the same time in rz_ssi_start().
--- sound/soc/sh/rz-ssi.c | 51 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-)
sound/soc/sh/rz-ssi.c +43 -8
@@ -132,6 +132,12 @@ struct rz_ssi_priv {bool bckp_rise;/* Bit clock polarity (SSICR.BCKP) */ bool dma_rt; +struct { +bool tx_active; +bool rx_active; +bool one_stream_triggered; + } dup; +/* Full duplex communication support */ struct {unsigned int rate;
@@ -352,13 +358,12 @@ static int rz_ssi_start(struct rz_ssi_prbool is_full_duplex;u32 ssicr, ssifcr; - is_full_duplex =rz_ssi_is_stream_running (&ssi->playback) || -rz_ssi_is_stream_running (&ssi->capture); + is_full_duplex = ssi->dup.tx_active && ssi->dup.rx_active; ssicr =rz_ssi_reg_readl (ssi,SSICR ); ssifcr =rz_ssi_reg_readl (ssi,SSIFCR );if (!is_full_duplex) { ssifcr &= ~0xF ; - }else { + }else if (ssi->dup.one_stream_triggered) {rz_ssi_reg_mask_setl (ssi,SSICR ,SSICR_TEN |SSICR_REN ,0 );rz_ssi_set_idle (ssi); ssifcr &= ~SSIFCR_FIFO_RST ;
@@ -394,12 +399,16 @@ static int rz_ssi_start(struct rz_ssi_pr SSISR_RUIRQ), 0); strm->running =1 ; -if (is_full_duplex) - ssicr |=SSICR_TEN |SSICR_REN ; -else +if (!is_full_duplex) { ssicr |= is_play ?SSICR_TEN :SSICR_REN ; - -rz_ssi_reg_writel (ssi,SSICR , ssicr); +rz_ssi_reg_writel (ssi,SSICR , ssicr); + }else if (ssi->dup.one_stream_triggered) { + ssicr |=SSICR_TEN |SSICR_REN ; +rz_ssi_reg_writel (ssi,SSICR , ssicr); + ssi->dup.one_stream_triggered = false; + }else { + ssi->dup.one_stream_triggered = true; + }return 0 ; }
@@ -897,6 +906,30 @@ static int rz_ssi_dai_set_fmt(struct sndreturn 0 ; } +static int rz_ssi_startup (struct snd_pcm_substream *substream, +struct snd_soc_dai *dai) +{ +struct rz_ssi_priv *ssi =snd_soc_dai_get_drvdata (dai); + +if (substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ) + ssi->dup.tx_active = true; +else + ssi->dup.rx_active = true; + +return 0 ; +} + +static void rz_ssi_shutdown (struct snd_pcm_substream *substream, +struct snd_soc_dai *dai) +{ +struct rz_ssi_priv *ssi =snd_soc_dai_get_drvdata (dai); + +if (substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ) + ssi->dup.tx_active = false; +else + ssi->dup.rx_active = false; +} + static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, unsigned int channels, unsigned int sample_width,
@@ -962,6 +995,8 @@ static int rz_ssi_dai_hw_params(struct s }static const struct snd_soc_dai_ops rz_ssi_dai_ops = { + .startup = rz_ssi_startup, + .shutdown = rz_ssi_shutdown, .trigger = rz_ssi_dai_trigger, .set_fmt = rz_ssi_dai_set_fmt, .hw_params = rz_ssi_dai_hw_params,
Greg Kroah-Hartman
maintainerPATCH 548/567 block: handle zone management operations completions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
[ Upstream commit efae226c2ef19528ffd81d29ba0eecf1b0896ca2 ]
The functions blk_zone_wplug_handle_reset_or_finish() and blk_zone_wplug_handle_reset_all() both modify the zone write pointer offset of zone write plugs that are the target of a reset, reset all or finish zone management operation. However, these functions do this modification before the BIO is executed. So if the zone operation fails, the modified zone write pointer offsets become invalid.
Avoid this by modifying the zone write pointer offset of a zone write plug that is the target of a zone management operation when the operation completes. To do so, modify blk_zone_bio_endio() to call the new function blk_zone_mgmt_bio_endio() which in turn calls the functions blk_zone_reset_all_bio_endio(), blk_zone_reset_bio_endio() or blk_zone_finish_bio_endio() depending on the operation of the completed BIO, to modify a zone write plug write pointer offset accordingly. These functions are called only if the BIO execution was successful.
--- block/blk-zoned.c | 141 +++++++++++++++++++++++++++++++++++------------------- block/blk.h | 14 +++++ 2 files changed, 106 insertions(+), 49 deletions(-)
block/blk-zoned.c +92 -49
@@ -73,6 +73,11 @@ struct blk_zone_wplug {struct gendisk *disk; }; +static inline unsigned int disk_zone_wplugs_hash_size (struct gendisk *disk) +{ +return 1U << disk->zone_wplugs_hash_bits; +} +/* * Zone write plug flags bits: * - BLK_ZONE_WPLUG_PLUGGED: Indicates that the zone write plug is plugged,
@@ -712,71 +717,91 @@ static int disk_zone_sync_wp_offset(stru disk_report_zones_cb, &args); } -static bool blk_zone_wplug_handle_reset_or_finish (struct bio *bio, -unsigned int wp_offset) +static void blk_zone_reset_bio_endio (struct bio *bio) {struct gendisk *disk = bio->bi_bdev->bd_disk; -sector_t sector = bio->bi_iter.bi_sector;struct blk_zone_wplug *zwplug; -unsigned long flags; - -/* Conventional zones cannot be reset nor finished. */ -if (disk_zone_is_conv (disk, sector)) { -bio_io_error (bio); -return true; - } - -/* -* No-wait reset or finish BIOs do not make much sense as the callers -* issue these as blocking operations in most cases. To avoid issues -* the BIO execution potentially failing with BLK_STS_AGAIN, warn about -* REQ_NOWAIT being set and ignore that flag. -*/ -if (WARN_ON_ONCE (bio->bi_opf &REQ_NOWAIT )) - bio->bi_opf &= ~REQ_NOWAIT ;/* -* If we have a zone write plug, set its write pointer offset to 0 -* (reset case) or to the zone size (finish case). This will abort all -* BIOs plugged for the target zone. It is fine as resetting or -* finishing zones while writes are still in-flight will result in the +* If we have a zone write plug, set its write pointer offset to 0. +* This will abort all BIOs plugged for the target zone. It is fine as +* resetting zones while writes are still in-flight will result in the * writes failing anyway. */ - zwplug =disk_get_zone_wplug (disk, sector); + zwplug =disk_get_zone_wplug (disk, bio->bi_iter.bi_sector);if (zwplug) { +unsigned long flags; +spin_lock_irqsave (&zwplug->lock, flags); -disk_zone_wplug_set_wp_offset (disk, zwplug, wp_offset); +disk_zone_wplug_set_wp_offset (disk, zwplug,0 );spin_unlock_irqrestore (&zwplug->lock, flags);disk_put_zone_wplug (zwplug); } - -return false; } -static bool blk_zone_wplug_handle_reset_all (struct bio *bio) +static void blk_zone_reset_all_bio_endio (struct bio *bio) {struct gendisk *disk = bio->bi_bdev->bd_disk;struct blk_zone_wplug *zwplug;unsigned long flags; -sector_t sector; +unsigned int i; -/* -* Set the write pointer offset of all zone write plugs to 0. This will -* abort all plugged BIOs. It is fine as resetting zones while writes -* are still in-flight will result in the writes failing anyway. -*/ -for (sector =0 ; sector <get_capacity (disk); - sector += disk->queue->limits.chunk_sectors) { - zwplug =disk_get_zone_wplug (disk, sector); -if (zwplug) { +/* Update the condition of all zone write plugs. */ +rcu_read_lock (); +for (i =0 ; i <disk_zone_wplugs_hash_size (disk); i++) { +hlist_for_each_entry_rcu (zwplug, &disk->zone_wplugs_hash[i], + node) {spin_lock_irqsave (&zwplug->lock, flags);disk_zone_wplug_set_wp_offset (disk, zwplug,0 );spin_unlock_irqrestore (&zwplug->lock, flags); -disk_put_zone_wplug (zwplug); } } +rcu_read_unlock (); +} -return false; +static void blk_zone_finish_bio_endio (struct bio *bio) +{ +struct block_device *bdev = bio->bi_bdev; +struct gendisk *disk = bdev->bd_disk; +struct blk_zone_wplug *zwplug; + +/* +* If we have a zone write plug, set its write pointer offset to the +* zone size. This will abort all BIOs plugged for the target zone. It +* is fine as resetting zones while writes are still in-flight will +* result in the writes failing anyway. +*/ + zwplug =disk_get_zone_wplug (disk, bio->bi_iter.bi_sector); +if (zwplug) { +unsigned long flags; + +spin_lock_irqsave (&zwplug->lock, flags); +disk_zone_wplug_set_wp_offset (disk, zwplug, +bdev_zone_sectors (bdev)); +spin_unlock_irqrestore (&zwplug->lock, flags); +disk_put_zone_wplug (zwplug); + } +} + +void blk_zone_mgmt_bio_endio (struct bio *bio) +{ +/* If the BIO failed, we have nothing to do. */ +if (bio->bi_status !=BLK_STS_OK ) +return ; + +switch (bio_op (bio)) { +case REQ_OP_ZONE_RESET : +blk_zone_reset_bio_endio (bio); +return ; +case REQ_OP_ZONE_RESET_ALL : +blk_zone_reset_all_bio_endio (bio); +return ; +case REQ_OP_ZONE_FINISH : +blk_zone_finish_bio_endio (bio); +return ; +default : +return ; + } } static void disk_zone_wplug_schedule_bio_work(struct gendisk *disk,
@@ -1119,6 +1144,32 @@ static void blk_zone_wplug_handle_nativedisk_put_zone_wplug (zwplug); } +static bool blk_zone_wplug_handle_zone_mgmt (struct bio *bio) +{ +struct gendisk *disk = bio->bi_bdev->bd_disk; + +if (bio_op (bio) !=REQ_OP_ZONE_RESET_ALL && +disk_zone_is_conv (disk, bio->bi_iter.bi_sector)) { +/* +* Zone reset and zone finish operations do not apply to +* conventional zones. +*/ +bio_io_error (bio); +return true; + } + +/* +* No-wait zone management BIOs do not make much sense as the callers +* issue these as blocking operations in most cases. To avoid issues +* with the BIO execution potentially failing with BLK_STS_AGAIN, warn +* about REQ_NOWAIT being set and ignore that flag. +*/ +if (WARN_ON_ONCE (bio->bi_opf &REQ_NOWAIT )) + bio->bi_opf &= ~REQ_NOWAIT ; + +return false; +} +/** * blk_zone_plug_bio - Handle a zone write BIO with zone write plugging * @bio: The BIO being submitted
@@ -1166,12 +1217,9 @@ bool blk_zone_plug_bio(struct bio *bio,case REQ_OP_WRITE_ZEROES :return blk_zone_wplug_handle_write (bio, nr_segs);case REQ_OP_ZONE_RESET : -return blk_zone_wplug_handle_reset_or_finish (bio,0 );case REQ_OP_ZONE_FINISH : -return blk_zone_wplug_handle_reset_or_finish (bio, -bdev_zone_sectors (bdev));case REQ_OP_ZONE_RESET_ALL : -return blk_zone_wplug_handle_reset_all (bio); +return blk_zone_wplug_handle_zone_mgmt (bio);default :return false; }
@@ -1328,11 +1376,6 @@ put_zwplug:disk_put_zone_wplug (zwplug); } -static inline unsigned int disk_zone_wplugs_hash_size (struct gendisk *disk) -{ -return 1U << disk->zone_wplugs_hash_bits; -} -void disk_init_zone_resources (struct gendisk *disk) {spin_lock_init (&disk->zone_wplugs_lock);
block/blk.h +14 -0
@@ -486,10 +486,24 @@ static inline void blk_zone_update_reque bio_flagged(bio, BIO_EMULATES_ZONE_APPEND)) bio->bi_iter.bi_sector = rq->__sector; } +void blk_zone_mgmt_bio_endio (struct bio *bio);void blk_zone_write_plug_bio_endio (struct bio *bio);static inline void blk_zone_bio_endio (struct bio *bio) {/* +* Zone management BIOs may impact zone write plugs (e.g. a zone reset +* changes a zone write plug zone write pointer offset), but these +* operation do not go through zone write plugging as they may operate +* on zones that do not have a zone write +* plug. blk_zone_mgmt_bio_endio() handles the potential changes to zone +* write plugs that are present. +*/ +if (op_is_zone_mgmt (bio_op (bio))) { +blk_zone_mgmt_bio_endio (bio); +return ; + } + +/* * For write BIOs to zoned devices, signal the completion of the BIO so * that the next write BIO can be submitted by zone write plugging. */
Greg Kroah-Hartman
maintainerPATCH 549/567 soundwire: stream: extend sdw_alloc_stream() to take type parameter
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pierre-Louis Bossart <[email protected]>
[ Upstream commit dc90bbefa792031d89fe2af9ad4a6febd6be96a9 ]
In the existing definition of sdw_stream_runtime, the 'type' member is never set and defaults to PCM. To prepare for the BPT/BRA support, we need to special-case streams and make use of the 'type'.
No functional change for now, the implicit PCM type is now explicit.
Stable-dep-of: bcba17279327 ("ASoC: qcom: sdw: fix memory leak for sdw_stream_runtime")
--- Documentation/driver-api/soundwire/stream.rst | 2 +- drivers/soundwire/stream.c | 6 ++++-- include/linux/soundwire/sdw.h | 2 +- sound/soc/qcom/sdw.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-)
Documentation/driver-api/soundwire/stream.rst +1 -1
@@ -291,7 +291,7 @@ per stream. From ASoC DPCM framework, th .. code-block:: c - int sdw_alloc_stream(char * stream_name); + int sdw_alloc_stream(char * stream_name, enum sdw_stream_type type); The SoundWire core provides a sdw_startup_stream() helper function, typically called during a dailink .startup() callback, which performs
drivers/soundwire/stream.c +4 -2
@@ -1744,12 +1744,13 @@ static int set_stream(struct snd_pcm_sub* sdw_alloc_stream() - Allocate and return stream runtime * * @stream_name: SoundWire stream name +* @type: stream type (could be PCM ,PDM or BPT) * * Allocates a SoundWire stream runtime instance. * sdw_alloc_stream should be called only once per stream. Typically * invoked from ALSA/ASoC machine/platform driver. */ -struct sdw_stream_runtime *sdw_alloc_stream (const char *stream_name) +struct sdw_stream_runtime *sdw_alloc_stream (const char *stream_name,enum sdw_stream_type type) {struct sdw_stream_runtime *stream;
@@ -1761,6 +1762,7 @@ struct sdw_stream_runtime *sdw_alloc_strINIT_LIST_HEAD (&stream->master_list); stream->state =SDW_STREAM_ALLOCATED ; stream->m_rt_count =0 ; + stream->type = type;return stream; }
@@ -1789,7 +1791,7 @@ int sdw_startup_stream(void *sdw_substreif (!name)return -ENOMEM ; - sdw_stream =sdw_alloc_stream (name); + sdw_stream =sdw_alloc_stream (name,SDW_STREAM_PCM );if (!sdw_stream) {dev_err (rtd->dev,"alloc stream failed for substream DAI %s\n" , substream->name); ret = -ENOMEM ;
include/linux/soundwire/sdw.h +1 -1
@@ -1024,7 +1024,7 @@ struct sdw_stream_runtime {int m_rt_count; }; -struct sdw_stream_runtime *sdw_alloc_stream (const char *stream_name); +struct sdw_stream_runtime *sdw_alloc_stream (const char *stream_name,enum sdw_stream_type type);void sdw_release_stream (struct sdw_stream_runtime *stream);int sdw_compute_params (struct sdw_bus *bus);
sound/soc/qcom/sdw.c +1 -1
@@ -27,7 +27,7 @@ int qcom_snd_sdw_startup(struct snd_pcm_struct snd_soc_dai *codec_dai;int ret, i; - sruntime =sdw_alloc_stream (cpu_dai->name); + sruntime =sdw_alloc_stream (cpu_dai->name,SDW_STREAM_PCM );if (!sruntime)return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 550/567 ASoC: qcom: sdw: fix memory leak for sdw_stream_runtime
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Kandagatla <[email protected]>
[ Upstream commit bcba17279327c6e85dee6a97014dc642e2dc93cc ]
For some reason we endedup allocating sdw_stream_runtime for every cpu dai, this has two issues.
1. we never set snd_soc_dai_set_stream for non soundwire dai, which means there is no way that we can free this, resulting in memory leak
2. startup and shutdown callbacks can be called without hw_params callback called. This combination results in memory leak
because machine driver sruntime array pointer is only set in hw_params callback.
Fix this by
1. adding a helper function to get sdw_runtime for substream
which can be used by shutdown callback to get hold of sruntime to free.
2. only allocate sdw_runtime for soundwire dais.
--- sound/soc/qcom/sc7280.c | 2 sound/soc/qcom/sc8280xp.c | 2 sound/soc/qcom/sdw.c | 105 +++++++++++++++++++++++++--------------------- sound/soc/qcom/sdw.h | 1 sound/soc/qcom/sm8250.c | 2 sound/soc/qcom/x1e80100.c | 2 6 files changed, 64 insertions(+), 50 deletions(-)
sound/soc/qcom/sc7280.c +1 -1
@@ -317,7 +317,7 @@ static void sc7280_snd_shutdown(struct sstruct snd_soc_card *card = rtd->card;struct sc7280_snd_data *data =snd_soc_card_get_drvdata (card);struct snd_soc_dai *cpu_dai =snd_soc_rtd_to_cpu (rtd,0 ); -struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; +struct sdw_stream_runtime *sruntime =qcom_snd_sdw_get_stream (substream);switch (cpu_dai->id) {case MI2S_PRIMARY :
sound/soc/qcom/sc8280xp.c +1 -1
@@ -69,7 +69,7 @@ static void sc8280xp_snd_shutdown(structstruct snd_soc_pcm_runtime *rtd =snd_soc_substream_to_rtd (substream);struct snd_soc_dai *cpu_dai =snd_soc_rtd_to_cpu (rtd,0 );struct sc8280xp_snd_data *pdata =snd_soc_card_get_drvdata (rtd->card); -struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id]; +struct sdw_stream_runtime *sruntime =qcom_snd_sdw_get_stream (substream); pdata->sruntime[cpu_dai->id] =NULL ;sdw_release_stream (sruntime);
sound/soc/qcom/sdw.c +59 -46
143 lines not on this page · raw message
sound/soc/qcom/sdw.h +1 -0
@@ -10,6 +10,7 @@ int qcom_snd_sdw_startup(struct snd_pcm_int qcom_snd_sdw_prepare (struct snd_pcm_substream *substream,struct sdw_stream_runtime *runtime,bool *stream_prepared); +struct sdw_stream_runtime *qcom_snd_sdw_get_stream (struct snd_pcm_substream *stream);int qcom_snd_sdw_hw_params (struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params,struct sdw_stream_runtime **psruntime);
sound/soc/qcom/sm8250.c +1 -1
@@ -86,7 +86,7 @@ static void sm2450_snd_shutdown(struct sstruct snd_soc_pcm_runtime *rtd =snd_soc_substream_to_rtd (substream);struct snd_soc_dai *cpu_dai =snd_soc_rtd_to_cpu (rtd,0 );struct sm8250_snd_data *data =snd_soc_card_get_drvdata (rtd->card); -struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; +struct sdw_stream_runtime *sruntime =qcom_snd_sdw_get_stream (substream); data->sruntime[cpu_dai->id] =NULL ;sdw_release_stream (sruntime);
sound/soc/qcom/x1e80100.c +1 -1
@@ -55,7 +55,7 @@ static void x1e80100_snd_shutdown(structstruct snd_soc_pcm_runtime *rtd =snd_soc_substream_to_rtd (substream);struct snd_soc_dai *cpu_dai =snd_soc_rtd_to_cpu (rtd,0 );struct x1e80100_snd_data *data =snd_soc_card_get_drvdata (rtd->card); -struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; +struct sdw_stream_runtime *sruntime =qcom_snd_sdw_get_stream (substream); data->sruntime[cpu_dai->id] =NULL ;sdw_release_stream (sruntime);
Greg Kroah-Hartman
maintainerPATCH 551/567 ASoC: renesas: rz-ssi: Fix rz_ssi_priv::hw_params_cache::sample_width
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Biju Das <[email protected]>
[ Upstream commit 2bae7beda19f3b2dc6ab2062c94df19c27923712 ]
The strm->sample_width is not filled during rz_ssi_dai_hw_params(). This wrong value is used for caching sample_width in struct hw_params_cache. Fix this issue by replacing 'strm->sample_width'->'params_width(params)' in rz_ssi_dai_hw_params(). After this drop the variable sample_width from struct rz_ssi_stream as it is unused.
--- sound/soc/sh/rz-ssi.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
sound/soc/sh/rz-ssi.c +4 -9
@@ -12,6 +12,7 @@#include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/reset.h> +#include <sound/pcm_params.h> #include <sound/soc.h> /* REGISTER OFFSET */
@@ -85,7 +86,6 @@ struct rz_ssi_stream {int fifo_sample_size;/* sample capacity of SSI FIFO */ int dma_buffer_pos;/* The address for the next DMA descriptor */ int period_counter;/* for keeping track of periods transferred */ -int sample_width;int buffer_pos;/* current frame position in the buffer */ int running;/* 0=stopped, 1=running */
@@ -231,10 +231,7 @@ static inline bool rz_ssi_is_stream_runnstatic void rz_ssi_stream_init (struct rz_ssi_stream *strm,struct snd_pcm_substream *substream) { -struct snd_pcm_runtime *runtime = substream->runtime; -rz_ssi_set_substream (strm, substream); - strm->sample_width =samples_to_bytes (runtime,1 ); strm->dma_buffer_pos =0 ; strm->period_counter =0 ; strm->buffer_pos =0 ;
@@ -960,9 +957,9 @@ static int rz_ssi_dai_hw_params(struct s struct snd_soc_dai *dai) {struct rz_ssi_priv *ssi =snd_soc_dai_get_drvdata (dai); -struct rz_ssi_stream *strm =rz_ssi_stream_get (ssi, substream);unsigned int sample_bits =hw_param_interval (params,SNDRV_PCM_HW_PARAM_SAMPLE_BITS )->min; +unsigned int sample_width =params_width (params);unsigned int channels =params_channels (params);unsigned int rate =params_rate (params);
@@ -980,16 +977,14 @@ static int rz_ssi_dai_hw_params(struct sif (rz_ssi_is_stream_running (&ssi->playback) ||rz_ssi_is_stream_running (&ssi->capture)) { -if (rz_ssi_is_valid_hw_params (ssi, rate, channels, - strm->sample_width, sample_bits)) +if (rz_ssi_is_valid_hw_params (ssi, rate, channels, sample_width, sample_bits))return 0 ;dev_err (ssi->dev,"Full duplex needs same HW params\n" );return -EINVAL ; } -rz_ssi_cache_hw_params (ssi, rate, channels, strm->sample_width, - sample_bits); +rz_ssi_cache_hw_params (ssi, rate, channels, sample_width, sample_bits);return rz_ssi_clk_setup (ssi, rate, channels); }