mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
powerpc/pseries: Fix dtl_access_lock to be a rw_semaphore
[ Upstream commit cadae3a45d23aa4f6485938a67cbc47aaaa25e38 ]
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: 06220d78f2 ("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
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
4d2655754e
commit
fa5b5ea257
@@ -1,8 +1,8 @@
|
|||||||
#ifndef _ASM_POWERPC_DTL_H
|
#ifndef _ASM_POWERPC_DTL_H
|
||||||
#define _ASM_POWERPC_DTL_H
|
#define _ASM_POWERPC_DTL_H
|
||||||
|
|
||||||
|
#include <linux/rwsem.h>
|
||||||
#include <asm/lppaca.h>
|
#include <asm/lppaca.h>
|
||||||
#include <linux/spinlock_types.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Layout of entries in the hypervisor's dispatch trace log buffer.
|
* Layout of entries in the hypervisor's dispatch trace log buffer.
|
||||||
@@ -35,7 +35,7 @@ struct dtl_entry {
|
|||||||
#define DTL_LOG_ALL (DTL_LOG_CEDE | DTL_LOG_PREEMPT | DTL_LOG_FAULT)
|
#define DTL_LOG_ALL (DTL_LOG_CEDE | DTL_LOG_PREEMPT | DTL_LOG_FAULT)
|
||||||
|
|
||||||
extern struct kmem_cache *dtl_cache;
|
extern struct kmem_cache *dtl_cache;
|
||||||
extern rwlock_t dtl_access_lock;
|
extern struct rw_semaphore dtl_access_lock;
|
||||||
|
|
||||||
extern void register_dtl_buffer(int cpu);
|
extern void register_dtl_buffer(int cpu);
|
||||||
extern void alloc_dtl_buffers(unsigned long *time_limit);
|
extern void alloc_dtl_buffers(unsigned long *time_limit);
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ static int dtl_enable(struct dtl *dtl)
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
/* ensure there are no other conflicting dtl users */
|
/* ensure there are no other conflicting dtl users */
|
||||||
if (!read_trylock(&dtl_access_lock))
|
if (!down_read_trylock(&dtl_access_lock))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
n_entries = dtl_buf_entries;
|
n_entries = dtl_buf_entries;
|
||||||
@@ -199,7 +199,7 @@ static int dtl_enable(struct dtl *dtl)
|
|||||||
if (!buf) {
|
if (!buf) {
|
||||||
printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
|
printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
|
||||||
__func__, dtl->cpu);
|
__func__, dtl->cpu);
|
||||||
read_unlock(&dtl_access_lock);
|
up_read(&dtl_access_lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ static int dtl_enable(struct dtl *dtl)
|
|||||||
spin_unlock(&dtl->lock);
|
spin_unlock(&dtl->lock);
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
read_unlock(&dtl_access_lock);
|
up_read(&dtl_access_lock);
|
||||||
kmem_cache_free(dtl_cache, buf);
|
kmem_cache_free(dtl_cache, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ static void dtl_disable(struct dtl *dtl)
|
|||||||
dtl->buf = NULL;
|
dtl->buf = NULL;
|
||||||
dtl->buf_entries = 0;
|
dtl->buf_entries = 0;
|
||||||
spin_unlock(&dtl->lock);
|
spin_unlock(&dtl->lock);
|
||||||
read_unlock(&dtl_access_lock);
|
up_read(&dtl_access_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* file interface */
|
/* file interface */
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ struct vcpu_dispatch_data {
|
|||||||
*/
|
*/
|
||||||
#define NR_CPUS_H NR_CPUS
|
#define NR_CPUS_H NR_CPUS
|
||||||
|
|
||||||
DEFINE_RWLOCK(dtl_access_lock);
|
DECLARE_RWSEM(dtl_access_lock);
|
||||||
static DEFINE_PER_CPU(struct vcpu_dispatch_data, vcpu_disp_data);
|
static DEFINE_PER_CPU(struct vcpu_dispatch_data, vcpu_disp_data);
|
||||||
static DEFINE_PER_CPU(u64, dtl_entry_ridx);
|
static DEFINE_PER_CPU(u64, dtl_entry_ridx);
|
||||||
static DEFINE_PER_CPU(struct dtl_worker, dtl_workers);
|
static DEFINE_PER_CPU(struct dtl_worker, dtl_workers);
|
||||||
@@ -461,7 +461,7 @@ static int dtl_worker_enable(unsigned long *time_limit)
|
|||||||
{
|
{
|
||||||
int rc = 0, state;
|
int rc = 0, state;
|
||||||
|
|
||||||
if (!write_trylock(&dtl_access_lock)) {
|
if (!down_write_trylock(&dtl_access_lock)) {
|
||||||
rc = -EBUSY;
|
rc = -EBUSY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -477,7 +477,7 @@ static int dtl_worker_enable(unsigned long *time_limit)
|
|||||||
pr_err("vcpudispatch_stats: unable to setup workqueue for DTL processing\n");
|
pr_err("vcpudispatch_stats: unable to setup workqueue for DTL processing\n");
|
||||||
free_dtl_buffers(time_limit);
|
free_dtl_buffers(time_limit);
|
||||||
reset_global_dtl_mask();
|
reset_global_dtl_mask();
|
||||||
write_unlock(&dtl_access_lock);
|
up_write(&dtl_access_lock);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -492,7 +492,7 @@ static void dtl_worker_disable(unsigned long *time_limit)
|
|||||||
cpuhp_remove_state(dtl_worker_state);
|
cpuhp_remove_state(dtl_worker_state);
|
||||||
free_dtl_buffers(time_limit);
|
free_dtl_buffers(time_limit);
|
||||||
reset_global_dtl_mask();
|
reset_global_dtl_mask();
|
||||||
write_unlock(&dtl_access_lock);
|
up_write(&dtl_access_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,
|
static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,
|
||||||
|
|||||||
Reference in New Issue
Block a user