Commit 848cefee authored by James Morse's avatar James Morse Committed by Zeng Heng
Browse files

arm_mpam: Add the class and component structures for ris firmware described

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



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

An MSC is a container of resources, each identified by their RIS index.
Some RIS are described by firmware to provide their position in the system.
Others are discovered when the driver probes the hardware.

To configure a resource it needs to be found by its class, e.g. 'L2'.
There are two kinds of grouping, a class is a set of components, which
are visible as there are likely to be multiple instances of the L2 cache.
struct mpam_components are a set of struct mpam_msc_ris, which are not
visible as each L2 cache may be composed of individual slices which need
to be configured the same as the hardware is not able to distribute the
configuration.

Add support for creating and destroying these structures.
A gfp is passed as the structure for 'unknown' may need creating
if a new RIS entry is discovered when probing the MSC.

Signed-off-by: default avatarJames Morse <james.morse@arm.com>
Signed-off-by: default avatarZeng Heng <zengheng4@huawei.com>
parent 3e9e723f
Loading
Loading
Loading
Loading
+358 −4
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
#include <linux/types.h>

#include <acpi/pcc.h>
@@ -37,11 +36,359 @@
static DEFINE_MUTEX(mpam_list_lock);
static LIST_HEAD(mpam_all_msc);

static struct srcu_struct mpam_srcu;
struct srcu_struct mpam_srcu;

/* MPAM isn't available until all the MSC have been probed. */
static u32 mpam_num_msc;

/*
 * An MSC is a container for resources, each identified by their RIS index.
 * Components are a group of RIS that control the same thing.
 * Classes are the set components of the same type.
 *
 * e.g. The set of RIS that make up the L2 are a component. These are sometimes
 * termed slices. They should be configured as if they were one MSC.
 *
 * e.g. The SoC probably has more than one L2, each attached to a distinct set
 * of CPUs. All the L2 components are grouped as a class.
 *
 * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list,
 * then linked via struct mpam_ris to a component and a class.
 * The same MSC may exist under different class->component paths, but the RIS
 * index will be unique.
 */
LIST_HEAD(mpam_classes);

static struct mpam_component *
mpam_component_alloc(struct mpam_class *class, int id, gfp_t gfp)
{
	struct mpam_component *comp;

	lockdep_assert_held(&mpam_list_lock);

	comp = kzalloc(sizeof(*comp), gfp);
	if (!comp)
		return ERR_PTR(-ENOMEM);

	comp->comp_id = id;
	INIT_LIST_HEAD_RCU(&comp->ris);
	/* affinity is updated when ris are added */
	INIT_LIST_HEAD_RCU(&comp->class_list);
	comp->class = class;

	list_add_rcu(&comp->class_list, &class->components);

	return comp;
}

static struct mpam_component *
mpam_component_get(struct mpam_class *class, int id, bool alloc, gfp_t gfp)
{
	struct mpam_component *comp;

	lockdep_assert_held(&mpam_list_lock);

	list_for_each_entry(comp, &class->components, class_list) {
		if (comp->comp_id == id)
			return comp;
	}

	if (!alloc)
		return ERR_PTR(-ENOENT);

	return mpam_component_alloc(class, id, gfp);
}

static struct mpam_class *
mpam_class_alloc(u8 level_idx, enum mpam_class_types type, gfp_t gfp)
{
	struct mpam_class *class;

	lockdep_assert_held(&mpam_list_lock);

	class = kzalloc(sizeof(*class), gfp);
	if (!class)
		return ERR_PTR(-ENOMEM);

	INIT_LIST_HEAD_RCU(&class->components);
	/* affinity is updated when ris are added */
	class->level = level_idx;
	class->type = type;
	INIT_LIST_HEAD_RCU(&class->classes_list);

	list_add_rcu(&class->classes_list, &mpam_classes);

	return class;
}

static struct mpam_class *
mpam_class_get(u8 level_idx, enum mpam_class_types type, bool alloc, gfp_t gfp)
{
	bool found = false;
	struct mpam_class *class;

	lockdep_assert_held(&mpam_list_lock);

	list_for_each_entry(class, &mpam_classes, classes_list) {
		if (class->type == type && class->level == level_idx) {
			found = true;
			break;
		}
	}

	if (found)
		return class;

	if (!alloc)
		return ERR_PTR(-ENOENT);

	return mpam_class_alloc(level_idx, type, gfp);
}

static void mpam_class_destroy(struct mpam_class *class)
{
	lockdep_assert_held(&mpam_list_lock);

	list_del_rcu(&class->classes_list);
	synchronize_srcu(&mpam_srcu);
	kfree(class);
}

