On Thu, Nov 03, 2016 at 01:42:03AM -0400, Anurup M wrote:
> +     do {
> +             /* Get count from individual L3C banks and sum them up */
> +             for (i = 0; i < num_banks; i++) {
> +                     total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
> +                                                                     idx, i);
> +             }
> +             prev_raw_count = local64_read(&hwc->prev_count);
> +
> +             /*
> +              * As prev_raw_count is updated with average value of
> +              * L3 cache banks, we multiply it by no of banks and
> +              * compute the delta
> +              */
> +             delta = (total_raw_count - (prev_raw_count * num_banks)) &
> +                                                             HISI_MAX_PERIOD;
> +
> +             local64_add(delta, &event->count);
> +
> +             /*
> +              * Divide by num of banks to get average count and
> +              * update prev_count with this value
> +              */
> +             avg_raw_count = total_raw_count / num_banks;
> +     } while (local64_cmpxchg(
> +                      &hwc->prev_count, prev_raw_count, avg_raw_count) !=
> +                                                      prev_raw_count);

Please don't aggregate like this; expose separate PMUs instead.

This is racy, and by averaging and multiplying we're making up and/or
throwing away data.

[...]

> +     event_value = (val -
> +                     HISI_HWEVENT_L3C_READ_ALLOCATE);
> +
> +     /* Select the appropriate Event select register */
> +     if (idx > 3)
> +             reg_offset += 4;
> +
> +     /* Value to write to event type register */
> +     val = event_value << (8 * idx);
> +

Please add helpers for these, and explain *why* the transformations are
necessary.

> +     /* Find the djtag Identifier of the Unit */
> +     client = l3c_hwmod_data->client;
> +
> +     /*
> +      * Set the event in L3C_EVENT_TYPEx Register
> +      * for all L3C banks
> +      */

As above, it seems like you should expose a separate PMU per bank
instead. That applies for all the other instances where you iterate over
banks.

[...]

> +     for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
> +             module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
> +             cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
> +             ret = hisi_djtag_writereg(module_id,
> +                                     cfg_en,
> +                                     reg_offset,
> +                                     value,
> +                                     client);
> +             if (!ret)
> +                     ret = value;
> +     }

This is impossible to read. Please factor this into helpers such that
you don't need this amount of indentation.

Please do similarly elsewhere when you see this indentation pattern.

[...]

> +static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
> +{
> +     struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
> +     int event_idx;
> +
> +     event_idx =
> +             find_first_zero_bit(
> +                     l3c_hwmod_data->hisi_l3c_event_used_mask,
> +                                      pl3c_pmu->num_counters);
> +
> +     if (event_idx == HISI_MAX_CFG_L3C_CNTR)
> +             return -EAGAIN;
> +
> +     __set_bit(event_idx,
> +             l3c_hwmod_data->hisi_l3c_event_used_mask);
> +
> +     return event_idx;
> +}

Please get rid of the weird hungarian notation (i.e. don't use 'p' as a
prefix for pointers), and use temporary variables consistently, e.g.

static int hisi_l3c_get_event_idx(struct hisi_pmu *l3c_pmu)
{
        struct hisi_l3c_data *l3c_hwmod_data = l3c_pmu->hwmod_data;
        unsigned long *used_mask = l3c_hwmod_data->hisi_l3c_event_used_mask;
        int num_counters = pl3c_pmu->num_counters
        int idx;

        idx = find_first_zero_bit(used_mask, num_counters);
        if (idx == num_counters)
                return -EAGAIN;

        set_bit(idx, used_mask);

        return idx;
}

[...]

> +     if (of_property_read_u32(node, "counter-reg",
> +                                  &pl3c_hwcfg->counter_reg0_off)) {
> +             dev_err(dev, "DT:Couldnot read counter-reg!\n");
> +             return -EINVAL;
> +     }

Please use spaces in these messages.

Otherwise, my comments on the binding apply here.

[...]

> +static int init_hisi_l3c_data(struct device *dev,
> +                                     struct hisi_pmu *pl3c_pmu,
> +                                     struct hisi_djtag_client *client)
> +{
> +     struct hisi_l3c_data *l3c_hwmod_data = NULL;
> +     int ret;
> +
> +     l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
> +                                                     GFP_KERNEL);

