* [PATCH v2 4/6] io_uring/zcrx: add shared-memory notification statistics
From: Clément Léger @ 2026-05-18 15:35 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
In-Reply-To: <20260518153532.2835502-1-cleger@meta.com>
Add support for an optional stats struct embedded in the refill queue
region, allowing userspace to monitor copy-fallback in real-time.
Userspace queries the stats struct size and alignment via
IO_URING_QUERY_ZCRX_NOTIF (notif_stats_size / notif_stats_alignment),
then provides a stats_offset in zcrx_notification_desc pointing to a
location within the refill queue region.
The kernel updates the stats counters in-place on every copy-fallback
event.
Signed-off-by: Clément Léger <cleger@meta.com>
---
include/uapi/linux/io_uring/query.h | 12 +++++++
include/uapi/linux/io_uring/zcrx.h | 15 ++++++--
io_uring/query.c | 16 +++++++++
io_uring/zcrx.c | 54 +++++++++++++++++++++++++++--
io_uring/zcrx.h | 1 +
5 files changed, 94 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/io_uring/query.h b/include/uapi/linux/io_uring/query.h
index 95500759cc13..1a68eca7c6b4 100644
--- a/include/uapi/linux/io_uring/query.h
+++ b/include/uapi/linux/io_uring/query.h
@@ -23,6 +23,7 @@ enum {
IO_URING_QUERY_OPCODES = 0,
IO_URING_QUERY_ZCRX = 1,
IO_URING_QUERY_SCQ = 2,
+ IO_URING_QUERY_ZCRX_NOTIF = 3,
__IO_URING_QUERY_MAX,
};
@@ -62,6 +63,17 @@ struct io_uring_query_zcrx {
__u64 __resv2;
};
+struct io_uring_query_zcrx_notif {
+ /* Bitmask of supported ZCRX_NOTIF_* flags */
+ __u32 notif_flags;
+ /* Size of io_uring_zcrx_notif_stats */
+ __u32 notif_stats_size;
+ /* Required alignment for the stats struct within the region (ie stats_offset) */
+ __u32 notif_stats_off_alignment;
+ __u32 __resv1;
+ __u64 __resv2[4];
+};
+
struct io_uring_query_scq {
/* The SQ/CQ rings header size */
__u64 hdr_size;
diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h
index 3f7b72b09878..384e185a180c 100644
--- a/include/uapi/linux/io_uring/zcrx.h
+++ b/include/uapi/linux/io_uring/zcrx.h
@@ -75,11 +75,22 @@ enum zcrx_notification_type {
__ZCRX_NOTIF_TYPE_LAST,
};
+enum zcrx_notification_desc_flags {
+ /* If set, stats_offset holds a valid offset to a notif_stats struct */
+ ZCRX_NOTIF_DESC_FLAG_STATS = 1 << 0,
+};
+
+struct io_uring_zcrx_notif_stats {
+ __u64 copy_count; /* cumulative copy-fallback CQEs */
+ __u64 copy_bytes; /* cumulative bytes copied */
+};
+
struct zcrx_notification_desc {
__u64 user_data;
__u32 type_mask;
- __u32 __resv1;
- __u64 __resv2[10];
+ __u32 flags; /* see enum zcrx_notification_desc_flags */
+ __u64 stats_offset; /* offset from the beginning of refill ring region for stats */
+ __u64 __resv2[9];
};
/*
diff --git a/io_uring/query.c b/io_uring/query.c
index c1704d088374..d17a83645bcd 100644
--- a/io_uring/query.c
+++ b/io_uring/query.c
@@ -9,6 +9,7 @@
union io_query_data {
struct io_uring_query_opcode opcodes;
struct io_uring_query_zcrx zcrx;
+ struct io_uring_query_zcrx_notif zcrx_notif;
struct io_uring_query_scq scq;
};
@@ -44,6 +45,18 @@ static ssize_t io_query_zcrx(union io_query_data *data)
return sizeof(*e);
}
+static ssize_t io_query_zcrx_notif(union io_query_data *data)
+{
+ struct io_uring_query_zcrx_notif *e = &data->zcrx_notif;
+
+ e->notif_flags = ZCRX_NOTIF_TYPE_MASK;
+ e->notif_stats_size = sizeof(struct io_uring_zcrx_notif_stats);
+ e->notif_stats_off_alignment = __alignof__(struct io_uring_zcrx_notif_stats);
+ e->__resv1 = 0;
+ memset(&e->__resv2, 0, sizeof(e->__resv2));
+ return sizeof(*e);
+}
+
static ssize_t io_query_scq(union io_query_data *data)
{
struct io_uring_query_scq *e = &data->scq;
@@ -83,6 +96,9 @@ static int io_handle_query_entry(union io_query_data *data, void __user *uhdr,
case IO_URING_QUERY_ZCRX:
ret = io_query_zcrx(data);
break;
+ case IO_URING_QUERY_ZCRX_NOTIF:
+ ret = io_query_zcrx_notif(data);
+ break;
case IO_URING_QUERY_SCQ:
ret = io_query_scq(data);
break;
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index f31f2ca0f7ec..2881ad76bacc 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -415,6 +415,7 @@ static void io_free_rbuf_ring(struct io_zcrx_ifq *ifq)
io_free_region(ifq->user, &ifq->rq_region);
ifq->rq.ring = IO_URING_PTR_POISON;
ifq->rq.rqes = IO_URING_PTR_POISON;
+ ifq->notif_stats = IO_URING_PTR_POISON;
}
static void io_zcrx_free_area(struct io_zcrx_ifq *ifq,
@@ -855,6 +856,33 @@ static int zcrx_register_netdev(struct io_zcrx_ifq *ifq,
return ret;
}
+static int zcrx_validate_notif_stats(struct io_zcrx_ifq *ifq,
+ const struct io_uring_zcrx_ifq_reg *reg,
+ const struct zcrx_notification_desc *notif)
+{
+ size_t stats_off = notif->stats_offset;
+ size_t used, end;
+
+ used = reg->offsets.rqes +
+ sizeof(struct io_uring_zcrx_rqe) * reg->rq_entries;
+
+ if (!IS_ALIGNED(stats_off, __alignof__(struct io_uring_zcrx_notif_stats)))
+ return -EINVAL;
+ if (stats_off < used)
+ return -ERANGE;
+ if (check_add_overflow(stats_off,
+ sizeof(struct io_uring_zcrx_notif_stats),
+ &end))
+ return -ERANGE;
+ if (end > io_region_size(&ifq->rq_region))
+ return -ERANGE;
+
+ ifq->notif_stats = io_region_get_ptr(&ifq->rq_region) + stats_off;
+ memset(ifq->notif_stats, 0, sizeof(*ifq->notif_stats));
+
+ return 0;
+}
+
int io_register_zcrx(struct io_ring_ctx *ctx,
struct io_uring_zcrx_ifq_reg __user *arg)
{
@@ -908,7 +936,13 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
return -EFAULT;
if (notif.type_mask & ~ZCRX_NOTIF_TYPE_MASK)
return -EINVAL;
- if (notif.__resv1 || !mem_is_zero(¬if.__resv2, sizeof(notif.__resv2)))
+ if (notif.flags & ~ZCRX_NOTIF_DESC_FLAG_STATS)
+ return -EINVAL;
+ if (!(notif.flags & ZCRX_NOTIF_DESC_FLAG_STATS)) {
+ if (notif.stats_offset)
+ return -EINVAL;
+ }
+ if (!mem_is_zero(¬if.__resv2, sizeof(notif.__resv2)))
return -EINVAL;
ifq = io_zcrx_ifq_alloc(ctx);
@@ -939,6 +973,12 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
if (ret)
goto err;
+ if (notif.flags & ZCRX_NOTIF_DESC_FLAG_STATS) {
+ ret = zcrx_validate_notif_stats(ifq, ®, ¬if);
+ if (ret)
+ goto err;
+ }
+
ifq->kern_readable = !(area.flags & IORING_ZCRX_AREA_DMABUF);
if (!(reg.flags & ZCRX_REG_NODEV)) {
@@ -1154,6 +1194,11 @@ static void zcrx_notif_tw(struct io_tw_req tw_req, io_tw_token_t tw)
kmem_cache_free(req_cachep, req);
}
+static void zcrx_stat_add(__u64 *p, s64 v)
+{
+ WRITE_ONCE(*p, READ_ONCE(*p) + v);
+}
+
static void zcrx_send_notif(struct io_zcrx_ifq *ifq, unsigned type)
{
gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN | __GFP_ZERO;
@@ -1537,8 +1582,13 @@ static int io_zcrx_copy_frag(struct io_kiocb *req, struct io_zcrx_ifq *ifq,
int ret;
ret = io_zcrx_copy_chunk(req, ifq, page, off + skb_frag_off(frag), len);
- if (ret > 0)
+ if (ret > 0) {
+ if (ifq->notif_stats) {
+ zcrx_stat_add(&ifq->notif_stats->copy_count, 1);
+ zcrx_stat_add(&ifq->notif_stats->copy_bytes, ret);
+ }
zcrx_send_notif(ifq, ZCRX_NOTIF_COPY);
+ }
return ret;
}
diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h
index 203b3049e14b..e1aab76c310d 100644
--- a/io_uring/zcrx.h
+++ b/io_uring/zcrx.h
@@ -81,6 +81,7 @@ struct io_zcrx_ifq {
u32 allowed_notif_mask;
u32 fired_notifs;
u64 notif_data;
+ struct io_uring_zcrx_notif_stats *notif_stats;
};
#if defined(CONFIG_IO_URING_ZCRX)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 2/6] io_uring/zcrx: notify user when out of buffers
From: Clément Léger @ 2026-05-18 15:35 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: linux-doc, linux-kernel, linux-kselftest, netdev, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Jonathan Corbet, Shuah Khan, Vishwanath Seshagiri,
Vishwanath Seshagiri
In-Reply-To: <20260518153532.2835502-1-cleger@meta.com>
From: Pavel Begunkov <asml.silence@gmail.com>
There are currently no easy ways for the user to know if zcrx is out of
buffers and page pool fails to allocate. Add uapi for zcrx to communicate
it back.
It's implemented as a separate CQE, which for now is posted to the creator
ctx. To use it, on registration the user space needs to pass an instance
of struct zcrx_notification_desc, which tells the kernel the user_data
for resulting CQEs and which event types are expected / allowed.
When an allowed event happens, zcrx will post a CQE containing the
specified user_data, and lower bits of cqe->res will be set to the event
mask. Before the kernel could post another notification of the given
type, the user needs to acknowledge that it processed the previous one
by issuing IORING_REGISTER_ZCRX_CTRL with ZCRX_CTRL_ARM_NOTIFICATION.
The only notification type the patch implements is
ZCRX_NOTIF_NO_BUFFERS, but we'll need more of them in the future.
Co-developed-by: Vishwanath Seshagiri <vishs@meta.com>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
include/uapi/linux/io_uring/zcrx.h | 24 ++++++++-
io_uring/io_uring.c | 2 +-
io_uring/io_uring.h | 1 +
io_uring/zcrx.c | 86 +++++++++++++++++++++++++++++-
io_uring/zcrx.h | 7 ++-
5 files changed, 115 insertions(+), 5 deletions(-)
diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h
index 5ce02c7a6096..67185566ad3c 100644
--- a/include/uapi/linux/io_uring/zcrx.h
+++ b/include/uapi/linux/io_uring/zcrx.h
@@ -65,6 +65,20 @@ enum zcrx_features {
* value in struct io_uring_zcrx_ifq_reg::rx_buf_len.
*/
ZCRX_FEATURE_RX_PAGE_SIZE = 1 << 0,
+ ZCRX_FEATURE_NOTIFICATION = 1 << 1,
+};
+
+enum zcrx_notification_type {
+ ZCRX_NOTIF_NO_BUFFERS,
+
+ __ZCRX_NOTIF_TYPE_LAST,
+};
+
+struct zcrx_notification_desc {
+ __u64 user_data;
+ __u32 type_mask;
+ __u32 __resv1;
+ __u64 __resv2[10];
};
/*
@@ -82,12 +96,14 @@ struct io_uring_zcrx_ifq_reg {
struct io_uring_zcrx_offsets offsets;
__u32 zcrx_id;
__u32 rx_buf_len;
- __u64 __resv[3];
+ __u64 notif_desc; /* see struct zcrx_notification_desc */
+ __u64 __resv[2];
};
enum zcrx_ctrl_op {
ZCRX_CTRL_FLUSH_RQ,
ZCRX_CTRL_EXPORT,
+ ZCRX_CTRL_ARM_NOTIFICATION,
__ZCRX_CTRL_LAST,
};
@@ -101,6 +117,11 @@ struct zcrx_ctrl_export {
__u32 __resv1[11];
};
+struct zcrx_ctrl_arm_notif {
+ __u32 notif_type;
+ __u32 __resv[11];
+};
+
struct zcrx_ctrl {
__u32 zcrx_id;
__u32 op; /* see enum zcrx_ctrl_op */
@@ -109,6 +130,7 @@ struct zcrx_ctrl {
union {
struct zcrx_ctrl_export zc_export;
struct zcrx_ctrl_flush_rq zc_flush;
+ struct zcrx_ctrl_arm_notif zc_arm_notif;
};
};
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 2ebb0ba37c4f..c5972274cce1 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -160,7 +160,7 @@ static void io_poison_cached_req(struct io_kiocb *req)
req->apoll = IO_URING_PTR_POISON;
}
-static void io_poison_req(struct io_kiocb *req)
+void io_poison_req(struct io_kiocb *req)
{
io_poison_cached_req(req);
req->async_data = IO_URING_PTR_POISON;
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index e612a66ee80e..de0a3bed58d1 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -213,6 +213,7 @@ bool __io_alloc_req_refill(struct io_ring_ctx *ctx);
void io_activate_pollwq(struct io_ring_ctx *ctx);
void io_restriction_clone(struct io_restriction *dst, struct io_restriction *src);
+void io_poison_req(struct io_kiocb *req);
static inline void io_lockdep_assert_cq_locked(struct io_ring_ctx *ctx)
{
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 34faf90423f4..463fbaead35b 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -768,6 +768,8 @@ static int import_zcrx(struct io_ring_ctx *ctx,
return -EINVAL;
if (reg->if_rxq || reg->rq_entries || reg->area_ptr || reg->region_ptr)
return -EINVAL;
+ if (reg->notif_desc)
+ return -EINVAL;
if (reg->flags & ~ZCRX_REG_IMPORT)
return -EINVAL;
@@ -856,6 +858,7 @@ static int zcrx_register_netdev(struct io_zcrx_ifq *ifq,
int io_register_zcrx(struct io_ring_ctx *ctx,
struct io_uring_zcrx_ifq_reg __user *arg)
{
+ struct zcrx_notification_desc notif;
struct io_uring_zcrx_area_reg area;
struct io_uring_zcrx_ifq_reg reg;
struct io_uring_region_desc rd;
@@ -899,10 +902,22 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
if (copy_from_user(&area, u64_to_user_ptr(reg.area_ptr), sizeof(area)))
return -EFAULT;
+ memset(¬if, 0, sizeof(notif));
+ if (reg.notif_desc && copy_from_user(¬if, u64_to_user_ptr(reg.notif_desc),
+ sizeof(notif)))
+ return -EFAULT;
+ if (notif.type_mask & ~ZCRX_NOTIF_TYPE_MASK)
+ return -EINVAL;
+ if (notif.__resv1 || !mem_is_zero(¬if.__resv2, sizeof(notif.__resv2)))
+ return -EINVAL;
+
ifq = io_zcrx_ifq_alloc(ctx);
if (!ifq)
return -ENOMEM;
+ ifq->notif_data = notif.user_data;
+ ifq->allowed_notif_mask = notif.type_mask;
+
if (ctx->user) {
get_uid(ctx->user);
ifq->user = ctx->user;
@@ -954,7 +969,8 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
goto err;
}
- zcrx_set_ring_ctx(ifq, ctx);
+ if (notif.type_mask)
+ zcrx_set_ring_ctx(ifq, ctx);
return 0;
err:
scoped_guard(mutex, &ctx->mmap_lock)
@@ -1127,6 +1143,48 @@ static unsigned io_zcrx_refill_slow(struct page_pool *pp, struct io_zcrx_ifq *if
return allocated;
}
+static void zcrx_notif_tw(struct io_tw_req tw_req, io_tw_token_t tw)
+{
+ struct io_kiocb *req = tw_req.req;
+ struct io_ring_ctx *ctx = req->ctx;
+
+ io_post_aux_cqe(ctx, req->cqe.user_data, req->cqe.res, 0);
+ percpu_ref_put(&ctx->refs);
+ io_poison_req(req);
+ kmem_cache_free(req_cachep, req);
+}
+
+static void zcrx_send_notif(struct io_zcrx_ifq *ifq, unsigned type)
+{
+ gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN | __GFP_ZERO;
+ u32 type_mask = 1 << type;
+ struct io_kiocb *req;
+
+ if (!(type_mask & ifq->allowed_notif_mask))
+ return;
+
+ guard(spinlock_bh)(&ifq->ctx_lock);
+ if (!ifq->master_ctx)
+ return;
+ if (type_mask & ifq->fired_notifs)
+ return;
+
+ req = kmem_cache_alloc(req_cachep, gfp);
+ if (unlikely(!req))
+ return;
+
+ ifq->fired_notifs |= type_mask;
+
+ req->opcode = IORING_OP_NOP;
+ req->cqe.user_data = ifq->notif_data;
+ req->cqe.res = type;
+ req->ctx = ifq->master_ctx;
+ percpu_ref_get(&req->ctx->refs);
+ req->tctx = NULL;
+ req->io_task_work.func = zcrx_notif_tw;
+ io_req_task_work_add(req);
+}
+
static netmem_ref io_pp_zc_alloc_netmems(struct page_pool *pp, gfp_t gfp)
{
struct io_zcrx_ifq *ifq = io_pp_to_ifq(pp);
@@ -1143,8 +1201,10 @@ static netmem_ref io_pp_zc_alloc_netmems(struct page_pool *pp, gfp_t gfp)
goto out_return;
allocated = io_zcrx_refill_slow(pp, ifq, netmems, to_alloc);
- if (!allocated)
+ if (!allocated) {
+ zcrx_send_notif(ifq, ZCRX_NOTIF_NO_BUFFERS);
return 0;
+ }
out_return:
zcrx_sync_for_device(pp, ifq, netmems, allocated);
allocated--;
@@ -1293,12 +1353,32 @@ static int zcrx_flush_rq(struct io_ring_ctx *ctx, struct io_zcrx_ifq *zcrx,
return 0;
}
+static int zcrx_arm_notif(struct io_ring_ctx *ctx, struct io_zcrx_ifq *zcrx,
+ struct zcrx_ctrl *ctrl)
+{
+ const struct zcrx_ctrl_arm_notif *an = &ctrl->zc_arm_notif;
+ unsigned type_mask;
+
+ if (an->notif_type >= __ZCRX_NOTIF_TYPE_LAST)
+ return -EINVAL;
+ if (!mem_is_zero(&an->__resv, sizeof(an->__resv)))
+ return -EINVAL;
+
+ guard(spinlock_bh)(&zcrx->ctx_lock);
+ type_mask = 1U << an->notif_type;
+ if (type_mask & ~zcrx->fired_notifs)
+ return -EINVAL;
+ zcrx->fired_notifs &= ~type_mask;
+ return 0;
+}
+
int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args)
{
struct zcrx_ctrl ctrl;
struct io_zcrx_ifq *zcrx;
BUILD_BUG_ON(sizeof(ctrl.zc_export) != sizeof(ctrl.zc_flush));
+ BUILD_BUG_ON(sizeof(ctrl.zc_export) != sizeof(ctrl.zc_arm_notif));
if (nr_args)
return -EINVAL;
@@ -1316,6 +1396,8 @@ int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args)
return zcrx_flush_rq(ctx, zcrx, &ctrl);
case ZCRX_CTRL_EXPORT:
return zcrx_export(ctx, zcrx, &ctrl, arg);
+ case ZCRX_CTRL_ARM_NOTIFICATION:
+ return zcrx_arm_notif(ctx, zcrx, &ctrl);
}
return -EOPNOTSUPP;
diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h
index 6b565d0bf6da..cca10d0d02ac 100644
--- a/io_uring/zcrx.h
+++ b/io_uring/zcrx.h
@@ -9,7 +9,9 @@
#include <net/net_trackers.h>
#define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT | ZCRX_REG_NODEV)
-#define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE)
+#define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE |\
+ ZCRX_FEATURE_NOTIFICATION)
+#define ZCRX_NOTIF_TYPE_MASK (1U << ZCRX_NOTIF_NO_BUFFERS)
struct io_zcrx_mem {
unsigned long size;
@@ -76,6 +78,9 @@ struct io_zcrx_ifq {
spinlock_t ctx_lock;
struct io_ring_ctx *master_ctx;
+ u32 allowed_notif_mask;
+ u32 fired_notifs;
+ u64 notif_data;
};
#if defined(CONFIG_IO_URING_ZCRX)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 3/6] io_uring/zcrx: notify user on frag copy fallback
From: Clément Léger @ 2026-05-18 15:35 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
In-Reply-To: <20260518153532.2835502-1-cleger@meta.com>
Add a ZCRX_NOTIF_COPY notification type to signal userspace when a
received fragment could not be delivered using zero-copy and was
instead copied into a buffer.
Signed-off-by: Clément Léger <cleger@meta.com>
---
include/uapi/linux/io_uring/zcrx.h | 1 +
io_uring/zcrx.c | 7 ++++++-
io_uring/zcrx.h | 2 +-
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h
index 67185566ad3c..3f7b72b09878 100644
--- a/include/uapi/linux/io_uring/zcrx.h
+++ b/include/uapi/linux/io_uring/zcrx.h
@@ -70,6 +70,7 @@ enum zcrx_features {
enum zcrx_notification_type {
ZCRX_NOTIF_NO_BUFFERS,
+ ZCRX_NOTIF_COPY,
__ZCRX_NOTIF_TYPE_LAST,
};
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 463fbaead35b..f31f2ca0f7ec 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -1534,8 +1534,13 @@ static int io_zcrx_copy_frag(struct io_kiocb *req, struct io_zcrx_ifq *ifq,
const skb_frag_t *frag, int off, int len)
{
struct page *page = skb_frag_page(frag);
+ int ret;
+
+ ret = io_zcrx_copy_chunk(req, ifq, page, off + skb_frag_off(frag), len);
+ if (ret > 0)
+ zcrx_send_notif(ifq, ZCRX_NOTIF_COPY);
- return io_zcrx_copy_chunk(req, ifq, page, off + skb_frag_off(frag), len);
+ return ret;
}
static int io_zcrx_recv_frag(struct io_kiocb *req, struct io_zcrx_ifq *ifq,
diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h
index cca10d0d02ac..203b3049e14b 100644
--- a/io_uring/zcrx.h
+++ b/io_uring/zcrx.h
@@ -11,7 +11,7 @@
#define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT | ZCRX_REG_NODEV)
#define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE |\
ZCRX_FEATURE_NOTIFICATION)
-#define ZCRX_NOTIF_TYPE_MASK (1U << ZCRX_NOTIF_NO_BUFFERS)
+#define ZCRX_NOTIF_TYPE_MASK ((1U << ZCRX_NOTIF_NO_BUFFERS) | (1U << ZCRX_NOTIF_COPY))
struct io_zcrx_mem {
unsigned long size;
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 1/6] io_uring/zcrx: add ctx pointer to zcrx
From: Clément Léger @ 2026-05-18 15:35 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: linux-doc, linux-kernel, linux-kselftest, netdev, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Jonathan Corbet, Shuah Khan, Vishwanath Seshagiri,
Vishwanath Seshagiri
In-Reply-To: <20260518153532.2835502-1-cleger@meta.com>
From: Pavel Begunkov <asml.silence@gmail.com>
zcrx will need to have a pointer to an owning ctx to communicate
different events. Reference the ctx while it's attached to zcrx, and
rely on zcrx termination to drop the ctx to avoid circular ref deps.
Co-developed-by: Vishwanath Seshagiri <vishs@meta.com>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
io_uring/zcrx.c | 39 +++++++++++++++++++++++++++++++--------
io_uring/zcrx.h | 3 +++
2 files changed, 34 insertions(+), 8 deletions(-)
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 3f9632e7790a..34faf90423f4 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -44,6 +44,17 @@ static inline struct io_zcrx_area *io_zcrx_iov_to_area(const struct net_iov *nio
return container_of(owner, struct io_zcrx_area, nia);
}
+static bool zcrx_set_ring_ctx(struct io_zcrx_ifq *zcrx,
+ struct io_ring_ctx *ctx)
+{
+ guard(spinlock_bh)(&zcrx->ctx_lock);
+ if (zcrx->master_ctx)
+ return false;
+ percpu_ref_get(&ctx->refs);
+ zcrx->master_ctx = ctx;
+ return true;
+}
+
static inline struct page *io_zcrx_iov_page(const struct net_iov *niov)
{
struct io_zcrx_area *area = io_zcrx_iov_to_area(niov);
@@ -531,6 +542,7 @@ static struct io_zcrx_ifq *io_zcrx_ifq_alloc(struct io_ring_ctx *ctx)
return NULL;
ifq->if_rxq = -1;
+ spin_lock_init(&ifq->ctx_lock);
spin_lock_init(&ifq->rq.lock);
mutex_init(&ifq->pp_lock);
refcount_set(&ifq->refs, 1);
@@ -580,6 +592,8 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq)
return;
if (WARN_ON_ONCE(ifq->netdev != NULL))
return;
+ if (WARN_ON_ONCE(ifq->master_ctx))
+ return;
if (ifq->area)
io_zcrx_free_area(ifq, ifq->area);
@@ -656,17 +670,24 @@ static void io_zcrx_scrub(struct io_zcrx_ifq *ifq)
}
}
-static void zcrx_unregister_user(struct io_zcrx_ifq *ifq)
+static void zcrx_unregister_user(struct io_zcrx_ifq *ifq, struct io_ring_ctx *ctx)
{
+ scoped_guard(spinlock_bh, &ifq->ctx_lock) {
+ if (ctx && ifq->master_ctx == ctx) {
+ ifq->master_ctx = NULL;
+ percpu_ref_put(&ctx->refs);
+ }
+ }
+
if (refcount_dec_and_test(&ifq->user_refs)) {
io_close_queue(ifq);
io_zcrx_scrub(ifq);
}
}
-static void zcrx_unregister(struct io_zcrx_ifq *ifq)
+static void zcrx_unregister(struct io_zcrx_ifq *ifq, struct io_ring_ctx *ctx)
{
- zcrx_unregister_user(ifq);
+ zcrx_unregister_user(ifq, ctx);
io_put_zcrx_ifq(ifq);
}
@@ -686,7 +707,7 @@ static int zcrx_box_release(struct inode *inode, struct file *file)
if (WARN_ON_ONCE(!ifq))
return -EFAULT;
- zcrx_unregister(ifq);
+ zcrx_unregister(ifq, NULL);
return 0;
}
@@ -711,7 +732,7 @@ static int zcrx_export(struct io_ring_ctx *ctx, struct io_zcrx_ifq *ifq,
file = anon_inode_create_getfile("[zcrx]", &zcrx_box_fops,
ifq, O_CLOEXEC, NULL);
if (IS_ERR(file)) {
- zcrx_unregister(ifq);
+ zcrx_unregister(ifq, NULL);
return PTR_ERR(file);
}
@@ -787,7 +808,7 @@ static int import_zcrx(struct io_ring_ctx *ctx,
scoped_guard(mutex, &ctx->mmap_lock)
xa_erase(&ctx->zcrx_ctxs, id);
err:
- zcrx_unregister(ifq);
+ zcrx_unregister(ifq, ctx);
return ret;
}
@@ -932,12 +953,14 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
ret = -EFAULT;
goto err;
}
+
+ zcrx_set_ring_ctx(ifq, ctx);
return 0;
err:
scoped_guard(mutex, &ctx->mmap_lock)
xa_erase(&ctx->zcrx_ctxs, id);
ifq_free:
- zcrx_unregister(ifq);
+ zcrx_unregister(ifq, ctx);
return ret;
}
@@ -967,7 +990,7 @@ void io_terminate_zcrx(struct io_ring_ctx *ctx)
break;
set_zcrx_entry_mark(ctx, id);
id++;
- zcrx_unregister_user(ifq);
+ zcrx_unregister_user(ifq, ctx);
}
}
diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h
index 9e1a6a1b11e8..6b565d0bf6da 100644
--- a/io_uring/zcrx.h
+++ b/io_uring/zcrx.h
@@ -73,6 +73,9 @@ struct io_zcrx_ifq {
*/
struct mutex pp_lock;
struct io_mapped_region rq_region;
+
+ spinlock_t ctx_lock;
+ struct io_ring_ctx *master_ctx;
};
#if defined(CONFIG_IO_URING_ZCRX)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v2 0/6] io_uring/zcrx: add CQE based notifications and stats reporting
From: Clément Léger @ 2026-05-18 15:35 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
The zcrx path can encounter various conditions that lead to internal
fallbacks or errors. These errors can have a large impact on performance
and functionality but are not yet not being reported to the user which
is then unable to take action.
This series addresses this problem by adding a new notification system
paired with a statistics structure. The notification system currently
report out of buffer and packets that fallback to copy. The statistics
structure report the number and total size of packets that were copied
rather than received via the zero-copy path.
The out of buffer notification allows the user to actually adjust the
buffer sizing when registering zcrx support for the ifq. Some future
work could allow the user to add more memory on the fly to the pool so
the page allocator doesn't run out of memory.
This series can be tested using the include kselftest modification and
using the liburing series that updates headers and tests/examples so
that it uses notifications and statistics.
Changes in v2:
- Rebase on top of Pavel's branch that now uses a single CQE per notif
- Change notification mask to type (ie one CQE per event)
- Use a type rather than a mask for rearm as well
- Update tests to use single typei
- Update documentatiopn to state that notif CQEs are sent for a single
event
- Fix zero init of zcrx_query_notif __resv field
- Rename resv1 to __resv1
- Reduce __resv2 size to match io_uring_query_opcode size
- Verifies that stats_offset is 0 if FLAG_STATS is zero
- Added zcrx notif query sequence to documentation
- Add _copy_fallback to test name
---
Clément Léger (4):
io_uring/zcrx: notify user on frag copy fallback
io_uring/zcrx: add shared-memory notification statistics
Documentation: networking: document zcrx notifications and statistics
selftests: iou-zcrx: add notification and stats test for zcrx
Pavel Begunkov (2):
io_uring/zcrx: add ctx pointer to zcrx
io_uring/zcrx: notify user when out of buffers
Documentation/networking/iou-zcrx.rst | 121 ++++++++++++
include/uapi/linux/io_uring/query.h | 12 ++
include/uapi/linux/io_uring/zcrx.h | 36 +++-
io_uring/io_uring.c | 2 +-
io_uring/io_uring.h | 1 +
io_uring/query.c | 16 ++
io_uring/zcrx.c | 180 +++++++++++++++++-
io_uring/zcrx.h | 11 +-
.../selftests/drivers/net/hw/iou-zcrx.c | 114 ++++++++++-
.../selftests/drivers/net/hw/iou-zcrx.py | 49 ++++-
10 files changed, 517 insertions(+), 25 deletions(-)
--
Clément Léger
^ permalink raw reply
* Re: [PATCH 0/2] docs: iio: update dated triggered buffer example
From: Jonathan Cameron @ 2026-05-18 15:33 UTC (permalink / raw)
To: David Lechner
Cc: Jonathan Corbet, Shuah Khan, Nuno Sá, Andy Shevchenko,
linux-doc, linux-kernel, linux-iio
In-Reply-To: <20260517-iio-doc-triggered-buffer-update-helpers-v1-0-7f00d4188f6f@baylibre.com>
On Sun, 17 May 2026 12:00:57 -0500
David Lechner <dlechner@baylibre.com> wrote:
> Noticed this example was out of date while grepping for something else.
> And when I did get_maintainer.pl on it, it didn't match the IIO
> subsystem, so we get a bonus patch to fix that too.
>
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---
> David Lechner (2):
> MAINTAINERS: add match for IIO API docs
Well I suppose we 'should' maintain those :)
> docs: iio: triggered-buffers: use new helpers in example
Series applied.
Thanks,
Jonathan
>
> Documentation/driver-api/iio/triggered-buffers.rst | 8 ++++----
> MAINTAINERS | 1 +
> 2 files changed, 5 insertions(+), 4 deletions(-)
> ---
> base-commit: 8678fb54958893818ddeccd05fea560a4e1fc759
> change-id: 20260517-iio-doc-triggered-buffer-update-helpers-ef7e3895c9f4
>
> Best regards,
> --
> David Lechner <dlechner@baylibre.com>
>
^ permalink raw reply
* Re: [PATCH] nios2: remove the architecture
From: Jonathan Cameron @ 2026-05-18 15:29 UTC (permalink / raw)
To: Ethan Nelson-Moore
Cc: linux-doc, devicetree, workflows, linux-arch, dmaengine,
linux-i2c, linux-iio, netdev, linux-pci, linux-pwm,
linux-hardening, linux-kbuild, linux-csky, Jonathan Corbet,
Shuah Khan, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Daniel Lezcano, Thomas Gleixner, Alex Shi, Yanteng Si,
Dongliang Mu, Hu Haowen, Dinh Nguyen, Kees Cook, Oleg Nesterov,
Will Deacon, Aneesh Kumar K.V, Andrew Morton, Nick Piggin,
Peter Zijlstra, Vinod Koul, Frank Li, Dave Penkler, Andi Shyti,
David Lechner, Nuno Sá, Andy Shevchenko, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Lorenzo Pieralisi, Krzysztof Wilczyński
In-Reply-To: <20260518042833.272221-1-enelsonmoore@gmail.com>
On Sun, 17 May 2026 21:28:33 -0700
Ethan Nelson-Moore <enelsonmoore@gmail.com> wrote:
> The Nios II architecture is a soft-core architecture developed by
> Altera (since acquired by Intel) and intended to run on their FPGAs.
>
> Licenses for the architecture have not been available for purchase
> since 2024 [1], and support for it has been removed from GCC 15 [2],
> Buildroot [3], and QEMU [4].
>
> Given all of these factors, it is time to remove Nios II support from
> the kernel. The maintainer stated in 2024 that they were planning to do
> so soon [5], but this did not come to pass.
>
> Remove Nios II support from the kernel and move the former maintainer
> to CREDITS. Thank you, Dinh Nguyen, for maintaining Nios II support!
>
> References:
> [1] https://docs.altera.com/v/u/docs/781327/is-discontinuing-ip-ordering-codes-listed-in-pdn2312-for-nios-ii-ip
> [2] https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=e876acab6cdd84bb2b32c98fc69fb0ba29c81153
> [3] https://github.com/buildroot/buildroot/commit/6775ccc5a199d574ad70b5f79ec58cce97a07c6f
> [4] https://github.com/qemu/qemu/commit/6c3014858c4c0024dd0560f08a6eda0f92f658d6
> [5] https://sourceware.org/pipermail/newlib/2024/021083.html
>
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
If it goes for IIO trivial changes.
Acked-by: Jonathan Cameron <jic23@kernel.org>
^ permalink raw reply
* Re: [PATCH RFC v4 09/10] Documentation: ABI: testing: add docs for ad9910 sysfs entries
From: Rodrigo Alencar @ 2026-05-18 15:27 UTC (permalink / raw)
To: Jonathan Cameron, Rodrigo Alencar
Cc: Rodrigo Alencar via B4 Relay, rodrigo.alencar, linux-iio,
devicetree, linux-kernel, linux-doc, linux-hardening,
Lars-Peter Clausen, Michael Hennerich, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Jonathan Corbet, Shuah Khan, Kees Cook,
Gustavo A. R. Silva
In-Reply-To: <20260518144537.7c998308@jic23-huawei>
On 26/05/18 02:45PM, Jonathan Cameron wrote:
> On Sun, 17 May 2026 18:30:27 +0100
> Rodrigo Alencar <455.rodrigo.alencar@gmail.com> wrote:
>
> > On 26/05/17 03:58PM, Jonathan Cameron wrote:
> > > On Fri, 08 May 2026 18:00:25 +0100
> > > Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> > >
> > > > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > > >
> > > > Add custom ABI documentation file for the DDS AD9910 with sysfs entries to
> > > > control Parallel Port, Digital Ramp Generator and OSK parameters.
> > > >
> > > > Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > > I'm fine with phase and frequency as defined, but for the scaling it made me wonder.
> > > For outvoltage0 channels the assumption the value is the peak voltage so if
> > > we know what input to be modulated by the ramp generator can we express them
> > > in volts (well milivolts) rather than as a scaling multiplier?
> >
> > The DAC output is current-based and differential. Voltage conversion would happen
> > outside the device...
>
> Why aren't we representing this as out_altcurrentX-Y_xxxx?
Good point! altcurrent makes more sense than altvoltage if we want to use raw to
control the output level rather than scale, which would be a constant to convert
raw into current units (what is the one that is used in the sysfs ABI? Ampere, mA or uA?)
Not sure about the benefits on setting "differential" in channel spec.. the name would
become out_altcurrentX-altcurrentY_xxxxx...
Is there any modifier for amplitude/peak/envelope? I see IIO_MOD_RMS, which could be used
if adding a 1/sqrt(2) factor to the fixed scale.
Then, I would consider something like out_altcurrent_rms_xxxx as a good alternative.
"scale" would be a constant in the top-level phy channel
single tone profile channels would have:
- frequency
- phase
- raw
drg ramp up/down channels:
- frequency and frequency_roc
- phase and phase_roc
- raw and raw_roc
parallel port channel(s):
- frequency_scale and frequency_offset (frequency destination)
- phase_offset (polar destination)
- offset (polar destination)
osk channel:
- raw
- raw_roc
raw_roc could be just roc, but that sounds like it carries the scale and refers to
a current value? and maybe that breaks consistency with other destination attributes?
I am fine with just roc if that refers to the raw value, not (raw * scale).
With all the above, still using altvoltage is not incorrect, just a matter on how
we want to express the units. Note that using raw instead of scale to control the
amplitude is just another option to tackle the problem. I suppose that the
important thing here is being technically corrent and consistent in terms of
usage. Maybe out_altcurrent_rms_* is more clear in terms of amplitude level.
>
>
> > using a resistor load or an op-amp transimpedance stage,
> > and I am no expert on that, but that often requires impedance matching so voltage
> > levels may depend on the frequency. Then, I suppose that voltage is not the right
> > unit to use.
>
> Understood that it can get complex!
> >
> > The scale here controls the amplitude of the varying signal. Assuming the peak voltage
> > (amplitude) is constant means we have a constant envelope, but that should not mean
> > we can't control it or it should not mean that the hardware can have other ways to
> > control it. That said, scale behaves as a "gain multiplier".
> Understood. Given it's the envelope then if scale happened to be 1 always it would
> be presented as _processed. So this is consistent with other channel types.
>
> >
> > >
> > > That seems to me like it fits better with the overall ABI.
> > >
> > > > +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale_offset
> > > > +KernelVersion:
> > > > +Contact: linux-iio@vger.kernel.org
> > > > +Description:
> > > > + For a channel that allows amplitude control through buffers, this
> > > > + represents the value for a base amplitude scale. The actual output
> > > > + amplitude scale is a result with the sum of this value.
> > > > +
> > >
> > > > +
> > > > +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale_roc
> > >
> > > Silly question perhaps but can work out how this related to millivolts/sec
> > > That might make a more intuitive interface than scaling multiplier per sec
> > > Perhaps the combination with offset makes this impossible though maybe that
> > > could be a expressed as a voltage offset? Afterall if the amplitude being
> > > scaled is 5V then 5 * (offset + scale) = 5 * offset + 5 * scale
> > >
> > > > +KernelVersion:
> > > > +Contact: linux-iio@vger.kernel.org
> > > > +Description:
> > > > + Amplitude scale rate of change in 1/s for channels that ramp
> > > > + amplitude. This value may be influenced by the channel's
> > > > + sampling_frequency setting.
> > >
> > >
> >
>
--
Kind regards,
Rodrigo Alencar
^ permalink raw reply
* Re: [PATCH v11 3/6] iio: adc: ad4691: add triggered buffer support
From: Jonathan Cameron @ 2026-05-18 15:25 UTC (permalink / raw)
To: David Lechner
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
linux-doc
In-Reply-To: <c9610990-6b40-40a8-948c-fa1209242dbe@baylibre.com>
On Mon, 18 May 2026 09:36:18 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 5/18/26 9:21 AM, Jonathan Cameron wrote:
> > On Sun, 17 May 2026 14:21:30 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >
> >> On 5/17/26 7:25 AM, Jonathan Cameron wrote:
> >>> On Sat, 16 May 2026 12:32:51 -0500
> >>> David Lechner <dlechner@baylibre.com> wrote:
> >>>
> >>>> On 5/15/26 8:31 AM, Radu Sabau via B4 Relay wrote:
> >>>>> From: Radu Sabau <radu.sabau@analog.com>
> >>>>>
> >>>>> Add buffered capture support using the IIO triggered buffer framework.
> >>>>>
> >>>>> CNV Burst Mode: the GP pin identified by interrupt-names in the device
> >>>>> tree is configured as DATA_READY output. The IRQ handler stops
> >>>>> conversions and fires the IIO trigger; the trigger handler executes a
> >>>>> pre-built SPI message that reads all active channels from the AVG_IN
> >>>>> accumulator registers and then resets accumulator state and restarts
> >>>>> conversions for the next cycle.
> >>>>>
> >>>>> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> >>>>> reads the previous result and starts the next conversion (pipelined
> >>>>> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> >>>>> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> >>>>> the pipeline). The trigger handler executes the message in a single
> >>>>> spi_sync() call and collects the results. An external trigger (e.g.
> >>>>> iio-trig-hrtimer) is required to drive the trigger at the desired
> >>>>> sample rate.
> >>>>>
> >>>>> Both modes share the same trigger handler and push a complete scan —
> >>>>> one big-endian 16-bit (__be16) slot per active channel, densely packed
> >>>>> in scan_index order, followed by a timestamp.
> >>>>>
> >>>>> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> >>>>> buffer-level attribute via IIO_DEVICE_ATTR.
> >>>>>
> >>>>> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> >>>
> >>>>> +
> >>>>> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> >>>>> +{
> >>>>> + struct ad4691_state *st = iio_priv(indio_dev);
> >>>>> + unsigned int k, i;
> >>>>> + int ret;
> >>>>> +
> >>>>> + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
> >>>>> + memset(st->scan_tx, 0, sizeof(st->scan_tx));
> >>>>> +
> >>>>> + spi_message_init(&st->scan_msg);
> >>>>> +
> >>>>> + k = 0;
> >>>>> + iio_for_each_active_channel(indio_dev, i) {
> >>>>> + if (i >= indio_dev->num_channels - 1)
> >>>>> + break; /* skip soft timestamp */
> >>>>
> >>>> I don't think timestamp gets set in the scan mask. It is handled separately.
> >>>
> >>> FWIW that is a sashiko false postive (I believe anyway!)
> >>> If we do hit this please shout as we have a core bug.
> >>>
> >>> If anyone has time to look at how hard it would be to tweak
> >>> iio_for_each_active_channel to skip a last element timestamp that
> >>> would be great.
> >>>
> >>> I think that iterates one too far which is what sashiko is tripping over.
> >>>
> >>> I'm only keen to fix that if we can make it low cost and hid it entirely
> >>> from drivers.
> >>>
> >>> Jonathan
> >>>
> >> This is what I came up with (totally untested).
> >>
> >> Since timestamp can never be set in scan_mask/active_scan_mask, it should
> >> be safe to exclude it from masklength without breaking existing code.
> > Probably...
> >>
> >> I didn't check all callers of masklength/iio_get_masklength() though.
> >
> > That was the bit that made me nervous. Particularly if there is an off
> > by one that is working by luck today - or someone who understood this
> > oddity and did it deliberately.
> >
> > At one point we also had a few other timestamps - the ones come from hardware.
> > I can't remember how we handled those wrt to the scan mask. I took a quick
> > look and thing they are all fine.
> > FWIW a nice precursor would be to make sure all timestamp channels are assigned
> > using the macro. There are a few that are hand crafted. I tested a few, but obviously
> > needs turning in to a proper set and cleaning up.
> >
> > diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c
> > index 627cbf5a37b0..890e25294baa 100644
> > --- a/drivers/iio/adc/ad4170-4.c
> > +++ b/drivers/iio/adc/ad4170-4.c
> > @@ -2385,9 +2385,7 @@ static int ad4170_parse_channels(struct iio_dev *indio_dev)
> > }
> >
> > /* Add timestamp channel */
> > - struct iio_chan_spec ts_chan = IIO_CHAN_SOFT_TIMESTAMP(chan_num);
> > -
> > - st->chans[chan_num] = ts_chan;
> > + st->chans[chan_num] = IIO_CHAN_SOFT_TIMESTAMP(chan_num);
> > num_channels = num_channels + 1;
> >
> > indio_dev->num_channels = num_channels;
> > diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
> > index 6e1930f7c65d..56baca1f5026 100644
> > --- a/drivers/iio/adc/at91_adc.c
> > +++ b/drivers/iio/adc/at91_adc.c
> > @@ -521,13 +521,7 @@ static int at91_adc_channel_init(struct iio_dev *idev)
> > }
> > timestamp = chan_array + idx;
> >
> > - timestamp->type = IIO_TIMESTAMP;
> > - timestamp->channel = -1;
> > - timestamp->scan_index = idx;
> > - timestamp->scan_type.sign = 's';
> > - timestamp->scan_type.realbits = 64;
> > - timestamp->scan_type.storagebits = 64;
> > -
> > + *timestamp = IIO_CHAN_SOFT_TIMESTAMP(idx);
> > idev->channels = chan_array;
> > return idev->num_channels;
> > }
> > diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
> > index 2c51b90b7101..d42b747325aa 100644
> > --- a/drivers/iio/adc/cc10001_adc.c
> > +++ b/drivers/iio/adc/cc10001_adc.c
> > @@ -262,7 +262,7 @@ static const struct iio_info cc10001_adc_info = {
> > static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
> > unsigned long channel_map)
> > {
> > - struct iio_chan_spec *chan_array, *timestamp;
> > + struct iio_chan_spec *chan_array;
> > unsigned int bit, idx = 0;
> >
> > indio_dev->num_channels = bitmap_weight(&channel_map,
> > @@ -289,13 +289,7 @@ static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
> > idx++;
> > }
> >
> > - timestamp = &chan_array[idx];
> > - timestamp->type = IIO_TIMESTAMP;
> > - timestamp->channel = -1;
> > - timestamp->scan_index = idx;
> > - timestamp->scan_type.sign = 's';
> > - timestamp->scan_type.realbits = 64;
> > - timestamp->scan_type.storagebits = 64;
> > + chan_array[idx] = IIO_CHAN_SOFT_TIMESTAMP(idx);
> >
> > indio_dev->channels = chan_array;
> >
> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > index 96b05c86c325..702b2fc66326 100644
> > --- a/include/linux/iio/iio.h
> > +++ b/include/linux/iio/iio.h
> > @@ -353,7 +353,7 @@ static inline bool iio_channel_has_available(const struct iio_chan_spec *chan,
> > (chan->info_mask_shared_by_all_available & BIT(type));
> > }
> >
> > -#define IIO_CHAN_SOFT_TIMESTAMP(_si) { \
> > +#define IIO_CHAN_SOFT_TIMESTAMP(_si) (struct iio_chan_spec) { \
> > .type = IIO_TIMESTAMP, \
> > .channel = -1, \
> > .scan_index = _si, \
> >
> > Doing that will mean we can spot any unusual use of IIO_TIMESTAMP much more
> > easily.
> >
> > Anyhow, basic approach looks good to me.
>
> I guess you didn't see the other series cleaning up IIO_TIMESTAMP I already
> sent yet.
>
:( That's what I get for not reading all my email before starting to reply!
> >
> > Jonathan
> >
> >
> >
> >>
> >> ---
> >> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> >> index 9d66510a1d49..17f539fc23e2 100644
> >> --- a/drivers/iio/industrialio-buffer.c
> >> +++ b/drivers/iio/industrialio-buffer.c
> >> @@ -2300,8 +2300,10 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> >> if (channels) {
> >> int ml = 0;
> >>
> >> - for (i = 0; i < indio_dev->num_channels; i++)
> >> - ml = max(ml, channels[i].scan_index + 1);
> >> + for (i = 0; i < indio_dev->num_channels; i++) {
> >> + if (channels[i].type != IIO_TIMESTAMP)
> >> + ml = max(ml, channels[i].scan_index + 1);
> >> + }
> >> ACCESS_PRIVATE(indio_dev, masklength) = ml;
> >> }
> >>
> >>
> >>
> >>
> >
>
^ permalink raw reply
* Re: [PATCH v11 4/6] iio: adc: ad4691: add SPI offload support
From: David Lechner @ 2026-05-18 15:16 UTC (permalink / raw)
To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
In-Reply-To: <LV9PR03MB841418AEF0059E802F7A69B2F7032@LV9PR03MB8414.namprd03.prod.outlook.com>
On 5/18/26 10:14 AM, Sabau, Radu bogdan wrote:
>> -----Original Message-----
>> From: David Lechner <dlechner@baylibre.com>
>> Sent: Saturday, May 16, 2026 8:53 PM
>
> ...
>
>>> static ssize_t sampling_frequency_show(struct device *dev,
>>> struct device_attribute *attr,
>>> char *buf)
>>> @@ -880,6 +1229,9 @@ static ssize_t sampling_frequency_show(struct
>> device *dev,
>>> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>>> struct ad4691_state *st = iio_priv(indio_dev);
>>>
>>> + if (st->manual_mode && st->offload)
>>> + return sysfs_emit(buf, "%llu\n", READ_ONCE(st->offload-
>>> trigger_hz));
>>
>> Why do we need READ_ONCE?
>>
>
> trigger_hz is u64 and if the target is 32-bit, a 64-bit access compiles to two 32-bit
> instructions, so show() reading it without a lock and store() writing it concurrently
> can produce a torn value at the compiler level. READ_ONCE/WRITE_ONCE suppress
> the compiler transformations that would allow that splitting or caching. We could
> have st->lock in show() instead, but that felt heavier than necessary for a single
> scalar where a transiently stale-but-whole read is fine.
>
I would go with the mutex. It will be easier for people to understand.
^ permalink raw reply
* RE: [PATCH v11 4/6] iio: adc: ad4691: add SPI offload support
From: Sabau, Radu bogdan @ 2026-05-18 15:14 UTC (permalink / raw)
To: David Lechner, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
In-Reply-To: <80f61c0b-1f36-4fee-9f76-b93f63b87abe@baylibre.com>
> -----Original Message-----
> From: David Lechner <dlechner@baylibre.com>
> Sent: Saturday, May 16, 2026 8:53 PM
...
> > static ssize_t sampling_frequency_show(struct device *dev,
> > struct device_attribute *attr,
> > char *buf)
> > @@ -880,6 +1229,9 @@ static ssize_t sampling_frequency_show(struct
> device *dev,
> > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > struct ad4691_state *st = iio_priv(indio_dev);
> >
> > + if (st->manual_mode && st->offload)
> > + return sysfs_emit(buf, "%llu\n", READ_ONCE(st->offload-
> >trigger_hz));
>
> Why do we need READ_ONCE?
>
trigger_hz is u64 and if the target is 32-bit, a 64-bit access compiles to two 32-bit
instructions, so show() reading it without a lock and store() writing it concurrently
can produce a torn value at the compiler level. READ_ONCE/WRITE_ONCE suppress
the compiler transformations that would allow that splitting or caching. We could
have st->lock in show() instead, but that felt heavier than necessary for a single
scalar where a transiently stale-but-whole read is fine.
^ permalink raw reply
* [PATCH v2 2/2] kselftest/arm64: Add 2025 dpISA coverage to hwcaps
From: Mark Brown @ 2026-05-18 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Jonathan Corbet, Shuah Khan
Cc: linux-arm-kernel, linux-kernel, linux-doc, linux-kselftest,
Mark Brown
In-Reply-To: <20260518-arm64-dpisa-2025-v2-0-b3367b73bd00@kernel.org>
Add coverage of the new hwcaps to the test program, encodings cross checked
against LLVM 22.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/abi/hwcap.c | 116 ++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/tools/testing/selftests/arm64/abi/hwcap.c b/tools/testing/selftests/arm64/abi/hwcap.c
index e22703d6b97c..19fca95f7c22 100644
--- a/tools/testing/selftests/arm64/abi/hwcap.c
+++ b/tools/testing/selftests/arm64/abi/hwcap.c
@@ -108,6 +108,24 @@ static void f8mm8_sigill(void)
asm volatile(".inst 0x6e80ec00");
}
+static void f16f32dot_sigill(void)
+{
+ /* FDOT V0.2S, V0.4H, V0.2H[0] */
+ asm volatile(".inst 0xf409000");
+}
+
+static void f16f32mm_sigill(void)
+{
+ /* FMMLA V0.4S, V0.8H, V0.8H */
+ asm volatile(".inst 0x4e40ec00");
+}
+
+static void f16mm_sigill(void)
+{
+ /* FMMLA V0.8H, V0.8H, V0.8H */
+ asm volatile(".inst 0x4ec0ec00");
+}
+
static void faminmax_sigill(void)
{
/* FAMIN V0.4H, V0.4H, V0.4H */
@@ -191,6 +209,12 @@ static void lut_sigill(void)
asm volatile(".inst 0x4e801000");
}
+static void sve_lut6_sigill(void)
+{
+ /* LUTI6 Z0.H, { Z0.H, Z1.H }, Z0[0] */
+ asm volatile(".inst 0x4560ac00");
+}
+
static void mops_sigill(void)
{
char dst[1], src[1];
@@ -282,6 +306,18 @@ static void sme2p2_sigill(void)
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}
+static void sme2p3_sigill(void)
+{
+ /* SMSTART SM */
+ asm volatile("msr S0_3_C4_C3_3, xzr" : : : );
+
+ /* ADDQP Z0.B, Z0.B, Z0.B */
+ asm volatile(".inst 0x4207800" : : : "z0");
+
+ /* SMSTOP */
+ asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
+}
+
static void sme_aes_sigill(void)
{
/* SMSTART SM */
@@ -378,6 +414,18 @@ static void smef8f32_sigill(void)
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}
+static void smelut6_sigill(void)
+{
+ /* SMSTART */
+ asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
+
+ /* LUTI6 { Z0.B-Z3.B }, ZT0, { Z0-Z2 } */
+ asm volatile(".inst 0xc08a0000" : : : );
+
+ /* SMSTOP */
+ asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
+}
+
static void smelutv2_sigill(void)
{
/* SMSTART */
@@ -486,6 +534,12 @@ static void sve2p2_sigill(void)
asm volatile(".inst 0x4cea000" : : : "z0");
}
+static void sve2p3_sigill(void)
+{
+ /* ADDQP Z0.B, Z0.B, Z0.B */
+ asm volatile(".inst 0x4207800" : : : "z0");
+}
+
static void sveaes_sigill(void)
{
/* AESD z0.b, z0.b, z0.b */
@@ -504,6 +558,12 @@ static void sveb16b16_sigill(void)
asm volatile(".inst 0x65000000" : : : );
}
+static void sveb16mm_sigill(void)
+{
+ /* BFMMLA Z0.H, Z0.H, Z0.H */
+ asm volatile(".inst 0x64e0e000" : : : );
+}
+
static void svebfscale_sigill(void)
{
/* BFSCALE Z0.H, P0/M, Z0.H, Z0.H */
@@ -729,6 +789,27 @@ static const struct hwcap_data {
.cpuinfo = "f8mm4",
.sigill_fn = f8mm4_sigill,
},
+ {
+ .name = "F16MM",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_F16MM,
+ .cpuinfo = "f16mm",
+ .sigill_fn = f16mm_sigill,
+ },
+ {
+ .name = "F16F32DOT",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_F16F32DOT,
+ .cpuinfo = "f16f32dot",
+ .sigill_fn = f16f32dot_sigill,
+ },
+ {
+ .name = "F16F32MM",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_F16F32MM,
+ .cpuinfo = "f16f32mm",
+ .sigill_fn = f16f32mm_sigill,
+ },
{
.name = "FAMINMAX",
.at_hwcap = AT_HWCAP2,
@@ -918,6 +999,13 @@ static const struct hwcap_data {
.cpuinfo = "sme2p2",
.sigill_fn = sme2p2_sigill,
},
+ {
+ .name = "SME 2.3",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_SME2P3,
+ .cpuinfo = "sme2p3",
+ .sigill_fn = sme2p3_sigill,
+ },
{
.name = "SME AES",
.at_hwcap = AT_HWCAP,
@@ -967,6 +1055,13 @@ static const struct hwcap_data {
.cpuinfo = "smef8f32",
.sigill_fn = smef8f32_sigill,
},
+ {
+ .name = "SME LUT6",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_SME_LUT6,
+ .cpuinfo = "smelut6",
+ .sigill_fn = smelut6_sigill,
+ },
{
.name = "SME LUTV2",
.at_hwcap = AT_HWCAP2,
@@ -1052,6 +1147,13 @@ static const struct hwcap_data {
.cpuinfo = "sve2p2",
.sigill_fn = sve2p2_sigill,
},
+ {
+ .name = "SVE 2.3",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_SVE2P3,
+ .cpuinfo = "sve2p3",
+ .sigill_fn = sve2p3_sigill,
+ },
{
.name = "SVE AES",
.at_hwcap = AT_HWCAP2,
@@ -1066,6 +1168,13 @@ static const struct hwcap_data {
.cpuinfo = "sveaes2",
.sigill_fn = sveaes2_sigill,
},
+ {
+ .name = "SVE B16MM",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_SVE_B16MM,
+ .cpuinfo = "sveb16mm",
+ .sigill_fn = sveb16mm_sigill,
+ },
{
.name = "SVE BFSCALE",
.at_hwcap = AT_HWCAP,
@@ -1087,6 +1196,13 @@ static const struct hwcap_data {
.cpuinfo = "svef16mm",
.sigill_fn = svef16mm_sigill,
},
+ {
+ .name = "SVE_LUT6",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_SVE_LUT6,
+ .cpuinfo = "svelut6",
+ .sigill_fn = sve_lut6_sigill,
+ },
{
.name = "SVE2 B16B16",
.at_hwcap = AT_HWCAP2,
--
2.47.3
^ permalink raw reply related
* [PATCH v2 1/2] arm64/cpufeature: Define hwcaps for 2025 dpISA features
From: Mark Brown @ 2026-05-18 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Jonathan Corbet, Shuah Khan
Cc: linux-arm-kernel, linux-kernel, linux-doc, linux-kselftest,
Mark Brown
In-Reply-To: <20260518-arm64-dpisa-2025-v2-0-b3367b73bd00@kernel.org>
The features added by the 2025 dpISA are all straightforward instruction
only features so there is no state to manage, we can just expose hwcaps to
let userspace know they are available.
F16MM is slightly odd in that the feature is FEAT_F16MM but it is discovered
via ID_AA64FPFR0_EL1.F16MM2. We follow the feature name.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
Documentation/arch/arm64/elf_hwcaps.rst | 24 ++++++++++++++++++++++++
arch/arm64/include/uapi/asm/hwcap.h | 8 ++++++++
arch/arm64/kernel/cpufeature.c | 11 +++++++++++
arch/arm64/kernel/cpuinfo.c | 8 ++++++++
4 files changed, 51 insertions(+)
diff --git a/Documentation/arch/arm64/elf_hwcaps.rst b/Documentation/arch/arm64/elf_hwcaps.rst
index 97315ae6c0da..07ff9ea1d605 100644
--- a/Documentation/arch/arm64/elf_hwcaps.rst
+++ b/Documentation/arch/arm64/elf_hwcaps.rst
@@ -451,6 +451,30 @@ HWCAP3_LS64
of CPU. User should only use ld64b/st64b on supported target (device)
memory location, otherwise fallback to the non-atomic alternatives.
+HWCAP3_SVE_B16MM
+ Functionality implied by ID_AA64ZFR0_EL1.B16B16 == 0b0011
+
+HWCAP3_SVE2P3
+ Functionality implied by ID_AA64ZFR0_EL1.SVEver == 0b0100
+
+HWCAP3_SME_LUT6
+ Functionality implied by ID_AA64SMFR0_EL1.LUT6 == 0b1
+
+HWCAP3_SME2P3
+ Functionality implied by ID_AA64SMFR0_EL1.SMEver == 0b0100
+
+HWCAP3_F16MM
+ Functionality implied by ID_AA64FPFR0_EL1.F16MM2 == 0b1
+
+HWCAP3_F16F32DOT
+ Functionality implied by ID_AA64ISAR0_EL1.FHM == 0b0010
+
+HWCAP3_F16F32MM
+ Functionality implied by ID_AA64ISAR0_EL1.FHM == 0b0011
+
+HWCAP3_SVE_LUT6
+ Functionality implied by ID_AA64ISAR2_EL1.LUT == 0b0010 and
+ ID_AA64PFR0_EL1.SVE == 0b0001.
4. Unused AT_HWCAP bits
-----------------------
diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h
index 06f83ca8de56..10272ddb4d6f 100644
--- a/arch/arm64/include/uapi/asm/hwcap.h
+++ b/arch/arm64/include/uapi/asm/hwcap.h
@@ -147,5 +147,13 @@
#define HWCAP3_MTE_STORE_ONLY (1UL << 1)
#define HWCAP3_LSFE (1UL << 2)
#define HWCAP3_LS64 (1UL << 3)
+#define HWCAP3_SVE_B16MM (1UL << 4)
+#define HWCAP3_SVE2P3 (1UL << 5)
+#define HWCAP3_SME_LUT6 (1UL << 6)
+#define HWCAP3_SME2P3 (1UL << 7)
+#define HWCAP3_F16MM (1UL << 8)
+#define HWCAP3_F16F32DOT (1UL << 9)
+#define HWCAP3_F16F32MM (1UL << 10)
+#define HWCAP3_SVE_LUT6 (1UL << 11)
#endif /* _UAPI__ASM_HWCAP_H */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6d53bb15cf7b..96de16582fca 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -365,6 +365,8 @@ static const struct arm64_ftr_bits ftr_id_aa64zfr0[] = {
static const struct arm64_ftr_bits ftr_id_aa64smfr0[] = {
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_FA64_SHIFT, 1, 0),
+ ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
+ FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_LUT6_SHIFT, 1, 0),
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
FTR_STRICT, FTR_EXACT, ID_AA64SMFR0_EL1_LUTv2_SHIFT, 1, 0),
ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SME),
@@ -419,6 +421,7 @@ static const struct arm64_ftr_bits ftr_id_aa64fpfr0[] = {
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64FPFR0_EL1_F8DP2_SHIFT, 1, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64FPFR0_EL1_F8MM8_SHIFT, 1, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64FPFR0_EL1_F8MM4_SHIFT, 1, 0),
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64FPFR0_EL1_F16MM2_SHIFT, 1, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64FPFR0_EL1_F8E4M3_SHIFT, 1, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64FPFR0_EL1_F8E5M2_SHIFT, 1, 0),
ARM64_FTR_END,
@@ -3284,6 +3287,8 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
HWCAP_CAP(ID_AA64ISAR0_EL1, SM4, IMP, CAP_HWCAP, KERNEL_HWCAP_SM4),
HWCAP_CAP(ID_AA64ISAR0_EL1, DP, IMP, CAP_HWCAP, KERNEL_HWCAP_ASIMDDP),
HWCAP_CAP(ID_AA64ISAR0_EL1, FHM, IMP, CAP_HWCAP, KERNEL_HWCAP_ASIMDFHM),
+ HWCAP_CAP(ID_AA64ISAR0_EL1, FHM, F16F32DOT, CAP_HWCAP, KERNEL_HWCAP_F16F32DOT),
+ HWCAP_CAP(ID_AA64ISAR0_EL1, FHM, F16F32MM, CAP_HWCAP, KERNEL_HWCAP_F16F32MM),
HWCAP_CAP(ID_AA64ISAR0_EL1, TS, FLAGM, CAP_HWCAP, KERNEL_HWCAP_FLAGM),
HWCAP_CAP(ID_AA64ISAR0_EL1, TS, FLAGM2, CAP_HWCAP, KERNEL_HWCAP_FLAGM2),
HWCAP_CAP(ID_AA64ISAR0_EL1, RNDR, IMP, CAP_HWCAP, KERNEL_HWCAP_RNG),
@@ -3313,7 +3318,9 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
HWCAP_CAP(ID_AA64ISAR3_EL1, LSFE, IMP, CAP_HWCAP, KERNEL_HWCAP_LSFE),
HWCAP_CAP(ID_AA64MMFR2_EL1, AT, IMP, CAP_HWCAP, KERNEL_HWCAP_USCAT),
#ifdef CONFIG_ARM64_SVE
+ HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ISAR2_EL1, LUT, LUT6, CAP_HWCAP, KERNEL_HWCAP_SVE_LUT6),
HWCAP_CAP(ID_AA64PFR0_EL1, SVE, IMP, CAP_HWCAP, KERNEL_HWCAP_SVE),
+ HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, SVEver, SVE2p3, CAP_HWCAP, KERNEL_HWCAP_SVE2P3),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, SVEver, SVE2p2, CAP_HWCAP, KERNEL_HWCAP_SVE2P2),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, SVEver, SVE2p1, CAP_HWCAP, KERNEL_HWCAP_SVE2P1),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, SVEver, SVE2, CAP_HWCAP, KERNEL_HWCAP_SVE2),
@@ -3323,6 +3330,7 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, BitPerm, IMP, CAP_HWCAP, KERNEL_HWCAP_SVEBITPERM),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, B16B16, IMP, CAP_HWCAP, KERNEL_HWCAP_SVE_B16B16),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, B16B16, BFSCALE, CAP_HWCAP, KERNEL_HWCAP_SVE_BFSCALE),
+ HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, B16B16, B16MM, CAP_HWCAP, KERNEL_HWCAP_SVE_B16MM),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, BF16, IMP, CAP_HWCAP, KERNEL_HWCAP_SVEBF16),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, BF16, EBF16, CAP_HWCAP, KERNEL_HWCAP_SVE_EBF16),
HWCAP_CAP_MATCH_ID(has_sve_feature, ID_AA64ZFR0_EL1, SHA3, IMP, CAP_HWCAP, KERNEL_HWCAP_SVESHA3),
@@ -3362,7 +3370,9 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
#ifdef CONFIG_ARM64_SME
HWCAP_CAP(ID_AA64PFR1_EL1, SME, IMP, CAP_HWCAP, KERNEL_HWCAP_SME),
HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, FA64, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_FA64),
+ HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, LUT6, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_LUT6),
HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, LUTv2, IMP, CAP_HWCAP, KERNEL_HWCAP_SME_LUTV2),
+ HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMEver, SME2p3, CAP_HWCAP, KERNEL_HWCAP_SME2P3),
HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMEver, SME2p2, CAP_HWCAP, KERNEL_HWCAP_SME2P2),
HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMEver, SME2p1, CAP_HWCAP, KERNEL_HWCAP_SME2P1),
HWCAP_CAP_MATCH_ID(has_sme_feature, ID_AA64SMFR0_EL1, SMEver, SME2, CAP_HWCAP, KERNEL_HWCAP_SME2),
@@ -3393,6 +3403,7 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
HWCAP_CAP(ID_AA64FPFR0_EL1, F8DP2, IMP, CAP_HWCAP, KERNEL_HWCAP_F8DP2),
HWCAP_CAP(ID_AA64FPFR0_EL1, F8MM8, IMP, CAP_HWCAP, KERNEL_HWCAP_F8MM8),
HWCAP_CAP(ID_AA64FPFR0_EL1, F8MM4, IMP, CAP_HWCAP, KERNEL_HWCAP_F8MM4),
+ HWCAP_CAP(ID_AA64FPFR0_EL1, F16MM2, IMP, CAP_HWCAP, KERNEL_HWCAP_F16MM),
HWCAP_CAP(ID_AA64FPFR0_EL1, F8E4M3, IMP, CAP_HWCAP, KERNEL_HWCAP_F8E4M3),
HWCAP_CAP(ID_AA64FPFR0_EL1, F8E5M2, IMP, CAP_HWCAP, KERNEL_HWCAP_F8E5M2),
#ifdef CONFIG_ARM64_POE
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 6149bc91251d..d50e2a9b066b 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -164,6 +164,14 @@ static const char *const hwcap_str[] = {
[KERNEL_HWCAP_MTE_FAR] = "mtefar",
[KERNEL_HWCAP_MTE_STORE_ONLY] = "mtestoreonly",
[KERNEL_HWCAP_LSFE] = "lsfe",
+ [KERNEL_HWCAP_SVE_B16MM] = "sveb16mm",
+ [KERNEL_HWCAP_SVE2P3] = "sve2p3",
+ [KERNEL_HWCAP_SME_LUT6] = "smelut6",
+ [KERNEL_HWCAP_SME2P3] = "sme2p3",
+ [KERNEL_HWCAP_F16MM] = "f16mm",
+ [KERNEL_HWCAP_F16F32DOT] = "f16f32dot",
+ [KERNEL_HWCAP_F16F32MM] = "f16f32mm",
+ [KERNEL_HWCAP_SVE_LUT6] = "svelut6",
};
#ifdef CONFIG_COMPAT
--
2.47.3
^ permalink raw reply related
* [PATCH v2 0/2] arm64: Implement support for 2025 dpISA extensions
From: Mark Brown @ 2026-05-18 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Jonathan Corbet, Shuah Khan
Cc: linux-arm-kernel, linux-kernel, linux-doc, linux-kselftest,
Mark Brown
The 2025 dpISA extensions introduce a number of architecture features
all of which are fairly straightforward from a kernel point of view
since they only introduce new instructions, not any architecture state.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
Changes in v2:
- Rename HWCAP3_LUT6 to HWCAP3_SVE_LUT6 and make it depend on SVE.
- Rebase onto v7.1-rc3.
- Link to v1: https://patch.msgid.link/20260302-arm64-dpisa-2025-v1-0-0855e7f41689@kernel.org
---
Mark Brown (2):
arm64/cpufeature: Define hwcaps for 2025 dpISA features
kselftest/arm64: Add 2025 dpISA coverage to hwcaps
Documentation/arch/arm64/elf_hwcaps.rst | 24 +++++++
arch/arm64/include/uapi/asm/hwcap.h | 8 +++
arch/arm64/kernel/cpufeature.c | 11 +++
arch/arm64/kernel/cpuinfo.c | 8 +++
tools/testing/selftests/arm64/abi/hwcap.c | 116 ++++++++++++++++++++++++++++++
5 files changed, 167 insertions(+)
---
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
change-id: 20260106-arm64-dpisa-2025-d6673ae6acee
Best regards,
--
Mark Brown <broonie@kernel.org>
^ permalink raw reply
* Re: [PATCH 00/12] misc/syncobj: add /dev/syncobj device
From: Christian König @ 2026-05-18 15:06 UTC (permalink / raw)
To: Michel Dänzer, Julian Orth
Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Jonathan Corbet, Shuah Khan,
Arnd Bergmann, Greg Kroah-Hartman, dri-devel, linux-kernel,
linux-media, linaro-mm-sig, linux-doc, wayland-devel
In-Reply-To: <1162f62e-9c65-446b-9788-bb289a202e6e@mailbox.org>
On 5/18/26 16:59, Michel Dänzer wrote:
> On 5/18/26 14:41, Christian König wrote:
>> On 5/18/26 14:02, Julian Orth wrote:
>>> On Mon, May 18, 2026 at 1:58 PM Christian König
>>> <christian.koenig@amd.com> wrote:
>>>> On 5/16/26 13:06, Julian Orth wrote:
>>>>> This series adds a new device /dev/syncobj that can be used to create
>>>>> and manipulate DRM syncobjs. Previously, these operations required the
>>>>> use of a DRM device and the device needed to support the DRIVER_SYNCOBJ
>>>>> and DRIVER_SYNCOBJ_TIMELINE features.
>>>>>
>>>>> There are several issues with the existing API:
>>>>>
>>>>> - Syncobjs are the only explicit sync mechanism available on wayland.
>>>>> Most compositors do not use GPU waits. Instead, they use the
>>>>> DRM_IOCTL_SYNCOBJ_EVENTFD ioctl to perform a CPU wait. Being tied to
>>>>> DRM devices means that compositors cannot consistently offer this
>>>>> feature even though no device-specific logic is involved.
>>>>
>>>> Well the drm_syncobj is a container for device specific dma fences.
>>>
>>> Not necessarily. The DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL ioctl attaches
>>> some kind of dummy fence that is already signaled. I don't believe
>>> this is device specific. That is also the path that llvmpipe would
>>> use.
>>
>> Yeah I feared that.
>>
>> This is the wait before signal path and if I'm not completely mistaken that one is not supported by a lot of compositors.
>
> Where did you get that impression from?
Kernel space seems to not handle that support very well. We added the flag at some point for drivers, but only a fraction actually implemented it.
I wasn't aware that the general eventfd implementation can handle it, but yeah when compositors use that one then that actually makes sense.
> It's arguably the main point of the syncobj Wayland protocol extension, which is supported by all major compositors (except Weston, where it's still a pending MR).
>
>
>> So as far as I can see using drm_syncobj for software rendering really doesn't make sense, eventfd is a much better fit for that use case.
>
> I agree with Julian's rebuttal to that.
That eventfd is missing the timeline functionality is a pretty good argument, but I'm still not sure if that justifies the extra kernel complexity.
Regards,
Christian.
^ permalink raw reply
* RE: [PATCH v11 2/6] iio: adc: ad4691: add initial driver for AD4691 family
From: Sabau, Radu bogdan @ 2026-05-18 15:05 UTC (permalink / raw)
To: Jonathan Cameron, Radu Sabau via B4 Relay
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
In-Reply-To: <20260517125241.7dbfb964@jic23-huawei>
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Sunday, May 17, 2026 2:53 PM
...
> > +static int ad4691_reset(struct ad4691_state *st)
> > +{
> > + struct device *dev = regmap_get_device(st->regmap);
> > + struct reset_control *rst;
> > +
> > + rst = devm_reset_control_get_optional_exclusive(dev, NULL);
> > + if (IS_ERR(rst))
> > + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get
> reset\n");
> > +
> > + if (rst) {
> > + /*
> > + * Assert the reset line before sleeping to guarantee a proper
> > + * reset pulse on every probe, including driver reloads where
> > + * the line may already be deasserted (reset_control_put()
> does
> > + * not re-assert on release).
> > + * devm_reset_control_get_optional_exclusive_deasserted()
> cannot
> > + * be used because it deasserts immediately without delay; the
> > + * datasheet (Table 5) requires a ≥300 µs reset pulse width
> > + * before deassertion.
> > + */
> > + reset_control_assert(rst);
> > + fsleep(300);
> > + return reset_control_deassert(rst);
> Sashiko makes the reasonable point that we'd kind of expect some time
> between
> that pin dropping the device out of reset and it being able to respond. If it
> really is that quick - then add a comment.
>
> https://urldefense.com/v3/__https://sashiko.dev/*/patchset/20260515-
> ad4692-multichannel-sar-adc-driver-v11-0-
> eab27d852ac2*40analog.com__;IyU!!A3Ni8CS0y2Y!5I5rXG5US4TFIZ_cAbgcy
> gH-_Cbt6wLDZ5jVeBiPSDg5KuzEZT-CMN4Z3aFYYVXH6Kx4f2ClJcbr9w$
> > + }
> > +
> > + /* No hardware reset available, fall back to software reset. */
> > + return regmap_write(st->regmap, AD4691_SPI_CONFIG_A_REG,
> > + AD4691_SW_RESET);
> Same applies here.
>
He is right. And also I think I misread the datasheet, the 300us should happen
after deassertion and actual RESETL time is 10ns minimum which would be
covered by the overhead itself.
I think the reason to why this was never a problem while testing is because
300us is the recommended time and the minimum one could be much lower
than this, so successful probe was nothing but luck on my end.
I will make sure to update the function accordingly.
^ permalink raw reply
* Re: [PATCH v11 2/6] iio: adc: ad4691: add initial driver for AD4691 family
From: David Lechner @ 2026-05-18 15:05 UTC (permalink / raw)
To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
In-Reply-To: <LV9PR03MB841445D5BD1087FB3204EBD9F7032@LV9PR03MB8414.namprd03.prod.outlook.com>
On 5/18/26 9:59 AM, Sabau, Radu bogdan wrote:
>> -----Original Message-----
>> From: David Lechner <dlechner@baylibre.com>
>> Sent: Saturday, May 16, 2026 8:11 PM
>
> ...
>
>>> +static int ad4691_reg_read(void *context, unsigned int reg, unsigned int
>> *val)
>>> +{
>>> + struct spi_device *spi = context;
>>> + u8 tx[2], rx[4];
>>> + int ret;
>>> +
>>> + /* Set bit 15 to mark the operation as READ. */
>>
>> Can't we just set read_flag_mask in the regmap config?
>>
>
> As far as I can tell read_flag_mask is applied by the standard SPI regmap bus
> backend, which constructs and sends the address byte itself before reading
> the response. When using devm_regmap_init() with custom reg_read/reg_write
> callbacks, the regmap core calls those callbacks directly with the raw register
> address - it never touches read_flag_mask.
>
>>> + put_unaligned_be16(0x8000 | reg, tx);
>>> +
>>> + switch (reg) {
>>> + case 0 ... AD4691_OSC_FREQ_REG:
>>> + case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1:
>
> ...
>
>>> +static int ad4691_write_raw(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec const *chan,
>>> + int val, int val2, long mask)
>>> +{
>>> + switch (mask) {
>>> + case IIO_CHAN_INFO_SAMP_FREQ:
>>
>> Should we aquire direct mode so that we can't change the rate during
>> buffered read?
>>
>
> It is in set_sampling_freq already. Do you think it would make more sense
> to move it here in order to help readability?
>
IIRC, I think it was resolved in a later patch in the series. So
could just be a problem of it not getting added in the right patch.
In general though, yes it would make it easier review if the
direct mode claim was made here.
^ permalink raw reply
* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Shah, Tanmay @ 2026-05-18 15:01 UTC (permalink / raw)
To: Shenwei Wang, Mathieu Poirier
Cc: Arnaud POULIQUEN, Beleswar Prasad Padhi, Andrew Lunn,
Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Frank Li,
Sascha Hauer, Shuah Khan, linux-gpio@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
Pengutronix Kernel Team, Fabio Estevam, Peng Fan,
devicetree@vger.kernel.org, linux-remoteproc@vger.kernel.org,
imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
dl-linux-imx, Bartosz Golaszewski
In-Reply-To: <PAXPR04MB918587A8812B51BBB2A46A2C89032@PAXPR04MB9185.eurprd04.prod.outlook.com>
On 5/18/2026 9:24 AM, Shenwei Wang wrote:
>
>
>
>>
>> On Thu, May 07, 2026 at 07:43:33PM +0000, Shenwei Wang wrote:
>>>
>>>
>>>> That was my initial approach. We don't even need an additional
>>>> "rpmsg-io-*" in rpmsg_gpio_channel_id_table[]. All we need is:
>>>>
>>>> /* rpmsg devices and drivers are matched using the service name */
>>>> static inline int rpmsg_id_match(const struct rpmsg_device *rpdev,
>>>> const struct rpmsg_device_id *id) {
>>>> + size_t len = strnlen(id->name, RPMSG_NAME_SIZE);
>>>>
>>>> - return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;
>>>> + return strncmp(id->name, rpdev->id.name, len) == 0;
>>>> }
>>>>
>>>
>>> If we encode the port index directly into ept->src, for example:
>>>
>>> ept->src = (baseaddr << 8) | port_index;
>>>
>>
>> There is no rpmsg_endpoint::src. You likely meant ept->addr. This would work
>> but not optimal on two front:
>>
>> (1) rpms_endpoint::addr is a u32 and idr_alloc() returns an 'int'. As such there is a
>> possibility of conflict. I concede the possibility is marginal, but it still exists.
>>
>
> I think there may be a misunderstanding in the implementation. In this case, we do not
> need the return value from idr_alloc.
>
> When the driver calls rpmsg_create_ept, it can pass an rpmsg_channel_info structure as an
> input parameter. This allows you to specify the source address you want to bind.
> Please refer to the definitions below:
>
> struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
> rpmsg_rx_cb_t cb, void *priv,
> struct rpmsg_channel_info chinfo)
>
> struct rpmsg_channel_info {
> char name[RPMSG_NAME_SIZE];
> u32 src;
> u32 dst;
> };
>
>> (2) By proceeding this way, the kernel exposes the GPIO controller it knows
>> about. It is preferrable to have the remote processor tell the kernel about the
>> GPIO controller it wants.
>>
>
> If everyone agrees with this namespace announcement approach, I will prepare the
> next revision based on it, even though it is not as clean as the source address encoding solution.
>
I have ack on the namespace announcement approach. To me it is very
simple contract between the firmware and the Linux which allows dynamic
endpoint allocation without giving up security concerns. Also this
approach can be easily extended for any other rpmsg devices like i2c,
spi etc.. With the fix in the rpmsg_core.c, this will work. I will have
to see the actual implementation in the next rev to provide further
feedback.
Thanks,
Tanmay
> Shenwei
>
>> I am done reviewing this revision. Given the amount of refactoring needed, I will
>> not look at the code. Please refer to this reply [1] for what I am expecting in the
>> next revision.
>>
>
^ permalink raw reply
* RE: [PATCH v11 2/6] iio: adc: ad4691: add initial driver for AD4691 family
From: Sabau, Radu bogdan @ 2026-05-18 14:59 UTC (permalink / raw)
To: David Lechner, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
In-Reply-To: <0696b662-f478-4d1a-95e0-0338bbdb719e@baylibre.com>
> -----Original Message-----
> From: David Lechner <dlechner@baylibre.com>
> Sent: Saturday, May 16, 2026 8:11 PM
...
> > +static int ad4691_reg_read(void *context, unsigned int reg, unsigned int
> *val)
> > +{
> > + struct spi_device *spi = context;
> > + u8 tx[2], rx[4];
> > + int ret;
> > +
> > + /* Set bit 15 to mark the operation as READ. */
>
> Can't we just set read_flag_mask in the regmap config?
>
As far as I can tell read_flag_mask is applied by the standard SPI regmap bus
backend, which constructs and sends the address byte itself before reading
the response. When using devm_regmap_init() with custom reg_read/reg_write
callbacks, the regmap core calls those callbacks directly with the raw register
address - it never touches read_flag_mask.
> > + put_unaligned_be16(0x8000 | reg, tx);
> > +
> > + switch (reg) {
> > + case 0 ... AD4691_OSC_FREQ_REG:
> > + case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1:
...
> > +static int ad4691_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int val, int val2, long mask)
> > +{
> > + switch (mask) {
> > + case IIO_CHAN_INFO_SAMP_FREQ:
>
> Should we aquire direct mode so that we can't change the rate during
> buffered read?
>
It is in set_sampling_freq already. Do you think it would make more sense
to move it here in order to help readability?
^ permalink raw reply
* Re: [PATCH 00/12] misc/syncobj: add /dev/syncobj device
From: Michel Dänzer @ 2026-05-18 14:59 UTC (permalink / raw)
To: Christian König, Julian Orth
Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Jonathan Corbet, Shuah Khan,
Arnd Bergmann, Greg Kroah-Hartman, dri-devel, linux-kernel,
linux-media, linaro-mm-sig, linux-doc, wayland-devel
In-Reply-To: <69dcbcc1-da58-4d34-bfb0-5c8d33b75d59@amd.com>
On 5/18/26 14:41, Christian König wrote:
> On 5/18/26 14:02, Julian Orth wrote:
>> On Mon, May 18, 2026 at 1:58 PM Christian König
>> <christian.koenig@amd.com> wrote:
>>> On 5/16/26 13:06, Julian Orth wrote:
>>>> This series adds a new device /dev/syncobj that can be used to create
>>>> and manipulate DRM syncobjs. Previously, these operations required the
>>>> use of a DRM device and the device needed to support the DRIVER_SYNCOBJ
>>>> and DRIVER_SYNCOBJ_TIMELINE features.
>>>>
>>>> There are several issues with the existing API:
>>>>
>>>> - Syncobjs are the only explicit sync mechanism available on wayland.
>>>> Most compositors do not use GPU waits. Instead, they use the
>>>> DRM_IOCTL_SYNCOBJ_EVENTFD ioctl to perform a CPU wait. Being tied to
>>>> DRM devices means that compositors cannot consistently offer this
>>>> feature even though no device-specific logic is involved.
>>>
>>> Well the drm_syncobj is a container for device specific dma fences.
>>
>> Not necessarily. The DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL ioctl attaches
>> some kind of dummy fence that is already signaled. I don't believe
>> this is device specific. That is also the path that llvmpipe would
>> use.
>
> Yeah I feared that.
>
> This is the wait before signal path and if I'm not completely mistaken that one is not supported by a lot of compositors.
Where did you get that impression from?
It's arguably the main point of the syncobj Wayland protocol extension, which is supported by all major compositors (except Weston, where it's still a pending MR).
> So as far as I can see using drm_syncobj for software rendering really doesn't make sense, eventfd is a much better fit for that use case.
I agree with Julian's rebuttal to that.
--
Earthling Michel Dänzer \ GNOME / Xwayland / Mesa developer
https://redhat.com \ Libre software enthusiast
^ permalink raw reply
* Re: [PATCH mm-unstable v17 02/14] mm/khugepaged: generalize alloc_charge_folio()
From: Lance Yang @ 2026-05-18 14:49 UTC (permalink / raw)
To: Usama Arif, Nico Pache
Cc: linux-doc, linux-kernel, linux-mm, linux-trace-kernel, akpm,
anshuman.khandual, apopple, baohua, baolin.wang, byungchul,
catalin.marinas, cl, corbet, dave.hansen, david, dev.jain, gourry,
hannes, hughd, jack, jackmanb, jannh, jglisse, joshua.hahnjy, kas,
liam, ljs, mathieu.desnoyers, matthew.brost, mhiramat, mhocko,
peterx, pfalcato, rakie.kim, raquini, rdunlap, richard.weiyang,
rientjes, rostedt, rppt, ryan.roberts, shivankg, sunnanyong,
surenb, thomas.hellstrom, tiwai, usamaarif642, vbabka,
vishal.moola, wangkefeng.wang, will, willy, yang, ying.huang, ziy,
zokeefe
In-Reply-To: <20260518115553.3513034-1-usama.arif@linux.dev>
On 2026/5/18 19:55, Usama Arif wrote:
[...]
>> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
>> index 979885694351..f0e29d5c7b1f 100644
>> --- a/mm/khugepaged.c
>> +++ b/mm/khugepaged.c
>> @@ -1068,21 +1068,26 @@ static enum scan_result __collapse_huge_page_swapin(struct mm_struct *mm,
>> }
>>
>> static enum scan_result alloc_charge_folio(struct folio **foliop, struct mm_struct *mm,
>> - struct collapse_control *cc)
>> + struct collapse_control *cc, unsigned int order)
>> {
>> gfp_t gfp = (cc->is_khugepaged ? alloc_hugepage_khugepaged_gfpmask() :
>> GFP_TRANSHUGE);
>> int node = collapse_find_target_node(cc);
>> struct folio *folio;
>>
>> - folio = __folio_alloc(gfp, HPAGE_PMD_ORDER, node, &cc->alloc_nmask);
>> + folio = __folio_alloc(gfp, order, node, &cc->alloc_nmask);
>> if (!folio) {
>> *foliop = NULL;
>> - count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
>> + if (is_pmd_order(order))
>> + count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
>> + count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC_FAILED);
>> return SCAN_ALLOC_HUGE_PAGE_FAIL;
>> }
>>
>> - count_vm_event(THP_COLLAPSE_ALLOC);
>> + if (is_pmd_order(order))
>> + count_vm_event(THP_COLLAPSE_ALLOC);
>> + count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC);
>> +
>
> The vmstat THP_COLLAPSE_ALLOC counter is pmd order only.
> But after this we have
>
> count_memcg_folio_events(folio, THP_COLLAPSE_ALLOC, 1);
>
> which is not being guarded with is_pmd_order().
Good catch!
>
> I think we want this to be pmd order only as well so that
> the meaning of the vmstat and cgroup counter remains the same?
Agreed. THP_COLLAPSE_ALLOC should remain PMD order only for
vmstat and memcg events.
So this should be guarded with is_pmd_order() as well :)
Cheers, Lance
^ permalink raw reply
* Re: [PATCH v11 3/6] iio: adc: ad4691: add triggered buffer support
From: David Lechner @ 2026-05-18 14:36 UTC (permalink / raw)
To: Jonathan Cameron
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
linux-doc
In-Reply-To: <20260518152103.4d428c1e@jic23-huawei>
On 5/18/26 9:21 AM, Jonathan Cameron wrote:
> On Sun, 17 May 2026 14:21:30 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
>> On 5/17/26 7:25 AM, Jonathan Cameron wrote:
>>> On Sat, 16 May 2026 12:32:51 -0500
>>> David Lechner <dlechner@baylibre.com> wrote:
>>>
>>>> On 5/15/26 8:31 AM, Radu Sabau via B4 Relay wrote:
>>>>> From: Radu Sabau <radu.sabau@analog.com>
>>>>>
>>>>> Add buffered capture support using the IIO triggered buffer framework.
>>>>>
>>>>> CNV Burst Mode: the GP pin identified by interrupt-names in the device
>>>>> tree is configured as DATA_READY output. The IRQ handler stops
>>>>> conversions and fires the IIO trigger; the trigger handler executes a
>>>>> pre-built SPI message that reads all active channels from the AVG_IN
>>>>> accumulator registers and then resets accumulator state and restarts
>>>>> conversions for the next cycle.
>>>>>
>>>>> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
>>>>> reads the previous result and starts the next conversion (pipelined
>>>>> N+1 scheme). At preenable time a pre-built, optimised SPI message of
>>>>> N+1 transfers is constructed (N channel reads plus one NOOP to drain
>>>>> the pipeline). The trigger handler executes the message in a single
>>>>> spi_sync() call and collects the results. An external trigger (e.g.
>>>>> iio-trig-hrtimer) is required to drive the trigger at the desired
>>>>> sample rate.
>>>>>
>>>>> Both modes share the same trigger handler and push a complete scan —
>>>>> one big-endian 16-bit (__be16) slot per active channel, densely packed
>>>>> in scan_index order, followed by a timestamp.
>>>>>
>>>>> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
>>>>> buffer-level attribute via IIO_DEVICE_ATTR.
>>>>>
>>>>> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
>>>
>>>>> +
>>>>> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
>>>>> +{
>>>>> + struct ad4691_state *st = iio_priv(indio_dev);
>>>>> + unsigned int k, i;
>>>>> + int ret;
>>>>> +
>>>>> + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
>>>>> + memset(st->scan_tx, 0, sizeof(st->scan_tx));
>>>>> +
>>>>> + spi_message_init(&st->scan_msg);
>>>>> +
>>>>> + k = 0;
>>>>> + iio_for_each_active_channel(indio_dev, i) {
>>>>> + if (i >= indio_dev->num_channels - 1)
>>>>> + break; /* skip soft timestamp */
>>>>
>>>> I don't think timestamp gets set in the scan mask. It is handled separately.
>>>
>>> FWIW that is a sashiko false postive (I believe anyway!)
>>> If we do hit this please shout as we have a core bug.
>>>
>>> If anyone has time to look at how hard it would be to tweak
>>> iio_for_each_active_channel to skip a last element timestamp that
>>> would be great.
>>>
>>> I think that iterates one too far which is what sashiko is tripping over.
>>>
>>> I'm only keen to fix that if we can make it low cost and hid it entirely
>>> from drivers.
>>>
>>> Jonathan
>>>
>> This is what I came up with (totally untested).
>>
>> Since timestamp can never be set in scan_mask/active_scan_mask, it should
>> be safe to exclude it from masklength without breaking existing code.
> Probably...
>>
>> I didn't check all callers of masklength/iio_get_masklength() though.
>
> That was the bit that made me nervous. Particularly if there is an off
> by one that is working by luck today - or someone who understood this
> oddity and did it deliberately.
>
> At one point we also had a few other timestamps - the ones come from hardware.
> I can't remember how we handled those wrt to the scan mask. I took a quick
> look and thing they are all fine.
> FWIW a nice precursor would be to make sure all timestamp channels are assigned
> using the macro. There are a few that are hand crafted. I tested a few, but obviously
> needs turning in to a proper set and cleaning up.
>
> diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c
> index 627cbf5a37b0..890e25294baa 100644
> --- a/drivers/iio/adc/ad4170-4.c
> +++ b/drivers/iio/adc/ad4170-4.c
> @@ -2385,9 +2385,7 @@ static int ad4170_parse_channels(struct iio_dev *indio_dev)
> }
>
> /* Add timestamp channel */
> - struct iio_chan_spec ts_chan = IIO_CHAN_SOFT_TIMESTAMP(chan_num);
> -
> - st->chans[chan_num] = ts_chan;
> + st->chans[chan_num] = IIO_CHAN_SOFT_TIMESTAMP(chan_num);
> num_channels = num_channels + 1;
>
> indio_dev->num_channels = num_channels;
> diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
> index 6e1930f7c65d..56baca1f5026 100644
> --- a/drivers/iio/adc/at91_adc.c
> +++ b/drivers/iio/adc/at91_adc.c
> @@ -521,13 +521,7 @@ static int at91_adc_channel_init(struct iio_dev *idev)
> }
> timestamp = chan_array + idx;
>
> - timestamp->type = IIO_TIMESTAMP;
> - timestamp->channel = -1;
> - timestamp->scan_index = idx;
> - timestamp->scan_type.sign = 's';
> - timestamp->scan_type.realbits = 64;
> - timestamp->scan_type.storagebits = 64;
> -
> + *timestamp = IIO_CHAN_SOFT_TIMESTAMP(idx);
> idev->channels = chan_array;
> return idev->num_channels;
> }
> diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
> index 2c51b90b7101..d42b747325aa 100644
> --- a/drivers/iio/adc/cc10001_adc.c
> +++ b/drivers/iio/adc/cc10001_adc.c
> @@ -262,7 +262,7 @@ static const struct iio_info cc10001_adc_info = {
> static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
> unsigned long channel_map)
> {
> - struct iio_chan_spec *chan_array, *timestamp;
> + struct iio_chan_spec *chan_array;
> unsigned int bit, idx = 0;
>
> indio_dev->num_channels = bitmap_weight(&channel_map,
> @@ -289,13 +289,7 @@ static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
> idx++;
> }
>
> - timestamp = &chan_array[idx];
> - timestamp->type = IIO_TIMESTAMP;
> - timestamp->channel = -1;
> - timestamp->scan_index = idx;
> - timestamp->scan_type.sign = 's';
> - timestamp->scan_type.realbits = 64;
> - timestamp->scan_type.storagebits = 64;
> + chan_array[idx] = IIO_CHAN_SOFT_TIMESTAMP(idx);
>
> indio_dev->channels = chan_array;
>
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index 96b05c86c325..702b2fc66326 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -353,7 +353,7 @@ static inline bool iio_channel_has_available(const struct iio_chan_spec *chan,
> (chan->info_mask_shared_by_all_available & BIT(type));
> }
>
> -#define IIO_CHAN_SOFT_TIMESTAMP(_si) { \
> +#define IIO_CHAN_SOFT_TIMESTAMP(_si) (struct iio_chan_spec) { \
> .type = IIO_TIMESTAMP, \
> .channel = -1, \
> .scan_index = _si, \
>
> Doing that will mean we can spot any unusual use of IIO_TIMESTAMP much more
> easily.
>
> Anyhow, basic approach looks good to me.
I guess you didn't see the other series cleaning up IIO_TIMESTAMP I already
sent yet.
>
> Jonathan
>
>
>
>>
>> ---
>> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
>> index 9d66510a1d49..17f539fc23e2 100644
>> --- a/drivers/iio/industrialio-buffer.c
>> +++ b/drivers/iio/industrialio-buffer.c
>> @@ -2300,8 +2300,10 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
>> if (channels) {
>> int ml = 0;
>>
>> - for (i = 0; i < indio_dev->num_channels; i++)
>> - ml = max(ml, channels[i].scan_index + 1);
>> + for (i = 0; i < indio_dev->num_channels; i++) {
>> + if (channels[i].type != IIO_TIMESTAMP)
>> + ml = max(ml, channels[i].scan_index + 1);
>> + }
>> ACCESS_PRIVATE(indio_dev, masklength) = ml;
>> }
>>
>>
>>
>>
>
^ permalink raw reply
* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Shenwei Wang @ 2026-05-18 14:24 UTC (permalink / raw)
To: Mathieu Poirier
Cc: Arnaud POULIQUEN, Beleswar Prasad Padhi, Andrew Lunn,
Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Frank Li,
Sascha Hauer, Shuah Khan, linux-gpio@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
Pengutronix Kernel Team, Fabio Estevam, Peng Fan,
devicetree@vger.kernel.org, linux-remoteproc@vger.kernel.org,
imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
dl-linux-imx, Bartosz Golaszewski
In-Reply-To: <agYHzH-nJLl1HFIn@p14s>
>
> On Thu, May 07, 2026 at 07:43:33PM +0000, Shenwei Wang wrote:
> >
> >
> > > That was my initial approach. We don't even need an additional
> > > "rpmsg-io-*" in rpmsg_gpio_channel_id_table[]. All we need is:
> > >
> > > /* rpmsg devices and drivers are matched using the service name */
> > > static inline int rpmsg_id_match(const struct rpmsg_device *rpdev,
> > > const struct rpmsg_device_id *id) {
> > > + size_t len = strnlen(id->name, RPMSG_NAME_SIZE);
> > >
> > > - return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;
> > > + return strncmp(id->name, rpdev->id.name, len) == 0;
> > > }
> > >
> >
> > If we encode the port index directly into ept->src, for example:
> >
> > ept->src = (baseaddr << 8) | port_index;
> >
>
> There is no rpmsg_endpoint::src. You likely meant ept->addr. This would work
> but not optimal on two front:
>
> (1) rpms_endpoint::addr is a u32 and idr_alloc() returns an 'int'. As such there is a
> possibility of conflict. I concede the possibility is marginal, but it still exists.
>
I think there may be a misunderstanding in the implementation. In this case, we do not
need the return value from idr_alloc.
When the driver calls rpmsg_create_ept, it can pass an rpmsg_channel_info structure as an
input parameter. This allows you to specify the source address you want to bind.
Please refer to the definitions below:
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
rpmsg_rx_cb_t cb, void *priv,
struct rpmsg_channel_info chinfo)
struct rpmsg_channel_info {
char name[RPMSG_NAME_SIZE];
u32 src;
u32 dst;
};
> (2) By proceeding this way, the kernel exposes the GPIO controller it knows
> about. It is preferrable to have the remote processor tell the kernel about the
> GPIO controller it wants.
>
If everyone agrees with this namespace announcement approach, I will prepare the
next revision based on it, even though it is not as clean as the source address encoding solution.
Shenwei
> I am done reviewing this revision. Given the amount of refactoring needed, I will
> not look at the code. Please refer to this reply [1] for what I am expecting in the
> next revision.
>
^ permalink raw reply
* Re: [PATCH v11 3/6] iio: adc: ad4691: add triggered buffer support
From: Jonathan Cameron @ 2026-05-18 14:21 UTC (permalink / raw)
To: David Lechner
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
linux-doc
In-Reply-To: <58a66855-9fb3-48ca-8cae-ff9277f745df@baylibre.com>
On Sun, 17 May 2026 14:21:30 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 5/17/26 7:25 AM, Jonathan Cameron wrote:
> > On Sat, 16 May 2026 12:32:51 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >
> >> On 5/15/26 8:31 AM, Radu Sabau via B4 Relay wrote:
> >>> From: Radu Sabau <radu.sabau@analog.com>
> >>>
> >>> Add buffered capture support using the IIO triggered buffer framework.
> >>>
> >>> CNV Burst Mode: the GP pin identified by interrupt-names in the device
> >>> tree is configured as DATA_READY output. The IRQ handler stops
> >>> conversions and fires the IIO trigger; the trigger handler executes a
> >>> pre-built SPI message that reads all active channels from the AVG_IN
> >>> accumulator registers and then resets accumulator state and restarts
> >>> conversions for the next cycle.
> >>>
> >>> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> >>> reads the previous result and starts the next conversion (pipelined
> >>> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> >>> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> >>> the pipeline). The trigger handler executes the message in a single
> >>> spi_sync() call and collects the results. An external trigger (e.g.
> >>> iio-trig-hrtimer) is required to drive the trigger at the desired
> >>> sample rate.
> >>>
> >>> Both modes share the same trigger handler and push a complete scan —
> >>> one big-endian 16-bit (__be16) slot per active channel, densely packed
> >>> in scan_index order, followed by a timestamp.
> >>>
> >>> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> >>> buffer-level attribute via IIO_DEVICE_ATTR.
> >>>
> >>> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> >
> >>> +
> >>> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> >>> +{
> >>> + struct ad4691_state *st = iio_priv(indio_dev);
> >>> + unsigned int k, i;
> >>> + int ret;
> >>> +
> >>> + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
> >>> + memset(st->scan_tx, 0, sizeof(st->scan_tx));
> >>> +
> >>> + spi_message_init(&st->scan_msg);
> >>> +
> >>> + k = 0;
> >>> + iio_for_each_active_channel(indio_dev, i) {
> >>> + if (i >= indio_dev->num_channels - 1)
> >>> + break; /* skip soft timestamp */
> >>
> >> I don't think timestamp gets set in the scan mask. It is handled separately.
> >
> > FWIW that is a sashiko false postive (I believe anyway!)
> > If we do hit this please shout as we have a core bug.
> >
> > If anyone has time to look at how hard it would be to tweak
> > iio_for_each_active_channel to skip a last element timestamp that
> > would be great.
> >
> > I think that iterates one too far which is what sashiko is tripping over.
> >
> > I'm only keen to fix that if we can make it low cost and hid it entirely
> > from drivers.
> >
> > Jonathan
> >
> This is what I came up with (totally untested).
>
> Since timestamp can never be set in scan_mask/active_scan_mask, it should
> be safe to exclude it from masklength without breaking existing code.
Probably...
>
> I didn't check all callers of masklength/iio_get_masklength() though.
That was the bit that made me nervous. Particularly if there is an off
by one that is working by luck today - or someone who understood this
oddity and did it deliberately.
At one point we also had a few other timestamps - the ones come from hardware.
I can't remember how we handled those wrt to the scan mask. I took a quick
look and thing they are all fine.
FWIW a nice precursor would be to make sure all timestamp channels are assigned
using the macro. There are a few that are hand crafted. I tested a few, but obviously
needs turning in to a proper set and cleaning up.
diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c
index 627cbf5a37b0..890e25294baa 100644
--- a/drivers/iio/adc/ad4170-4.c
+++ b/drivers/iio/adc/ad4170-4.c
@@ -2385,9 +2385,7 @@ static int ad4170_parse_channels(struct iio_dev *indio_dev)
}
/* Add timestamp channel */
- struct iio_chan_spec ts_chan = IIO_CHAN_SOFT_TIMESTAMP(chan_num);
-
- st->chans[chan_num] = ts_chan;
+ st->chans[chan_num] = IIO_CHAN_SOFT_TIMESTAMP(chan_num);
num_channels = num_channels + 1;
indio_dev->num_channels = num_channels;
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 6e1930f7c65d..56baca1f5026 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -521,13 +521,7 @@ static int at91_adc_channel_init(struct iio_dev *idev)
}
timestamp = chan_array + idx;
- timestamp->type = IIO_TIMESTAMP;
- timestamp->channel = -1;
- timestamp->scan_index = idx;
- timestamp->scan_type.sign = 's';
- timestamp->scan_type.realbits = 64;
- timestamp->scan_type.storagebits = 64;
-
+ *timestamp = IIO_CHAN_SOFT_TIMESTAMP(idx);
idev->channels = chan_array;
return idev->num_channels;
}
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
index 2c51b90b7101..d42b747325aa 100644
--- a/drivers/iio/adc/cc10001_adc.c
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -262,7 +262,7 @@ static const struct iio_info cc10001_adc_info = {
static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
unsigned long channel_map)
{
- struct iio_chan_spec *chan_array, *timestamp;
+ struct iio_chan_spec *chan_array;
unsigned int bit, idx = 0;
indio_dev->num_channels = bitmap_weight(&channel_map,
@@ -289,13 +289,7 @@ static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
idx++;
}
- timestamp = &chan_array[idx];
- timestamp->type = IIO_TIMESTAMP;
- timestamp->channel = -1;
- timestamp->scan_index = idx;
- timestamp->scan_type.sign = 's';
- timestamp->scan_type.realbits = 64;
- timestamp->scan_type.storagebits = 64;
+ chan_array[idx] = IIO_CHAN_SOFT_TIMESTAMP(idx);
indio_dev->channels = chan_array;
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 96b05c86c325..702b2fc66326 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -353,7 +353,7 @@ static inline bool iio_channel_has_available(const struct iio_chan_spec *chan,
(chan->info_mask_shared_by_all_available & BIT(type));
}
-#define IIO_CHAN_SOFT_TIMESTAMP(_si) { \
+#define IIO_CHAN_SOFT_TIMESTAMP(_si) (struct iio_chan_spec) { \
.type = IIO_TIMESTAMP, \
.channel = -1, \
.scan_index = _si, \
Doing that will mean we can spot any unusual use of IIO_TIMESTAMP much more
easily.
Anyhow, basic approach looks good to me.
Jonathan
>
> ---
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index 9d66510a1d49..17f539fc23e2 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -2300,8 +2300,10 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> if (channels) {
> int ml = 0;
>
> - for (i = 0; i < indio_dev->num_channels; i++)
> - ml = max(ml, channels[i].scan_index + 1);
> + for (i = 0; i < indio_dev->num_channels; i++) {
> + if (channels[i].type != IIO_TIMESTAMP)
> + ml = max(ml, channels[i].scan_index + 1);
> + }
> ACCESS_PRIVATE(indio_dev, masklength) = ml;
> }
>
>
>
>
^ permalink raw reply related
* Re: [PATCH v3 2/2] cpufreq: CPPC: add autonomous mode boot parameter support
From: Mario Limonciello @ 2026-05-18 14:21 UTC (permalink / raw)
To: Sumit Gupta, rafael, viresh.kumar, pierre.gondois,
ionela.voinescu, zhenglifeng1, zhanjie9, corbet, skhan, rdunlap,
linux-pm, linux-doc, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
mochs, bbasu
In-Reply-To: <e1a546f2-6e7e-4236-97bb-f72bea0137f7@nvidia.com>
On 5/18/26 09:15, Sumit Gupta wrote:
>
> On 18/05/26 19:20, Mario Limonciello wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> On 5/18/26 08:44, Sumit Gupta wrote:
>>> Hi Mario,
>>>
>>>
>>> On 16/05/26 02:43, Mario Limonciello wrote:
>>>> External email: Use caution opening links or attachments
>>>>
>>>>
>>>> On 5/15/26 07:26, Sumit Gupta wrote:
>>>>> Add a kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable
>>>>> CPPC autonomous performance selection on all CPUs at system startup.
>>>>> When autonomous mode is enabled, the hardware automatically adjusts
>>>>> CPU performance based on workload demands using Energy Performance
>>>>> Preference (EPP) hints.
>>>>>
>>>>> When the parameter is set:
>>>>> - Configure all CPUs for autonomous operation on first init
>>>>> - Use HW min/max_perf when available; otherwise initialize from caps
>>>>> - Initialize desired_perf to max_perf as a starting hint
>>>>> - Hardware controls frequency instead of the OS governor
>>>>> - EPP behavior depends on parameter value:
>>>>> - performance (or 1): override EPP to performance preference (0x0)
>>>>> - default_epp (or 2): preserve EPP value programmed by BIOS/
>>>>> firmware
>>>>>
>>>>> The boot parameter is applied only during first policy initialization.
>>>>> Skip applying it on CPU hotplug to preserve runtime sysfs
>>>>> configuration.
>>>>>
>>>>> This patch depends on patch series [1] ("cpufreq: Set policy->min and
>>>>> max as real QoS constraints") so that the policy->min/max set in
>>>>> cppc_cpufreq_cpu_init() are not overridden by cpufreq_set_policy()
>>>>> during init.
>>>>>
>>>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>>>> ---
>>>>> [1] https://lore.kernel.org/lkml/20260511135538.522653-1-
>>>>> pierre.gondois@arm.com/
>>>>> ---
>>>>> .../admin-guide/kernel-parameters.txt | 16 +++
>>>>> drivers/cpufreq/cppc_cpufreq.c | 122 +++++++++++++
>>>>> ++++-
>>>>> 2 files changed, 133 insertions(+), 5 deletions(-)
>>>>>
>>>>> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/
>>>>> Documentation/admin-guide/kernel-parameters.txt
>>>>> index 0eb64aab3685..7e4b3a8fd76f 100644
>>>>> --- a/Documentation/admin-guide/kernel-parameters.txt
>>>>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>>>>> @@ -1048,6 +1048,22 @@ Kernel parameters
>>>>> policy to use. This governor must be registered
>>>>> in the
>>>>> kernel before the cpufreq driver probes.
>>>>>
>>>>> + cppc_cpufreq.auto_sel_mode=
>>>>> + [CPU_FREQ] Enable ACPI CPPC autonomous
>>>>> performance
>>>>> + selection. When enabled, hardware automatically
>>>>> adjusts
>>>>> + CPU frequency on all CPUs based on workload
>>>>> demands.
>>>>> + In Autonomous mode, Energy Performance
>>>>> Preference (EPP)
>>>>> + hints guide hardware toward performance (0x0)
>>>>> or energy
>>>>> + efficiency (0xff).
>>>>> + Requires ACPI CPPC autonomous selection register
>>>>> + support.
>>>>> + Accepts:
>>>>> + performance, 1: enable auto_sel + set EPP to
>>>>> + performance (0x0)
>>>>> + default_epp, 2: enable auto_sel, preserve EPP
>>>>> value
>>>>> + programmed by BIOS/firmware
>>>>> + Unset: cpufreq governors are used (auto_sel
>>>>> disabled).
>>>>
>>>> Rather than unset doing nothing, have you considered having it take a
>>>> midpoint like 128? That's what we do in amd-pstate (default to
>>>> balance_performance). I think it turns into a reasonable balance.
>>>
>>> Thanks for the suggestion.
>>> I can add balance_performance that enables auto_sel with EPP=128 in v4.
>>>
>>> On changing the driver default (no param behavior) to auto enable
>>> balance_performance, it would be good to keep the current behavior for
>>> now since cppc_cpufreq is generic across ARM64/RISC-V platforms where
>>> EPP and Autonomous Selection registers are optional.
>>> A default change would affect existing users relying on governors.
>>>
>>> Thank you,
>>> Sumit Gupta
>>
>> But couldn't you make the "no module parameter set" follow the behavior
>> to only set the registers if they're available?
>>
>> So the systems that support it start using it, the ones that don't it's
>> a NOP.
>>
>
> Would it work to add balance_performance as a new mode in v4,
> and discuss changing the default separately as a follow-up?
>
Sure.
> Runtime detection helps for unsupported platforms. But platforms which
> support the registers use OS governors today, and silently switching
> them to autonomous mode on a kernel update is a behavior change for
> existing users. They would also have no way to boot into sw governor.
>
But hopefully it should be better battery life/responsiveness for those
scenarios too, right?
>
>
>>>
>>>
>>>>
>>>>> +
>>>>> cpu_init_udelay=N
>>>>> [X86,EARLY] Delay for N microsec between assert
>>>>> and de-assert
>>>>> of APIC INIT to start processors. This delay
>>>>> occurs
>>>>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/
>>>>> cppc_cpufreq.c
>>>>> index 6b54427b52e1..5f4d735e7c7d 100644
>>>>> --- a/drivers/cpufreq/cppc_cpufreq.c
>>>>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>>>>> @@ -28,6 +28,43 @@
>>>>>
>>>>> static struct cpufreq_driver cppc_cpufreq_driver;
>>>>>
>>>>> +/* Autonomous Selection boot parameter modes */
>>>>> +enum {
>>>>> + AUTO_SEL_PERFORMANCE = 1,
>>>>> + AUTO_SEL_DEFAULT_EPP = 2,
>>>>> +};
>>>>> +
>>>>> +static int auto_sel_mode;
>>>>> +
>>>>> +static int auto_sel_mode_set(const char *val, const struct
>>>>> kernel_param *kp)
>>>>> +{
>>>>> + if (sysfs_streq(val, "performance") || sysfs_streq(val, "1"))
>>>>> + *(int *)kp->arg = AUTO_SEL_PERFORMANCE;
>>>>> + else if (sysfs_streq(val, "default_epp") || sysfs_streq(val,
>>>>> "2"))
>>>>> + *(int *)kp->arg = AUTO_SEL_DEFAULT_EPP;
>>>>> + else
>>>>> + return -EINVAL;
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int auto_sel_mode_get(char *buffer, const struct kernel_param
>>>>> *kp)
>>>>> +{
>>>>> + switch (*(int *)kp->arg) {
>>>>> + case AUTO_SEL_PERFORMANCE:
>>>>> + return sysfs_emit(buffer, "performance\n");
>>>>> + case AUTO_SEL_DEFAULT_EPP:
>>>>> + return sysfs_emit(buffer, "default_epp\n");
>>>>> + default:
>>>>> + return sysfs_emit(buffer, "disabled\n");
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static const struct kernel_param_ops auto_sel_mode_ops = {
>>>>> + .set = auto_sel_mode_set,
>>>>> + .get = auto_sel_mode_get,
>>>>> +};
>>>>> +
>>>>> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>>>>> static enum {
>>>>> FIE_UNSET = -1,
>>>>> @@ -715,11 +752,75 @@ static int cppc_cpufreq_cpu_init(struct
>>>>> cpufreq_policy *policy)
>>>>> policy->cur = cppc_perf_to_khz(caps, caps->highest_perf);
>>>>> cpu_data->perf_ctrls.desired_perf = caps->highest_perf;
>>>>>
>>>>> - ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
>>>>> - if (ret) {
>>>>> - pr_debug("Err setting perf value:%d on CPU:%d. ret:
>>>>> %d\n",
>>>>> - caps->highest_perf, cpu, ret);
>>>>> - goto out;
>>>>> + /*
>>>>> + * Enable autonomous mode on first init if boot param is set.
>>>>> + * Check last_governor to detect first init and skip if auto_sel
>>>>> + * is already enabled.
>>>>> + */
>>>>> + if (auto_sel_mode && policy->last_governor[0] == '\0' &&
>>>>> + !cpu_data->perf_ctrls.auto_sel) {
>>>>> + /* Init min/max_perf from caps if not already set by
>>>>> HW. */
>>>>> + if (!cpu_data->perf_ctrls.min_perf)
>>>>> + cpu_data->perf_ctrls.min_perf = caps-
>>>>> >lowest_nonlinear_perf;
>>>>> + if (!cpu_data->perf_ctrls.max_perf)
>>>>> + cpu_data->perf_ctrls.max_perf = policy-
>>>>> >boost_enabled ?
>>>>> + caps->highest_perf : caps->nominal_perf;
>>>>> +
>>>>> + /*
>>>>> + * In autonomous mode desired_perf is only a hint;
>>>>> EPP and
>>>>> + * the platform drive actual selection within [min,
>>>>> max].
>>>>> + * Initialize it to max_perf so HW starts at the upper
>>>>> bound.
>>>>> + */
>>>>> + cpu_data->perf_ctrls.desired_perf = cpu_data-
>>>>> >perf_ctrls.max_perf;
>>>>> +
>>>>> + policy->cur = cppc_perf_to_khz(caps,
>>>>> + cpu_data->perf_ctrls.desired_perf);
>>>>> +
>>>>> + /*
>>>>> + * Override EPP only in 'performance' mode;
>>>>> 'default_epp' mode
>>>>> + * preserves the BIOS/firmware programmed EPP value.
>>>>> + * EPP is optional - some platforms may not support it.
>>>>> + */
>>>>> + if (auto_sel_mode == AUTO_SEL_PERFORMANCE) {
>>>>> + ret = cppc_set_epp(cpu,
>>>>> CPPC_EPP_PERFORMANCE_PREF);
>>>>> + if (ret && ret != -EOPNOTSUPP)
>>>>> + pr_warn("Failed to set EPP for CPU%d
>>>>> (%d)\n", cpu, ret);
>>>>> + else if (!ret)
>>>>> + cpu_data->perf_ctrls.energy_perf = CPPC_EPP_PERFORMANCE_PREF;
>>>>> + }
>>>>> +
>>>>> + /* Program min/max/desired into CPPC regs (non-fatal on
>>>>> failure). */
>>>>> + ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
>>>>> + if (ret)
>>>>> + pr_warn("set_perf failed CPU%d (%d); using HW
>>>>> values\n",
>>>>> + cpu, ret);
>>>>> +
>>>>> + ret = cppc_set_auto_sel(cpu, true);
>>>>> + if (ret && ret != -EOPNOTSUPP)
>>>>> + pr_warn("auto_sel CPU%d failed (%d); using OS
>>>>> mode\n",
>>>>> + cpu, ret);
>>>>> + else if (!ret)
>>>>> + cpu_data->perf_ctrls.auto_sel = true;
>>>>> + }
>>>>> +
>>>>> + if (cpu_data->perf_ctrls.auto_sel) {
>>>>> + /* Sync policy limits from HW when autonomous mode is
>>>>> active */
>>>>> + policy->min = cppc_perf_to_khz(caps,
>>>>> + cpu_data->perf_ctrls.min_perf ?:
>>>>> + caps->lowest_nonlinear_perf);
>>>>> + policy->max = cppc_perf_to_khz(caps,
>>>>> + cpu_data->perf_ctrls.max_perf ?:
>>>>> + (policy->boost_enabled ?
>>>>> + caps->highest_perf :
>>>>> + caps->nominal_perf));
>>>>> + } else {
>>>>> + /* Normal mode: governors control frequency */
>>>>> + ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
>>>>> + if (ret) {
>>>>> + pr_debug("Err setting perf value:%d on CPU:%d.
>>>>> ret:%d\n",
>>>>> + caps->highest_perf, cpu, ret);
>>>>> + goto out;
>>>>> + }
>>>>> }
>>>>>
>>>>> cppc_cpufreq_cpu_fie_init(policy);
>>>>> @@ -1079,10 +1180,21 @@ static int __init cppc_cpufreq_init(void)
>>>>>
>>>>> static void __exit cppc_cpufreq_exit(void)
>>>>> {
>>>>> + unsigned int cpu;
>>>>> +
>>>>> + for_each_present_cpu(cpu)
>>>>> + cppc_set_auto_sel(cpu, false);
>>>>> +
>>>>> cpufreq_unregister_driver(&cppc_cpufreq_driver);
>>>>> cppc_freq_invariance_exit();
>>>>> }
>>>>>
>>>>> +module_param_cb(auto_sel_mode, &auto_sel_mode_ops, &auto_sel_mode,
>>>>> 0444);
>>>>> +MODULE_PARM_DESC(auto_sel_mode,
>>>>> + "Enable CPPC autonomous performance selection at
>>>>> boot: "
>>>>> + "performance or 1 (EPP=performance), "
>>>>> + "default_epp or 2 (preserve BIOS/firmware EPP)");
>>>>> +
>>>>> module_exit(cppc_cpufreq_exit);
>>>>> MODULE_AUTHOR("Ashwin Chaugule");
>>>>> MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+
>>>>> spec");
>>>>
>>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox