Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/pseries: Fix dtl_access_lock to be a rw_semaphore

The dtl_access_lock needs to be a rw_sempahore, a sleeping lock, because
the code calls kmalloc() while holding it, which can sleep:

# echo 1 > /proc/powerpc/vcpudispatch_stats
BUG: sleeping function called from invalid context at include/linux/sched/mm.h:337
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 199, name: sh
preempt_count: 1, expected: 0
3 locks held by sh/199:
#0: c00000000a0743f8 (sb_writers#3){.+.+}-{0:0}, at: vfs_write+0x324/0x438
#1: c0000000028c7058 (dtl_enable_mutex){+.+.}-{3:3}, at: vcpudispatch_stats_write+0xd4/0x5f4
#2: c0000000028c70b8 (dtl_access_lock){+.+.}-{2:2}, at: vcpudispatch_stats_write+0x220/0x5f4
CPU: 0 PID: 199 Comm: sh Not tainted 6.10.0-rc4 #152
Hardware name: IBM pSeries (emulated by qemu) POWER9 (raw) 0x4e1202 0xf000005 of:SLOF,HEAD hv:linux,kvm pSeries
Call Trace:
dump_stack_lvl+0x130/0x148 (unreliable)
__might_resched+0x174/0x410
kmem_cache_alloc_noprof+0x340/0x3d0
alloc_dtl_buffers+0x124/0x1ac
vcpudispatch_stats_write+0x2a8/0x5f4
proc_reg_write+0xf4/0x150
vfs_write+0xfc/0x438
ksys_write+0x88/0x148
system_call_exception+0x1c4/0x5a0
system_call_common+0xf4/0x258

Fixes: 06220d78f24a ("powerpc/pseries: Introduce rwlock to gatekeep DTLB usage")
Tested-by: Kajol Jain <kjain@linux.ibm.com>
Reviewed-by: Nysal Jan K.A <nysal@linux.ibm.com>
Reviewed-by: Kajol Jain <kjain@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://patch.msgid.link/20240819122401.513203-1-mpe@ellerman.id.au

+10 -10
+2 -2
arch/powerpc/include/asm/dtl.h
··· 1 1 #ifndef _ASM_POWERPC_DTL_H 2 2 #define _ASM_POWERPC_DTL_H 3 3 4 + #include <linux/rwsem.h> 4 5 #include <asm/lppaca.h> 5 - #include <linux/spinlock_types.h> 6 6 7 7 /* 8 8 * Layout of entries in the hypervisor's dispatch trace log buffer. ··· 35 35 #define DTL_LOG_ALL (DTL_LOG_CEDE | DTL_LOG_PREEMPT | DTL_LOG_FAULT) 36 36 37 37 extern struct kmem_cache *dtl_cache; 38 - extern rwlock_t dtl_access_lock; 38 + extern struct rw_semaphore dtl_access_lock; 39 39 40 40 extern void register_dtl_buffer(int cpu); 41 41 extern void alloc_dtl_buffers(unsigned long *time_limit);
+4 -4
arch/powerpc/platforms/pseries/dtl.c
··· 191 191 return -EBUSY; 192 192 193 193 /* ensure there are no other conflicting dtl users */ 194 - if (!read_trylock(&dtl_access_lock)) 194 + if (!down_read_trylock(&dtl_access_lock)) 195 195 return -EBUSY; 196 196 197 197 n_entries = dtl_buf_entries; ··· 199 199 if (!buf) { 200 200 printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n", 201 201 __func__, dtl->cpu); 202 - read_unlock(&dtl_access_lock); 202 + up_read(&dtl_access_lock); 203 203 return -ENOMEM; 204 204 } 205 205 ··· 217 217 spin_unlock(&dtl->lock); 218 218 219 219 if (rc) { 220 - read_unlock(&dtl_access_lock); 220 + up_read(&dtl_access_lock); 221 221 kmem_cache_free(dtl_cache, buf); 222 222 } 223 223 ··· 232 232 dtl->buf = NULL; 233 233 dtl->buf_entries = 0; 234 234 spin_unlock(&dtl->lock); 235 - read_unlock(&dtl_access_lock); 235 + up_read(&dtl_access_lock); 236 236 } 237 237 238 238 /* file interface */
+4 -4
arch/powerpc/platforms/pseries/lpar.c
··· 170 170 */ 171 171 #define NR_CPUS_H NR_CPUS 172 172 173 - DEFINE_RWLOCK(dtl_access_lock); 173 + DECLARE_RWSEM(dtl_access_lock); 174 174 static DEFINE_PER_CPU(struct vcpu_dispatch_data, vcpu_disp_data); 175 175 static DEFINE_PER_CPU(u64, dtl_entry_ridx); 176 176 static DEFINE_PER_CPU(struct dtl_worker, dtl_workers); ··· 464 464 { 465 465 int rc = 0, state; 466 466 467 - if (!write_trylock(&dtl_access_lock)) { 467 + if (!down_write_trylock(&dtl_access_lock)) { 468 468 rc = -EBUSY; 469 469 goto out; 470 470 } ··· 480 480 pr_err("vcpudispatch_stats: unable to setup workqueue for DTL processing\n"); 481 481 free_dtl_buffers(time_limit); 482 482 reset_global_dtl_mask(); 483 - write_unlock(&dtl_access_lock); 483 + up_write(&dtl_access_lock); 484 484 rc = -EINVAL; 485 485 goto out; 486 486 } ··· 495 495 cpuhp_remove_state(dtl_worker_state); 496 496 free_dtl_buffers(time_limit); 497 497 reset_global_dtl_mask(); 498 - write_unlock(&dtl_access_lock); 498 + up_write(&dtl_access_lock); 499 499 } 500 500 501 501 static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,