Add an alternative filesystem view for the discovered Data Events, where
the tree of DEs is laid out following the discovered topological order
instead of the existing flat layout.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
.../firmware/arm_scmi/scmi_system_telemetry.c | 730 ++++++++++++++++++
1 file changed, 730 insertions(+)
diff --git a/drivers/firmware/arm_scmi/scmi_system_telemetry.c b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
index f591aad10302..a4b6d23b211e 100644
--- a/drivers/firmware/arm_scmi/scmi_system_telemetry.c
+++ b/drivers/firmware/arm_scmi/scmi_system_telemetry.c
@@ -189,6 +189,8 @@ struct scmi_tlm_inode {
* @all_nodes: An array to keep track of all the initialized TLM nodes that
* have been created as a result of the usual probe time SCMI
* enumeration process.
+ * @des_nodes: An array to use as handy reference only to the set of nodes
+ * representing the DEs top directories.
* @info: A handy reference to this instance SCMI Telemetry info data.
*
* The most notable field in this structure is the @all_nodes array, which
@@ -208,6 +210,7 @@ struct scmi_tlm_instance {
int max_nodes;
int num_nodes;
struct scmi_tlm_inode **all_nodes;
+ struct scmi_tlm_inode **des_nodes;
const struct scmi_telemetry_info *info;
};
@@ -216,6 +219,526 @@ static int scmi_telemetry_instance_register(struct super_block *sb,
static LIST_HEAD(scmi_telemetry_instances);
+#define TYPES_ARRAY_SZ 256
+
+static const char *compo_types[TYPES_ARRAY_SZ] = {
+ "unspec",
+ "cpu",
+ "cluster",
+ "gpu",
+ "npu",
+ "interconnnect",
+ "mem_cntrl",
+ "l1_cache",
+ "l2_cache",
+ "l3_cache",
+ "ll_cache",
+ "sys_cache",
+ "disp_cntrl",
+ "ipu",
+ "chiplet",
+ "package",
+ "soc",
+ "system",
+ "smcu",
+ "accel",
+ "battery",
+ "charger",
+ "pmic",
+ "board",
+ "memory",
+ "periph",
+ "periph_subc",
+ "lid",
+ "display",
+ "res_29",
+ "res_30",
+ "res_31",
+ "res_32",
+ "res_33",
+ "res_34",
+ "res_35",
+ "res_36",
+ "res_37",
+ "res_38",
+ "res_39",
+ "res_40",
+ "res_41",
+ "res_42",
+ "res_43",
+ "res_44",
+ "res_45",
+ "res_46",
+ "res_47",
+ "res_48",
+ "res_49",
+ "res_50",
+ "res_51",
+ "res_52",
+ "res_53",
+ "res_54",
+ "res_55",
+ "res_56",
+ "res_57",
+ "res_58",
+ "res_59",
+ "res_60",
+ "res_61",
+ "res_62",
+ "res_63",
+ "res_64",
+ "res_65",
+ "res_66",
+ "res_67",
+ "res_68",
+ "res_69",
+ "res_70",
+ "res_71",
+ "res_72",
+ "res_73",
+ "res_74",
+ "res_75",
+ "res_76",
+ "res_77",
+ "res_78",
+ "res_79",
+ "res_80",
+ "res_81",
+ "res_82",
+ "res_83",
+ "res_84",
+ "res_85",
+ "res_86",
+ "res_87",
+ "res_88",
+ "res_89",
+ "res_90",
+ "res_91",
+ "res_92",
+ "res_93",
+ "res_94",
+ "res_95",
+ "res_96",
+ "res_97",
+ "res_98",
+ "res_99",
+ "res_100",
+ "res_101",
+ "res_102",
+ "res_103",
+ "res_104",
+ "res_105",
+ "res_106",
+ "res_107",
+ "res_108",
+ "res_109",
+ "res_110",
+ "res_111",
+ "res_112",
+ "res_113",
+ "res_114",
+ "res_115",
+ "res_116",
+ "res_117",
+ "res_118",
+ "res_119",
+ "res_120",
+ "res_121",
+ "res_122",
+ "res_123",
+ "res_124",
+ "res_125",
+ "res_126",
+ "res_127",
+ "res_128",
+ "res_129",
+ "res_130",
+ "res_131",
+ "res_132",
+ "res_133",
+ "res_134",
+ "res_135",
+ "res_136",
+ "res_137",
+ "res_138",
+ "res_139",
+ "res_140",
+ "res_141",
+ "res_142",
+ "res_143",
+ "res_144",
+ "res_145",
+ "res_146",
+ "res_147",
+ "res_148",
+ "res_149",
+ "res_150",
+ "res_151",
+ "res_152",
+ "res_153",
+ "res_154",
+ "res_155",
+ "res_156",
+ "res_157",
+ "res_158",
+ "res_159",
+ "res_160",
+ "res_161",
+ "res_162",
+ "res_163",
+ "res_164",
+ "res_165",
+ "res_166",
+ "res_167",
+ "res_168",
+ "res_169",
+ "res_170",
+ "res_171",
+ "res_172",
+ "res_173",
+ "res_174",
+ "res_175",
+ "res_176",
+ "res_177",
+ "res_178",
+ "res_179",
+ "res_180",
+ "res_181",
+ "res_182",
+ "res_183",
+ "res_184",
+ "res_185",
+ "res_186",
+ "res_187",
+ "res_188",
+ "res_189",
+ "res_190",
+ "res_191",
+ "res_192",
+ "res_193",
+ "res_194",
+ "res_195",
+ "res_196",
+ "res_197",
+ "res_198",
+ "res_199",
+ "res_200",
+ "res_201",
+ "res_202",
+ "res_203",
+ "res_204",
+ "res_205",
+ "res_206",
+ "res_207",
+ "res_208",
+ "res_209",
+ "res_210",
+ "res_211",
+ "res_212",
+ "res_213",
+ "res_214",
+ "res_215",
+ "res_216",
+ "res_217",
+ "res_218",
+ "res_219",
+ "res_220",
+ "res_221",
+ "res_222",
+ "res_223",
+ "oem_224",
+ "oem_225",
+ "oem_226",
+ "oem_227",
+ "oem_228",
+ "oem_229",
+ "oem_230",
+ "oem_231",
+ "oem_232",
+ "oem_233",
+ "oem_234",
+ "oem_235",
+ "oem_236",
+ "oem_237",
+ "oem_238",
+ "oem_239",
+ "oem_240",
+ "oem_241",
+ "oem_242",
+ "oem_243",
+ "oem_244",
+ "oem_245",
+ "oem_246",
+ "oem_247",
+ "oem_248",
+ "oem_249",
+ "oem_250",
+ "oem_251",
+ "oem_252",
+ "oem_253",
+ "oem_254",
+ "oem_255",
+};
+
+static const char *unit_types[TYPES_ARRAY_SZ] = {
+ "none",
+ "unspec",
+ "celsius",
+ "fahrenheit",
+ "kelvin",
+ "volts",
+ "amps",
+ "watts",
+ "joules",
+ "coulombs",
+ "va",
+ "nits",
+ "lumens",
+ "lux",
+ "candelas",
+ "kpa",
+ "psi",
+ "newtons",
+ "cfm",
+ "rpm",
+ "hertz",
+ "seconds",
+ "minutes",
+ "hours",
+ "days",
+ "weeks",
+ "mils",
+ "inches",
+ "feet",
+ "cubic_inches",
+ "cubic_feet",
+ "meters",
+ "cubic_centimeters",
+ "cubic_meters",
+ "liters",
+ "fluid_ounces",
+ "radians",
+ "steradians",
+ "revolutions",
+ "cycles",
+ "gravities",
+ "ounces",
+ "pounds",
+ "foot_pounds",
+ "ounce_inches",
+ "gauss",
+ "gilberts",
+ "henries",
+ "farads",
+ "ohms",
+ "siemens",
+ "moles",
+ "becquerels",
+ "ppm",
+ "decibels",
+ "dba",
+ "dbc",
+ "grays",
+ "sieverts",
+ "color_temp_kelvin",
+ "bits",
+ "bytes",
+ "words",
+ "dwords",
+ "qwords",
+ "percentage",
+ "pascals",
+ "counts",
+ "grams",
+ "newton_meters",
+ "hits",
+ "misses",
+ "retries",
+ "overruns",
+ "underruns",
+ "collisions",
+ "packets",
+ "messages",
+ "chars",
+ "errors",
+ "corrected_err",
+ "uncorrectable_err",
+ "square_mils",
+ "square_inches",
+ "square_feet",
+ "square_centimeters",
+ "square_meters",
+ "radians_per_secs",
+ "beats_per_minute",
+ "meters_per_secs_squared",
+ "meters_per_secs",
+ "cubic_meter_per_secs",
+ "millimeters_mercury",
+ "radians_per_secs_squared",
+ "state",
+ "bps",
+ "res_96",
+ "res_97",
+ "res_98",
+ "res_99",
+ "res_100",
+ "res_101",
+ "res_102",
+ "res_103",
+ "res_104",
+ "res_105",
+ "res_106",
+ "res_107",
+ "res_108",
+ "res_109",
+ "res_110",
+ "res_111",
+ "res_112",
+ "res_113",
+ "res_114",
+ "res_115",
+ "res_116",
+ "res_117",
+ "res_118",
+ "res_119",
+ "res_120",
+ "res_121",
+ "res_122",
+ "res_123",
+ "res_124",
+ "res_125",
+ "res_126",
+ "res_127",
+ "res_128",
+ "res_129",
+ "res_130",
+ "res_131",
+ "res_132",
+ "res_133",
+ "res_134",
+ "res_135",
+ "res_136",
+ "res_137",
+ "res_138",
+ "res_139",
+ "res_140",
+ "res_141",
+ "res_142",
+ "res_143",
+ "res_144",
+ "res_145",
+ "res_146",
+ "res_147",
+ "res_148",
+ "res_149",
+ "res_150",
+ "res_151",
+ "res_152",
+ "res_153",
+ "res_154",
+ "res_155",
+ "res_156",
+ "res_157",
+ "res_158",
+ "res_159",
+ "res_160",
+ "res_161",
+ "res_162",
+ "res_163",
+ "res_164",
+ "res_165",
+ "res_166",
+ "res_167",
+ "res_168",
+ "res_169",
+ "res_170",
+ "res_171",
+ "res_172",
+ "res_173",
+ "res_174",
+ "res_175",
+ "res_176",
+ "res_177",
+ "res_178",
+ "res_179",
+ "res_180",
+ "res_181",
+ "res_182",
+ "res_183",
+ "res_184",
+ "res_185",
+ "res_186",
+ "res_187",
+ "res_188",
+ "res_189",
+ "res_190",
+ "res_191",
+ "res_192",
+ "res_193",
+ "res_194",
+ "res_195",
+ "res_196",
+ "res_197",
+ "res_198",
+ "res_199",
+ "res_200",
+ "res_201",
+ "res_202",
+ "res_203",
+ "res_204",
+ "res_205",
+ "res_206",
+ "res_207",
+ "res_208",
+ "res_209",
+ "res_210",
+ "res_211",
+ "res_212",
+ "res_213",
+ "res_214",
+ "res_215",
+ "res_216",
+ "res_217",
+ "res_218",
+ "res_219",
+ "res_220",
+ "res_221",
+ "res_222",
+ "res_223",
+ "res_224",
+ "res_225",
+ "res_226",
+ "res_227",
+ "res_228",
+ "res_229",
+ "res_230",
+ "res_231",
+ "res_232",
+ "res_233",
+ "res_234",
+ "res_235",
+ "res_236",
+ "res_237",
+ "res_238",
+ "res_239",
+ "res_240",
+ "res_241",
+ "res_242",
+ "res_243",
+ "res_244",
+ "res_245",
+ "res_246",
+ "res_247",
+ "res_248",
+ "res_249",
+ "res_250",
+ "res_251",
+ "res_252",
+ "res_253",
+ "res_254",
+ "oem_unit",
+};
+
static inline int
__scmi_tlm_generic_open(struct inode *ino, struct file *filp,
int (*bulk_op)(const struct scmi_tlm_setup *tsp,
@@ -753,6 +1276,10 @@ static int scmi_telemetry_des_initialize(struct device *dev,
struct scmi_tlm_inode *des_top_inode;
des_top_inode = TLM_INODE_SETUP(ti, tsp, &des_dir_cls, NULL, NULL);
+ ti->des_nodes = devm_kcalloc(dev, ti->info->base.num_des,
+ sizeof(*ti->des_nodes), GFP_KERNEL);
+ if (!ti->des_nodes)
+ return -ENOMEM;
for (int i = 0; i < ti->info->base.num_des; i++) {
const struct scmi_telemetry_de *de = ti->info->des[i];
@@ -796,6 +1323,8 @@ static int scmi_telemetry_des_initialize(struct device *dev,
&dei->persistent);
TLM_INODE_SETUP(ti, tsp, &value_tlmo, de_dir_inode, de);
+
+ ti->des_nodes[i] = de_dir_inode;
}
dev_info(dev, "Found %d Telemetry DE resources.\n", ti->info->base.num_des);
@@ -1633,10 +2162,204 @@ scmi_tlm_node_add(struct super_block *sb, struct dentry *parent,
return dentry;
}
+static struct inode *scmi_telemetry_make_dir_inode(struct super_block *sb)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (!inode)
+ return NULL;
+
+ inode->i_ino = get_next_ino();
+ inode_init_owner(&nop_mnt_idmap, inode, NULL, S_IFDIR | 0700);
+ simple_inode_init_ts(inode);
+
+ inode->i_op = &tlm_dir_inode_ops;
+ inode->i_fop = &simple_dir_operations;
+
+ return inode;
+}
+
+static struct dentry *scmi_telemetry_topology_dir_alloc(struct super_block *sb,
+ struct dentry *parent,
+ const char *name)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ inode = scmi_telemetry_make_dir_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ dentry = d_alloc_name(parent, name);
+ if (!dentry)
+ return ERR_PTR(-ENOMEM);
+
+ d_add(dentry, inode);
+
+ dget(dentry);
+
+ return dentry;
+}
+
+static struct scmi_tlm_inode *
+scmi_telemetry_de_subdir_clone(struct super_block *sb,
+ struct scmi_tlm_inode *tlmi,
+ struct dentry *parent)
+{
+ struct device *dev = tlmi->tsp->dev;
+ struct scmi_tlm_inode *dtlmi;
+ struct dentry *twin, *child;
+ struct qstr qstr;
+
+ /*
+ * NOTE THAT I CANNOT HARD-LINK A DIRECTORY ... BREAKS VFS
+ * ...so we just duplicate the tlm_inode here
+ */
+ dtlmi = devm_kmemdup(dev, tlmi, sizeof(*tlmi), GFP_KERNEL);
+ if (!dtlmi)
+ return ERR_PTR(-ENOMEM);
+
+ qstr.name = dtlmi->cls->name;
+ qstr.len = strlen(dtlmi->cls->name);
+ qstr.hash = full_name_hash(parent, qstr.name, qstr.len);
+
+ /* by_compo_type/<COMPO_TYPE_STR>/<N/<DE_UNIT_TYPE_STR>/<M>/<DE_ID> */
+ twin = d_lookup(parent, &qstr);
+ if (twin) {
+ dev_err(dev, "Found duplicated entry '%s' in topology.\n",
+ dtlmi->cls->name);
+ return ERR_PTR(-EBUSY);
+ }
+
+ twin = d_alloc_name(parent, dtlmi->cls->name);
+ if (!twin)
+ return ERR_PTR(-ENOMEM);
+
+ dtlmi->dentry = twin;
+ dtlmi->parent = NULL;
+ if (!scmi_tlm_inode_initialize(sb, S_IFDIR | dtlmi->cls->mode, dtlmi))
+ return ERR_PTR(-ENOMEM);
+
+ /* Add another dentry to the duplicated inode under another parent */
+ d_add(twin, &dtlmi->vfs_inode);
+
+ /* Hard-link all child of tlmi to the duplicate */
+ spin_lock(&tlmi->dentry->d_parent->d_lock);
+ hlist_for_each_entry(child, &tlmi->dentry->d_children, d_sib) {
+ struct dentry *hdl;
+
+ if (!child->d_inode)
+ continue;
+
+ hdl = d_alloc_name(twin, child->d_name.name);
+ if (!hdl) {
+ spin_unlock(&tlmi->dentry->d_parent->d_lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ inode_inc_link_count(child->d_inode);
+ d_add(hdl, child->d_inode);
+ }
+ spin_unlock(&tlmi->dentry->d_parent->d_lock);
+
+ return 0;
+}
+
+static struct dentry *
+scmi_telemetry_topology_path_get(struct super_block *sb, struct dentry *parent,
+ const char *dname)
+{
+ struct dentry *dentry;
+ struct qstr qstr;
+
+ qstr.name = dname;
+ qstr.len = strlen(dname);
+ qstr.hash = full_name_hash(parent, qstr.name, qstr.len);
+
+ dentry = d_lookup(parent, &qstr);
+ if (!dentry) {
+ dentry = scmi_telemetry_topology_dir_alloc(sb, parent, dname);
+ if (!dentry)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return dentry;
+}
+
+static int scmi_telemetry_topology_add_node(struct super_block *sb,
+ struct dentry *top_dentry,
+ struct scmi_tlm_inode *tlmi)
+{
+ struct dentry *ctype, *cinst, *cunit, *dinst;
+ const struct scmi_telemetry_de *de = tlmi->priv;
+ struct scmi_tlm_de_info *dei = de->info;
+ struct scmi_tlm_inode *dtlmi;
+ char inst_str[32];
+
+ /* by_compo_type/<COMPO_TYPE_STR>/ */
+ ctype = scmi_telemetry_topology_path_get(sb, top_dentry,
+ compo_types[dei->compo_type]);
+ if (!ctype)
+ return -ENOMEM;
+
+ /* by_compo_type/<COMPO_TYPE_STR>/<N>/ */
+ snprintf(inst_str, 32, "%u", dei->compo_instance_id);
+ cinst = scmi_telemetry_topology_path_get(sb, ctype, inst_str);
+ dput(ctype);
+ if (!cinst)
+ return -ENOMEM;
+
+ /* by_compo_type/<COMPO_TYPE_STR>/<N>/<DE_UNIT_TYPE_STR>/ */
+ cunit = scmi_telemetry_topology_path_get(sb, cinst,
+ unit_types[dei->unit]);
+ dput(cinst);
+ if (!cunit)
+ return -ENOMEM;
+
+ /* by_compo_type/<COMPO_TYPE_STR>/<N>/<DE_UNIT_TYPE_STR>/<N> */
+ snprintf(inst_str, 32, "%u", dei->instance_id);
+ dinst = scmi_telemetry_topology_path_get(sb, cunit, inst_str);
+ dput(cunit);
+ if (!dinst)
+ return -ENOMEM;
+
+ dtlmi = scmi_telemetry_de_subdir_clone(sb, tlmi, dinst);
+ dput(dinst);
+
+ return PTR_ERR(dtlmi);
+}
+
+static int scmi_telemetry_topology_view_add(struct super_block *sb,
+ struct scmi_tlm_instance *ti)
+{
+ struct device *dev = ti->tsp->dev;
+ struct dentry *topo;
+
+ topo = scmi_telemetry_topology_dir_alloc(sb, ti->top_inode->dentry,
+ "components");
+ if (!topo)
+ return -ENOMEM;
+
+ for (int i = 0; i < ti->info->base.num_des; i++) {
+ int ret;
+
+ ret = scmi_telemetry_topology_add_node(sb, topo,
+ ti->des_nodes[i]);
+ if (ret)
+ dev_err(dev, "Fail to add node %s to topology. Skip.\n",
+ ti->des_nodes[i]->cls->name);
+ }
+
+ dput(topo);
+
+ return 0;
+}
+
static int scmi_telemetry_instance_register(struct super_block *sb,
struct scmi_tlm_instance *ti)
{
struct dentry *top;
+ int ret;
/* AT first create instance top dir ... */
top = scmi_tlm_node_add(sb, sb->s_root, ti->top_cls.name,
@@ -1664,6 +2387,13 @@ static int scmi_telemetry_instance_register(struct super_block *sb,
return PTR_ERR(dentry);
}
+ /* Add an alternative topological view for the DES nodes */
+ ret = scmi_telemetry_topology_view_add(sb, ti);
+ if (ret)
+ dev_warn(ti->tsp->dev,
+ "Failed to create topology view for instance %s.\n",
+ ti->top_cls.name);
+
return 0;
}
--
2.51.0
© 2016 - 2025 Red Hat, Inc.