diff mbox series

powerpc/pseries: Fix dtl_access_lock to be a rw_semaphore

Message ID 20240819122401.513203-1-mpe@ellerman.id.au (mailing list archive)
State Under Review
Headers show
Series powerpc/pseries: Fix dtl_access_lock to be a rw_semaphore | expand

Checks

Context Check Description
snowpatch_ozlabs/github-powerpc_selftests success Successfully ran 8 jobs.
snowpatch_ozlabs/github-powerpc_ppctests success Successfully ran 8 jobs.
snowpatch_ozlabs/github-powerpc_kernel_qemu success Successfully ran 21 jobs.
snowpatch_ozlabs/github-powerpc_clang success Successfully ran 5 jobs.
snowpatch_ozlabs/github-powerpc_sparse success Successfully ran 4 jobs.

Commit Message

Michael Ellerman Aug. 19, 2024, 12:24 p.m. UTC
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")
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
 arch/powerpc/include/asm/dtl.h        | 3 ++-
 arch/powerpc/platforms/pseries/dtl.c  | 8 ++++----
 arch/powerpc/platforms/pseries/lpar.c | 8 ++++----
 3 files changed, 10 insertions(+), 9 deletions(-)

Comments

Nysal Jan K.A. Aug. 21, 2024, 6:12 p.m. UTC | #1
On Mon, Aug 19, 2024 at 10:24:01PM GMT, Michael Ellerman wrote:

<snip>

> Fixes: 06220d78f24a ("powerpc/pseries: Introduce rwlock to gatekeep DTLB usage")
> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
> ---
>  arch/powerpc/include/asm/dtl.h        | 3 ++-
>  arch/powerpc/platforms/pseries/dtl.c  | 8 ++++----
>  arch/powerpc/platforms/pseries/lpar.c | 8 ++++----
>  3 files changed, 10 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/dtl.h b/arch/powerpc/include/asm/dtl.h
> index d6f43d149f8d..e21bac6c065c 100644
> --- a/arch/powerpc/include/asm/dtl.h
> +++ b/arch/powerpc/include/asm/dtl.h
> @@ -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>

The above include is redundant now and can be removed.

Reviewed-by: Nysal Jan K.A <nysal@linux.ibm.com>

>  
> @@ -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);
> diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
> index 3f1cdccebc9c..ecc04ef8c53e 100644
> --- a/arch/powerpc/platforms/pseries/dtl.c
> +++ b/arch/powerpc/platforms/pseries/dtl.c
> @@ -191,7 +191,7 @@ 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;
> @@ -199,7 +199,7 @@ static int dtl_enable(struct dtl *dtl)
>  	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;
>  	}
>  
> @@ -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);
>  	}
>  
> @@ -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 */
> diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
> index c1d8bee8f701..bb09990eec30 100644
> --- a/arch/powerpc/platforms/pseries/lpar.c
> +++ b/arch/powerpc/platforms/pseries/lpar.c
> @@ -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);
> @@ -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;
>  	}
> @@ -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;
>  	}
> @@ -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,
> -- 
> 2.46.0
> 
>
Kajol Jain Aug. 26, 2024, 11:30 a.m. UTC | #2
Hi Michael,
   Thanks for the patch, the patch looks fine to me.

Tested-by: Kajol Jain<kjain@linux.ibm.com>
Reviewed-by: Kajol Jain<kjain@linux.ibm.com>

Thanks,
Kajol Jain

On 8/19/24 17:54, Michael Ellerman wrote:
> 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")
> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
> ---
>  arch/powerpc/include/asm/dtl.h        | 3 ++-
>  arch/powerpc/platforms/pseries/dtl.c  | 8 ++++----
>  arch/powerpc/platforms/pseries/lpar.c | 8 ++++----
>  3 files changed, 10 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/dtl.h b/arch/powerpc/include/asm/dtl.h
> index d6f43d149f8d..e21bac6c065c 100644
> --- a/arch/powerpc/include/asm/dtl.h
> +++ b/arch/powerpc/include/asm/dtl.h
> @@ -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>
>  
> @@ -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);
> diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
> index 3f1cdccebc9c..ecc04ef8c53e 100644
> --- a/arch/powerpc/platforms/pseries/dtl.c
> +++ b/arch/powerpc/platforms/pseries/dtl.c
> @@ -191,7 +191,7 @@ 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;
> @@ -199,7 +199,7 @@ static int dtl_enable(struct dtl *dtl)
>  	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;
>  	}
>  
> @@ -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);
>  	}
>  
> @@ -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 */
> diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
> index c1d8bee8f701..bb09990eec30 100644
> --- a/arch/powerpc/platforms/pseries/lpar.c
> +++ b/arch/powerpc/platforms/pseries/lpar.c
> @@ -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);
> @@ -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;
>  	}
> @@ -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;
>  	}
> @@ -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,
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/dtl.h b/arch/powerpc/include/asm/dtl.h
index d6f43d149f8d..e21bac6c065c 100644
--- a/arch/powerpc/include/asm/dtl.h
+++ b/arch/powerpc/include/asm/dtl.h
@@ -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>
 
@@ -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);
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index 3f1cdccebc9c..ecc04ef8c53e 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -191,7 +191,7 @@  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;
@@ -199,7 +199,7 @@  static int dtl_enable(struct dtl *dtl)
 	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;
 	}
 
@@ -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);
 	}
 
@@ -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 */
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index c1d8bee8f701..bb09990eec30 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -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);
@@ -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;
 	}
@@ -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;
 	}
@@ -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,