[PATCH v2 3/7] powerpc/rtas: serialize ibm,get-vpd service with papr-vpd sequences

Nathan Lynch nathanl at linux.ibm.com
Tue Oct 17 00:02:37 AEDT 2023


> Take the papr-vpd driver's internal mutex when sys_rtas performs
> ibm,get-vpd calls. This prevents sys_rtas(ibm,get-vpd) calls from
> interleaving with sequences performed by the driver, ensuring that
> such sequences are not disrupted.

....

> @@ -1861,6 +1862,28 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
>  		goto copy_return;
>  	}
>  
> +	if (token == rtas_function_token(RTAS_FN_IBM_GET_VPD)) {
> +		/*
> +		 * ibm,get-vpd potentially needs to be invoked
> +		 * multiple times to obtain complete results.
> +		 * Interleaved ibm,get-vpd sequences disrupt each
> +		 * other.
> +		 *
> +		 * /dev/papr-vpd doesn't have this problem and users
> +		 * do not need to be aware of each other to use it
> +		 * safely.
> +		 *
> +		 * We can prevent this call from disrupting a
> +		 * /dev/papr-vpd-initiated sequence in progress by
> +		 * reaching into the driver to take its internal
> +		 * lock. Unfortunately there is no way to prevent
> +		 * interference in the other direction without
> +		 * resorting to even worse hacks.
> +		 */
> +		pr_notice_once("Calling ibm,get-vpd via sys_rtas is allowed but deprecated. Use /dev/papr-vpd instead.\n");
> +		papr_vpd_mutex_lock();
> +	}
> +
>  	buff_copy = get_errorlog_buffer();
>  
>  	raw_spin_lock_irqsave(&rtas_lock, flags);
> @@ -1870,6 +1893,9 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
>  	do_enter_rtas(&rtas_args);
>  	args = rtas_args;
>  
> +	if (token == rtas_function_token(RTAS_FN_IBM_GET_VPD))
> +		papr_vpd_mutex_unlock();
> +

The mutex ought to nest entirely outside rtas_lock, this releases it
too early.

Anyway, I'm considering a different way to get the synchronization right
between drivers like papr-vpd and sys_rtas. Instead of having sys_rtas
acquire the driver's internal lock, rtas.c should provide a way for code
like papr-vpd to temporarily lock the syscall path.

Something like this:

// rtas.c

+ static DEFINE_MUTEX(rtas_syscall_lock);
+ void rtas_syscall_lock(void) { mutex_lock(&rtas_syscall_lock); }
+ void rtas_syscall_unlock(void) { mutex_unlock(&rtas_syscall_lock); }

  SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
  {
+        rtas_syscall_lock();

         ...

         do_enter_rtas(&rtas_args);

         ...

+        rtas_syscall_unlock();

         return 0;
  }

// papr-vpd.c

  static void vpd_sequence_begin(struct vpd_sequence *seq,
                                 const struct papr_location_code *loc_code)
  {
          static struct papr_location_code static_loc_code;
          papr_vpd_mutex_lock();
+         rtas_syscall_lock();
          static_loc_code = *loc_code;
          *seq = (struct vpd_sequence) {
                  .params = {
                          .work_area = rtas_work_area_alloc(SZ_4K),
                          .loc_code = &static_loc_code,
                          .sequence = 1,
                  },
          };
  }

  /**
   * vpd_sequence_end() - Finalize a VPD retrieval sequence.
   * @seq: Sequence state.
   *
   * Releases resources obtained by vpd_sequence_begin().
   */
  static void vpd_sequence_end(struct vpd_sequence *seq)
  {
          rtas_work_area_free(seq->params.work_area);
+         rtas_syscall_unlock();
          papr_vpd_mutex_unlock();
  }


This is a sketch to communicate the idea. The locking in the real code
could be finer, perhaps a mutex per RTAS function.


More information about the Linuxppc-dev mailing list