301 lines · 6.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * AMD L3 cache_disable_{0,1} sysfs handling * Documentation/ABI/testing/sysfs-devices-system-cpu */ #include <linux/cacheinfo.h>#include <linux/capability.h>#include <linux/pci.h>#include <linux/sysfs.h> #include <asm/amd/nb.h> #include "cpu.h" /* * L3 cache descriptors */static void amd_calc_l3_indices(struct amd_northbridge *nb){	struct amd_l3_cache *l3 = &nb->l3_cache;	unsigned int sc0, sc1, sc2, sc3;	u32 val = 0; 	pci_read_config_dword(nb->misc, 0x1C4, &val); 	/* calculate subcache sizes */	l3->subcaches[0] = sc0 = !(val & BIT(0));	l3->subcaches[1] = sc1 = !(val & BIT(4)); 	if (boot_cpu_data.x86 == 0x15) {		l3->subcaches[0] = sc0 += !(val & BIT(1));		l3->subcaches[1] = sc1 += !(val & BIT(5));	} 	l3->subcaches[2] = sc2 = !(val & BIT(8))  + !(val & BIT(9));	l3->subcaches[3] = sc3 = !(val & BIT(12)) + !(val & BIT(13)); 	l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1;} /* * check whether a slot used for disabling an L3 index is occupied. * @l3: L3 cache descriptor * @slot: slot number (0..1) * * @returns: the disabled index if used or negative value if slot free. */static int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned int slot){	unsigned int reg = 0; 	pci_read_config_dword(nb->misc, 0x1BC + slot * 4, &reg); 	/* check whether this slot is activated already */	if (reg & (3UL << 30))		return reg & 0xfff; 	return -1;} static ssize_t show_cache_disable(struct cacheinfo *ci, char *buf, unsigned int slot){	int index;	struct amd_northbridge *nb = ci->priv; 	index = amd_get_l3_disable_slot(nb, slot);	if (index >= 0)		return sysfs_emit(buf, "%d\n", index); 	return sysfs_emit(buf, "FREE\n");} #define SHOW_CACHE_DISABLE(slot)					\static ssize_t								\cache_disable_##slot##_show(struct device *dev,				\			    struct device_attribute *attr, char *buf)	\{									\	struct cacheinfo *ci = dev_get_drvdata(dev);			\	return show_cache_disable(ci, buf, slot);			\} SHOW_CACHE_DISABLE(0)SHOW_CACHE_DISABLE(1) static void amd_l3_disable_index(struct amd_northbridge *nb, int cpu,				 unsigned int slot, unsigned long idx){	int i; 	idx |= BIT(30); 	/*	 *  disable index in all 4 subcaches	 */	for (i = 0; i < 4; i++) {		u32 reg = idx | (i << 20); 		if (!nb->l3_cache.subcaches[i])			continue; 		pci_write_config_dword(nb->misc, 0x1BC + slot * 4, reg); 		/*		 * We need to WBINVD on a core on the node containing the L3		 * cache which indices we disable therefore a simple wbinvd()		 * is not sufficient.		 */		wbinvd_on_cpu(cpu); 		reg |= BIT(31);		pci_write_config_dword(nb->misc, 0x1BC + slot * 4, reg);	}} /* * disable a L3 cache index by using a disable-slot * * @l3:    L3 cache descriptor * @cpu:   A CPU on the node containing the L3 cache * @slot:  slot number (0..1) * @index: index to disable * * @return: 0 on success, error status on failure */static int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu,				   unsigned int slot, unsigned long index){	int ret = 0; 	/*  check if @slot is already used or the index is already disabled */	ret = amd_get_l3_disable_slot(nb, slot);	if (ret >= 0)		return -EEXIST; 	if (index > nb->l3_cache.indices)		return -EINVAL; 	/* check whether the other slot has disabled the same index already */	if (index == amd_get_l3_disable_slot(nb, !slot))		return -EEXIST; 	amd_l3_disable_index(nb, cpu, slot, index); 	return 0;} static ssize_t store_cache_disable(struct cacheinfo *ci, const char *buf,				   size_t count, unsigned int slot){	struct amd_northbridge *nb = ci->priv;	unsigned long val = 0;	int cpu, err = 0; 	if (!capable(CAP_SYS_ADMIN))		return -EPERM; 	cpu = cpumask_first(&ci->shared_cpu_map); 	if (kstrtoul(buf, 10, &val) < 0)		return -EINVAL; 	err = amd_set_l3_disable_slot(nb, cpu, slot, val);	if (err) {		if (err == -EEXIST)			pr_warn("L3 slot %d in use/index already disabled!\n",				   slot);		return err;	}	return count;} #define STORE_CACHE_DISABLE(slot)					\static ssize_t								\cache_disable_##slot##_store(struct device *dev,			\			     struct device_attribute *attr,		\			     const char *buf, size_t count)		\{									\	struct cacheinfo *ci = dev_get_drvdata(dev);			\	return store_cache_disable(ci, buf, count, slot);		\} STORE_CACHE_DISABLE(0)STORE_CACHE_DISABLE(1) static ssize_t subcaches_show(struct device *dev, struct device_attribute *attr,			      char *buf){	struct cacheinfo *ci = dev_get_drvdata(dev);	int cpu = cpumask_first(&ci->shared_cpu_map); 	return sysfs_emit(buf, "%x\n", amd_get_subcaches(cpu));} static ssize_t subcaches_store(struct device *dev,			       struct device_attribute *attr,			       const char *buf, size_t count){	struct cacheinfo *ci = dev_get_drvdata(dev);	int cpu = cpumask_first(&ci->shared_cpu_map);	unsigned long val; 	if (!capable(CAP_SYS_ADMIN))		return -EPERM; 	if (kstrtoul(buf, 16, &val) < 0)		return -EINVAL; 	if (amd_set_subcaches(cpu, val))		return -EINVAL; 	return count;} static DEVICE_ATTR_RW(cache_disable_0);static DEVICE_ATTR_RW(cache_disable_1);static DEVICE_ATTR_RW(subcaches); static umode_t cache_private_attrs_is_visible(struct kobject *kobj,					      struct attribute *attr, int unused){	struct device *dev = kobj_to_dev(kobj);	struct cacheinfo *ci = dev_get_drvdata(dev);	umode_t mode = attr->mode; 	if (!ci->priv)		return 0; 	if ((attr == &dev_attr_subcaches.attr) &&	    amd_nb_has_feature(AMD_NB_L3_PARTITIONING))		return mode; 	if ((attr == &dev_attr_cache_disable_0.attr ||	     attr == &dev_attr_cache_disable_1.attr) &&	    amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))		return mode; 	return 0;} static struct attribute_group cache_private_group = {	.is_visible = cache_private_attrs_is_visible,}; static void init_amd_l3_attrs(void){	static struct attribute **amd_l3_attrs;	int n = 1; 	if (amd_l3_attrs) /* already initialized */		return; 	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))		n += 2;	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))		n += 1; 	amd_l3_attrs = kzalloc_objs(*amd_l3_attrs, n);	if (!amd_l3_attrs)		return; 	n = 0;	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) {		amd_l3_attrs[n++] = &dev_attr_cache_disable_0.attr;		amd_l3_attrs[n++] = &dev_attr_cache_disable_1.attr;	}	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))		amd_l3_attrs[n++] = &dev_attr_subcaches.attr; 	cache_private_group.attrs = amd_l3_attrs;} const struct attribute_group *cache_get_priv_group(struct cacheinfo *ci){	struct amd_northbridge *nb = ci->priv; 	if (ci->level < 3 || !nb)		return NULL; 	if (nb && nb->l3_cache.indices)		init_amd_l3_attrs(); 	return &cache_private_group;} struct amd_northbridge *amd_init_l3_cache(int index){	struct amd_northbridge *nb;	int node; 	/* only for L3, and not in virtualized environments */	if (index < 3)		return NULL; 	node = topology_amd_node_id(smp_processor_id());	nb = node_to_amd_nb(node);	if (nb && !nb->l3_cache.indices)		amd_calc_l3_indices(nb); 	return nb;}