static void mpam_comp_destroy(struct mpam_component *comp)
{
	struct mpam_class *class = comp->class;

	lockdep_assert_held(&mpam_list_lock);

	list_del_rcu(&comp->class_list);
	synchronize_srcu(&mpam_srcu);
	kfree(comp);

	if (list_empty(&class->components))
		mpam_class_destroy(class);
}

/* synchronise_srcu() before freeing ris */
static void mpam_ris_destroy(struct mpam_msc_ris *ris)
{
	struct mpam_component *comp = ris->comp;
	struct mpam_class *class = comp->class;
	struct mpam_msc *msc = ris->msc;

	lockdep_assert_held(&mpam_list_lock);
	lockdep_assert_preemption_enabled();

	clear_bit(ris->ris_idx, msc->ris_idxs);
	list_del_rcu(&ris->comp_list);
	list_del_rcu(&ris->msc_list);

	cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity);
	cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity);

	if (list_empty(&comp->ris))
		mpam_comp_destroy(comp);
}

/*
 * There are two ways of reaching a struct mpam_msc_ris. Via the
 * class->component->ris, or via the msc.
 * When destroying the msc, the other side needs unlinking and cleaning up too.
 * synchronise_srcu() before freeing msc.
 */
static void mpam_msc_destroy(struct mpam_msc *msc)
{
	struct mpam_msc_ris *ris, *tmp;

	lockdep_assert_held(&mpam_list_lock);
	lockdep_assert_preemption_enabled();

	list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list)
		mpam_ris_destroy(ris);
}

/*
 * The cacheinfo structures are only populated when CPUs are online.
 * This helper walks the device tree to include offline CPUs too.
 */
static int get_cpumask_from_cache_id(u32 cache_id, u32 cache_level,
				     cpumask_t *affinity)
{
	int cpu, err;
	u32 iter_level;
	int iter_cache_id;
	struct device_node *iter;

	if (!acpi_disabled)
		return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity);

	for_each_possible_cpu(cpu) {
		iter = of_get_cpu_node(cpu, NULL);
		if (!iter) {
			pr_err("Failed to find cpu%d device node\n", cpu);
			return -ENOENT;
		}

		while ((iter = of_find_next_cache_node(iter))) {
			err = of_property_read_u32(iter, "cache-level",
						   &iter_level);
			if (err || (iter_level != cache_level)) {
				of_node_put(iter);
				continue;
			}

			/*
			 * get_cpu_cacheinfo_id() isn't ready until sometime
			 * during device_initcall(). Use cache_of_get_id().
			 */
			iter_cache_id = cache_of_get_id(iter);
			if (cache_id == ~0UL) {
				of_node_put(iter);
				continue;
			}

			if (iter_cache_id == cache_id)
				cpumask_set_cpu(cpu, affinity);

			of_node_put(iter);
		}
	}

	return 0;
}


/*
 * cpumask_of_node() only knows about online CPUs. This can't tell us whether
 * a class is represented on all possible CPUs.
 */
static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity)
{
	int cpu;

	for_each_possible_cpu(cpu) {
		if (node_id == cpu_to_node(cpu))
			cpumask_set_cpu(cpu, affinity);
	}
}

static int get_cpumask_from_cache(struct device_node *cache,
				  cpumask_t *affinity)
{
	int err;
	u32 cache_level;
	int cache_id;

	err = of_property_read_u32(cache, "cache-level", &cache_level);
	if (err) {
		pr_err("Failed to read cache-level from cache node\n");
		return -ENOENT;
	}

	cache_id = cache_of_get_id(cache);
	if (cache_id == ~0UL) {
		pr_err("Failed to calculate cache-id from cache node\n");
		return -ENOENT;
	}

	return get_cpumask_from_cache_id(cache_id, cache_level, affinity);
}

static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity,
				 enum mpam_class_types type,
				 struct mpam_class *class,
				 struct mpam_component *comp)
{
	int err;

	switch (type) {
	case MPAM_CLASS_CACHE:
		err = get_cpumask_from_cache_id(comp->comp_id, class->level,
						affinity);
		if (err)
			return err;

		if (cpumask_empty(affinity))
			pr_warn_once("%s no CPUs associated with cache node",
				     dev_name(&msc->pdev->dev));

		break;
	case MPAM_CLASS_MEMORY:
		get_cpumask_from_node_id(comp->comp_id, affinity);
		if (cpumask_empty(affinity))
			pr_warn_once("%s no CPUs associated with memory node",
				     dev_name(&msc->pdev->dev));
		break;
	case MPAM_CLASS_UNKNOWN:
		return 0;
	}

