1066 lines · 26.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/fsnotify.h> #include <asm/setup.h> /* COMMAND_LINE_SIZE */ #include "trace.h" /* Used if snapshot allocated at boot */static bool allocate_snapshot;static bool snapshot_at_boot; static char boot_snapshot_info[COMMAND_LINE_SIZE] __initdata;static int boot_snapshot_index; static int __init boot_alloc_snapshot(char *str){	char *slot = boot_snapshot_info + boot_snapshot_index;	int left = sizeof(boot_snapshot_info) - boot_snapshot_index;	int ret; 	if (str[0] == '=') {		str++;		if (strlen(str) >= left)			return -1; 		ret = snprintf(slot, left, "%s\t", str);		boot_snapshot_index += ret;	} else {		allocate_snapshot = true;		/* We also need the main ring buffer expanded */		trace_set_ring_buffer_expanded(NULL);	}	return 1;}__setup("alloc_snapshot", boot_alloc_snapshot);  static int __init boot_snapshot(char *str){	snapshot_at_boot = true;	boot_alloc_snapshot(str);	return 1;}__setup("ftrace_boot_snapshot", boot_snapshot);static void tracing_snapshot_instance_cond(struct trace_array *tr,					   void *cond_data){	unsigned long flags; 	if (in_nmi()) {		trace_array_puts(tr, "*** SNAPSHOT CALLED FROM NMI CONTEXT ***\n");		trace_array_puts(tr, "*** snapshot is being ignored        ***\n");		return;	} 	if (!tr->allocated_snapshot) {		trace_array_puts(tr, "*** SNAPSHOT NOT ALLOCATED ***\n");		trace_array_puts(tr, "*** stopping trace here!   ***\n");		tracer_tracing_off(tr);		return;	} 	if (tr->mapped) {		trace_array_puts(tr, "*** BUFFER MEMORY MAPPED ***\n");		trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n");		return;	} 	/* Note, snapshot can not be used when the tracer uses it */	if (tracer_uses_snapshot(tr->current_trace)) {		trace_array_puts(tr, "*** LATENCY TRACER ACTIVE ***\n");		trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n");		return;	} 	local_irq_save(flags);	update_max_tr(tr, current, smp_processor_id(), cond_data);	local_irq_restore(flags);} void tracing_snapshot_instance(struct trace_array *tr){	tracing_snapshot_instance_cond(tr, NULL);} /** * tracing_snapshot_cond - conditionally take a snapshot of the current buffer. * @tr:		The tracing instance to snapshot * @cond_data:	The data to be tested conditionally, and possibly saved * * This is the same as tracing_snapshot() except that the snapshot is * conditional - the snapshot will only happen if the * cond_snapshot.update() implementation receiving the cond_data * returns true, which means that the trace array's cond_snapshot * update() operation used the cond_data to determine whether the * snapshot should be taken, and if it was, presumably saved it along * with the snapshot. */void tracing_snapshot_cond(struct trace_array *tr, void *cond_data){	tracing_snapshot_instance_cond(tr, cond_data);}EXPORT_SYMBOL_GPL(tracing_snapshot_cond); /** * tracing_cond_snapshot_data - get the user data associated with a snapshot * @tr:		The tracing instance * * When the user enables a conditional snapshot using * tracing_snapshot_cond_enable(), the user-defined cond_data is saved * with the snapshot.  This accessor is used to retrieve it. * * Should not be called from cond_snapshot.update(), since it takes * the tr->max_lock lock, which the code calling * cond_snapshot.update() has already done. * * Returns the cond_data associated with the trace array's snapshot. */void *tracing_cond_snapshot_data(struct trace_array *tr){	void *cond_data = NULL; 	local_irq_disable();	arch_spin_lock(&tr->max_lock); 	if (tr->cond_snapshot)		cond_data = tr->cond_snapshot->cond_data; 	arch_spin_unlock(&tr->max_lock);	local_irq_enable(); 	return cond_data;}EXPORT_SYMBOL_GPL(tracing_cond_snapshot_data); /* resize @tr's buffer to the size of @size_tr's entries */int resize_buffer_duplicate_size(struct array_buffer *trace_buf,				 struct array_buffer *size_buf, int cpu_id){	int cpu, ret = 0; 	if (cpu_id == RING_BUFFER_ALL_CPUS) {		for_each_tracing_cpu(cpu) {			ret = ring_buffer_resize(trace_buf->buffer,				 per_cpu_ptr(size_buf->data, cpu)->entries, cpu);			if (ret < 0)				break;			per_cpu_ptr(trace_buf->data, cpu)->entries =				per_cpu_ptr(size_buf->data, cpu)->entries;		}	} else {		ret = ring_buffer_resize(trace_buf->buffer,				 per_cpu_ptr(size_buf->data, cpu_id)->entries, cpu_id);		if (ret == 0)			per_cpu_ptr(trace_buf->data, cpu_id)->entries =				per_cpu_ptr(size_buf->data, cpu_id)->entries;	} 	return ret;} int tracing_alloc_snapshot_instance(struct trace_array *tr){	int order;	int ret; 	if (!tr->allocated_snapshot) { 		/* Make the snapshot buffer have the same order as main buffer */		order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer);		ret = ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, order);		if (ret < 0)			return ret; 		/* allocate spare buffer */		ret = resize_buffer_duplicate_size(&tr->snapshot_buffer,				   &tr->array_buffer, RING_BUFFER_ALL_CPUS);		if (ret < 0)			return ret; 		tr->allocated_snapshot = true;	} 	return 0;} void free_snapshot(struct trace_array *tr){	/*	 * We don't free the ring buffer. instead, resize it because	 * The max_tr ring buffer has some state (e.g. ring->clock) and	 * we want preserve it.	 */	ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, 0);	ring_buffer_resize(tr->snapshot_buffer.buffer, 1, RING_BUFFER_ALL_CPUS);	trace_set_buffer_entries(&tr->snapshot_buffer, 1);	tracing_reset_online_cpus(&tr->snapshot_buffer);	tr->allocated_snapshot = false;} int tracing_arm_snapshot_locked(struct trace_array *tr){	int ret; 	lockdep_assert_held(&trace_types_lock); 	spin_lock(&tr->snapshot_trigger_lock);	if (tr->snapshot == UINT_MAX || tr->mapped) {		spin_unlock(&tr->snapshot_trigger_lock);		return -EBUSY;	} 	tr->snapshot++;	spin_unlock(&tr->snapshot_trigger_lock); 	ret = tracing_alloc_snapshot_instance(tr);	if (ret) {		spin_lock(&tr->snapshot_trigger_lock);		tr->snapshot--;		spin_unlock(&tr->snapshot_trigger_lock);	} 	return ret;} int tracing_arm_snapshot(struct trace_array *tr){	guard(mutex)(&trace_types_lock);	return tracing_arm_snapshot_locked(tr);} void tracing_disarm_snapshot(struct trace_array *tr){	spin_lock(&tr->snapshot_trigger_lock);	if (!WARN_ON(!tr->snapshot))		tr->snapshot--;	spin_unlock(&tr->snapshot_trigger_lock);} /** * tracing_snapshot_alloc - allocate and take a snapshot of the current buffer. * * This is similar to tracing_snapshot(), but it will allocate the * snapshot buffer if it isn't already allocated. Use this only * where it is safe to sleep, as the allocation may sleep. * * This causes a swap between the snapshot buffer and the current live * tracing buffer. You can use this to take snapshots of the live * trace when some condition is triggered, but continue to trace. */void tracing_snapshot_alloc(void){	int ret; 	ret = tracing_alloc_snapshot();	if (ret < 0)		return; 	tracing_snapshot();}EXPORT_SYMBOL_GPL(tracing_snapshot_alloc); /** * tracing_snapshot_cond_enable - enable conditional snapshot for an instance * @tr:		The tracing instance * @cond_data:	User data to associate with the snapshot * @update:	Implementation of the cond_snapshot update function * * Check whether the conditional snapshot for the given instance has * already been enabled, or if the current tracer is already using a * snapshot; if so, return -EBUSY, else create a cond_snapshot and * save the cond_data and update function inside. * * Returns 0 if successful, error otherwise. */int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data,				 cond_update_fn_t update){	struct cond_snapshot *cond_snapshot __free(kfree) =		kzalloc_obj(*cond_snapshot);	int ret; 	if (!cond_snapshot)		return -ENOMEM; 	cond_snapshot->cond_data = cond_data;	cond_snapshot->update = update; 	guard(mutex)(&trace_types_lock); 	if (tracer_uses_snapshot(tr->current_trace))		return -EBUSY; 	/*	 * The cond_snapshot can only change to NULL without the	 * trace_types_lock. We don't care if we race with it going	 * to NULL, but we want to make sure that it's not set to	 * something other than NULL when we get here, which we can	 * do safely with only holding the trace_types_lock and not	 * having to take the max_lock.	 */	if (tr->cond_snapshot)		return -EBUSY; 	ret = tracing_arm_snapshot_locked(tr);	if (ret)		return ret; 	local_irq_disable();	arch_spin_lock(&tr->max_lock);	tr->cond_snapshot = no_free_ptr(cond_snapshot);	arch_spin_unlock(&tr->max_lock);	local_irq_enable(); 	return 0;}EXPORT_SYMBOL_GPL(tracing_snapshot_cond_enable); /** * tracing_snapshot_cond_disable - disable conditional snapshot for an instance * @tr:		The tracing instance * * Check whether the conditional snapshot for the given instance is * enabled; if so, free the cond_snapshot associated with it, * otherwise return -EINVAL. * * Returns 0 if successful, error otherwise. */int tracing_snapshot_cond_disable(struct trace_array *tr){	int ret = 0; 	local_irq_disable();	arch_spin_lock(&tr->max_lock); 	if (!tr->cond_snapshot)		ret = -EINVAL;	else {		kfree(tr->cond_snapshot);		tr->cond_snapshot = NULL;	} 	arch_spin_unlock(&tr->max_lock);	local_irq_enable(); 	tracing_disarm_snapshot(tr); 	return ret;}EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable); #ifdef CONFIG_TRACER_MAX_TRACE#ifdef LATENCY_FS_NOTIFYstatic struct workqueue_struct *fsnotify_wq; static void latency_fsnotify_workfn(struct work_struct *work){	struct trace_array *tr = container_of(work, struct trace_array,					      fsnotify_work);	fsnotify_inode(tr->d_max_latency->d_inode, FS_MODIFY);} static void latency_fsnotify_workfn_irq(struct irq_work *iwork){	struct trace_array *tr = container_of(iwork, struct trace_array,					      fsnotify_irqwork);	queue_work(fsnotify_wq, &tr->fsnotify_work);} __init static int latency_fsnotify_init(void){	fsnotify_wq = alloc_workqueue("tr_max_lat_wq",				      WQ_UNBOUND | WQ_HIGHPRI, 0);	if (!fsnotify_wq) {		pr_err("Unable to allocate tr_max_lat_wq\n");		return -ENOMEM;	}	return 0;} late_initcall_sync(latency_fsnotify_init); void latency_fsnotify(struct trace_array *tr){	if (!fsnotify_wq)		return;	/*	 * We cannot call queue_work(&tr->fsnotify_work) from here because it's	 * possible that we are called from __schedule() or do_idle(), which	 * could cause a deadlock.	 */	irq_work_queue(&tr->fsnotify_irqwork);}#endif /* LATENCY_FS_NOTIFY */ static const struct file_operations tracing_max_lat_fops; void trace_create_maxlat_file(struct trace_array *tr,			      struct dentry *d_tracer){#ifdef LATENCY_FS_NOTIFY	INIT_WORK(&tr->fsnotify_work, latency_fsnotify_workfn);	init_irq_work(&tr->fsnotify_irqwork, latency_fsnotify_workfn_irq);#endif	tr->d_max_latency = trace_create_file("tracing_max_latency",					      TRACE_MODE_WRITE,					      d_tracer, tr,					      &tracing_max_lat_fops);} /* * Copy the new maximum trace into the separate maximum-trace * structure. (this way the maximum trace is permanently saved, * for later retrieval via /sys/kernel/tracing/tracing_max_latency) */static void__update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu){	struct array_buffer *trace_buf = &tr->array_buffer;	struct trace_array_cpu *data = per_cpu_ptr(trace_buf->data, cpu);	struct array_buffer *max_buf = &tr->snapshot_buffer;	struct trace_array_cpu *max_data = per_cpu_ptr(max_buf->data, cpu); 	max_buf->cpu = cpu;	max_buf->time_start = data->preempt_timestamp; 	max_data->saved_latency = tr->max_latency;	max_data->critical_start = data->critical_start;	max_data->critical_end = data->critical_end; 	strscpy(max_data->comm, tsk->comm);	max_data->pid = tsk->pid;	/*	 * If tsk == current, then use current_uid(), as that does not use	 * RCU. The irq tracer can be called out of RCU scope.	 */	if (tsk == current)		max_data->uid = current_uid();	else		max_data->uid = task_uid(tsk); 	max_data->nice = tsk->static_prio - 20 - MAX_RT_PRIO;	max_data->policy = tsk->policy;	max_data->rt_priority = tsk->rt_priority; 	/* record this tasks comm */	tracing_record_cmdline(tsk);	latency_fsnotify(tr);}#elsestatic inline void __update_max_tr(struct trace_array *tr,				   struct task_struct *tsk, int cpu) { }#endif /* CONFIG_TRACER_MAX_TRACE */ /** * update_max_tr - snapshot all trace buffers from global_trace to max_tr * @tr: tracer * @tsk: the task with the latency * @cpu: The cpu that initiated the trace. * @cond_data: User data associated with a conditional snapshot * * Flip the buffers between the @tr and the max_tr and record information * about which task was the cause of this latency. */voidupdate_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu,	      void *cond_data){	if (tr->stop_count)		return; 	WARN_ON_ONCE(!irqs_disabled()); 	if (!tr->allocated_snapshot) {		/* Only the nop tracer should hit this when disabling */		WARN_ON_ONCE(tr->current_trace != &nop_trace);		return;	} 	arch_spin_lock(&tr->max_lock); 	/* Inherit the recordable setting from array_buffer */	if (ring_buffer_record_is_set_on(tr->array_buffer.buffer))		ring_buffer_record_on(tr->snapshot_buffer.buffer);	else		ring_buffer_record_off(tr->snapshot_buffer.buffer); 	if (tr->cond_snapshot && !tr->cond_snapshot->update(tr, cond_data)) {		arch_spin_unlock(&tr->max_lock);		return;	} 	swap(tr->array_buffer.buffer, tr->snapshot_buffer.buffer); 	__update_max_tr(tr, tsk, cpu); 	arch_spin_unlock(&tr->max_lock); 	/* Any waiters on the old snapshot buffer need to wake up */	ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS);

Lines 1–500 of 1066.