diff --git a/drivers/base/core.c b/drivers/base/core.c index 7327871344164f2f09ef151d77864809ccadc2d1..56be3f45cbe48067383cb76095ff4861de3b1f14 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -560,20 +561,11 @@ static struct class devlink_class = { static int devlink_add_symlinks(struct device *dev) { + char *buf_con __free(kfree) = NULL, *buf_sup __free(kfree) = NULL; int ret; - size_t len; struct device_link *link = to_devlink(dev); struct device *sup = link->supplier; struct device *con = link->consumer; - char *buf; - - len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)), - strlen(dev_bus_name(con)) + strlen(dev_name(con))); - len += strlen(":"); - len += strlen("supplier:") + 1; - buf = kzalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; ret = sysfs_create_link(&link->link_dev.kobj, &sup->kobj, "supplier"); if (ret) @@ -583,58 +575,64 @@ static int devlink_add_symlinks(struct device *dev) if (ret) goto err_con; - snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); - ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf); + buf_con = kasprintf(GFP_KERNEL, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); + if (!buf_con) { + ret = -ENOMEM; + goto err_con_dev; + } + + ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf_con); if (ret) goto err_con_dev; - snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); - ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf); + buf_sup = kasprintf(GFP_KERNEL, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); + if (!buf_sup) { + ret = -ENOMEM; + goto err_sup_dev; + } + + ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf_sup); if (ret) goto err_sup_dev; goto out; err_sup_dev: - snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); - sysfs_remove_link(&sup->kobj, buf); + sysfs_remove_link(&sup->kobj, buf_con); err_con_dev: sysfs_remove_link(&link->link_dev.kobj, "consumer"); err_con: sysfs_remove_link(&link->link_dev.kobj, "supplier"); out: - kfree(buf); return ret; } static void devlink_remove_symlinks(struct device *dev) { + char *buf_con __free(kfree) = NULL, *buf_sup __free(kfree) = NULL; struct device_link *link = to_devlink(dev); - size_t len; struct device *sup = link->supplier; struct device *con = link->consumer; - char *buf; sysfs_remove_link(&link->link_dev.kobj, "consumer"); sysfs_remove_link(&link->link_dev.kobj, "supplier"); - len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)), - strlen(dev_bus_name(con)) + strlen(dev_name(con))); - len += strlen(":"); - len += strlen("supplier:") + 1; - buf = kzalloc(len, GFP_KERNEL); - if (!buf) { - WARN(1, "Unable to properly free device link symlinks!\n"); - return; - } - if (device_is_registered(con)) { - snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); - sysfs_remove_link(&con->kobj, buf); + buf_sup = kasprintf(GFP_KERNEL, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); + if (!buf_sup) + goto out; + sysfs_remove_link(&con->kobj, buf_sup); } - snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); - sysfs_remove_link(&sup->kobj, buf); - kfree(buf); + + buf_con = kasprintf(GFP_KERNEL, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); + if (!buf_con) + goto out; + sysfs_remove_link(&sup->kobj, buf_con); + + return; + +out: + WARN(1, "Unable to properly free device link symlinks!\n"); } static struct class_interface devlink_class_intf = { diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index e3008215ab2d2f0a01097c54e3505c12e4e039cb..6f1d8e77f774e22c47f4d2a7c31d0253b459d22b 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -39,6 +39,11 @@ #define SMCCC_SOC_ID_T241 0x036b0241 static void __iomem *t241_scratch_regs[T241_CHIPS_MAX]; +/* Values for the HISI workaround */ +#define HIP12_ACPI_PLAT { "HISI ", "HIP12 ", 0, ACPI_SIG_MPAM, all_versions } +#define HISI_CONFIGURE_CPBM_WD 19 +#define HISI_HARDWARE_CPBM_WD 21 + /* * mpam_list_lock protects the SRCU lists when writing. Once the * mpam_enabled key is enabled these lists are read-only, @@ -671,6 +676,62 @@ static int mpam_enable_quirk_nvidia_t241_1(struct mpam_msc *msc, return 0; } +static int mpam_enable_quirk_hisi_csu(struct mpam_msc *msc, + const struct mpam_quirk *quirk) +{ + struct mpam_msc_ris *ris; + + if (!quirk->plat || acpi_match_platform_list(quirk->plat) < 0) + return -EINVAL; + + list_for_each_entry(ris, &msc->ris, msc_list) { + struct mpam_class *class = ris->vmsc->comp->class; + + if (class->type == MPAM_CLASS_CACHE && class->level == 3) + return 0; + } + return -EINVAL; +} + +static int mpam_enable_quirk_hisi_cpbm_wd(struct mpam_msc *msc, + const struct mpam_quirk *quirk) +{ + struct mpam_msc_ris *ris; + + if (!quirk->plat || acpi_match_platform_list(quirk->plat) < 0) + return -EINVAL; + + list_for_each_entry(ris, &msc->ris, msc_list) { + struct mpam_class *class = ris->vmsc->comp->class; + + if (class->type == MPAM_CLASS_CACHE && class->level == 3) { + mpam_set_quirk(quirk->workaround, msc); + class->quirks |= msc->quirks; + return -EEXIST; + } + } + return -EINVAL; +} + +/* + * Hardware restriction: Cacheways 17 to 20 only have half the normal capacity. + * To meet the expected capacity requirements: + * 1. Configuring BIT(17) requires using both BIT(17) and BIT(18). + * 2. Configuring BIT(18) requires using both BIT(19) and BIT(20). + */ +static u32 mpam_cpbm_hisi_workaround(u32 cpbm) +{ + if (cpbm & BIT(18)) + cpbm |= (BIT(19) | BIT(20)); + + if (cpbm & BIT(17)) + cpbm |= BIT(18); + else + cpbm &= ~BIT(18); + + return cpbm; +} + static const struct mpam_quirk mpam_quirks[] = { { /* NVIDIA t241 erratum T241-MPAM-1 */ @@ -697,6 +758,22 @@ static const struct mpam_quirk mpam_quirks[] = { .iidr_mask = MPAM_IIDR_MATCH_ONE, .workaround = IGNORE_CSU_NRDY, }, + { + .init = mpam_enable_quirk_hisi_csu, + .iidr_mask = MPAM_IIDR_MATCH_ONE, + .plat = (struct acpi_platform_list[]) { + HIP12_ACPI_PLAT, + {} }, + .workaround = HISI_CSU_WORKAROUND, + }, + { + .init = mpam_enable_quirk_hisi_cpbm_wd, + .iidr_mask = MPAM_IIDR_MATCH_ONE, + .plat = (struct acpi_platform_list[]) { + HIP12_ACPI_PLAT, + {} }, + .workaround = HISI_EXPAND_CPBM_WD, + }, { NULL } /* Sentinel */ }; @@ -795,7 +872,11 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { u32 cpor_features = mpam_read_partsel_reg(msc, CPOR_IDR); - props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features); + if (mpam_has_quirk(HISI_EXPAND_CPBM_WD, class)) + props->cpbm_wd = HISI_CONFIGURE_CPBM_WD; + else + props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features); + if (props->cpbm_wd) mpam_set_feature(mpam_feat_cpor_part, props); } @@ -1209,6 +1290,9 @@ static void __ris_msmon_read(void *arg) FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); + /* Selects a monitor instance to configure PARTID. */ + wmb(); + switch (m->type) { case mpam_feat_msmon_mbwu_31counter: case mpam_feat_msmon_mbwu_44counter: @@ -1250,12 +1334,20 @@ static void __ris_msmon_read(void *arg) MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L)); } + /* + * Selects the monitor instance associated to the specified PARTID + * to read counter value. + */ + wmb(); + switch (m->type) { case mpam_feat_msmon_csu: now = mpam_read_monsel_reg(msc, CSU); if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops)) nrdy = now & MSMON___NRDY; now = FIELD_GET(MSMON___VALUE, now); + if (mpam_has_quirk(HISI_CSU_WORKAROUND, ris->vmsc->msc)) + now >>= 1; if (mpam_has_quirk(IGNORE_CSU_NRDY, msc) && m->waited_timeout) nrdy = false; @@ -1562,10 +1654,21 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, } if (mpam_has_feature(mpam_feat_cpor_part, rprops)) { - if (mpam_has_feature(mpam_feat_cpor_part, cfg)) - mpam_write_partsel_reg(msc, CPBM, cfg->cpbm); - else - mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd); + if (mpam_has_feature(mpam_feat_cpor_part, cfg)) { + u32 cpbm = cfg->cpbm; + + if (mpam_has_quirk(HISI_EXPAND_CPBM_WD, ris->vmsc->comp->class)) + cpbm = mpam_cpbm_hisi_workaround(cpbm); + + mpam_write_partsel_reg(msc, CPBM, cpbm); + } else { + u16 cpbm_wd = rprops->cpbm_wd; + + if (mpam_has_quirk(HISI_EXPAND_CPBM_WD, ris->vmsc->comp->class)) + cpbm_wd = HISI_HARDWARE_CPBM_WD; + + mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, cpbm_wd); + } } if (mpam_has_feature(mpam_feat_mbw_part, rprops)) { diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h index dbb99d9b07958190f3daca2dd9aa8cbc4b32c0f7..0d0c9ba25b5ab82eb9bbfec3f6d8b606a4b32bd5 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -227,6 +227,8 @@ enum mpam_device_quirks { T241_FORCE_MBW_MIN_TO_ONE, T241_MBW_COUNTER_SCALE_64, IGNORE_CSU_NRDY, + HISI_CSU_WORKAROUND, + HISI_EXPAND_CPBM_WD, MPAM_QUIRK_LAST }; @@ -239,6 +241,7 @@ struct mpam_quirk { u32 iidr; u32 iidr_mask; + struct acpi_platform_list *plat; enum mpam_device_quirks workaround; }; diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c index 5ebc56c515a7518c3a67ae3b23742ee34316673e..1dd3bbd0772cddbd0ed2998ed8b9a2250c98fc05 100644 --- a/drivers/resctrl/mpam_resctrl.c +++ b/drivers/resctrl/mpam_resctrl.c @@ -22,6 +22,9 @@ #include "mpam_internal.h" +/* Values for the HISI workaround */ +#define HISI_VALID_CPBM_MASK ~(BIT(17) | BIT(18)) + DECLARE_WAIT_QUEUE_HEAD(resctrl_mon_ctx_waiters); /* @@ -1218,6 +1221,10 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d, switch (r->rid) { case RDT_RESOURCE_L2: case RDT_RESOURCE_L3: + if (mpam_has_quirk(HISI_EXPAND_CPBM_WD, res->class) && + !(cfg_val & HISI_VALID_CPBM_MASK)) + return -EINVAL; + cfg.cpbm = cfg_val; mpam_set_feature(mpam_feat_cpor_part, &cfg); break;