// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2025 - Google LLC * Author: Vincent Donnefort <vdonnefort@google.com> */ #include <linux/kstrtox.h>#include <linux/lockdep.h>#include <linux/mutex.h>#include <linux/tracefs.h>#include <linux/trace_remote.h>#include <linux/trace_seq.h>#include <linux/types.h> #include "trace.h" #define TRACEFS_DIR "remotes"#define TRACEFS_MODE_WRITE 0640#define TRACEFS_MODE_READ 0440 enum tri_type { TRI_CONSUMING, TRI_NONCONSUMING,}; struct trace_remote_iterator { struct trace_remote *remote; struct trace_seq seq; struct delayed_work poll_work; unsigned long lost_events; u64 ts; struct ring_buffer_iter *rb_iter; struct ring_buffer_iter **rb_iters; struct remote_event_hdr *evt; int cpu; int evt_cpu; loff_t pos; enum tri_type type;}; struct trace_remote { struct trace_remote_callbacks *cbs; void *priv; struct trace_buffer *trace_buffer; struct trace_buffer_desc *trace_buffer_desc; struct dentry *dentry; struct eventfs_inode *eventfs; struct remote_event *events; unsigned long nr_events; unsigned long trace_buffer_size; struct ring_buffer_remote rb_remote; struct mutex lock; struct rw_semaphore reader_lock; struct rw_semaphore *pcpu_reader_locks; unsigned int nr_readers; unsigned int poll_ms; bool tracing_on;}; static bool trace_remote_loaded(struct trace_remote *remote){ return !!remote->trace_buffer;} static int trace_remote_load(struct trace_remote *remote){ struct ring_buffer_remote *rb_remote = &remote->rb_remote; struct trace_buffer_desc *desc; lockdep_assert_held(&remote->lock); if (trace_remote_loaded(remote)) return 0; desc = remote->cbs->load_trace_buffer(remote->trace_buffer_size, remote->priv); if (IS_ERR(desc)) return PTR_ERR(desc); rb_remote->desc = desc; rb_remote->swap_reader_page = remote->cbs->swap_reader_page; rb_remote->priv = remote->priv; rb_remote->reset = remote->cbs->reset; remote->trace_buffer = ring_buffer_alloc_remote(rb_remote); if (!remote->trace_buffer) { remote->cbs->unload_trace_buffer(desc, remote->priv); return -ENOMEM; } remote->trace_buffer_desc = desc; return 0;} static void trace_remote_try_unload(struct trace_remote *remote){ lockdep_assert_held(&remote->lock); if (!trace_remote_loaded(remote)) return; /* The buffer is being read or writable */ if (remote->nr_readers || remote->tracing_on) return; /* The buffer has readable data */ if (!ring_buffer_empty(remote->trace_buffer)) return; ring_buffer_free(remote->trace_buffer); remote->trace_buffer = NULL; remote->cbs->unload_trace_buffer(remote->trace_buffer_desc, remote->priv);} static int trace_remote_enable_tracing(struct trace_remote *remote){ int ret; lockdep_assert_held(&remote->lock); if (remote->tracing_on) return 0; ret = trace_remote_load(remote); if (ret) return ret; ret = remote->cbs->enable_tracing(true, remote->priv); if (ret) { trace_remote_try_unload(remote); return ret; } remote->tracing_on = true; return 0;} static int trace_remote_disable_tracing(struct trace_remote *remote){ int ret; lockdep_assert_held(&remote->lock); if (!remote->tracing_on) return 0; ret = remote->cbs->enable_tracing(false, remote->priv); if (ret) return ret; ring_buffer_poll_remote(remote->trace_buffer, RING_BUFFER_ALL_CPUS); remote->tracing_on = false; trace_remote_try_unload(remote); return 0;} static void trace_remote_reset(struct trace_remote *remote, int cpu){ lockdep_assert_held(&remote->lock); if (!trace_remote_loaded(remote)) return; if (cpu == RING_BUFFER_ALL_CPUS) ring_buffer_reset(remote->trace_buffer); else ring_buffer_reset_cpu(remote->trace_buffer, cpu); trace_remote_try_unload(remote);} static ssize_ttracing_on_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ struct seq_file *seq = filp->private_data; struct trace_remote *remote = seq->private; unsigned long val; int ret; ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret; guard(mutex)(&remote->lock); ret = val ? trace_remote_enable_tracing(remote) : trace_remote_disable_tracing(remote); if (ret) return ret; return cnt;}static int tracing_on_show(struct seq_file *s, void *unused){ struct trace_remote *remote = s->private; seq_printf(s, "%d\n", remote->tracing_on); return 0;}DEFINE_SHOW_STORE_ATTRIBUTE(tracing_on); static ssize_t buffer_size_kb_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ struct seq_file *seq = filp->private_data; struct trace_remote *remote = seq->private; unsigned long val; int ret; ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret; /* KiB to Bytes */ if (!val || check_shl_overflow(val, 10, &val)) return -EINVAL; guard(mutex)(&remote->lock); if (trace_remote_loaded(remote)) return -EBUSY; remote->trace_buffer_size = val; return cnt;} static int buffer_size_kb_show(struct seq_file *s, void *unused){ struct trace_remote *remote = s->private; seq_printf(s, "%lu (%s)\n", remote->trace_buffer_size >> 10, trace_remote_loaded(remote) ? "loaded" : "unloaded"); return 0;}DEFINE_SHOW_STORE_ATTRIBUTE(buffer_size_kb); static int trace_remote_get(struct trace_remote *remote, int cpu){ int ret; if (remote->nr_readers == UINT_MAX) return -EBUSY; ret = trace_remote_load(remote); if (ret) return ret; if (cpu != RING_BUFFER_ALL_CPUS && !remote->pcpu_reader_locks) { int lock_cpu; remote->pcpu_reader_locks = kzalloc_objs(*remote->pcpu_reader_locks, nr_cpu_ids); if (!remote->pcpu_reader_locks) { trace_remote_try_unload(remote); return -ENOMEM; } for_each_possible_cpu(lock_cpu) init_rwsem(&remote->pcpu_reader_locks[lock_cpu]); } remote->nr_readers++; return 0;} static void trace_remote_put(struct trace_remote *remote){ if (WARN_ON(!remote->nr_readers)) return; remote->nr_readers--; if (remote->nr_readers) return; kfree(remote->pcpu_reader_locks); remote->pcpu_reader_locks = NULL; trace_remote_try_unload(remote);} static bool trace_remote_has_cpu(struct trace_remote *remote, int cpu){ if (cpu == RING_BUFFER_ALL_CPUS) return true; return ring_buffer_poll_remote(remote->trace_buffer, cpu) == 0;} static void __poll_remote(struct work_struct *work){ struct delayed_work *dwork = to_delayed_work(work); struct trace_remote_iterator *iter; iter = container_of(dwork, struct trace_remote_iterator, poll_work); ring_buffer_poll_remote(iter->remote->trace_buffer, iter->cpu); schedule_delayed_work((struct delayed_work *)work, msecs_to_jiffies(iter->remote->poll_ms));} static void __free_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu){ if (cpu != RING_BUFFER_ALL_CPUS) { ring_buffer_read_finish(iter->rb_iter); return; } for_each_possible_cpu(cpu) { if (iter->rb_iters[cpu]) ring_buffer_read_finish(iter->rb_iters[cpu]); } kfree(iter->rb_iters);} static int __alloc_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu){ if (cpu != RING_BUFFER_ALL_CPUS) { iter->rb_iter = ring_buffer_read_start(iter->remote->trace_buffer, cpu, GFP_KERNEL); return iter->rb_iter ? 0 : -ENOMEM; } iter->rb_iters = kzalloc_objs(*iter->rb_iters, nr_cpu_ids); if (!iter->rb_iters) return -ENOMEM; for_each_possible_cpu(cpu) { iter->rb_iters[cpu] = ring_buffer_read_start(iter->remote->trace_buffer, cpu, GFP_KERNEL); if (!iter->rb_iters[cpu]) { /* This CPU isn't part of trace_buffer. Skip it */ if (!trace_remote_has_cpu(iter->remote, cpu)) continue; __free_ring_buffer_iter(iter, RING_BUFFER_ALL_CPUS); return -ENOMEM; } } return 0;} static struct trace_remote_iterator*trace_remote_iter(struct trace_remote *remote, int cpu, enum tri_type type){ struct trace_remote_iterator *iter = NULL; int ret; lockdep_assert_held(&remote->lock); if (type == TRI_NONCONSUMING && !trace_remote_loaded(remote)) return NULL; ret = trace_remote_get(remote, cpu); if (ret) return ERR_PTR(ret); if (!trace_remote_has_cpu(remote, cpu)) { ret = -ENODEV; goto err; } iter = kzalloc_obj(*iter); if (iter) { iter->remote = remote; iter->cpu = cpu; iter->type = type; trace_seq_init(&iter->seq); switch (type) { case TRI_CONSUMING: ring_buffer_poll_remote(remote->trace_buffer, cpu); INIT_DELAYED_WORK(&iter->poll_work, __poll_remote); schedule_delayed_work(&iter->poll_work, msecs_to_jiffies(remote->poll_ms)); break; case TRI_NONCONSUMING: ret = __alloc_ring_buffer_iter(iter, cpu); break; } if (ret) goto err; return iter; } ret = -ENOMEM; err: kfree(iter); trace_remote_put(remote); return ERR_PTR(ret);} static void trace_remote_iter_free(struct trace_remote_iterator *iter){ struct trace_remote *remote; if (!iter) return; remote = iter->remote; lockdep_assert_held(&remote->lock); switch (iter->type) { case TRI_CONSUMING: cancel_delayed_work_sync(&iter->poll_work); break; case TRI_NONCONSUMING: __free_ring_buffer_iter(iter, iter->cpu); break; } kfree(iter); trace_remote_put(remote);} static void trace_remote_iter_read_start(struct trace_remote_iterator *iter){ struct trace_remote *remote = iter->remote; int cpu = iter->cpu; /* Acquire global reader lock */ if (cpu == RING_BUFFER_ALL_CPUS && iter->type == TRI_CONSUMING) down_write(&remote->reader_lock); else down_read(&remote->reader_lock); if (cpu == RING_BUFFER_ALL_CPUS) return; /* * No need for the remote lock here, iter holds a reference on * remote->nr_readers */ /* Get the per-CPU one */ if (WARN_ON_ONCE(!remote->pcpu_reader_locks)) return; if (iter->type == TRI_CONSUMING) down_write(&remote->pcpu_reader_locks[cpu]); else down_read(&remote->pcpu_reader_locks[cpu]);} static void trace_remote_iter_read_finished(struct trace_remote_iterator *iter){ struct trace_remote *remote = iter->remote; int cpu = iter->cpu; /* Release per-CPU reader lock */ if (cpu != RING_BUFFER_ALL_CPUS) { /* * No need for the remote lock here, iter holds a reference on * remote->nr_readers */ if (iter->type == TRI_CONSUMING) up_write(&remote->pcpu_reader_locks[cpu]); else up_read(&remote->pcpu_reader_locks[cpu]); } /* Release global reader lock */ if (cpu == RING_BUFFER_ALL_CPUS && iter->type == TRI_CONSUMING) up_write(&remote->reader_lock); else up_read(&remote->reader_lock);} static struct ring_buffer_iter *__get_rb_iter(struct trace_remote_iterator *iter, int cpu){ return iter->cpu != RING_BUFFER_ALL_CPUS ? iter->rb_iter : iter->rb_iters[cpu];} static struct ring_buffer_event *__peek_event(struct trace_remote_iterator *iter, int cpu, u64 *ts, unsigned long *lost_events){ struct ring_buffer_event *rb_evt; struct ring_buffer_iter *rb_iter; switch (iter->type) { case TRI_CONSUMING: return ring_buffer_peek(iter->remote->trace_buffer, cpu, ts, lost_events); case TRI_NONCONSUMING: rb_iter = __get_rb_iter(iter, cpu); if (!rb_iter) return NULL; rb_evt = ring_buffer_iter_peek(rb_iter, ts); if (!rb_evt) return NULL; *lost_events = ring_buffer_iter_dropped(rb_iter); Lines 1–500 of 1388. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2025 - Google LLC * Author: Vincent Donnefort <vdonnefort@google.com> */ #include <linux/kstrtox.h> #include <linux/lockdep.h> #include <linux/mutex.h> #include <linux/tracefs.h> #include <linux/trace_remote.h> #include <linux/trace_seq.h> #include <linux/types.h> #include "trace.h" #define TRACEFS_DIR "remotes" #define TRACEFS_MODE_WRITE 0640 #define TRACEFS_MODE_READ 0440 enum tri_type { TRI_CONSUMING, TRI_NONCONSUMING, }; struct trace_remote_iterator { struct trace_remote *remote; struct trace_seq seq; struct delayed_work poll_work; unsigned long lost_events; u64 ts; struct ring_buffer_iter *rb_iter; struct ring_buffer_iter **rb_iters; struct remote_event_hdr *evt; int cpu; int evt_cpu; loff_t pos; enum tri_type type; }; struct trace_remote { struct trace_remote_callbacks *cbs; void *priv; struct trace_buffer *trace_buffer; struct trace_buffer_desc *trace_buffer_desc; struct dentry *dentry; struct eventfs_inode *eventfs; struct remote_event *events; unsigned long nr_events; unsigned long trace_buffer_size; struct ring_buffer_remote rb_remote; struct mutex lock; struct rw_semaphore reader_lock; struct rw_semaphore *pcpu_reader_locks; unsigned int nr_readers; unsigned int poll_ms; bool tracing_on; }; static bool trace_remote_loaded(struct trace_remote *remote) { return !!remote->trace_buffer; } static int trace_remote_load(struct trace_remote *remote) { struct ring_buffer_remote *rb_remote = &remote->rb_remote; struct trace_buffer_desc *desc; lockdep_assert_held(&remote->lock); if (trace_remote_loaded(remote)) return 0; desc = remote->cbs->load_trace_buffer(remote->trace_buffer_size, remote->priv); if (IS_ERR(desc)) return PTR_ERR(desc); rb_remote->desc = desc; rb_remote->swap_reader_page = remote->cbs->swap_reader_page; rb_remote->priv = remote->priv; rb_remote->reset = remote->cbs->reset; remote->trace_buffer = ring_buffer_alloc_remote(rb_remote); if (!remote->trace_buffer) { remote->cbs->unload_trace_buffer(desc, remote->priv); return -ENOMEM; } remote->trace_buffer_desc = desc; return 0; } static void trace_remote_try_unload(struct trace_remote *remote) { lockdep_assert_held(&remote->lock); if (!trace_remote_loaded(remote)) return; /* The buffer is being read or writable */ if (remote->nr_readers || remote->tracing_on) return; /* The buffer has readable data */ if (!ring_buffer_empty(remote->trace_buffer)) return; ring_buffer_free(remote->trace_buffer); remote->trace_buffer = NULL; remote->cbs->unload_trace_buffer(remote->trace_buffer_desc, remote->priv); } static int trace_remote_enable_tracing(struct trace_remote *remote) { int ret; lockdep_assert_held(&remote->lock); if (remote->tracing_on) return 0; ret = trace_remote_load(remote); if (ret) return ret; ret = remote->cbs->enable_tracing(true, remote->priv); if (ret) { trace_remote_try_unload(remote); return ret; } remote->tracing_on = true; return 0; } static int trace_remote_disable_tracing(struct trace_remote *remote) { int ret; lockdep_assert_held(&remote->lock); if (!remote->tracing_on) return 0; ret = remote->cbs->enable_tracing(false, remote->priv); if (ret) return ret; ring_buffer_poll_remote(remote->trace_buffer, RING_BUFFER_ALL_CPUS); remote->tracing_on = false; trace_remote_try_unload(remote); return 0; } static void trace_remote_reset(struct trace_remote *remote, int cpu) { lockdep_assert_held(&remote->lock); if (!trace_remote_loaded(remote)) return; if (cpu == RING_BUFFER_ALL_CPUS) ring_buffer_reset(remote->trace_buffer); else ring_buffer_reset_cpu(remote->trace_buffer, cpu); trace_remote_try_unload(remote); } static ssize_t tracing_on_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct seq_file *seq = filp->private_data; struct trace_remote *remote = seq->private; unsigned long val; int ret; ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret; guard(mutex)(&remote->lock); ret = val ? trace_remote_enable_tracing(remote) : trace_remote_disable_tracing(remote); if (ret) return ret; return cnt; } static int tracing_on_show(struct seq_file *s, void *unused) { struct trace_remote *remote = s->private; seq_printf(s, "%d\n", remote->tracing_on); return 0; } DEFINE_SHOW_STORE_ATTRIBUTE(tracing_on); static ssize_t buffer_size_kb_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct seq_file *seq = filp->private_data; struct trace_remote *remote = seq->private; unsigned long val; int ret; ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret; /* KiB to Bytes */ if (!val || check_shl_overflow(val, 10, &val)) return -EINVAL; guard(mutex)(&remote->lock); if (trace_remote_loaded(remote)) return -EBUSY; remote->trace_buffer_size = val; return cnt; } static int buffer_size_kb_show(struct seq_file *s, void *unused) { struct trace_remote *remote = s->private; seq_printf(s, "%lu (%s)\n", remote->trace_buffer_size >> 10, trace_remote_loaded(remote) ? "loaded" : "unloaded"); return 0; } DEFINE_SHOW_STORE_ATTRIBUTE(buffer_size_kb); static int trace_remote_get(struct trace_remote *remote, int cpu) { int ret; if (remote->nr_readers == UINT_MAX) return -EBUSY; ret = trace_remote_load(remote); if (ret) return ret; if (cpu != RING_BUFFER_ALL_CPUS && !remote->pcpu_reader_locks) { int lock_cpu; remote->pcpu_reader_locks = kzalloc_objs(*remote->pcpu_reader_locks, nr_cpu_ids); if (!remote->pcpu_reader_locks) { trace_remote_try_unload(remote); return -ENOMEM; } for_each_possible_cpu(lock_cpu) init_rwsem(&remote->pcpu_reader_locks[lock_cpu]); } remote->nr_readers++; return 0; } static void trace_remote_put(struct trace_remote *remote) { if (WARN_ON(!remote->nr_readers)) return; remote->nr_readers--; if (remote->nr_readers) return; kfree(remote->pcpu_reader_locks); remote->pcpu_reader_locks = NULL; trace_remote_try_unload(remote); } static bool trace_remote_has_cpu(struct trace_remote *remote, int cpu) { if (cpu == RING_BUFFER_ALL_CPUS) return true; return ring_buffer_poll_remote(remote->trace_buffer, cpu) == 0; } static void __poll_remote(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct trace_remote_iterator *iter; iter = container_of(dwork, struct trace_remote_iterator, poll_work); ring_buffer_poll_remote(iter->remote->trace_buffer, iter->cpu); schedule_delayed_work((struct delayed_work *)work, msecs_to_jiffies(iter->remote->poll_ms)); } static void __free_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu) { if (cpu != RING_BUFFER_ALL_CPUS) { ring_buffer_read_finish(iter->rb_iter); return; } for_each_possible_cpu(cpu) { if (iter->rb_iters[cpu]) ring_buffer_read_finish(iter->rb_iters[cpu]); } kfree(iter->rb_iters); } static int __alloc_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu) { if (cpu != RING_BUFFER_ALL_CPUS) { iter->rb_iter = ring_buffer_read_start(iter->remote->trace_buffer, cpu, GFP_KERNEL); return iter->rb_iter ? 0 : -ENOMEM; } iter->rb_iters = kzalloc_objs(*iter->rb_iters, nr_cpu_ids); if (!iter->rb_iters) return -ENOMEM; for_each_possible_cpu(cpu) { iter->rb_iters[cpu] = ring_buffer_read_start(iter->remote->trace_buffer, cpu, GFP_KERNEL); if (!iter->rb_iters[cpu]) { /* This CPU isn't part of trace_buffer. Skip it */ if (!trace_remote_has_cpu(iter->remote, cpu)) continue; __free_ring_buffer_iter(iter, RING_BUFFER_ALL_CPUS); return -ENOMEM; } } return 0; } static struct trace_remote_iterator *trace_remote_iter(struct trace_remote *remote, int cpu, enum tri_type type) { struct trace_remote_iterator *iter = NULL; int ret; lockdep_assert_held(&remote->lock); if (type == TRI_NONCONSUMING && !trace_remote_loaded(remote)) return NULL; ret = trace_remote_get(remote, cpu); if (ret) return ERR_PTR(ret); if (!trace_remote_has_cpu(remote, cpu)) { ret = -ENODEV; goto err; } iter = kzalloc_obj(*iter); if (iter) { iter->remote = remote; iter->cpu = cpu; iter->type = type; trace_seq_init(&iter->seq); switch (type) { case TRI_CONSUMING: ring_buffer_poll_remote(remote->trace_buffer, cpu); INIT_DELAYED_WORK(&iter->poll_work, __poll_remote); schedule_delayed_work(&iter->poll_work, msecs_to_jiffies(remote->poll_ms)); break; case TRI_NONCONSUMING: ret = __alloc_ring_buffer_iter(iter, cpu); break; } if (ret) goto err; return iter; } ret = -ENOMEM; err: kfree(iter); trace_remote_put(remote); return ERR_PTR(ret); } static void trace_remote_iter_free(struct trace_remote_iterator *iter) { struct trace_remote *remote; if (!iter) return; remote = iter->remote; lockdep_assert_held(&remote->lock); switch (iter->type) { case TRI_CONSUMING: cancel_delayed_work_sync(&iter->poll_work); break; case TRI_NONCONSUMING: __free_ring_buffer_iter(iter, iter->cpu); break; } kfree(iter); trace_remote_put(remote); } static void trace_remote_iter_read_start(struct trace_remote_iterator *iter) { struct trace_remote *remote = iter->remote; int cpu = iter->cpu; /* Acquire global reader lock */ if (cpu == RING_BUFFER_ALL_CPUS && iter->type == TRI_CONSUMING) down_write(&remote->reader_lock); else down_read(&remote->reader_lock); if (cpu == RING_BUFFER_ALL_CPUS) return; /* * No need for the remote lock here, iter holds a reference on * remote->nr_readers */ /* Get the per-CPU one */ if (WARN_ON_ONCE(!remote->pcpu_reader_locks)) return; if (iter->type == TRI_CONSUMING) down_write(&remote->pcpu_reader_locks[cpu]); else down_read(&remote->pcpu_reader_locks[cpu]); } static void trace_remote_iter_read_finished(struct trace_remote_iterator *iter) { struct trace_remote *remote = iter->remote; int cpu = iter->cpu; /* Release per-CPU reader lock */ if (cpu != RING_BUFFER_ALL_CPUS) { /* * No need for the remote lock here, iter holds a reference on * remote->nr_readers */ if (iter->type == TRI_CONSUMING) up_write(&remote->pcpu_reader_locks[cpu]); else up_read(&remote->pcpu_reader_locks[cpu]); } /* Release global reader lock */ if (cpu == RING_BUFFER_ALL_CPUS && iter->type == TRI_CONSUMING) up_write(&remote->reader_lock); else up_read(&remote->reader_lock); } static struct ring_buffer_iter *__get_rb_iter(struct trace_remote_iterator *iter, int cpu) { return iter->cpu != RING_BUFFER_ALL_CPUS ? iter->rb_iter : iter->rb_iters[cpu]; } static struct ring_buffer_event * __peek_event(struct trace_remote_iterator *iter, int cpu, u64 *ts, unsigned long *lost_events) { struct ring_buffer_event *rb_evt; struct ring_buffer_iter *rb_iter; switch (iter->type) { case TRI_CONSUMING: return ring_buffer_peek(iter->remote->trace_buffer, cpu, ts, lost_events); case TRI_NONCONSUMING: rb_iter = __get_rb_iter(iter, cpu); if (!rb_iter) return NULL; rb_evt = ring_buffer_iter_peek(rb_iter, ts); if (!rb_evt) return NULL; *lost_events = ring_buffer_iter_dropped(rb_iter);