mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-01 06:42:31 +00:00
6625b861f8
The patch series implementa or fixes 3 things that were specifically requested or suggested by relayfs users: - support for non-relay files (patches 1-6) Currently, the relayfs API only supports the creation of directories (relayfs_create_dir()) and relay files (relay_open()). These patches adds support for non-relay files (relayfs_create_file()). This is so relayfs applications can create 'control files' in relayfs itself rather than in /proc or via a netlink channel, as is currently done in the relay-app examples. Basically what this amounts to is exporting relayfs_create_file() with an additional file_ops param that clients can use to supply file operations for their own special-purpose files in relayfs. - make exported relay file ops useful (patches 7-8) The relayfs relay_file_operations have always been exported, the intent being to make it possible to create relay files in other filesystems such as debugfs. The problem, though, is that currently the file operations are too tightly coupled to relayfs to actually be used for this purpose. This patch fixes that by adding a couple of callback functions that allow a client to hook into relay_open()/close() and supply the files that will be used to represent the channel buffers; the default implementation if no callbacks are defined is to create the files in relayfs. - add an option to create global relay buffer (patches 9-10) The file creation callback also supplies an optional param, is_global, that can be used by clients to create a single global relayfs buffer instead of the default per-cpu buffers. This was suggested as being useful for certain debugging applications where it's more convenient to be able to get all the data from a single channel without having to go to the bother of dealing with per-cpu files. - cleanup, some renaming and Documentation updates (patches 11-12) There were several comments that the use of netlink in the example code was non-intuitive and in fact the whole relay-app business was needlessly confusing. Based on that feedback, the example code has been completely converted over to relayfs control files as supported by this patch, and have also been made completely self-contained. The converted examples along with a couple of new examples that demonstrate using exported relay files can be found in relay-apps tarball: http://prdownloads.sourceforge.net/relayfs/relay-apps-0.9.tar.gz?download This patch: Separate buffer create/destroy from inode create/destroy. We want to be able to associate other data and not just relay buffers with inodes. Buffer create/destroy is moved out of inode.c and into relayfs core code. Signed-off-by: Tom Zanussi <zanussi@us.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
191 lines
4.3 KiB
C
191 lines
4.3 KiB
C
/*
|
|
* RelayFS buffer management code.
|
|
*
|
|
* Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
|
|
* Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
|
|
*
|
|
* This file is released under the GPL.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/relayfs_fs.h>
|
|
#include "relay.h"
|
|
#include "buffers.h"
|
|
|
|
/*
|
|
* close() vm_op implementation for relayfs file mapping.
|
|
*/
|
|
static void relay_file_mmap_close(struct vm_area_struct *vma)
|
|
{
|
|
struct rchan_buf *buf = vma->vm_private_data;
|
|
buf->chan->cb->buf_unmapped(buf, vma->vm_file);
|
|
}
|
|
|
|
/*
|
|
* nopage() vm_op implementation for relayfs file mapping.
|
|
*/
|
|
static struct page *relay_buf_nopage(struct vm_area_struct *vma,
|
|
unsigned long address,
|
|
int *type)
|
|
{
|
|
struct page *page;
|
|
struct rchan_buf *buf = vma->vm_private_data;
|
|
unsigned long offset = address - vma->vm_start;
|
|
|
|
if (address > vma->vm_end)
|
|
return NOPAGE_SIGBUS; /* Disallow mremap */
|
|
if (!buf)
|
|
return NOPAGE_OOM;
|
|
|
|
page = vmalloc_to_page(buf->start + offset);
|
|
if (!page)
|
|
return NOPAGE_OOM;
|
|
get_page(page);
|
|
|
|
if (type)
|
|
*type = VM_FAULT_MINOR;
|
|
|
|
return page;
|
|
}
|
|
|
|
/*
|
|
* vm_ops for relay file mappings.
|
|
*/
|
|
static struct vm_operations_struct relay_file_mmap_ops = {
|
|
.nopage = relay_buf_nopage,
|
|
.close = relay_file_mmap_close,
|
|
};
|
|
|
|
/**
|
|
* relay_mmap_buf: - mmap channel buffer to process address space
|
|
* @buf: relay channel buffer
|
|
* @vma: vm_area_struct describing memory to be mapped
|
|
*
|
|
* Returns 0 if ok, negative on error
|
|
*
|
|
* Caller should already have grabbed mmap_sem.
|
|
*/
|
|
int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma)
|
|
{
|
|
unsigned long length = vma->vm_end - vma->vm_start;
|
|
struct file *filp = vma->vm_file;
|
|
|
|
if (!buf)
|
|
return -EBADF;
|
|
|
|
if (length != (unsigned long)buf->chan->alloc_size)
|
|
return -EINVAL;
|
|
|
|
vma->vm_ops = &relay_file_mmap_ops;
|
|
vma->vm_private_data = buf;
|
|
buf->chan->cb->buf_mapped(buf, filp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size)
|
|
{
|
|
void *mem;
|
|
unsigned int i, j, n_pages;
|
|
|
|
size = PAGE_ALIGN(size);
|
|
n_pages = size >> PAGE_SHIFT;
|
|
|
|
buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL);
|
|
if (!buf->page_array)
|
|
return NULL;
|
|
|
|
for (i = 0; i < n_pages; i++) {
|
|
buf->page_array[i] = alloc_page(GFP_KERNEL);
|
|
if (unlikely(!buf->page_array[i]))
|
|
goto depopulate;
|
|
}
|
|
mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL);
|
|
if (!mem)
|
|
goto depopulate;
|
|
|
|
memset(mem, 0, size);
|
|
buf->page_count = n_pages;
|
|
return mem;
|
|
|
|
depopulate:
|
|
for (j = 0; j < i; j++)
|
|
__free_page(buf->page_array[j]);
|
|
kfree(buf->page_array);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* relay_create_buf - allocate and initialize a channel buffer
|
|
* @alloc_size: size of the buffer to allocate
|
|
* @n_subbufs: number of sub-buffers in the channel
|
|
*
|
|
* Returns channel buffer if successful, NULL otherwise
|
|
*/
|
|
struct rchan_buf *relay_create_buf(struct rchan *chan)
|
|
{
|
|
struct rchan_buf *buf = kcalloc(1, sizeof(struct rchan_buf), GFP_KERNEL);
|
|
if (!buf)
|
|
return NULL;
|
|
|
|
buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL);
|
|
if (!buf->padding)
|
|
goto free_buf;
|
|
|
|
buf->start = relay_alloc_buf(buf, chan->alloc_size);
|
|
if (!buf->start)
|
|
goto free_buf;
|
|
|
|
buf->chan = chan;
|
|
kref_get(&buf->chan->kref);
|
|
return buf;
|
|
|
|
free_buf:
|
|
kfree(buf->padding);
|
|
kfree(buf);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* relay_destroy_buf - destroy an rchan_buf struct and associated buffer
|
|
* @buf: the buffer struct
|
|
*/
|
|
void relay_destroy_buf(struct rchan_buf *buf)
|
|
{
|
|
struct rchan *chan = buf->chan;
|
|
unsigned int i;
|
|
|
|
if (likely(buf->start)) {
|
|
vunmap(buf->start);
|
|
for (i = 0; i < buf->page_count; i++)
|
|
__free_page(buf->page_array[i]);
|
|
kfree(buf->page_array);
|
|
}
|
|
kfree(buf->padding);
|
|
kfree(buf);
|
|
kref_put(&chan->kref, relay_destroy_channel);
|
|
}
|
|
|
|
/**
|
|
* relay_remove_buf - remove a channel buffer
|
|
*
|
|
* Removes the file from the relayfs fileystem, which also frees the
|
|
* rchan_buf_struct and the channel buffer. Should only be called from
|
|
* kref_put().
|
|
*/
|
|
void relay_remove_buf(struct kref *kref)
|
|
{
|
|
struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref);
|
|
relayfs_remove(buf->dentry);
|
|
relay_destroy_buf(buf);
|
|
}
|