[PATCH 10/22] x86: mp: Support APs waiting for instructions

Wolfgang Wallner wolfgang.wallner at br-automation.com
Wed Jun 10 15:27:00 CEST 2020


Hi Simon,


-----"Simon Glass" <sjg at chromium.org> schrieb: -----
> Betreff: [PATCH 10/22] x86: mp: Support APs waiting for instructions
> 
> At present the APs (non-boot CPUs) are inited once and then parked ready
> for the OS to use them. However in some cases we want to send new requests
> through, such as to change MTRRs and keep them consistent across CPUs.
> 
> Change the last state of the flight plan to go into a wait loop, accepting
> instructions from the main CPU.
> 
> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
> 
>  arch/x86/cpu/mp_init.c    | 99 ++++++++++++++++++++++++++++++++++++---
>  arch/x86/include/asm/mp.h | 11 +++++
>  2 files changed, 104 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c
> index ccb68b8b89b..c424f283807 100644
> --- a/arch/x86/cpu/mp_init.c
> +++ b/arch/x86/cpu/mp_init.c
> @@ -43,14 +43,36 @@ struct mp_flight_plan {
>  	struct mp_flight_record *records;
>  };
>  
> -static struct mp_flight_plan mp_info;
> -
>  struct cpu_map {
>  	struct udevice *dev;
>  	int apic_id;
>  	int err_code;
>  };
>  
> +struct mp_callback {
> +	/**
> +	 * func() - Function to call on the AP
> +	 *
> +	 * @arg: Argument to pass
> +	 */
> +	void (*func)(void *arg);
> +	void *arg;
> +	int logical_cpu_number;
> +};
> +
> +static struct mp_flight_plan mp_info;
> +
> +/*
> + * ap_callbacks - Callback mailbox array
> + *
> + * Array of callback, one entry for each available CPU, indexed by the CPU
> + * number, which is dev->req_seq. The entry for the main CPU is never used.
> + * When this is NULL, there is no pending work for the CPU to run. When
> + * non-NULL it points to the mp_callback structure. This is shared between all
> + * CPUs, so should only be written by the main CPU.
> + */
> +static struct mp_callback **ap_callbacks;
> +
>  static inline void barrier_wait(atomic_t *b)
>  {
>  	while (atomic_read(b) == 0)
> @@ -147,11 +169,9 @@ static void ap_init(unsigned int cpu_index)
>  	debug("AP: slot %d apic_id %x, dev %s\n", cpu_index, apic_id,
>  	      dev ? dev->name : "(apic_id not found)");
>  
> -	/* Walk the flight plan */
> +	/* Walk the flight plan, which never returns */
>  	ap_do_flight_plan(dev);
> -
> -	/* Park the AP */
> -	debug("parking\n");
> +	debug("Unexpected return\n");
>  done:
>  	stop_this_cpu();
>  }
> @@ -442,6 +462,68 @@ static int get_bsp(struct udevice **devp, int *cpu_countp)
>  	return dev->req_seq;
>  }
>  
> +static struct mp_callback *read_callback(struct mp_callback **slot)
> +{
> +	struct mp_callback *ret;
> +
> +	asm volatile ("mov	%1, %0\n"
> +		: "=r" (ret)
> +		: "m" (*slot)
> +		: "memory"
> +	);
> +	return ret;
> +}
> +
> +static void store_callback(struct mp_callback **slot, struct mp_callback *val)
> +{
> +	asm volatile ("mov	%1, %0\n"
> +		: "=m" (*slot)
> +		: "r" (val)
> +		: "memory"
> +	);
> +}

Descriptions for the two functions above would make them easier to understand.

> +
> +/**
> + * ap_wait_for_instruction() - Wait for and process requests from the main CPU
> + *
> + * This is called by APs (here, everything other than the main boot CPU) to
> + * await instructions. They arrive in the form of a function call and argument,
> + * which is then called. This uses a simple mailbox with atomic read/set
> + *
> + * @cpu: CPU that is waiting
> + * @unused: Optional argument provided by struct mp_flight_record, not used here
> + * @return Does not return
> + */
> +static int ap_wait_for_instruction(struct udevice *cpu, void *unused)
> +{
> +	struct mp_callback lcb;
> +	struct mp_callback **per_cpu_slot;
> +
> +	per_cpu_slot = &ap_callbacks[cpu->req_seq];
> +
> +	while (1) {
> +		struct mp_callback *cb = read_callback(per_cpu_slot);
> +
> +		if (!cb) {
> +			asm ("pause");
> +			continue;
> +		}
> +
> +		/* Copy to local variable before using the value */
> +		memcpy(&lcb, cb, sizeof(lcb));
> +		mfence();
> +		if (lcb.logical_cpu_number == MP_SELECT_ALL ||
> +		    lcb.logical_cpu_number == MP_SELECT_APS ||
> +		    cpu->req_seq == lcb.logical_cpu_number)
> +			lcb.func(lcb.arg);
> +
> +		/* Indicate we are finished */
> +		store_callback(per_cpu_slot, NULL);
> +	}
> +
> +	return 0;
> +}
> +
>  static int mp_init_cpu(struct udevice *cpu, void *unused)
>  {
>  	struct cpu_platdata *plat = dev_get_parent_platdata(cpu);
> @@ -454,6 +536,7 @@ static int mp_init_cpu(struct udevice *cpu, void *unused)
>  
>  static struct mp_flight_record mp_steps[] = {
>  	MP_FR_BLOCK_APS(mp_init_cpu, NULL, mp_init_cpu, NULL),
> +	MP_FR_BLOCK_APS(ap_wait_for_instruction, NULL, NULL, NULL),
>  };
>  
>  int mp_init(void)
> @@ -491,6 +574,10 @@ int mp_init(void)
>  	if (ret)
>  		log_warning("Warning: Device tree does not describe all CPUs. Extra ones will not be started correctly\n");
>  
> +	ap_callbacks = calloc(num_cpus, sizeof(struct mp_callback *));
> +	if (!ap_callbacks)
> +		return -ENOMEM;
> +
>  	/* Copy needed parameters so that APs have a reference to the plan */
>  	mp_info.num_records = ARRAY_SIZE(mp_steps);
>  	mp_info.records = mp_steps;
> diff --git a/arch/x86/include/asm/mp.h b/arch/x86/include/asm/mp.h
> index 94af819ad9a..41b1575f4be 100644
> --- a/arch/x86/include/asm/mp.h
> +++ b/arch/x86/include/asm/mp.h
> @@ -11,6 +11,17 @@
>  #include <asm/atomic.h>
>  #include <asm/cache.h>
>  
> +enum {
> +	/* Indicates that the function should run on all CPUs */
> +	MP_SELECT_ALL	= -1,
> +
> +	/* Run on boot CPUs */
> +	MP_SELECT_BSP	= -2,
> +
> +	/* Run on non-boot CPUs */
> +	MP_SELECT_APS	= -3,
> +};
> +
>  typedef int (*mp_callback_t)(struct udevice *cpu, void *arg);
>  
>  /*
> -- 
> 2.27.0.rc0.183.gde8f92d652-goog

regards, Wolfgang


More information about the U-Boot mailing list