Use:

        l3c_hwmod_data = kzalloc(sizeof(*l3c_hwmod_data, GFP_KERNEL):

[...]

> +static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
> +{
> +     struct hisi_pmu *pl3c_pmu = NULL;
> +     struct device *dev = &client->dev;
> +     int ret;
> +
> +     pl3c_pmu = hisi_pmu_alloc(dev);
> +     if (IS_ERR(pl3c_pmu))
> +             return PTR_ERR(pl3c_pmu);

Why use error pointers for this?

hisi_pmu_alloc() only ever returns ERR_PTR(-ENOMEM) if it failed to
allocate.

It's far simpler to have it pass on NULL there, and here do:

        pl3c_pmu = hisi_pmu_alloc(dev);
        if (!pl3c_pmu)
                return -ENOMEM;

Please also s/pl3c_pmu/l3c_pmu/ here, and elsewhere throughout the
driver. The 'p' only serves to make this harder to read.

[...]

> +     /* Register with perf PMU */
> +     pl3c_pmu->pmu = (struct pmu) {
> +             .name = pl3c_pmu->name,
> +             .task_ctx_nr = perf_invalid_context,
> +             .event_init = hisi_uncore_pmu_event_init,
> +             .add = hisi_uncore_pmu_add,
> +             .del = hisi_uncore_pmu_del,
> +             .start = hisi_uncore_pmu_start,
> +             .stop = hisi_uncore_pmu_stop,
> +             .read = hisi_uncore_pmu_read,
> +     };

Please remove the comment above this.

[...]

> +int hisi_uncore_pmu_event_init(struct perf_event *event)
> +{
> +     int err;
> +     struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);

This is undefined behaviour. This must be done *after* we check the
event->pmu->type.

> +
> +     if (event->attr.type != event->pmu->type)
> +             return -ENOENT;
> +
> +     /* we do not support sampling as the counters are all
> +      * shared by all CPU cores in a CPU die(SCCL). Also we
> +      * donot support attach to a task(per-process mode)
> +      */
> +     if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
> +             return -EOPNOTSUPP;
> +
> +     /* counters do not have these bits */
> +     if (event->attr.exclude_user    ||
> +         event->attr.exclude_kernel  ||
> +         event->attr.exclude_host    ||
> +         event->attr.exclude_guest   ||
> +         event->attr.exclude_hv      ||
> +         event->attr.exclude_idle)
> +             return -EINVAL;
> +
> +     if (event->cpu < 0)
> +             return -EINVAL;
> +
> +     event->cpu = cpumask_first(&phisi_pmu->cpu);

You should also check the event grouping.

Take a look at what we do in arch/arm/mm/cache-l2x0-pmu.c.

[...]

> +/*
> + * Enable counter and set the counter to count
> + * the event that we're interested in.
> + */
> +void hisi_uncore_pmu_enable_event(struct perf_event *event)
> +{
> +     struct hw_perf_event *hwc = &event->hw;
> +     struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +     /* Disable the hardware event counting */
> +     if (phisi_pmu->ops->disable_counter)
> +             phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));

Why isn't the counter already disabled?

> +     /*
> +      * Set event (if destined for Hisilicon SoC counters).
> +      */
> +     if (phisi_pmu->ops->set_evtype)
> +             phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
> +                                                     hwc->config_base);

Why isn't this done in the pmu::event_add callback?

> +
> +     /* Enable the hardware event counting */
> +     if (phisi_pmu->ops->enable_counter)
> +             phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));

This should be the only necessary part of this function.

> +}
> +
> +void hisi_pmu_set_event_period(struct perf_event *event)
> +{
> +     struct hw_perf_event *hwc = &event->hw;
> +     struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +     /*
> +      * The Hisilicon PMU counters have a period of 2^32. To account for the
> +      * possiblity of extreme interrupt latency we program for a period of
> +      * half that. Hopefully we can handle the interrupt before another 2^31
> +      * events occur and the counter overtakes its previous value.
> +      */
> +     u64 val = 1ULL << 31;
> +
> +     local64_set(&hwc->prev_count, val);
> +
> +     /* Write to the hardware event counter */
> +     phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
> +}
> +
> +void hisi_uncore_pmu_start(struct perf_event *event, int flags)
> +{
> +     struct hw_perf_event *hwc = &event->hw;
> +     struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +     struct hisi_pmu_hw_events *hw_events;
> +
> +     hw_events = &phisi_pmu->hw_events;
> +
> +     if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
> +             return;
> +
> +     WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
> +     hwc->state = 0;
> +
> +     if (phisi_pmu->ops->set_event_period)
> +             phisi_pmu->ops->set_event_period(event);

When will this differ from hisi_pmu_set_event_period() above?

> +     if (flags & PERF_EF_RELOAD) {
> +             u64 prev_raw_count =  local64_read(&hwc->prev_count);
> +
> +             phisi_pmu->ops->write_counter(phisi_pmu, hwc,
> +                                             (u32)prev_raw_count);
> +     }

If we always go through hisi_pmu_set_event_period(), this looks
redundant.

> +
> +     hisi_uncore_pmu_enable_event(event);

There's no matching disable_event() call in this function, so this looks
suspicious.

> +     perf_event_update_userpage(event);
> +}
> +
> +void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
> +{
> +     struct hw_perf_event *hwc = &event->hw;
> +     struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +     if (hwc->state & PERF_HES_UPTODATE)
> +             return;

Why?

[...]

> +int hisi_uncore_common_fwprop_read(struct device *dev,
> +                                     struct hisi_pmu *phisi_pmu)
> +{
> +     if (device_property_read_u32(dev, "num-events",
> +                                     &phisi_pmu->num_events)) {
> +             dev_err(dev, "Cant read num-events from DT!\n");
> +             return -EINVAL;
> +     }

For consistency with the rest of the driver, and given there is no ACPI
support, please use the of_property_* API here.

Thanks,
Mark.
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to