Re: [PATCH v3 5/6] platform/x86:intel/pmc: Show device and function number
From: Xi Pardee
Date: Thu Sep 11 2025 - 14:33:06 EST
Hi Ilpo,
Thanks for the review. I will fix the patch based on your comments and send another version separately.
On 9/8/2025 4:03 AM, Ilpo Järvinen wrote:
On Sun, 7 Sep 2025, Xi Pardee wrote:Yes, I will do that in next version.
Hi Ilpo,
Thanks for the review.
On 8/28/2025 6:56 AM, Ilpo Järvinen wrote:
On Fri, 15 Aug 2025, Xi Pardee wrote:
Add support to show device and function number for S0ix blockers. This
feature depends on S0ix blocker substate requirement table and BDF
association table. This feature is available for platforms starting from
Pather Lake.
Only a subset of S0ix blockers has device and function number associated
to it. Get the availability information from the substate requirement
table. Get the device and function number mapping information for each
S0ix blocker from the BDF association table.
Signed-off-by: Xi Pardee <xi.pardee@xxxxxxxxxxxxxxx>
---
drivers/platform/x86/intel/pmc/core.c | 182 +++++++++++++++++++++++++-
drivers/platform/x86/intel/pmc/core.h | 23 +++-
2 files changed, 203 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/intel/pmc/core.c
b/drivers/platform/x86/intel/pmc/core.c
index a0b948a875a5a..69ee40cbb8b8a 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
I don't remember if I mentioned it earlier but if you're going to addressWill change it in next version.+ const char *name = NULL;%u
+ struct list_head *cur;
+ struct bdf_entry *bdf;
+ struct pmc *pmc;
+
+ pmc = pmcdev->pmcs[pmcidx];
+ if (!pmc)
+ continue;
+
+ list_for_each(cur, pmc->bdf_list) {
+ bdf = list_entry(cur, struct bdf_entry, node);
+ if (bdf->name != name) {
+ seq_printf(s, "pmc%d: %30s | %15x | %15x |\n",
pmcidx,
the review comment fully. There's no need to "ack" them like this. I
trust you make the changes you don't contest.
By doing so, we can both save time by only focusing on the points which
are contested or need further discussion. :-)
But why you cannot reset i_idx after the inner for () loop and dropWill move it in next version.+ if (!maps[*r_idx][*i_idx].name)Can't you put this to the innermost block?
+ (*r_idx)++;
+
+ // Iteration reaches the end of the maps
+ if (*r_idx >= arr_size)
+ return NULL;
+
+ for (; *r_idx < arr_size; (*r_idx)++) {
+ const char *ip_name;
The purpose of this function is to return the name of the NEXT s0ix blocker+ if (reset)Why you need this?
with BDF information.
r_idx and i_idx are used to keep track of the current position of the
iteration, therefore i_idx could not be reset to 0 at the first run of the
inner for loop. After the first run of inner for loop reset should be set to
true so in next run of the outer for loop i_idx could be reset to 0 (which
mean the iteration reaches the next s0ix blocker map).
this reset variable entirely?
I can use the empty list check in pmc_core_dbgfs_register() instead.
My out-of-band suggestion was to convert i_idx into a correctly typedWill change to while loop in next version.+ *i_idx = 0;true
+
+ for (; maps[*r_idx][*i_idx].name; reset = TRUE, (*i_idx)++) {
This is hard enough to understand even without that "for (;". Would
probably be better to use while () instead.
To achieve the NEXT name of the s0ix blocker with BDF information we need to+ if (!maps[*r_idx][*i_idx].blk)TBH, this entire function is horrible mess, two nested iterators as
+ continue;
+
+ bool exist = **lpm_req_regs & BIT(BDF_EXIST_BIT);
+ (*lpm_req_regs)++;
+ if (exist) {
+ ip_name = maps[*r_idx][*i_idx].name;
+ (*i_idx)++;
+ return ip_name;
+ }
+ }
+ }
+ return NULL;
+}
pointers, etc.
I'm very far from following all what going on here.
I suppose I've not seen this patch previously?
iterate through two (set of) maps in parallel. The s0ix_blocker_maps provide
names of all s0ix blockers and the lpm_req_regs map shows that which s0ix
blocker has associated BDF information.
if maps[*r_idx][*i_idx].blk is set, that means it is a s0ix blocker. For each
s0ix blocker, if **lpm_req_regs & BIT(BDF_EXIST_BIT) is set, that means this
blocker has associated BDF information. Pointers are used to keep track of the
current position of the two (set of) maps so the function provides the NEXT
name of the s0ix blocker with associated BDF info.
I will probably switch to use a temporary data structure, such as list, to
store all s0ix blockers with BDF info and then iterate through this list in
pmc_core_process_bdf() call instead. That will make the logic easier to follow
and maintain. I will also add more comments to next version of this patch.
pointer as it's the last-level array, you only need to do two things for
the pointer:
- set it to start of the next array when r_idx increases.
- increment the pointer with ++.
Yes, these are fields which are to be defined with GENMASK()/BIT(). ThenEach 16 bit represents one device and the associated function numbers for one+static int pmc_core_process_bdf(struct pmc_dev *pmcdev, struct pmc *pmc,I think you can iterate 0 ... __fls(FIELD_MAX()).
u32 data,
+ unsigned int *r_idx, unsigned int *i_idx, u32
**lpm_req_regs,
+ const char **name)
+{
+ unsigned int i;
+
+ if (!data)
+ return 0;
+
+ if (!*name)
+ return -EINVAL;
+
+ for (i = BDF_FUN_LOW_BIT; i <= BDF_FUN_HIGH_BIT; i++) {
s0ix blocker.
Bit 4-0 indicates the device number.
Bit 12-5 indicates function numbers.
Bit 15 indicates if the next 16 bit is associated to the same s0ix blocker as
the current word.
Between bit 12 and bit 5, each bit position represents one function number.
Bit 5 represents function 0 and bit 12 represents function 7. I will add more
comments in the next version.
Will change to use __fls(FIELD_MAX()) in next version.
this code just has to figure out how to deal with that change and my
suggestion was to use fls construct. If you find better approach, those
can be used as well but my point is that this iteration should be sourced
from the GENMASK_U16(12, 5).
But can't you use empty list check for that?The memory for bdf_list is only allocated when the bdf information is+ name = pmc_core_get_next_bdf_ip_name(pmc, &r_idx, &i_idx,Should use sizeof(*xx).
&lpm_reg_regs);
+ if (!name)
+ return -EINVAL;
+
+ pmc->bdf_list = devm_kzalloc(&pmcdev->pdev->dev, sizeof(struct
list_head), GFP_KERNEL);
But why you need to allocate the list head and not have it in place
within the pmc's struct?
available to achieve.
intel_pmc_core driver can check if the memory is allocated to decide whether
to create a file in debugfs for bdf in pmc_core_dbgfs_register().
I suspect naming the fields with defines and using FIELD_GET() willWill change it in next version.+ if (!pmc->bdf_list)One line.
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(pmc->bdf_list);
+
+ for (; sample_id < max_sample_id; sample_id++) {
+ u32 data;
+
+ ret = pmt_telem_read32(ep, sample_id, &data, 1);
+ if (ret) {
+ dev_err(&pmcdev->pdev->dev,
+ "couldn't read bdf: %d\n", ret);
Will change it in next version.+ return ret;Define the field and use FIELD_GET().
+ }
+
+ if (sample_id == header_id) {
+ size = (data & GENMASK(BDF_SIZE_HIGH_BIT,
BDF_SIZE_LOW_BIT))
+ >> BDF_SIZE_LOW_BIT;
There is one header line (32 bit) between each type of s0ix blocker in the bdf+ header_id += size + 1;No, I just cannot understand what's going on here, it's hopeless. Always
when I think I've finally understood what its all about you throw a curve
ball like this.
association table. The bit 23-26 in header line indicates the size of each
section of one specific type of s0ix blocker in this table.
header_id is used to keep track of the id of each header so we will process
the header line differently from the other lines.
I will add more detailed comment in next version.
already get you far.
Perhaps BDF_SIZE (=what remains when you take those custom coded bit field
postfix out of the current naming) should be renamed into something like
BDF_SECTION_SIZE for better clarity.
Will use FIELD_GET() to get the information in next version.
Thanks for reviewing the patch.
Xi