1384 lines · 31.2 KB · C
RawDownload
// 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 = kcalloc(nr_cpu_ids, sizeof(*remote->pcpu_reader_locks),						    GFP_KERNEL);		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 = kcalloc(nr_cpu_ids, sizeof(*iter->rb_iters), GFP_KERNEL);	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 1384.