📄 relay.c
字号:
if (chan->has_base_filename) { dentry = relay_create_buf_file(chan, buf, cpu); if (!dentry) goto free_buf; relay_set_buf_dentry(buf, dentry); } buf->cpu = cpu; __relay_reset(buf, 1); if(chan->is_global) { chan->buf[0] = buf; buf->cpu = 0; } return buf;free_buf: relay_destroy_buf(buf); return NULL;}/** * relay_close_buf - close a channel buffer * @buf: channel buffer * * Marks the buffer finalized and restores the default callbacks. * The channel buffer and channel buffer data structure are then freed * automatically when the last reference is given up. */static void relay_close_buf(struct rchan_buf *buf){ buf->finalized = 1; del_timer_sync(&buf->timer); kref_put(&buf->kref, relay_remove_buf);}static void setup_callbacks(struct rchan *chan, struct rchan_callbacks *cb){ if (!cb) { chan->cb = &default_channel_callbacks; return; } if (!cb->subbuf_start) cb->subbuf_start = subbuf_start_default_callback; if (!cb->buf_mapped) cb->buf_mapped = buf_mapped_default_callback; if (!cb->buf_unmapped) cb->buf_unmapped = buf_unmapped_default_callback; if (!cb->create_buf_file) cb->create_buf_file = create_buf_file_default_callback; if (!cb->remove_buf_file) cb->remove_buf_file = remove_buf_file_default_callback; chan->cb = cb;}/** * relay_hotcpu_callback - CPU hotplug callback * @nb: notifier block * @action: hotplug action to take * @hcpu: CPU number * * Returns the success/failure of the operation. (%NOTIFY_OK, %NOTIFY_BAD) */static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu){ unsigned int hotcpu = (unsigned long)hcpu; struct rchan *chan; switch(action) { case CPU_UP_PREPARE: case CPU_UP_PREPARE_FROZEN: mutex_lock(&relay_channels_mutex); list_for_each_entry(chan, &relay_channels, list) { if (chan->buf[hotcpu]) continue; chan->buf[hotcpu] = relay_open_buf(chan, hotcpu); if(!chan->buf[hotcpu]) { printk(KERN_ERR "relay_hotcpu_callback: cpu %d buffer " "creation failed\n", hotcpu); mutex_unlock(&relay_channels_mutex); return NOTIFY_BAD; } } mutex_unlock(&relay_channels_mutex); break; case CPU_DEAD: case CPU_DEAD_FROZEN: /* No need to flush the cpu : will be flushed upon * final relay_flush() call. */ break; } return NOTIFY_OK;}/** * relay_open - create a new relay channel * @base_filename: base name of files to create, %NULL for buffering only * @parent: dentry of parent directory, %NULL for root directory or buffer * @subbuf_size: size of sub-buffers * @n_subbufs: number of sub-buffers * @cb: client callback functions * @private_data: user-defined data * * Returns channel pointer if successful, %NULL otherwise. * * Creates a channel buffer for each cpu using the sizes and * attributes specified. The created channel buffer files * will be named base_filename0...base_filenameN-1. File * permissions will be %S_IRUSR. */struct rchan *relay_open(const char *base_filename, struct dentry *parent, size_t subbuf_size, size_t n_subbufs, struct rchan_callbacks *cb, void *private_data){ unsigned int i; struct rchan *chan; if (!(subbuf_size && n_subbufs)) return NULL; chan = kzalloc(sizeof(struct rchan), GFP_KERNEL); if (!chan) return NULL; chan->version = RELAYFS_CHANNEL_VERSION; chan->n_subbufs = n_subbufs; chan->subbuf_size = subbuf_size; chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); chan->parent = parent; chan->private_data = private_data; if (base_filename) { chan->has_base_filename = 1; strlcpy(chan->base_filename, base_filename, NAME_MAX); } setup_callbacks(chan, cb); kref_init(&chan->kref); mutex_lock(&relay_channels_mutex); for_each_online_cpu(i) { chan->buf[i] = relay_open_buf(chan, i); if (!chan->buf[i]) goto free_bufs; } list_add(&chan->list, &relay_channels); mutex_unlock(&relay_channels_mutex); return chan;free_bufs: for_each_online_cpu(i) { if (!chan->buf[i]) break; relay_close_buf(chan->buf[i]); } kref_put(&chan->kref, relay_destroy_channel); mutex_unlock(&relay_channels_mutex); return NULL;}EXPORT_SYMBOL_GPL(relay_open);struct rchan_percpu_buf_dispatcher { struct rchan_buf *buf; struct dentry *dentry;};/* Called in atomic context. */static void __relay_set_buf_dentry(void *info){ struct rchan_percpu_buf_dispatcher *p = info; relay_set_buf_dentry(p->buf, p->dentry);}/** * relay_late_setup_files - triggers file creation * @chan: channel to operate on * @base_filename: base name of files to create * @parent: dentry of parent directory, %NULL for root directory * * Returns 0 if successful, non-zero otherwise. * * Use to setup files for a previously buffer-only channel. * Useful to do early tracing in kernel, before VFS is up, for example. */int relay_late_setup_files(struct rchan *chan, const char *base_filename, struct dentry *parent){ int err = 0; unsigned int i, curr_cpu; unsigned long flags; struct dentry *dentry; struct rchan_percpu_buf_dispatcher disp; if (!chan || !base_filename) return -EINVAL; strlcpy(chan->base_filename, base_filename, NAME_MAX); mutex_lock(&relay_channels_mutex); /* Is chan already set up? */ if (unlikely(chan->has_base_filename)) return -EEXIST; chan->has_base_filename = 1; chan->parent = parent; curr_cpu = get_cpu(); /* * The CPU hotplug notifier ran before us and created buffers with * no files associated. So it's safe to call relay_setup_buf_file() * on all currently online CPUs. */ for_each_online_cpu(i) { if (unlikely(!chan->buf[i])) { printk(KERN_ERR "relay_late_setup_files: CPU %u " "has no buffer, it must have!\n", i); BUG(); err = -EINVAL; break; } dentry = relay_create_buf_file(chan, chan->buf[i], i); if (unlikely(!dentry)) { err = -EINVAL; break; } if (curr_cpu == i) { local_irq_save(flags); relay_set_buf_dentry(chan->buf[i], dentry); local_irq_restore(flags); } else { disp.buf = chan->buf[i]; disp.dentry = dentry; smp_mb(); /* relay_channels_mutex must be held, so wait. */ err = smp_call_function_single(i, __relay_set_buf_dentry, &disp, 1); } if (unlikely(err)) break; } put_cpu(); mutex_unlock(&relay_channels_mutex); return err;}/** * relay_switch_subbuf - switch to a new sub-buffer * @buf: channel buffer * @length: size of current event * * Returns either the length passed in or 0 if full. * * Performs sub-buffer-switch tasks such as invoking callbacks, * updating padding counts, waking up readers, etc. */size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length){ void *old, *new; size_t old_subbuf, new_subbuf; if (unlikely(length > buf->chan->subbuf_size)) goto toobig; if (buf->offset != buf->chan->subbuf_size + 1) { buf->prev_padding = buf->chan->subbuf_size - buf->offset; old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; buf->padding[old_subbuf] = buf->prev_padding; buf->subbufs_produced++; if (buf->dentry) buf->dentry->d_inode->i_size += buf->chan->subbuf_size - buf->padding[old_subbuf]; else buf->early_bytes += buf->chan->subbuf_size - buf->padding[old_subbuf]; smp_mb(); if (waitqueue_active(&buf->read_wait)) /* * Calling wake_up_interruptible() from here * will deadlock if we happen to be logging * from the scheduler (trying to re-grab * rq->lock), so defer it. */ __mod_timer(&buf->timer, jiffies + 1); } old = buf->data; new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; new = buf->start + new_subbuf * buf->chan->subbuf_size; buf->offset = 0; if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) { buf->offset = buf->chan->subbuf_size + 1; return 0; } buf->data = new; buf->padding[new_subbuf] = 0; if (unlikely(length + buf->offset > buf->chan->subbuf_size)) goto toobig; return length;toobig: buf->chan->last_toobig = length; return 0;}EXPORT_SYMBOL_GPL(relay_switch_subbuf);/** * relay_subbufs_consumed - update the buffer's sub-buffers-consumed count * @chan: the channel * @cpu: the cpu associated with the channel buffer to update * @subbufs_consumed: number of sub-buffers to add to current buf's count * * Adds to the channel buffer's consumed sub-buffer count. * subbufs_consumed should be the number of sub-buffers newly consumed, * not the total consumed. * * NOTE. Kernel clients don't need to call this function if the channel * mode is 'overwrite'. */void relay_subbufs_consumed(struct rchan *chan, unsigned int cpu, size_t subbufs_consumed){ struct rchan_buf *buf; if (!chan) return; if (cpu >= NR_CPUS || !chan->buf[cpu]) return; buf = chan->buf[cpu]; buf->subbufs_consumed += subbufs_consumed; if (buf->subbufs_consumed > buf->subbufs_produced) buf->subbufs_consumed = buf->subbufs_produced;}EXPORT_SYMBOL_GPL(relay_subbufs_consumed);/** * relay_close - close the channel * @chan: the channel * * Closes all channel buffers and frees the channel. */void relay_close(struct rchan *chan){ unsigned int i; if (!chan) return; mutex_lock(&relay_channels_mutex); if (chan->is_global && chan->buf[0]) relay_close_buf(chan->buf[0]); else for_each_possible_cpu(i) if (chan->buf[i]) relay_close_buf(chan->buf[i]); if (chan->last_toobig) printk(KERN_WARNING "relay: one or more items not logged " "[item size (%Zd) > sub-buffer size (%Zd)]\n", chan->last_toobig, chan->subbuf_size); list_del(&chan->list); kref_put(&chan->kref, relay_destroy_channel); mutex_unlock(&relay_channels_mutex);}EXPORT_SYMBOL_GPL(relay_close);/** * relay_flush - close the channel * @chan: the channel * * Flushes all channel buffers, i.e. forces buffer switch. */void relay_flush(struct rchan *chan){ unsigned int i; if (!chan) return; if (chan->is_global && chan->buf[0]) { relay_switch_subbuf(chan->buf[0], 0); return; } mutex_lock(&relay_channels_mutex); for_each_possible_cpu(i) if (chan->buf[i]) relay_switch_subbuf(chan->buf[i], 0); mutex_unlock(&relay_channels_mutex);}EXPORT_SYMBOL_GPL(relay_flush);/** * relay_file_open - open file op for relay files * @inode: the inode * @filp: the file * * Increments the channel buffer refcount. */static int relay_file_open(struct inode *inode, struct file *filp){ struct rchan_buf *buf = inode->i_private; kref_get(&buf->kref); filp->private_data = buf; return nonseekable_open(inode, filp);}/** * relay_file_mmap - mmap file op for relay files * @filp: the file * @vma: the vma describing what to map * * Calls upon relay_mmap_buf() to map the file into user space. */static int relay_file_mmap(struct file *filp, struct vm_area_struct *vma){ struct rchan_buf *buf = filp->private_data; return relay_mmap_buf(buf, vma);}/** * relay_file_poll - poll file op for relay files * @filp: the file * @wait: poll table * * Poll implemention. */static unsigned int relay_file_poll(struct file *filp, poll_table *wait){ unsigned int mask = 0; struct rchan_buf *buf = filp->private_data; if (buf->finalized) return POLLERR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -