[PATCH 01/11] RDMA/core: read selected counters with timestamps

From: Doug Ledford

Date: Sat Oct 31 2026 - 00:36:20 EST


The COUNTERS object reads every counter attached to it into one
array, which suits a fixed set but not a device with thousands of
status values of which a monitor wants a few. Add two optional
attributes to the read method. ITEMS is an array of driver defined
item numbers, and the values come back in the same order. TIMESTAMPS
returns, per item, when the driver sampled it, in nanoseconds since the
epoch. Either may be used without the other.

A driver advertises the extension by providing read_counters_ex(). A
counters object created for such a driver need not have any counters
attached, because the items are chosen at read time; the check for an
attached counter is skipped for those drivers. Without the callback
the new attributes are rejected with EOPNOTSUPP, and the original
behaviour is unchanged.

Signed-off-by: Doug Ledford <doug.ledford@xxxxxxx>
---
drivers/infiniband/core/device.c | 1 +
.../infiniband/core/uverbs_std_types_counters.c | 68 ++++++++++++++++++++--
include/rdma/ib_verbs.h | 11 ++++
include/uapi/rdma/ib_user_ioctl_cmds.h | 2 +
4 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 1b838bf3600d..18c0d9243909 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -3026,6 +3026,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, query_ucontext);
SET_DEVICE_OP(dev_ops, rdma_netdev_get_params);
SET_DEVICE_OP(dev_ops, read_counters);
+ SET_DEVICE_OP(dev_ops, read_counters_ex);
SET_DEVICE_OP(dev_ops, read_comp_cntr);
SET_DEVICE_OP(dev_ops, reg_dm_mr);
SET_DEVICE_OP(dev_ops, reg_user_mr);
diff --git a/drivers/infiniband/core/uverbs_std_types_counters.c b/drivers/infiniband/core/uverbs_std_types_counters.c
index 381aa5797641..e452854ddaa9 100644
--- a/drivers/infiniband/core/uverbs_std_types_counters.c
+++ b/drivers/infiniband/core/uverbs_std_types_counters.c
@@ -92,12 +92,20 @@ static int UVERBS_HANDLER(UVERBS_METHOD_COUNTERS_READ)(
const struct uverbs_attr *uattr;
struct ib_counters *counters =
uverbs_attr_get_obj(attrs, UVERBS_ATTR_READ_COUNTERS_HANDLE);
+ bool has_items, has_timestamps;
+ size_t len;
int ret;

- if (!counters->device->ops.read_counters)
+ /*
+ * A driver with read_counters_ex defines what its counters are, so
+ * they need not be attached to anything before they are read.
+ */
+ if (!counters->device->ops.read_counters &&
+ !counters->device->ops.read_counters_ex)
return -EOPNOTSUPP;

- if (!atomic_read(&counters->usecnt))
+ if (!counters->device->ops.read_counters_ex &&
+ !atomic_read(&counters->usecnt))
return -EINVAL;

ret = uverbs_get_flags32(&read_attr.flags, attrs,
@@ -115,10 +123,56 @@ static int UVERBS_HANDLER(UVERBS_METHOD_COUNTERS_READ)(
if (IS_ERR(read_attr.counters_buff))
return PTR_ERR(read_attr.counters_buff);

- ret = counters->device->ops.read_counters(counters, &read_attr, attrs);
+ has_items = uverbs_attr_is_valid(attrs, UVERBS_ATTR_READ_COUNTERS_ITEMS);
+ has_timestamps = uverbs_attr_is_valid(attrs,
+ UVERBS_ATTR_READ_COUNTERS_TIMESTAMPS);
+ if ((has_items || has_timestamps) &&
+ !counters->device->ops.read_counters_ex)
+ return -EOPNOTSUPP;
+
+ if (has_items) {
+ u64 *items;
+
+ len = uverbs_attr_get_len(attrs, UVERBS_ATTR_READ_COUNTERS_ITEMS);
+ if (len != array_size(read_attr.ncounters, sizeof(u64)))
+ return -EINVAL;
+ items = uverbs_zalloc(attrs, len);
+ if (IS_ERR(items))
+ return PTR_ERR(items);
+ ret = _uverbs_copy_from(items, attrs,
+ UVERBS_ATTR_READ_COUNTERS_ITEMS, len);
+ if (ret)
+ return ret;
+ read_attr.items = items;
+ }
+
+ if (has_timestamps) {
+ len = uverbs_attr_get_len(attrs,
+ UVERBS_ATTR_READ_COUNTERS_TIMESTAMPS);
+ if (len != array_size(read_attr.ncounters, sizeof(u64)))
+ return -EINVAL;
+ read_attr.timestamps = uverbs_zalloc(attrs, len);
+ if (IS_ERR(read_attr.timestamps))
+ return PTR_ERR(read_attr.timestamps);
+ }
+
+ if (counters->device->ops.read_counters_ex)
+ ret = counters->device->ops.read_counters_ex(counters,
+ &read_attr, attrs);
+ else
+ ret = counters->device->ops.read_counters(counters, &read_attr,
+ attrs);
if (ret)
return ret;

+ if (has_timestamps) {
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_READ_COUNTERS_TIMESTAMPS,
+ read_attr.timestamps,
+ read_attr.ncounters * sizeof(u64));
+ if (ret)
+ return ret;
+ }
+
return uverbs_copy_to(attrs, UVERBS_ATTR_READ_COUNTERS_BUFF,
read_attr.counters_buff,
read_attr.ncounters * sizeof(u64));
@@ -148,7 +202,13 @@ DECLARE_UVERBS_NAMED_METHOD(
UVERBS_ATTR_MIN_SIZE(0),
UA_MANDATORY),
UVERBS_ATTR_FLAGS_IN(UVERBS_ATTR_READ_COUNTERS_FLAGS,
- enum ib_uverbs_read_counters_flags));
+ enum ib_uverbs_read_counters_flags),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_READ_COUNTERS_ITEMS,
+ UVERBS_ATTR_MIN_SIZE(sizeof(u64)),
+ UA_OPTIONAL),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_READ_COUNTERS_TIMESTAMPS,
+ UVERBS_ATTR_MIN_SIZE(sizeof(u64)),
+ UA_OPTIONAL));

DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_COUNTERS,
UVERBS_TYPE_ALLOC_IDR(uverbs_free_counters),
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index a93715323ad5..5aa1fa576a44 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -2595,6 +2595,10 @@ struct ib_counters_read_attr {
u64 *counters_buff;
u32 ncounters;
u32 flags; /* use enum ib_read_counters_flags */
+ /* Optional: counter numbers to read, ncounters entries */
+ const u64 *items;
+ /* Optional: filled with a timestamp for each counter read */
+ u64 *timestamps;
};

struct uverbs_attr_bundle;
@@ -2936,6 +2940,13 @@ struct ib_device_ops {
int (*create_counters)(struct ib_counters *counters,
struct uverbs_attr_bundle *attrs);
int (*destroy_counters)(struct ib_counters *counters);
+ /*
+ * Used instead of read_counters when the user selects counters with
+ * items or asks for timestamps.
+ */
+ int (*read_counters_ex)(struct ib_counters *counters,
+ struct ib_counters_read_attr *counters_read_attr,
+ struct uverbs_attr_bundle *attrs);
int (*read_counters)(struct ib_counters *counters,
struct ib_counters_read_attr *counters_read_attr,
struct uverbs_attr_bundle *attrs);
diff --git a/include/uapi/rdma/ib_user_ioctl_cmds.h b/include/uapi/rdma/ib_user_ioctl_cmds.h
index 8de110b8db34..9ee083eed044 100644
--- a/include/uapi/rdma/ib_user_ioctl_cmds.h
+++ b/include/uapi/rdma/ib_user_ioctl_cmds.h
@@ -404,6 +404,8 @@ enum uverbs_attrs_read_counters_cmd_attr_ids {
UVERBS_ATTR_READ_COUNTERS_HANDLE,
UVERBS_ATTR_READ_COUNTERS_BUFF,
UVERBS_ATTR_READ_COUNTERS_FLAGS,
+ UVERBS_ATTR_READ_COUNTERS_ITEMS,
+ UVERBS_ATTR_READ_COUNTERS_TIMESTAMPS,
};

enum uverbs_methods_actions_counters_ops {

--
2.56.0