/* * Public API and common code for kernel->userspace relay file support. * * See Documentation/filesystems/relay.rst for an overview. * * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com) * * Moved to kernel/relay.c by Paul Mundt, 2006. * November 2006 - CPU hotplug support by Mathieu Desnoyers * (mathieu.desnoyers@polymtl.ca) * * This file is released under the GPL.
*/ #include <linux/errno.h> #include <linux/stddef.h> #include <linux/slab.h> #include <linux/export.h> #include <linux/string.h> #include <linux/relay.h> #include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/cpu.h> #include <linux/splice.h>
/* list of open channels, for cpu hotplug */ static DEFINE_MUTEX(relay_channels_mutex); static LIST_HEAD(relay_channels);
/** * relay_alloc_buf - allocate a channel buffer * @buf: the buffer struct * @size: total size of the buffer * * Returns a pointer to the resulting buffer, %NULL if unsuccessful. The * passed in size will get page aligned, if it isn't already.
*/ staticvoid *relay_alloc_buf(struct rchan_buf *buf, size_t *size)
{ void *mem; unsignedint i, j, n_pages;
/** * relay_destroy_channel - free the channel struct * @kref: target kernel reference that contains the relay channel * * Should only be called from kref_put().
*/ staticvoid relay_destroy_channel(struct kref *kref)
{ struct rchan *chan = container_of(kref, struct rchan, kref);
free_percpu(chan->buf);
kfree(chan);
}
/** * relay_destroy_buf - destroy an rchan_buf struct and associated buffer * @buf: the buffer struct
*/ staticvoid relay_destroy_buf(struct rchan_buf *buf)
{ struct rchan *chan = buf->chan; unsignedint i;
if (likely(buf->start)) {
vunmap(buf->start); for (i = 0; i < buf->page_count; i++)
__free_page(buf->page_array[i]);
relay_free_page_array(buf->page_array);
}
*per_cpu_ptr(chan->buf, buf->cpu) = NULL;
kfree(buf->padding);
kfree(buf);
kref_put(&chan->kref, relay_destroy_channel);
}
/** * relay_remove_buf - remove a channel buffer * @kref: target kernel reference that contains the relay buffer * * Removes the file from the filesystem, which also frees the * rchan_buf_struct and the channel buffer. Should only be called from * kref_put().
*/ staticvoid relay_remove_buf(struct kref *kref)
{ struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref);
relay_destroy_buf(buf);
}
/** * relay_buf_empty - boolean, is the channel buffer empty? * @buf: channel buffer * * Returns 1 if the buffer is empty, 0 otherwise.
*/ staticint relay_buf_empty(struct rchan_buf *buf)
{ return (buf->subbufs_produced - buf->subbufs_consumed) ? 0 : 1;
}
/** * relay_buf_full - boolean, is the channel buffer full? * @buf: channel buffer * * Returns 1 if the buffer is full, 0 otherwise.
*/ int relay_buf_full(struct rchan_buf *buf)
{
size_t ready = buf->subbufs_produced - buf->subbufs_consumed; return (ready >= buf->chan->n_subbufs) ? 1 : 0;
}
EXPORT_SYMBOL_GPL(relay_buf_full);
/* * High-level relay kernel API and associated functions.
*/
staticint relay_subbuf_start(struct rchan_buf *buf, void *subbuf, void *prev_subbuf)
{ int full = relay_buf_full(buf);
/** * wakeup_readers - wake up readers waiting on a channel * @work: contains the channel buffer * * This is the function used to defer reader waking
*/ staticvoid wakeup_readers(struct irq_work *work)
{ struct rchan_buf *buf;
/** * __relay_reset - reset a channel buffer * @buf: the channel buffer * @init: 1 if this is a first-time initialization * * See relay_reset() for description of effect.
*/ staticvoid __relay_reset(struct rchan_buf *buf, unsignedint init)
{
size_t i;
for (i = 0; i < buf->chan->n_subbufs; i++)
buf->padding[i] = 0;
relay_subbuf_start(buf, buf->data, NULL);
}
/** * relay_reset - reset the channel * @chan: the channel * * This has the effect of erasing all data from all channel buffers * and restarting the channel in its initial state. The buffers * are not freed, so any mappings are still in effect. * * NOTE. Care should be taken that the channel isn't actually * being used by anything when this call is made.
*/ void relay_reset(struct rchan *chan)
{ struct rchan_buf *buf; unsignedint i;
/** * 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.
*/ staticvoid relay_close_buf(struct rchan_buf *buf)
{
buf->finalized = 1;
irq_work_sync(&buf->wakeup_work);
buf->chan->cb->remove_buf_file(buf->dentry);
kref_put(&buf->kref, relay_remove_buf);
}
int relay_prepare_cpu(unsignedint cpu)
{ struct rchan *chan; struct rchan_buf *buf;
/** * relay_open - create a new relay channel * @base_filename: base name of files to create * @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(constchar *base_filename, struct dentry *parent,
size_t subbuf_size,
size_t n_subbufs, conststruct rchan_callbacks *cb, void *private_data)
{ unsignedint i; struct rchan *chan; struct rchan_buf *buf;
if (!(subbuf_size && n_subbufs)) return NULL; if (subbuf_size > UINT_MAX / n_subbufs) return NULL; if (!cb || !cb->create_buf_file || !cb->remove_buf_file) return NULL;
chan = kzalloc(sizeof(struct rchan), GFP_KERNEL); if (!chan) return NULL;
/** * 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) {
size_t prev_padding;
prev_padding = buf->chan->subbuf_size - buf->offset;
old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs;
buf->padding[old_subbuf] = prev_padding;
buf->subbufs_produced++; if (buf->dentry)
d_inode(buf->dentry)->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.
*/
irq_work_queue(&buf->wakeup_work);
}
}
/** * 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, unsignedint cpu,
size_t subbufs_consumed)
{ struct rchan_buf *buf;
/** * relay_stats - get channel buffer statistics * @chan: the channel * @flags: select particular information to get * * Returns the count of certain field that caller specifies.
*/
size_t relay_stats(struct rchan *chan, int flags)
{ unsignedint i, count = 0; struct rchan_buf *rbuf;
/** * relay_file_open - open file op for relay files * @inode: the inode * @filp: the file * * Increments the channel buffer refcount.
*/ staticint 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.
*/ staticint relay_file_mmap(struct file *filp, struct vm_area_struct *vma)
{ struct rchan_buf *buf = filp->private_data; return relay_mmap_buf(buf, vma);
}
if (filp->f_mode & FMODE_READ) {
poll_wait(filp, &buf->read_wait, wait); if (!relay_buf_empty(buf))
mask |= EPOLLIN | EPOLLRDNORM;
}
return mask;
}
/** * relay_file_release - release file op for relay files * @inode: the inode * @filp: the file * * Decrements the channel refcount, as the filesystem is * no longer using it.
*/ staticint relay_file_release(struct inode *inode, struct file *filp)
{ struct rchan_buf *buf = filp->private_data;
kref_put(&buf->kref, relay_remove_buf);
/** * relay_file_read_start_pos - find the first available byte to read * @buf: relay channel buffer * * If the read_pos is in the middle of padding, return the * position of the first actually available byte, otherwise * return the original value.
*/ static size_t relay_file_read_start_pos(struct rchan_buf *buf)
{
size_t read_subbuf, padding, padding_start, padding_end;
size_t subbuf_size = buf->chan->subbuf_size;
size_t n_subbufs = buf->chan->n_subbufs;
size_t consumed = buf->subbufs_consumed % n_subbufs;
size_t read_pos = (consumed * subbuf_size + buf->bytes_consumed)
% (n_subbufs * subbuf_size);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.