	cpumask_and(affinity, affinity, &msc->accessibility);

	return 0;
}

static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx,
				  enum mpam_class_types type, u8 class_id,
				  int component_id, gfp_t gfp)
{
	int err;
	struct mpam_msc_ris *ris;
	struct mpam_class *class;
	struct mpam_component *comp;

	lockdep_assert_held(&mpam_list_lock);

	if (test_and_set_bit(ris_idx, msc->ris_idxs))
		return -EBUSY;

	ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), gfp);
	if (!ris)
		return -ENOMEM;

	class = mpam_class_get(class_id, type, true, gfp);
	if (IS_ERR(class))
		return PTR_ERR(class);

	comp = mpam_component_get(class, component_id, true, gfp);
	if (IS_ERR(comp)) {
		if (list_empty(&class->components))
			mpam_class_destroy(class);
		return PTR_ERR(comp);
	}

	err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp);
	if (err) {
		if (list_empty(&class->components))
			mpam_class_destroy(class);
		return err;
	}

	ris->ris_idx = ris_idx;
	INIT_LIST_HEAD_RCU(&ris->comp_list);
	INIT_LIST_HEAD_RCU(&ris->msc_list);
	ris->msc = msc;
	ris->comp = comp;

	cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity);
	cpumask_or(&class->affinity, &class->affinity, &ris->affinity);
	list_add_rcu(&ris->comp_list, &comp->ris);

	return 0;
}

int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx,
		    enum mpam_class_types type, u8 class_id, int component_id)
{
	int err;

	mutex_lock(&mpam_list_lock);
	err = mpam_ris_create_locked(msc, ris_idx, type, class_id,
				     component_id, GFP_KERNEL);
	mutex_unlock(&mpam_list_lock);

	return err;
}

static void mpam_discovery_complete(void)
{
	pr_err("Discovered all MSC\n");
@@ -152,9 +499,15 @@ static int get_msc_affinity(struct mpam_msc *msc)
	if (parent == of_root) {
		cpumask_copy(&msc->accessibility, cpu_possible_mask);
		err = 0;
	} else {
		if (of_device_is_compatible(parent, "cache")) {
			err = get_cpumask_from_cache(parent,
						     &msc->accessibility);
		} else {
			err = -EINVAL;
		pr_err("Cannot determine CPU accessibility of MSC\n");
			pr_err("Cannot determine accessibility of MSC: %s\n",
			       dev_name(&msc->pdev->dev));
		}
	}
	of_node_put(parent);

@@ -291,6 +644,7 @@ static int mpam_msc_drv_remove(struct platform_device *pdev)
	mpam_num_msc--;
	platform_set_drvdata(pdev, NULL);
	list_del_rcu(&msc->glbl_list);
	mpam_msc_destroy(msc);
	synchronize_srcu(&mpam_srcu);
	mutex_unlock(&mpam_list_lock);

+52 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/mutex.h>
#include <linux/resctrl.h>
#include <linux/sizes.h>
#include <linux/srcu.h>

struct mpam_msc
{
@@ -45,4 +46,55 @@ struct mpam_msc
	size_t			mapped_hwpage_sz;
};

struct mpam_class
{
	/* mpam_components in this class */
	struct list_head	components;

	cpumask_t		affinity;

	u8			level;
	enum mpam_class_types	type;

	/* member of mpam_classes */
	struct list_head	classes_list;
};

struct mpam_component
{
	u32			comp_id;

	/* mpam_msc_ris in this component */
	struct list_head	ris;

	cpumask_t		affinity;

	/* member of mpam_class:components */
	struct list_head	class_list;

	/* parent: */
	struct mpam_class	*class;
};

struct mpam_msc_ris
{
	u8			ris_idx;

	cpumask_t		affinity;

	/* member of mpam_component:ris */
	struct list_head	comp_list;

	/* member of mpam_msc:ris */
	struct list_head	msc_list;

	/* parents: */
	struct mpam_msc		*msc;
	struct mpam_component	*comp;
};

/* List of all classes */
extern struct list_head mpam_classes;
extern struct srcu_struct mpam_srcu;

#endif /* MPAM_INTERNAL_H */
+2 −5
Original line number Diff line number Diff line
@@ -34,10 +34,7 @@ static inline int acpi_mpam_parse_resources(struct mpam_msc *msc,
static inline int acpi_mpam_count_msc(void) { return -EINVAL; }
#endif

static inline int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx,
		    enum mpam_class_types type, u8 class_id, int component_id)
{
	return -EINVAL;
}
int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx,
		    enum mpam_class_types type, u8 class_id, int component_id);

#endif /* __LINUX_ARM_MPAM_H */