Skip to content

Commit

Permalink
powerpc/pseries: Fix dtl_access_lock to be a rw_semaphore
Browse files Browse the repository at this point in the history
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 torvalds#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: 06220d7 ("powerpc/pseries: Introduce rwlock to gatekeep DTLB usage")
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
  • Loading branch information
mpe authored and intel-lab-lkp committed Aug 19, 2024
1 parent fa740ca commit c6e0e94
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 9 deletions.
3 changes: 2 additions & 1 deletion arch/powerpc/include/asm/dtl.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef _ASM_POWERPC_DTL_H
#define _ASM_POWERPC_DTL_H

#include <linux/rwsem.h>
#include <asm/lppaca.h>
#include <linux/spinlock_types.h>

Expand Down Expand Up @@ -35,7 +36,7 @@ struct dtl_entry {
#define DTL_LOG_ALL (DTL_LOG_CEDE | DTL_LOG_PREEMPT | DTL_LOG_FAULT)

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 alloc_dtl_buffers(unsigned long *time_limit);
Expand Down
8 changes: 4 additions & 4 deletions arch/powerpc/platforms/pseries/dtl.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,15 @@ static int dtl_enable(struct dtl *dtl)
return -EBUSY;

/* ensure there are no other conflicting dtl users */
if (!read_trylock(&dtl_access_lock))
if (!down_read_trylock(&dtl_access_lock))
return -EBUSY;

n_entries = dtl_buf_entries;
buf = kmem_cache_alloc_node(dtl_cache, GFP_KERNEL, cpu_to_node(dtl->cpu));
if (!buf) {
printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
__func__, dtl->cpu);
read_unlock(&dtl_access_lock);
up_read(&dtl_access_lock);
return -ENOMEM;
}

Expand All @@ -217,7 +217,7 @@ static int dtl_enable(struct dtl *dtl)
spin_unlock(&dtl->lock);

if (rc) {
read_unlock(&dtl_access_lock);
up_read(&dtl_access_lock);
kmem_cache_free(dtl_cache, buf);
}

Expand All @@ -232,7 +232,7 @@ static void dtl_disable(struct dtl *dtl)
dtl->buf = NULL;
dtl->buf_entries = 0;
spin_unlock(&dtl->lock);
read_unlock(&dtl_access_lock);
up_read(&dtl_access_lock);
}

/* file interface */
Expand Down
8 changes: 4 additions & 4 deletions arch/powerpc/platforms/pseries/lpar.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ struct vcpu_dispatch_data {
*/
#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(u64, dtl_entry_ridx);
static DEFINE_PER_CPU(struct dtl_worker, dtl_workers);
Expand Down Expand Up @@ -463,7 +463,7 @@ static int dtl_worker_enable(unsigned long *time_limit)
{
int rc = 0, state;

if (!write_trylock(&dtl_access_lock)) {
if (!down_write_trylock(&dtl_access_lock)) {
rc = -EBUSY;
goto out;
}
Expand All @@ -479,7 +479,7 @@ static int dtl_worker_enable(unsigned long *time_limit)
pr_err("vcpudispatch_stats: unable to setup workqueue for DTL processing\n");
free_dtl_buffers(time_limit);
reset_global_dtl_mask();
write_unlock(&dtl_access_lock);
up_write(&dtl_access_lock);
rc = -EINVAL;
goto out;
}
Expand All @@ -494,7 +494,7 @@ static void dtl_worker_disable(unsigned long *time_limit)
cpuhp_remove_state(dtl_worker_state);
free_dtl_buffers(time_limit);
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,
Expand Down

0 comments on commit c6e0e94

Please sign in to comment.