Commit 3f4b9c31 authored by James Morse's avatar James Morse Committed by Zeng Heng
Browse files

arm_mpam: Add cpuhp callbacks to probe MSC hardware

maillist inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8T2RT

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/log/?h=mpam/snapshot/v6.7-rc2



---------------------------

Because an MSC can only by accessed from the CPUs in its cpu-affinity
set we need to be running on one of those CPUs to probe the MSC
hardware.

Do this work in the cpuhp callback. Probing the hardware will only
happen before MPAM is enabled, walk all the MSCs and probe those we can
reach that haven't already been probed.

Later, enabling MPAM will enable a static key which will allow
mpam_discovery_cpu_online() and its mutex to be skipped.

Enabling a static key will also take the cpuhp lock, so can't be done
from the cpuhp callback. Whenever a new MSC has been probed schedule
work to test if all the MSCs have now been probed.

Signed-off-by: default avatarJames Morse <james.morse@arm.com>
Signed-off-by: default avatarZeng Heng <zengheng4@huawei.com>
parent ffcdaed5
Loading
Loading
Loading
Loading
+145 −3
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#define pr_fmt(fmt) "mpam: " fmt

#include <linux/acpi.h>
#include <linux/atomic.h>
#include <linux/arm_mpam.h>
#include <linux/cacheinfo.h>
#include <linux/cpu.h>
@@ -21,6 +22,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/workqueue.h>

#include <acpi/pcc.h>

@@ -41,6 +43,16 @@ struct srcu_struct mpam_srcu;
/* MPAM isn't available until all the MSC have been probed. */
static u32 mpam_num_msc;

static int mpam_cpuhp_state;
static DEFINE_MUTEX(mpam_cpuhp_state_lock);

/*
 * mpam is enabled once all devices have been probed from CPU online callbacks,
 * scheduled via this work_struct. If access to an MSC depends on a CPU that
 * was not brought online at boot, this can happen surprisingly late.
 */
static DECLARE_WORK(mpam_enable_work, &mpam_enable);

/*
 * An MSC is a container for resources, each identified by their RIS index.
 * Components are a group of RIS that control the same thing.
@@ -59,6 +71,24 @@ static u32 mpam_num_msc;
 */
LIST_HEAD(mpam_classes);

static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg)
{
	WARN_ON_ONCE(reg > msc->mapped_hwpage_sz);
	WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));

	return readl_relaxed(msc->mapped_hwpage + reg);
}

#define mpam_read_partsel_reg(msc, reg)			\
({							\
	u32 ____ret;					\
							\
	lockdep_assert_held_once(&msc->part_sel_lock);	\
	____ret = __mpam_read_reg(msc, MPAMF_##reg);	\
							\
	____ret;					\
})

static struct mpam_component *
mpam_component_alloc(struct mpam_class *class, int id, gfp_t gfp)
{
@@ -389,9 +419,81 @@ int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx,
	return err;
}

static void mpam_discovery_complete(void)
static int mpam_msc_hw_probe(struct mpam_msc *msc)
{
	pr_err("Discovered all MSC\n");
	u64 idr;
	int err;

	lockdep_assert_held(&msc->lock);

	spin_lock(&msc->part_sel_lock);
	idr = mpam_read_partsel_reg(msc, AIDR);
	if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) {
		pr_err_once("%s does not match MPAM architecture v1.0\n",
			    dev_name(&msc->pdev->dev));
		err = -EIO;
	} else {
		msc->probed = true;
		err = 0;
	}
	spin_unlock(&msc->part_sel_lock);

	return err;
}

static int mpam_cpu_online(unsigned int cpu)
{
	return 0;
}

/* Before mpam is enabled, try to probe new MSC */
static int mpam_discovery_cpu_online(unsigned int cpu)
{
	int err = 0;
	struct mpam_msc *msc;
	bool new_device_probed = false;

	mutex_lock(&mpam_list_lock);
	list_for_each_entry(msc, &mpam_all_msc, glbl_list) {
		if (!cpumask_test_cpu(cpu, &msc->accessibility))
			continue;

		mutex_lock(&msc->lock);
		if (!msc->probed)
			err = mpam_msc_hw_probe(msc);
		mutex_unlock(&msc->lock);

		if (!err)
			new_device_probed = true;
		else
			break; // mpam_broken
	}
	mutex_unlock(&mpam_list_lock);

	if (new_device_probed && !err)
		schedule_work(&mpam_enable_work);

	if (err < 0)
		return err;

	return mpam_cpu_online(cpu);
}

static int mpam_cpu_offline(unsigned int cpu)
{
	return 0;
}

static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int online))
{
	mutex_lock(&mpam_cpuhp_state_lock);
	mpam_cpuhp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mpam:online",
					     online, mpam_cpu_offline);
	if (mpam_cpuhp_state <= 0) {
		pr_err("Failed to register cpuhp callbacks");
		mpam_cpuhp_state = 0;
	}
	mutex_unlock(&mpam_cpuhp_state_lock);
}

static int mpam_dt_count_msc(void)
@@ -628,11 +730,51 @@ static int mpam_msc_drv_probe(struct platform_device *pdev)
	}

	if (!err && fw_num_msc == mpam_num_msc)
		mpam_discovery_complete();
		mpam_register_cpuhp_callbacks(&mpam_discovery_cpu_online);

	return err;
}

static void mpam_enable_once(void)
{
	mutex_lock(&mpam_cpuhp_state_lock);
	cpuhp_remove_state(mpam_cpuhp_state);
	mpam_cpuhp_state = 0;
	mutex_unlock(&mpam_cpuhp_state_lock);

	mpam_register_cpuhp_callbacks(mpam_cpu_online);

	pr_info("MPAM enabled\n");
}

/*
 * Enable mpam once all devices have been probed.
 * Scheduled by mpam_discovery_cpu_online() once all devices have been created.
 * Also scheduled when new devices are probed when new CPUs come online.
 */
void mpam_enable(struct work_struct *work)
{
	static atomic_t once;
	struct mpam_msc *msc;
	bool all_devices_probed = true;

	/* Have we probed all the hw devices? */
	mutex_lock(&mpam_list_lock);
	list_for_each_entry(msc, &mpam_all_msc, glbl_list) {
		mutex_lock(&msc->lock);
		if (!msc->probed)
			all_devices_probed = false;
		mutex_unlock(&msc->lock);

		if (!all_devices_probed)
			break;
	}
	mutex_unlock(&mpam_list_lock);

	if (all_devices_probed && !atomic_fetch_inc(&once))
		mpam_enable_once();
}

static int mpam_msc_drv_remove(struct platform_device *pdev)
{
	struct mpam_msc *msc = platform_get_drvdata(pdev);
+4 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ struct mpam_msc
	cpumask_t		accessibility;

	struct mutex		lock;
	bool			probed;
	unsigned long		ris_idxs[128 / BITS_PER_LONG];
	u32			ris_max;

@@ -97,6 +98,8 @@ struct mpam_msc_ris
extern struct list_head mpam_classes;
extern struct srcu_struct mpam_srcu;

/* Scheduled work callback to enable mpam once all MSC have been probed */
void mpam_enable(struct work_struct *work);

/*
 * MPAM MSCs have the following register layout. See:
@@ -196,7 +199,7 @@ extern struct srcu_struct mpam_srcu;

/* MPAMF_MBWUMON_IDR - MPAM memory bandwidth usage monitor ID register */
#define MPAMF_MBWUMON_IDR_NUM_MON       GENMASK(15, 0)
#define MPAMF_MBWUMON_IDR_RWBW           BIT(28)
#define MPAMF_MBWUMON_IDR_HAS_RWBW      BIT(28)
#define MPAMF_MBWUMON_IDR_LWD           BIT(29)
#define MPAMF_MBWUMON_IDR_HAS_LONG      BIT(30)
#define MPAMF_MBWUMON_IDR_HAS_CAPTURE   BIT(31)