> -----Original Message----- > From: Xu Yang <xu.yang_2@xxxxxxx> > Sent: Monday, January 29, 2024 4:15 AM > To: Frank Li <frank.li@xxxxxxx>; will@xxxxxxxxxx; mark.rutland@xxxxxxx; > robh+dt@xxxxxxxxxx; krzysztof.kozlowski+dt@xxxxxxxxxx; > conor+dt@xxxxxxxxxx; shawnguo@xxxxxxxxxx; s.hauer@xxxxxxxxxxxxxx; > kernel@xxxxxxxxxxxxxx; festevam@xxxxxxxxx; john.g.garry@xxxxxxxxxx; > jolsa@xxxxxxxxxx; namhyung@xxxxxxxxxx; irogers@xxxxxxxxxx > Cc: dl-linux-imx <linux-imx@xxxxxxx>; mike.leach@xxxxxxxxxx; > leo.yan@xxxxxxxxxx; peterz@xxxxxxxxxxxxx; mingo@xxxxxxxxxx; > acme@xxxxxxxxxx; alexander.shishkin@xxxxxxxxxxxxxxx; > adrian.hunter@xxxxxxxxx; Xu Yang <xu.yang_2@xxxxxxx>; linux-arm- > kernel@xxxxxxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; linux- > kernel@xxxxxxxxxxxxxxx; linux-perf-users@xxxxxxxxxxxxxxx > Subject: [PATCH v3 3/4] perf: imx_perf: limit counter ID from user space and > optimize counter usage > > The user can pass any counter ID to perf app. However, current pmu driver > doesn't judge the validity of the counter ID. This will add necessary > check for counter ID from user space. Besides, this pmu has 10 counters > except cycle counter which can be used to count reference events and > counter specific evnets. This will also add supports to auto allocate > counter if the user doesn't pass it the perf. Then, the usage of counter > will be optimized. > > Signed-off-by: Xu Yang <xu.yang_2@xxxxxxx> > > --- > Changes in v2: > - limit counter ID from user to 0-10 > - combine dynamic and static allocation of counter > Changes in v3: > - no changes > --- > drivers/perf/fsl_imx9_ddr_perf.c | 69 > +++++++++++++++++++++++++++++++- > 1 file changed, 67 insertions(+), 2 deletions(-) > > diff --git a/drivers/perf/fsl_imx9_ddr_perf.c > b/drivers/perf/fsl_imx9_ddr_perf.c > index fd118773508d..4bb80050920c 100644 > --- a/drivers/perf/fsl_imx9_ddr_perf.c > +++ b/drivers/perf/fsl_imx9_ddr_perf.c > @@ -51,6 +51,7 @@ > > #define NUM_COUNTERS 11 > #define CYCLES_COUNTER 0 > +#define CYCLES_EVENT_ID 0 > > #define to_ddr_pmu(p) container_of(p, struct ddr_pmu, pmu) > > @@ -235,6 +236,14 @@ static struct attribute *ddr_perf_events_attrs[] = { > NULL, > }; > > +static bool ddr_perf_is_specific_event(int event) Why call specific? Name is too general. Such as is_fixed? Or Is_with_filter? Need know what specific? > +{ > + if (event >= 64 && event <= 73) > + return true; > + else > + return false; > +} > + > static const struct attribute_group ddr_perf_events_attr_group = { > .name = "events", > .attrs = ddr_perf_events_attrs, > @@ -507,6 +516,7 @@ static int ddr_perf_event_init(struct perf_event > *event) > struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); > struct hw_perf_event *hwc = &event->hw; > struct perf_event *sibling; > + int event_id, counter; > > if (event->attr.type != event->pmu->type) > return -ENOENT; > @@ -519,6 +529,18 @@ static int ddr_perf_event_init(struct perf_event > *event) > return -EOPNOTSUPP; > } > > + counter = (event->attr.config & 0xFF00) >> 8; Define 0xFF00? > + if (counter > NUM_COUNTERS) { > + dev_warn(pmu->dev, "Only counter 0-10 is supported!\n"); > + return -EINVAL; > + } > + > + event_id = event->attr.config & 0x00FF; same for hardcode 0x00FF > + if (ddr_perf_is_specific_event(event_id) && counter == 0) { > + dev_err(pmu->dev, "Need specify counter for counter > specific events!\n"); > + return -EINVAL; > + } > + > /* > * We must NOT create groups containing mixed PMUs, although > software > * events are acceptable (for example to create a CCN group > @@ -552,6 +574,39 @@ static void ddr_perf_event_start(struct perf_event > *event, int flags) > hwc->state = 0; > } > > +static int ddr_perf_alloc_counter(struct ddr_pmu *pmu, int event, int > counter) > +{ > + int i; > + > + if (event == CYCLES_EVENT_ID) { > + /* > + * Always map cycle event to counter 0. > + * Cycles counter is dedicated for cycle event > + * can't used for the other events. > + */ > + if (pmu->events[CYCLES_COUNTER] == NULL) > + return CYCLES_COUNTER; > + } else if (counter != 0) { > + /* > + * 1. ddr_perf_event_init() will make sure counter > + * is not 0 for counter specific events. > + * 2. Allow specify counter for referene event too. > + */ > + if (pmu->events[counter] == NULL) > + return counter; > + } else { > + /* > + * Counter may be 0 if user doesn't specify it. > + * Auto allocate counter for referene event. > + */ > + for (i = 1; i < NUM_COUNTERS; i++) > + if (pmu->events[i] == NULL) > + return i; > + } > + > + return -ENOENT; > +} > + > static int ddr_perf_event_add(struct perf_event *event, int flags) > { > struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); > @@ -559,9 +614,17 @@ static int ddr_perf_event_add(struct perf_event > *event, int flags) > int cfg = event->attr.config; > int cfg1 = event->attr.config1; > int cfg2 = event->attr.config2; > - int counter; > + int event_id, counter; > > - counter = (cfg & 0x0000FF00) >> 8; > + event_id = cfg & 0x00FF; > + counter = (cfg & 0xFF00) >> 8; > + > + /* check if counter is available */ > + counter = ddr_perf_alloc_counter(pmu, event_id, counter); > + if (counter < 0) { > + dev_dbg(pmu->dev, "There are not enough counters\n"); > + return -EOPNOTSUPP; > + } > > pmu->events[counter] = event; > pmu->active_events++; > @@ -597,9 +660,11 @@ static void ddr_perf_event_del(struct perf_event > *event, int flags) > { > struct ddr_pmu *pmu = to_ddr_pmu(event->pmu); > struct hw_perf_event *hwc = &event->hw; > + int counter = hwc->idx; > > ddr_perf_event_stop(event, PERF_EF_UPDATE); > > + pmu->events[counter] = NULL; > pmu->active_events--; > hwc->idx = -1; > } > -- > 2.34.1