Commit b86ff981a8252d83d6a7719ae09f3a05307e3592
1 parent
b0e6e96299
Exists in
master
and in
4 other branches
[PATCH] relay: migrate from relayfs to a generic relay API
Original patch from Paul Mundt, sysfs parts removed by me since they were broken. Signed-off-by: Jens Axboe <axboe@suse.de>
Showing 12 changed files with 1212 additions and 1290 deletions Side-by-side Diff
fs/Kconfig
... | ... | @@ -859,18 +859,6 @@ |
859 | 859 | To compile this as a module, choose M here: the module will be called |
860 | 860 | ramfs. |
861 | 861 | |
862 | -config RELAYFS_FS | |
863 | - tristate "Relayfs file system support" | |
864 | - ---help--- | |
865 | - Relayfs is a high-speed data relay filesystem designed to provide | |
866 | - an efficient mechanism for tools and facilities to relay large | |
867 | - amounts of data from kernel space to user space. | |
868 | - | |
869 | - To compile this code as a module, choose M here: the module will be | |
870 | - called relayfs. | |
871 | - | |
872 | - If unsure, say N. | |
873 | - | |
874 | 862 | config CONFIGFS_FS |
875 | 863 | tristate "Userspace-driven configuration filesystem (EXPERIMENTAL)" |
876 | 864 | depends on EXPERIMENTAL |
fs/Makefile
fs/relayfs/Makefile
fs/relayfs/buffers.c
1 | -/* | |
2 | - * RelayFS buffer management code. | |
3 | - * | |
4 | - * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp | |
5 | - * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com) | |
6 | - * | |
7 | - * This file is released under the GPL. | |
8 | - */ | |
9 | - | |
10 | -#include <linux/module.h> | |
11 | -#include <linux/vmalloc.h> | |
12 | -#include <linux/mm.h> | |
13 | -#include <linux/relayfs_fs.h> | |
14 | -#include "relay.h" | |
15 | -#include "buffers.h" | |
16 | - | |
17 | -/* | |
18 | - * close() vm_op implementation for relayfs file mapping. | |
19 | - */ | |
20 | -static void relay_file_mmap_close(struct vm_area_struct *vma) | |
21 | -{ | |
22 | - struct rchan_buf *buf = vma->vm_private_data; | |
23 | - buf->chan->cb->buf_unmapped(buf, vma->vm_file); | |
24 | -} | |
25 | - | |
26 | -/* | |
27 | - * nopage() vm_op implementation for relayfs file mapping. | |
28 | - */ | |
29 | -static struct page *relay_buf_nopage(struct vm_area_struct *vma, | |
30 | - unsigned long address, | |
31 | - int *type) | |
32 | -{ | |
33 | - struct page *page; | |
34 | - struct rchan_buf *buf = vma->vm_private_data; | |
35 | - unsigned long offset = address - vma->vm_start; | |
36 | - | |
37 | - if (address > vma->vm_end) | |
38 | - return NOPAGE_SIGBUS; /* Disallow mremap */ | |
39 | - if (!buf) | |
40 | - return NOPAGE_OOM; | |
41 | - | |
42 | - page = vmalloc_to_page(buf->start + offset); | |
43 | - if (!page) | |
44 | - return NOPAGE_OOM; | |
45 | - get_page(page); | |
46 | - | |
47 | - if (type) | |
48 | - *type = VM_FAULT_MINOR; | |
49 | - | |
50 | - return page; | |
51 | -} | |
52 | - | |
53 | -/* | |
54 | - * vm_ops for relay file mappings. | |
55 | - */ | |
56 | -static struct vm_operations_struct relay_file_mmap_ops = { | |
57 | - .nopage = relay_buf_nopage, | |
58 | - .close = relay_file_mmap_close, | |
59 | -}; | |
60 | - | |
61 | -/** | |
62 | - * relay_mmap_buf: - mmap channel buffer to process address space | |
63 | - * @buf: relay channel buffer | |
64 | - * @vma: vm_area_struct describing memory to be mapped | |
65 | - * | |
66 | - * Returns 0 if ok, negative on error | |
67 | - * | |
68 | - * Caller should already have grabbed mmap_sem. | |
69 | - */ | |
70 | -int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma) | |
71 | -{ | |
72 | - unsigned long length = vma->vm_end - vma->vm_start; | |
73 | - struct file *filp = vma->vm_file; | |
74 | - | |
75 | - if (!buf) | |
76 | - return -EBADF; | |
77 | - | |
78 | - if (length != (unsigned long)buf->chan->alloc_size) | |
79 | - return -EINVAL; | |
80 | - | |
81 | - vma->vm_ops = &relay_file_mmap_ops; | |
82 | - vma->vm_private_data = buf; | |
83 | - buf->chan->cb->buf_mapped(buf, filp); | |
84 | - | |
85 | - return 0; | |
86 | -} | |
87 | - | |
88 | -/** | |
89 | - * relay_alloc_buf - allocate a channel buffer | |
90 | - * @buf: the buffer struct | |
91 | - * @size: total size of the buffer | |
92 | - * | |
93 | - * Returns a pointer to the resulting buffer, NULL if unsuccessful | |
94 | - */ | |
95 | -static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size) | |
96 | -{ | |
97 | - void *mem; | |
98 | - unsigned int i, j, n_pages; | |
99 | - | |
100 | - size = PAGE_ALIGN(size); | |
101 | - n_pages = size >> PAGE_SHIFT; | |
102 | - | |
103 | - buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL); | |
104 | - if (!buf->page_array) | |
105 | - return NULL; | |
106 | - | |
107 | - for (i = 0; i < n_pages; i++) { | |
108 | - buf->page_array[i] = alloc_page(GFP_KERNEL); | |
109 | - if (unlikely(!buf->page_array[i])) | |
110 | - goto depopulate; | |
111 | - } | |
112 | - mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL); | |
113 | - if (!mem) | |
114 | - goto depopulate; | |
115 | - | |
116 | - memset(mem, 0, size); | |
117 | - buf->page_count = n_pages; | |
118 | - return mem; | |
119 | - | |
120 | -depopulate: | |
121 | - for (j = 0; j < i; j++) | |
122 | - __free_page(buf->page_array[j]); | |
123 | - kfree(buf->page_array); | |
124 | - return NULL; | |
125 | -} | |
126 | - | |
127 | -/** | |
128 | - * relay_create_buf - allocate and initialize a channel buffer | |
129 | - * @alloc_size: size of the buffer to allocate | |
130 | - * @n_subbufs: number of sub-buffers in the channel | |
131 | - * | |
132 | - * Returns channel buffer if successful, NULL otherwise | |
133 | - */ | |
134 | -struct rchan_buf *relay_create_buf(struct rchan *chan) | |
135 | -{ | |
136 | - struct rchan_buf *buf = kcalloc(1, sizeof(struct rchan_buf), GFP_KERNEL); | |
137 | - if (!buf) | |
138 | - return NULL; | |
139 | - | |
140 | - buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); | |
141 | - if (!buf->padding) | |
142 | - goto free_buf; | |
143 | - | |
144 | - buf->start = relay_alloc_buf(buf, chan->alloc_size); | |
145 | - if (!buf->start) | |
146 | - goto free_buf; | |
147 | - | |
148 | - buf->chan = chan; | |
149 | - kref_get(&buf->chan->kref); | |
150 | - return buf; | |
151 | - | |
152 | -free_buf: | |
153 | - kfree(buf->padding); | |
154 | - kfree(buf); | |
155 | - return NULL; | |
156 | -} | |
157 | - | |
158 | -/** | |
159 | - * relay_destroy_buf - destroy an rchan_buf struct and associated buffer | |
160 | - * @buf: the buffer struct | |
161 | - */ | |
162 | -void relay_destroy_buf(struct rchan_buf *buf) | |
163 | -{ | |
164 | - struct rchan *chan = buf->chan; | |
165 | - unsigned int i; | |
166 | - | |
167 | - if (likely(buf->start)) { | |
168 | - vunmap(buf->start); | |
169 | - for (i = 0; i < buf->page_count; i++) | |
170 | - __free_page(buf->page_array[i]); | |
171 | - kfree(buf->page_array); | |
172 | - } | |
173 | - kfree(buf->padding); | |
174 | - kfree(buf); | |
175 | - kref_put(&chan->kref, relay_destroy_channel); | |
176 | -} | |
177 | - | |
178 | -/** | |
179 | - * relay_remove_buf - remove a channel buffer | |
180 | - * | |
181 | - * Removes the file from the relayfs fileystem, which also frees the | |
182 | - * rchan_buf_struct and the channel buffer. Should only be called from | |
183 | - * kref_put(). | |
184 | - */ | |
185 | -void relay_remove_buf(struct kref *kref) | |
186 | -{ | |
187 | - struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref); | |
188 | - buf->chan->cb->remove_buf_file(buf->dentry); | |
189 | - relay_destroy_buf(buf); | |
190 | -} |
fs/relayfs/buffers.h
1 | -#ifndef _BUFFERS_H | |
2 | -#define _BUFFERS_H | |
3 | - | |
4 | -/* This inspired by rtai/shmem */ | |
5 | -#define FIX_SIZE(x) (((x) - 1) & PAGE_MASK) + PAGE_SIZE | |
6 | - | |
7 | -extern int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma); | |
8 | -extern struct rchan_buf *relay_create_buf(struct rchan *chan); | |
9 | -extern void relay_destroy_buf(struct rchan_buf *buf); | |
10 | -extern void relay_remove_buf(struct kref *kref); | |
11 | - | |
12 | -#endif/* _BUFFERS_H */ |
fs/relayfs/inode.c
1 | -/* | |
2 | - * VFS-related code for RelayFS, a high-speed data relay filesystem. | |
3 | - * | |
4 | - * Copyright (C) 2003-2005 - Tom Zanussi <zanussi@us.ibm.com>, IBM Corp | |
5 | - * Copyright (C) 2003-2005 - Karim Yaghmour <karim@opersys.com> | |
6 | - * | |
7 | - * Based on ramfs, Copyright (C) 2002 - Linus Torvalds | |
8 | - * | |
9 | - * This file is released under the GPL. | |
10 | - */ | |
11 | - | |
12 | -#include <linux/module.h> | |
13 | -#include <linux/fs.h> | |
14 | -#include <linux/mount.h> | |
15 | -#include <linux/pagemap.h> | |
16 | -#include <linux/init.h> | |
17 | -#include <linux/string.h> | |
18 | -#include <linux/backing-dev.h> | |
19 | -#include <linux/namei.h> | |
20 | -#include <linux/poll.h> | |
21 | -#include <linux/relayfs_fs.h> | |
22 | -#include "relay.h" | |
23 | -#include "buffers.h" | |
24 | - | |
25 | -#define RELAYFS_MAGIC 0xF0B4A981 | |
26 | - | |
27 | -static struct vfsmount * relayfs_mount; | |
28 | -static int relayfs_mount_count; | |
29 | - | |
30 | -static struct backing_dev_info relayfs_backing_dev_info = { | |
31 | - .ra_pages = 0, /* No readahead */ | |
32 | - .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | |
33 | -}; | |
34 | - | |
35 | -static struct inode *relayfs_get_inode(struct super_block *sb, | |
36 | - int mode, | |
37 | - struct file_operations *fops, | |
38 | - void *data) | |
39 | -{ | |
40 | - struct inode *inode; | |
41 | - | |
42 | - inode = new_inode(sb); | |
43 | - if (!inode) | |
44 | - return NULL; | |
45 | - | |
46 | - inode->i_mode = mode; | |
47 | - inode->i_uid = 0; | |
48 | - inode->i_gid = 0; | |
49 | - inode->i_blksize = PAGE_CACHE_SIZE; | |
50 | - inode->i_blocks = 0; | |
51 | - inode->i_mapping->backing_dev_info = &relayfs_backing_dev_info; | |
52 | - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | |
53 | - switch (mode & S_IFMT) { | |
54 | - case S_IFREG: | |
55 | - inode->i_fop = fops; | |
56 | - if (data) | |
57 | - inode->u.generic_ip = data; | |
58 | - break; | |
59 | - case S_IFDIR: | |
60 | - inode->i_op = &simple_dir_inode_operations; | |
61 | - inode->i_fop = &simple_dir_operations; | |
62 | - | |
63 | - /* directory inodes start off with i_nlink == 2 (for "." entry) */ | |
64 | - inode->i_nlink++; | |
65 | - break; | |
66 | - default: | |
67 | - break; | |
68 | - } | |
69 | - | |
70 | - return inode; | |
71 | -} | |
72 | - | |
73 | -/** | |
74 | - * relayfs_create_entry - create a relayfs directory or file | |
75 | - * @name: the name of the file to create | |
76 | - * @parent: parent directory | |
77 | - * @mode: mode | |
78 | - * @fops: file operations to use for the file | |
79 | - * @data: user-associated data for this file | |
80 | - * | |
81 | - * Returns the new dentry, NULL on failure | |
82 | - * | |
83 | - * Creates a file or directory with the specifed permissions. | |
84 | - */ | |
85 | -static struct dentry *relayfs_create_entry(const char *name, | |
86 | - struct dentry *parent, | |
87 | - int mode, | |
88 | - struct file_operations *fops, | |
89 | - void *data) | |
90 | -{ | |
91 | - struct dentry *d; | |
92 | - struct inode *inode; | |
93 | - int error = 0; | |
94 | - | |
95 | - BUG_ON(!name || !(S_ISREG(mode) || S_ISDIR(mode))); | |
96 | - | |
97 | - error = simple_pin_fs("relayfs", &relayfs_mount, &relayfs_mount_count); | |
98 | - if (error) { | |
99 | - printk(KERN_ERR "Couldn't mount relayfs: errcode %d\n", error); | |
100 | - return NULL; | |
101 | - } | |
102 | - | |
103 | - if (!parent && relayfs_mount && relayfs_mount->mnt_sb) | |
104 | - parent = relayfs_mount->mnt_sb->s_root; | |
105 | - | |
106 | - if (!parent) { | |
107 | - simple_release_fs(&relayfs_mount, &relayfs_mount_count); | |
108 | - return NULL; | |
109 | - } | |
110 | - | |
111 | - parent = dget(parent); | |
112 | - mutex_lock(&parent->d_inode->i_mutex); | |
113 | - d = lookup_one_len(name, parent, strlen(name)); | |
114 | - if (IS_ERR(d)) { | |
115 | - d = NULL; | |
116 | - goto release_mount; | |
117 | - } | |
118 | - | |
119 | - if (d->d_inode) { | |
120 | - d = NULL; | |
121 | - goto release_mount; | |
122 | - } | |
123 | - | |
124 | - inode = relayfs_get_inode(parent->d_inode->i_sb, mode, fops, data); | |
125 | - if (!inode) { | |
126 | - d = NULL; | |
127 | - goto release_mount; | |
128 | - } | |
129 | - | |
130 | - d_instantiate(d, inode); | |
131 | - dget(d); /* Extra count - pin the dentry in core */ | |
132 | - | |
133 | - if (S_ISDIR(mode)) | |
134 | - parent->d_inode->i_nlink++; | |
135 | - | |
136 | - goto exit; | |
137 | - | |
138 | -release_mount: | |
139 | - simple_release_fs(&relayfs_mount, &relayfs_mount_count); | |
140 | - | |
141 | -exit: | |
142 | - mutex_unlock(&parent->d_inode->i_mutex); | |
143 | - dput(parent); | |
144 | - return d; | |
145 | -} | |
146 | - | |
147 | -/** | |
148 | - * relayfs_create_file - create a file in the relay filesystem | |
149 | - * @name: the name of the file to create | |
150 | - * @parent: parent directory | |
151 | - * @mode: mode, if not specied the default perms are used | |
152 | - * @fops: file operations to use for the file | |
153 | - * @data: user-associated data for this file | |
154 | - * | |
155 | - * Returns file dentry if successful, NULL otherwise. | |
156 | - * | |
157 | - * The file will be created user r on behalf of current user. | |
158 | - */ | |
159 | -struct dentry *relayfs_create_file(const char *name, | |
160 | - struct dentry *parent, | |
161 | - int mode, | |
162 | - struct file_operations *fops, | |
163 | - void *data) | |
164 | -{ | |
165 | - BUG_ON(!fops); | |
166 | - | |
167 | - if (!mode) | |
168 | - mode = S_IRUSR; | |
169 | - mode = (mode & S_IALLUGO) | S_IFREG; | |
170 | - | |
171 | - return relayfs_create_entry(name, parent, mode, fops, data); | |
172 | -} | |
173 | - | |
174 | -/** | |
175 | - * relayfs_create_dir - create a directory in the relay filesystem | |
176 | - * @name: the name of the directory to create | |
177 | - * @parent: parent directory, NULL if parent should be fs root | |
178 | - * | |
179 | - * Returns directory dentry if successful, NULL otherwise. | |
180 | - * | |
181 | - * The directory will be created world rwx on behalf of current user. | |
182 | - */ | |
183 | -struct dentry *relayfs_create_dir(const char *name, struct dentry *parent) | |
184 | -{ | |
185 | - int mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; | |
186 | - return relayfs_create_entry(name, parent, mode, NULL, NULL); | |
187 | -} | |
188 | - | |
189 | -/** | |
190 | - * relayfs_remove - remove a file or directory in the relay filesystem | |
191 | - * @dentry: file or directory dentry | |
192 | - * | |
193 | - * Returns 0 if successful, negative otherwise. | |
194 | - */ | |
195 | -int relayfs_remove(struct dentry *dentry) | |
196 | -{ | |
197 | - struct dentry *parent; | |
198 | - int error = 0; | |
199 | - | |
200 | - if (!dentry) | |
201 | - return -EINVAL; | |
202 | - parent = dentry->d_parent; | |
203 | - if (!parent) | |
204 | - return -EINVAL; | |
205 | - | |
206 | - parent = dget(parent); | |
207 | - mutex_lock(&parent->d_inode->i_mutex); | |
208 | - if (dentry->d_inode) { | |
209 | - if (S_ISDIR(dentry->d_inode->i_mode)) | |
210 | - error = simple_rmdir(parent->d_inode, dentry); | |
211 | - else | |
212 | - error = simple_unlink(parent->d_inode, dentry); | |
213 | - if (!error) | |
214 | - d_delete(dentry); | |
215 | - } | |
216 | - if (!error) | |
217 | - dput(dentry); | |
218 | - mutex_unlock(&parent->d_inode->i_mutex); | |
219 | - dput(parent); | |
220 | - | |
221 | - if (!error) | |
222 | - simple_release_fs(&relayfs_mount, &relayfs_mount_count); | |
223 | - | |
224 | - return error; | |
225 | -} | |
226 | - | |
227 | -/** | |
228 | - * relayfs_remove_file - remove a file from relay filesystem | |
229 | - * @dentry: directory dentry | |
230 | - * | |
231 | - * Returns 0 if successful, negative otherwise. | |
232 | - */ | |
233 | -int relayfs_remove_file(struct dentry *dentry) | |
234 | -{ | |
235 | - return relayfs_remove(dentry); | |
236 | -} | |
237 | - | |
238 | -/** | |
239 | - * relayfs_remove_dir - remove a directory in the relay filesystem | |
240 | - * @dentry: directory dentry | |
241 | - * | |
242 | - * Returns 0 if successful, negative otherwise. | |
243 | - */ | |
244 | -int relayfs_remove_dir(struct dentry *dentry) | |
245 | -{ | |
246 | - return relayfs_remove(dentry); | |
247 | -} | |
248 | - | |
249 | -/** | |
250 | - * relay_file_open - open file op for relay files | |
251 | - * @inode: the inode | |
252 | - * @filp: the file | |
253 | - * | |
254 | - * Increments the channel buffer refcount. | |
255 | - */ | |
256 | -static int relay_file_open(struct inode *inode, struct file *filp) | |
257 | -{ | |
258 | - struct rchan_buf *buf = inode->u.generic_ip; | |
259 | - kref_get(&buf->kref); | |
260 | - filp->private_data = buf; | |
261 | - | |
262 | - return 0; | |
263 | -} | |
264 | - | |
265 | -/** | |
266 | - * relay_file_mmap - mmap file op for relay files | |
267 | - * @filp: the file | |
268 | - * @vma: the vma describing what to map | |
269 | - * | |
270 | - * Calls upon relay_mmap_buf to map the file into user space. | |
271 | - */ | |
272 | -static int relay_file_mmap(struct file *filp, struct vm_area_struct *vma) | |
273 | -{ | |
274 | - struct rchan_buf *buf = filp->private_data; | |
275 | - return relay_mmap_buf(buf, vma); | |
276 | -} | |
277 | - | |
278 | -/** | |
279 | - * relay_file_poll - poll file op for relay files | |
280 | - * @filp: the file | |
281 | - * @wait: poll table | |
282 | - * | |
283 | - * Poll implemention. | |
284 | - */ | |
285 | -static unsigned int relay_file_poll(struct file *filp, poll_table *wait) | |
286 | -{ | |
287 | - unsigned int mask = 0; | |
288 | - struct rchan_buf *buf = filp->private_data; | |
289 | - | |
290 | - if (buf->finalized) | |
291 | - return POLLERR; | |
292 | - | |
293 | - if (filp->f_mode & FMODE_READ) { | |
294 | - poll_wait(filp, &buf->read_wait, wait); | |
295 | - if (!relay_buf_empty(buf)) | |
296 | - mask |= POLLIN | POLLRDNORM; | |
297 | - } | |
298 | - | |
299 | - return mask; | |
300 | -} | |
301 | - | |
302 | -/** | |
303 | - * relay_file_release - release file op for relay files | |
304 | - * @inode: the inode | |
305 | - * @filp: the file | |
306 | - * | |
307 | - * Decrements the channel refcount, as the filesystem is | |
308 | - * no longer using it. | |
309 | - */ | |
310 | -static int relay_file_release(struct inode *inode, struct file *filp) | |
311 | -{ | |
312 | - struct rchan_buf *buf = filp->private_data; | |
313 | - kref_put(&buf->kref, relay_remove_buf); | |
314 | - | |
315 | - return 0; | |
316 | -} | |
317 | - | |
318 | -/** | |
319 | - * relay_file_read_consume - update the consumed count for the buffer | |
320 | - */ | |
321 | -static void relay_file_read_consume(struct rchan_buf *buf, | |
322 | - size_t read_pos, | |
323 | - size_t bytes_consumed) | |
324 | -{ | |
325 | - size_t subbuf_size = buf->chan->subbuf_size; | |
326 | - size_t n_subbufs = buf->chan->n_subbufs; | |
327 | - size_t read_subbuf; | |
328 | - | |
329 | - if (buf->bytes_consumed + bytes_consumed > subbuf_size) { | |
330 | - relay_subbufs_consumed(buf->chan, buf->cpu, 1); | |
331 | - buf->bytes_consumed = 0; | |
332 | - } | |
333 | - | |
334 | - buf->bytes_consumed += bytes_consumed; | |
335 | - read_subbuf = read_pos / buf->chan->subbuf_size; | |
336 | - if (buf->bytes_consumed + buf->padding[read_subbuf] == subbuf_size) { | |
337 | - if ((read_subbuf == buf->subbufs_produced % n_subbufs) && | |
338 | - (buf->offset == subbuf_size)) | |
339 | - return; | |
340 | - relay_subbufs_consumed(buf->chan, buf->cpu, 1); | |
341 | - buf->bytes_consumed = 0; | |
342 | - } | |
343 | -} | |
344 | - | |
345 | -/** | |
346 | - * relay_file_read_avail - boolean, are there unconsumed bytes available? | |
347 | - */ | |
348 | -static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos) | |
349 | -{ | |
350 | - size_t bytes_produced, bytes_consumed, write_offset; | |
351 | - size_t subbuf_size = buf->chan->subbuf_size; | |
352 | - size_t n_subbufs = buf->chan->n_subbufs; | |
353 | - size_t produced = buf->subbufs_produced % n_subbufs; | |
354 | - size_t consumed = buf->subbufs_consumed % n_subbufs; | |
355 | - | |
356 | - write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset; | |
357 | - | |
358 | - if (consumed > produced) { | |
359 | - if ((produced > n_subbufs) && | |
360 | - (produced + n_subbufs - consumed <= n_subbufs)) | |
361 | - produced += n_subbufs; | |
362 | - } else if (consumed == produced) { | |
363 | - if (buf->offset > subbuf_size) { | |
364 | - produced += n_subbufs; | |
365 | - if (buf->subbufs_produced == buf->subbufs_consumed) | |
366 | - consumed += n_subbufs; | |
367 | - } | |
368 | - } | |
369 | - | |
370 | - if (buf->offset > subbuf_size) | |
371 | - bytes_produced = (produced - 1) * subbuf_size + write_offset; | |
372 | - else | |
373 | - bytes_produced = produced * subbuf_size + write_offset; | |
374 | - bytes_consumed = consumed * subbuf_size + buf->bytes_consumed; | |
375 | - | |
376 | - if (bytes_produced == bytes_consumed) | |
377 | - return 0; | |
378 | - | |
379 | - relay_file_read_consume(buf, read_pos, 0); | |
380 | - | |
381 | - return 1; | |
382 | -} | |
383 | - | |
384 | -/** | |
385 | - * relay_file_read_subbuf_avail - return bytes available in sub-buffer | |
386 | - */ | |
387 | -static size_t relay_file_read_subbuf_avail(size_t read_pos, | |
388 | - struct rchan_buf *buf) | |
389 | -{ | |
390 | - size_t padding, avail = 0; | |
391 | - size_t read_subbuf, read_offset, write_subbuf, write_offset; | |
392 | - size_t subbuf_size = buf->chan->subbuf_size; | |
393 | - | |
394 | - write_subbuf = (buf->data - buf->start) / subbuf_size; | |
395 | - write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset; | |
396 | - read_subbuf = read_pos / subbuf_size; | |
397 | - read_offset = read_pos % subbuf_size; | |
398 | - padding = buf->padding[read_subbuf]; | |
399 | - | |
400 | - if (read_subbuf == write_subbuf) { | |
401 | - if (read_offset + padding < write_offset) | |
402 | - avail = write_offset - (read_offset + padding); | |
403 | - } else | |
404 | - avail = (subbuf_size - padding) - read_offset; | |
405 | - | |
406 | - return avail; | |
407 | -} | |
408 | - | |
409 | -/** | |
410 | - * relay_file_read_start_pos - find the first available byte to read | |
411 | - * | |
412 | - * If the read_pos is in the middle of padding, return the | |
413 | - * position of the first actually available byte, otherwise | |
414 | - * return the original value. | |
415 | - */ | |
416 | -static size_t relay_file_read_start_pos(size_t read_pos, | |
417 | - struct rchan_buf *buf) | |
418 | -{ | |
419 | - size_t read_subbuf, padding, padding_start, padding_end; | |
420 | - size_t subbuf_size = buf->chan->subbuf_size; | |
421 | - size_t n_subbufs = buf->chan->n_subbufs; | |
422 | - | |
423 | - read_subbuf = read_pos / subbuf_size; | |
424 | - padding = buf->padding[read_subbuf]; | |
425 | - padding_start = (read_subbuf + 1) * subbuf_size - padding; | |
426 | - padding_end = (read_subbuf + 1) * subbuf_size; | |
427 | - if (read_pos >= padding_start && read_pos < padding_end) { | |
428 | - read_subbuf = (read_subbuf + 1) % n_subbufs; | |
429 | - read_pos = read_subbuf * subbuf_size; | |
430 | - } | |
431 | - | |
432 | - return read_pos; | |
433 | -} | |
434 | - | |
435 | -/** | |
436 | - * relay_file_read_end_pos - return the new read position | |
437 | - */ | |
438 | -static size_t relay_file_read_end_pos(struct rchan_buf *buf, | |
439 | - size_t read_pos, | |
440 | - size_t count) | |
441 | -{ | |
442 | - size_t read_subbuf, padding, end_pos; | |
443 | - size_t subbuf_size = buf->chan->subbuf_size; | |
444 | - size_t n_subbufs = buf->chan->n_subbufs; | |
445 | - | |
446 | - read_subbuf = read_pos / subbuf_size; | |
447 | - padding = buf->padding[read_subbuf]; | |
448 | - if (read_pos % subbuf_size + count + padding == subbuf_size) | |
449 | - end_pos = (read_subbuf + 1) * subbuf_size; | |
450 | - else | |
451 | - end_pos = read_pos + count; | |
452 | - if (end_pos >= subbuf_size * n_subbufs) | |
453 | - end_pos = 0; | |
454 | - | |
455 | - return end_pos; | |
456 | -} | |
457 | - | |
458 | -/** | |
459 | - * relay_file_read - read file op for relay files | |
460 | - * @filp: the file | |
461 | - * @buffer: the userspace buffer | |
462 | - * @count: number of bytes to read | |
463 | - * @ppos: position to read from | |
464 | - * | |
465 | - * Reads count bytes or the number of bytes available in the | |
466 | - * current sub-buffer being read, whichever is smaller. | |
467 | - */ | |
468 | -static ssize_t relay_file_read(struct file *filp, | |
469 | - char __user *buffer, | |
470 | - size_t count, | |
471 | - loff_t *ppos) | |
472 | -{ | |
473 | - struct rchan_buf *buf = filp->private_data; | |
474 | - struct inode *inode = filp->f_dentry->d_inode; | |
475 | - size_t read_start, avail; | |
476 | - ssize_t ret = 0; | |
477 | - void *from; | |
478 | - | |
479 | - mutex_lock(&inode->i_mutex); | |
480 | - if(!relay_file_read_avail(buf, *ppos)) | |
481 | - goto out; | |
482 | - | |
483 | - read_start = relay_file_read_start_pos(*ppos, buf); | |
484 | - avail = relay_file_read_subbuf_avail(read_start, buf); | |
485 | - if (!avail) | |
486 | - goto out; | |
487 | - | |
488 | - from = buf->start + read_start; | |
489 | - ret = count = min(count, avail); | |
490 | - if (copy_to_user(buffer, from, count)) { | |
491 | - ret = -EFAULT; | |
492 | - goto out; | |
493 | - } | |
494 | - relay_file_read_consume(buf, read_start, count); | |
495 | - *ppos = relay_file_read_end_pos(buf, read_start, count); | |
496 | -out: | |
497 | - mutex_unlock(&inode->i_mutex); | |
498 | - return ret; | |
499 | -} | |
500 | - | |
501 | -struct file_operations relay_file_operations = { | |
502 | - .open = relay_file_open, | |
503 | - .poll = relay_file_poll, | |
504 | - .mmap = relay_file_mmap, | |
505 | - .read = relay_file_read, | |
506 | - .llseek = no_llseek, | |
507 | - .release = relay_file_release, | |
508 | -}; | |
509 | - | |
510 | -static struct super_operations relayfs_ops = { | |
511 | - .statfs = simple_statfs, | |
512 | - .drop_inode = generic_delete_inode, | |
513 | -}; | |
514 | - | |
515 | -static int relayfs_fill_super(struct super_block * sb, void * data, int silent) | |
516 | -{ | |
517 | - struct inode *inode; | |
518 | - struct dentry *root; | |
519 | - int mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; | |
520 | - | |
521 | - sb->s_blocksize = PAGE_CACHE_SIZE; | |
522 | - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | |
523 | - sb->s_magic = RELAYFS_MAGIC; | |
524 | - sb->s_op = &relayfs_ops; | |
525 | - inode = relayfs_get_inode(sb, mode, NULL, NULL); | |
526 | - | |
527 | - if (!inode) | |
528 | - return -ENOMEM; | |
529 | - | |
530 | - root = d_alloc_root(inode); | |
531 | - if (!root) { | |
532 | - iput(inode); | |
533 | - return -ENOMEM; | |
534 | - } | |
535 | - sb->s_root = root; | |
536 | - | |
537 | - return 0; | |
538 | -} | |
539 | - | |
540 | -static struct super_block * relayfs_get_sb(struct file_system_type *fs_type, | |
541 | - int flags, const char *dev_name, | |
542 | - void *data) | |
543 | -{ | |
544 | - return get_sb_single(fs_type, flags, data, relayfs_fill_super); | |
545 | -} | |
546 | - | |
547 | -static struct file_system_type relayfs_fs_type = { | |
548 | - .owner = THIS_MODULE, | |
549 | - .name = "relayfs", | |
550 | - .get_sb = relayfs_get_sb, | |
551 | - .kill_sb = kill_litter_super, | |
552 | -}; | |
553 | - | |
554 | -static int __init init_relayfs_fs(void) | |
555 | -{ | |
556 | - return register_filesystem(&relayfs_fs_type); | |
557 | -} | |
558 | - | |
559 | -static void __exit exit_relayfs_fs(void) | |
560 | -{ | |
561 | - | |
562 | - | |
563 | - | |
564 | - | |
565 | - | |
566 | - unregister_filesystem(&relayfs_fs_type); | |
567 | -} | |
568 | - | |
569 | -module_init(init_relayfs_fs) | |
570 | -module_exit(exit_relayfs_fs) | |
571 | - | |
572 | -EXPORT_SYMBOL_GPL(relay_file_operations); | |
573 | -EXPORT_SYMBOL_GPL(relayfs_create_dir); | |
574 | -EXPORT_SYMBOL_GPL(relayfs_remove_dir); | |
575 | -EXPORT_SYMBOL_GPL(relayfs_create_file); | |
576 | -EXPORT_SYMBOL_GPL(relayfs_remove_file); | |
577 | - | |
578 | -MODULE_AUTHOR("Tom Zanussi <zanussi@us.ibm.com> and Karim Yaghmour <karim@opersys.com>"); | |
579 | -MODULE_DESCRIPTION("Relay Filesystem"); | |
580 | -MODULE_LICENSE("GPL"); |
fs/relayfs/relay.c
1 | -/* | |
2 | - * Public API and common code for RelayFS. | |
3 | - * | |
4 | - * See Documentation/filesystems/relayfs.txt for an overview of relayfs. | |
5 | - * | |
6 | - * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp | |
7 | - * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com) | |
8 | - * | |
9 | - * This file is released under the GPL. | |
10 | - */ | |
11 | - | |
12 | -#include <linux/errno.h> | |
13 | -#include <linux/stddef.h> | |
14 | -#include <linux/slab.h> | |
15 | -#include <linux/module.h> | |
16 | -#include <linux/string.h> | |
17 | -#include <linux/relayfs_fs.h> | |
18 | -#include "relay.h" | |
19 | -#include "buffers.h" | |
20 | - | |
21 | -/** | |
22 | - * relay_buf_empty - boolean, is the channel buffer empty? | |
23 | - * @buf: channel buffer | |
24 | - * | |
25 | - * Returns 1 if the buffer is empty, 0 otherwise. | |
26 | - */ | |
27 | -int relay_buf_empty(struct rchan_buf *buf) | |
28 | -{ | |
29 | - return (buf->subbufs_produced - buf->subbufs_consumed) ? 0 : 1; | |
30 | -} | |
31 | - | |
32 | -/** | |
33 | - * relay_buf_full - boolean, is the channel buffer full? | |
34 | - * @buf: channel buffer | |
35 | - * | |
36 | - * Returns 1 if the buffer is full, 0 otherwise. | |
37 | - */ | |
38 | -int relay_buf_full(struct rchan_buf *buf) | |
39 | -{ | |
40 | - size_t ready = buf->subbufs_produced - buf->subbufs_consumed; | |
41 | - return (ready >= buf->chan->n_subbufs) ? 1 : 0; | |
42 | -} | |
43 | - | |
44 | -/* | |
45 | - * High-level relayfs kernel API and associated functions. | |
46 | - */ | |
47 | - | |
48 | -/* | |
49 | - * rchan_callback implementations defining default channel behavior. Used | |
50 | - * in place of corresponding NULL values in client callback struct. | |
51 | - */ | |
52 | - | |
53 | -/* | |
54 | - * subbuf_start() default callback. Does nothing. | |
55 | - */ | |
56 | -static int subbuf_start_default_callback (struct rchan_buf *buf, | |
57 | - void *subbuf, | |
58 | - void *prev_subbuf, | |
59 | - size_t prev_padding) | |
60 | -{ | |
61 | - if (relay_buf_full(buf)) | |
62 | - return 0; | |
63 | - | |
64 | - return 1; | |
65 | -} | |
66 | - | |
67 | -/* | |
68 | - * buf_mapped() default callback. Does nothing. | |
69 | - */ | |
70 | -static void buf_mapped_default_callback(struct rchan_buf *buf, | |
71 | - struct file *filp) | |
72 | -{ | |
73 | -} | |
74 | - | |
75 | -/* | |
76 | - * buf_unmapped() default callback. Does nothing. | |
77 | - */ | |
78 | -static void buf_unmapped_default_callback(struct rchan_buf *buf, | |
79 | - struct file *filp) | |
80 | -{ | |
81 | -} | |
82 | - | |
83 | -/* | |
84 | - * create_buf_file_create() default callback. Creates file to represent buf. | |
85 | - */ | |
86 | -static struct dentry *create_buf_file_default_callback(const char *filename, | |
87 | - struct dentry *parent, | |
88 | - int mode, | |
89 | - struct rchan_buf *buf, | |
90 | - int *is_global) | |
91 | -{ | |
92 | - return relayfs_create_file(filename, parent, mode, | |
93 | - &relay_file_operations, buf); | |
94 | -} | |
95 | - | |
96 | -/* | |
97 | - * remove_buf_file() default callback. Removes file representing relay buffer. | |
98 | - */ | |
99 | -static int remove_buf_file_default_callback(struct dentry *dentry) | |
100 | -{ | |
101 | - return relayfs_remove(dentry); | |
102 | -} | |
103 | - | |
104 | -/* relay channel default callbacks */ | |
105 | -static struct rchan_callbacks default_channel_callbacks = { | |
106 | - .subbuf_start = subbuf_start_default_callback, | |
107 | - .buf_mapped = buf_mapped_default_callback, | |
108 | - .buf_unmapped = buf_unmapped_default_callback, | |
109 | - .create_buf_file = create_buf_file_default_callback, | |
110 | - .remove_buf_file = remove_buf_file_default_callback, | |
111 | -}; | |
112 | - | |
113 | -/** | |
114 | - * wakeup_readers - wake up readers waiting on a channel | |
115 | - * @private: the channel buffer | |
116 | - * | |
117 | - * This is the work function used to defer reader waking. The | |
118 | - * reason waking is deferred is that calling directly from write | |
119 | - * causes problems if you're writing from say the scheduler. | |
120 | - */ | |
121 | -static void wakeup_readers(void *private) | |
122 | -{ | |
123 | - struct rchan_buf *buf = private; | |
124 | - wake_up_interruptible(&buf->read_wait); | |
125 | -} | |
126 | - | |
127 | -/** | |
128 | - * __relay_reset - reset a channel buffer | |
129 | - * @buf: the channel buffer | |
130 | - * @init: 1 if this is a first-time initialization | |
131 | - * | |
132 | - * See relay_reset for description of effect. | |
133 | - */ | |
134 | -static inline void __relay_reset(struct rchan_buf *buf, unsigned int init) | |
135 | -{ | |
136 | - size_t i; | |
137 | - | |
138 | - if (init) { | |
139 | - init_waitqueue_head(&buf->read_wait); | |
140 | - kref_init(&buf->kref); | |
141 | - INIT_WORK(&buf->wake_readers, NULL, NULL); | |
142 | - } else { | |
143 | - cancel_delayed_work(&buf->wake_readers); | |
144 | - flush_scheduled_work(); | |
145 | - } | |
146 | - | |
147 | - buf->subbufs_produced = 0; | |
148 | - buf->subbufs_consumed = 0; | |
149 | - buf->bytes_consumed = 0; | |
150 | - buf->finalized = 0; | |
151 | - buf->data = buf->start; | |
152 | - buf->offset = 0; | |
153 | - | |
154 | - for (i = 0; i < buf->chan->n_subbufs; i++) | |
155 | - buf->padding[i] = 0; | |
156 | - | |
157 | - buf->chan->cb->subbuf_start(buf, buf->data, NULL, 0); | |
158 | -} | |
159 | - | |
160 | -/** | |
161 | - * relay_reset - reset the channel | |
162 | - * @chan: the channel | |
163 | - * | |
164 | - * This has the effect of erasing all data from all channel buffers | |
165 | - * and restarting the channel in its initial state. The buffers | |
166 | - * are not freed, so any mappings are still in effect. | |
167 | - * | |
168 | - * NOTE: Care should be taken that the channel isn't actually | |
169 | - * being used by anything when this call is made. | |
170 | - */ | |
171 | -void relay_reset(struct rchan *chan) | |
172 | -{ | |
173 | - unsigned int i; | |
174 | - struct rchan_buf *prev = NULL; | |
175 | - | |
176 | - if (!chan) | |
177 | - return; | |
178 | - | |
179 | - for (i = 0; i < NR_CPUS; i++) { | |
180 | - if (!chan->buf[i] || chan->buf[i] == prev) | |
181 | - break; | |
182 | - __relay_reset(chan->buf[i], 0); | |
183 | - prev = chan->buf[i]; | |
184 | - } | |
185 | -} | |
186 | - | |
187 | -/** | |
188 | - * relay_open_buf - create a new channel buffer in relayfs | |
189 | - * | |
190 | - * Internal - used by relay_open(). | |
191 | - */ | |
192 | -static struct rchan_buf *relay_open_buf(struct rchan *chan, | |
193 | - const char *filename, | |
194 | - struct dentry *parent, | |
195 | - int *is_global) | |
196 | -{ | |
197 | - struct rchan_buf *buf; | |
198 | - struct dentry *dentry; | |
199 | - | |
200 | - if (*is_global) | |
201 | - return chan->buf[0]; | |
202 | - | |
203 | - buf = relay_create_buf(chan); | |
204 | - if (!buf) | |
205 | - return NULL; | |
206 | - | |
207 | - /* Create file in fs */ | |
208 | - dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR, | |
209 | - buf, is_global); | |
210 | - if (!dentry) { | |
211 | - relay_destroy_buf(buf); | |
212 | - return NULL; | |
213 | - } | |
214 | - | |
215 | - buf->dentry = dentry; | |
216 | - __relay_reset(buf, 1); | |
217 | - | |
218 | - return buf; | |
219 | -} | |
220 | - | |
221 | -/** | |
222 | - * relay_close_buf - close a channel buffer | |
223 | - * @buf: channel buffer | |
224 | - * | |
225 | - * Marks the buffer finalized and restores the default callbacks. | |
226 | - * The channel buffer and channel buffer data structure are then freed | |
227 | - * automatically when the last reference is given up. | |
228 | - */ | |
229 | -static inline void relay_close_buf(struct rchan_buf *buf) | |
230 | -{ | |
231 | - buf->finalized = 1; | |
232 | - buf->chan->cb = &default_channel_callbacks; | |
233 | - cancel_delayed_work(&buf->wake_readers); | |
234 | - flush_scheduled_work(); | |
235 | - kref_put(&buf->kref, relay_remove_buf); | |
236 | -} | |
237 | - | |
238 | -static inline void setup_callbacks(struct rchan *chan, | |
239 | - struct rchan_callbacks *cb) | |
240 | -{ | |
241 | - if (!cb) { | |
242 | - chan->cb = &default_channel_callbacks; | |
243 | - return; | |
244 | - } | |
245 | - | |
246 | - if (!cb->subbuf_start) | |
247 | - cb->subbuf_start = subbuf_start_default_callback; | |
248 | - if (!cb->buf_mapped) | |
249 | - cb->buf_mapped = buf_mapped_default_callback; | |
250 | - if (!cb->buf_unmapped) | |
251 | - cb->buf_unmapped = buf_unmapped_default_callback; | |
252 | - if (!cb->create_buf_file) | |
253 | - cb->create_buf_file = create_buf_file_default_callback; | |
254 | - if (!cb->remove_buf_file) | |
255 | - cb->remove_buf_file = remove_buf_file_default_callback; | |
256 | - chan->cb = cb; | |
257 | -} | |
258 | - | |
259 | -/** | |
260 | - * relay_open - create a new relayfs channel | |
261 | - * @base_filename: base name of files to create | |
262 | - * @parent: dentry of parent directory, NULL for root directory | |
263 | - * @subbuf_size: size of sub-buffers | |
264 | - * @n_subbufs: number of sub-buffers | |
265 | - * @cb: client callback functions | |
266 | - * | |
267 | - * Returns channel pointer if successful, NULL otherwise. | |
268 | - * | |
269 | - * Creates a channel buffer for each cpu using the sizes and | |
270 | - * attributes specified. The created channel buffer files | |
271 | - * will be named base_filename0...base_filenameN-1. File | |
272 | - * permissions will be S_IRUSR. | |
273 | - */ | |
274 | -struct rchan *relay_open(const char *base_filename, | |
275 | - struct dentry *parent, | |
276 | - size_t subbuf_size, | |
277 | - size_t n_subbufs, | |
278 | - struct rchan_callbacks *cb) | |
279 | -{ | |
280 | - unsigned int i; | |
281 | - struct rchan *chan; | |
282 | - char *tmpname; | |
283 | - int is_global = 0; | |
284 | - | |
285 | - if (!base_filename) | |
286 | - return NULL; | |
287 | - | |
288 | - if (!(subbuf_size && n_subbufs)) | |
289 | - return NULL; | |
290 | - | |
291 | - chan = kcalloc(1, sizeof(struct rchan), GFP_KERNEL); | |
292 | - if (!chan) | |
293 | - return NULL; | |
294 | - | |
295 | - chan->version = RELAYFS_CHANNEL_VERSION; | |
296 | - chan->n_subbufs = n_subbufs; | |
297 | - chan->subbuf_size = subbuf_size; | |
298 | - chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); | |
299 | - setup_callbacks(chan, cb); | |
300 | - kref_init(&chan->kref); | |
301 | - | |
302 | - tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL); | |
303 | - if (!tmpname) | |
304 | - goto free_chan; | |
305 | - | |
306 | - for_each_online_cpu(i) { | |
307 | - sprintf(tmpname, "%s%d", base_filename, i); | |
308 | - chan->buf[i] = relay_open_buf(chan, tmpname, parent, | |
309 | - &is_global); | |
310 | - chan->buf[i]->cpu = i; | |
311 | - if (!chan->buf[i]) | |
312 | - goto free_bufs; | |
313 | - } | |
314 | - | |
315 | - kfree(tmpname); | |
316 | - return chan; | |
317 | - | |
318 | -free_bufs: | |
319 | - for (i = 0; i < NR_CPUS; i++) { | |
320 | - if (!chan->buf[i]) | |
321 | - break; | |
322 | - relay_close_buf(chan->buf[i]); | |
323 | - if (is_global) | |
324 | - break; | |
325 | - } | |
326 | - kfree(tmpname); | |
327 | - | |
328 | -free_chan: | |
329 | - kref_put(&chan->kref, relay_destroy_channel); | |
330 | - return NULL; | |
331 | -} | |
332 | - | |
333 | -/** | |
334 | - * relay_switch_subbuf - switch to a new sub-buffer | |
335 | - * @buf: channel buffer | |
336 | - * @length: size of current event | |
337 | - * | |
338 | - * Returns either the length passed in or 0 if full. | |
339 | - | |
340 | - * Performs sub-buffer-switch tasks such as invoking callbacks, | |
341 | - * updating padding counts, waking up readers, etc. | |
342 | - */ | |
343 | -size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length) | |
344 | -{ | |
345 | - void *old, *new; | |
346 | - size_t old_subbuf, new_subbuf; | |
347 | - | |
348 | - if (unlikely(length > buf->chan->subbuf_size)) | |
349 | - goto toobig; | |
350 | - | |
351 | - if (buf->offset != buf->chan->subbuf_size + 1) { | |
352 | - buf->prev_padding = buf->chan->subbuf_size - buf->offset; | |
353 | - old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; | |
354 | - buf->padding[old_subbuf] = buf->prev_padding; | |
355 | - buf->subbufs_produced++; | |
356 | - if (waitqueue_active(&buf->read_wait)) { | |
357 | - PREPARE_WORK(&buf->wake_readers, wakeup_readers, buf); | |
358 | - schedule_delayed_work(&buf->wake_readers, 1); | |
359 | - } | |
360 | - } | |
361 | - | |
362 | - old = buf->data; | |
363 | - new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; | |
364 | - new = buf->start + new_subbuf * buf->chan->subbuf_size; | |
365 | - buf->offset = 0; | |
366 | - if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) { | |
367 | - buf->offset = buf->chan->subbuf_size + 1; | |
368 | - return 0; | |
369 | - } | |
370 | - buf->data = new; | |
371 | - buf->padding[new_subbuf] = 0; | |
372 | - | |
373 | - if (unlikely(length + buf->offset > buf->chan->subbuf_size)) | |
374 | - goto toobig; | |
375 | - | |
376 | - return length; | |
377 | - | |
378 | -toobig: | |
379 | - buf->chan->last_toobig = length; | |
380 | - return 0; | |
381 | -} | |
382 | - | |
383 | -/** | |
384 | - * relay_subbufs_consumed - update the buffer's sub-buffers-consumed count | |
385 | - * @chan: the channel | |
386 | - * @cpu: the cpu associated with the channel buffer to update | |
387 | - * @subbufs_consumed: number of sub-buffers to add to current buf's count | |
388 | - * | |
389 | - * Adds to the channel buffer's consumed sub-buffer count. | |
390 | - * subbufs_consumed should be the number of sub-buffers newly consumed, | |
391 | - * not the total consumed. | |
392 | - * | |
393 | - * NOTE: kernel clients don't need to call this function if the channel | |
394 | - * mode is 'overwrite'. | |
395 | - */ | |
396 | -void relay_subbufs_consumed(struct rchan *chan, | |
397 | - unsigned int cpu, | |
398 | - size_t subbufs_consumed) | |
399 | -{ | |
400 | - struct rchan_buf *buf; | |
401 | - | |
402 | - if (!chan) | |
403 | - return; | |
404 | - | |
405 | - if (cpu >= NR_CPUS || !chan->buf[cpu]) | |
406 | - return; | |
407 | - | |
408 | - buf = chan->buf[cpu]; | |
409 | - buf->subbufs_consumed += subbufs_consumed; | |
410 | - if (buf->subbufs_consumed > buf->subbufs_produced) | |
411 | - buf->subbufs_consumed = buf->subbufs_produced; | |
412 | -} | |
413 | - | |
414 | -/** | |
415 | - * relay_destroy_channel - free the channel struct | |
416 | - * | |
417 | - * Should only be called from kref_put(). | |
418 | - */ | |
419 | -void relay_destroy_channel(struct kref *kref) | |
420 | -{ | |
421 | - struct rchan *chan = container_of(kref, struct rchan, kref); | |
422 | - kfree(chan); | |
423 | -} | |
424 | - | |
425 | -/** | |
426 | - * relay_close - close the channel | |
427 | - * @chan: the channel | |
428 | - * | |
429 | - * Closes all channel buffers and frees the channel. | |
430 | - */ | |
431 | -void relay_close(struct rchan *chan) | |
432 | -{ | |
433 | - unsigned int i; | |
434 | - struct rchan_buf *prev = NULL; | |
435 | - | |
436 | - if (!chan) | |
437 | - return; | |
438 | - | |
439 | - for (i = 0; i < NR_CPUS; i++) { | |
440 | - if (!chan->buf[i] || chan->buf[i] == prev) | |
441 | - break; | |
442 | - relay_close_buf(chan->buf[i]); | |
443 | - prev = chan->buf[i]; | |
444 | - } | |
445 | - | |
446 | - if (chan->last_toobig) | |
447 | - printk(KERN_WARNING "relayfs: one or more items not logged " | |
448 | - "[item size (%Zd) > sub-buffer size (%Zd)]\n", | |
449 | - chan->last_toobig, chan->subbuf_size); | |
450 | - | |
451 | - kref_put(&chan->kref, relay_destroy_channel); | |
452 | -} | |
453 | - | |
454 | -/** | |
455 | - * relay_flush - close the channel | |
456 | - * @chan: the channel | |
457 | - * | |
458 | - * Flushes all channel buffers i.e. forces buffer switch. | |
459 | - */ | |
460 | -void relay_flush(struct rchan *chan) | |
461 | -{ | |
462 | - unsigned int i; | |
463 | - struct rchan_buf *prev = NULL; | |
464 | - | |
465 | - if (!chan) | |
466 | - return; | |
467 | - | |
468 | - for (i = 0; i < NR_CPUS; i++) { | |
469 | - if (!chan->buf[i] || chan->buf[i] == prev) | |
470 | - break; | |
471 | - relay_switch_subbuf(chan->buf[i], 0); | |
472 | - prev = chan->buf[i]; | |
473 | - } | |
474 | -} | |
475 | - | |
476 | -EXPORT_SYMBOL_GPL(relay_open); | |
477 | -EXPORT_SYMBOL_GPL(relay_close); | |
478 | -EXPORT_SYMBOL_GPL(relay_flush); | |
479 | -EXPORT_SYMBOL_GPL(relay_reset); | |
480 | -EXPORT_SYMBOL_GPL(relay_subbufs_consumed); | |
481 | -EXPORT_SYMBOL_GPL(relay_switch_subbuf); | |
482 | -EXPORT_SYMBOL_GPL(relay_buf_full); |
fs/relayfs/relay.h
include/linux/relay.h
1 | +/* | |
2 | + * linux/include/linux/relay.h | |
3 | + * | |
4 | + * Copyright (C) 2002, 2003 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp | |
5 | + * Copyright (C) 1999, 2000, 2001, 2002 - Karim Yaghmour (karim@opersys.com) | |
6 | + * | |
7 | + * CONFIG_RELAY definitions and declarations | |
8 | + */ | |
9 | + | |
10 | +#ifndef _LINUX_RELAY_H | |
11 | +#define _LINUX_RELAY_H | |
12 | + | |
13 | +#include <linux/config.h> | |
14 | +#include <linux/types.h> | |
15 | +#include <linux/sched.h> | |
16 | +#include <linux/wait.h> | |
17 | +#include <linux/list.h> | |
18 | +#include <linux/fs.h> | |
19 | +#include <linux/poll.h> | |
20 | +#include <linux/kref.h> | |
21 | + | |
22 | +/* Needs a _much_ better name... */ | |
23 | +#define FIX_SIZE(x) ((((x) - 1) & PAGE_MASK) + PAGE_SIZE) | |
24 | + | |
25 | +/* | |
26 | + * Tracks changes to rchan/rchan_buf structs | |
27 | + */ | |
28 | +#define RELAYFS_CHANNEL_VERSION 6 | |
29 | + | |
30 | +/* | |
31 | + * Per-cpu relay channel buffer | |
32 | + */ | |
33 | +struct rchan_buf | |
34 | +{ | |
35 | + void *start; /* start of channel buffer */ | |
36 | + void *data; /* start of current sub-buffer */ | |
37 | + size_t offset; /* current offset into sub-buffer */ | |
38 | + size_t subbufs_produced; /* count of sub-buffers produced */ | |
39 | + size_t subbufs_consumed; /* count of sub-buffers consumed */ | |
40 | + struct rchan *chan; /* associated channel */ | |
41 | + wait_queue_head_t read_wait; /* reader wait queue */ | |
42 | + struct work_struct wake_readers; /* reader wake-up work struct */ | |
43 | + struct dentry *dentry; /* channel file dentry */ | |
44 | + struct kref kref; /* channel buffer refcount */ | |
45 | + struct page **page_array; /* array of current buffer pages */ | |
46 | + unsigned int page_count; /* number of current buffer pages */ | |
47 | + unsigned int finalized; /* buffer has been finalized */ | |
48 | + size_t *padding; /* padding counts per sub-buffer */ | |
49 | + size_t prev_padding; /* temporary variable */ | |
50 | + size_t bytes_consumed; /* bytes consumed in cur read subbuf */ | |
51 | + unsigned int cpu; /* this buf's cpu */ | |
52 | +} ____cacheline_aligned; | |
53 | + | |
54 | +/* | |
55 | + * Relay channel data structure | |
56 | + */ | |
57 | +struct rchan | |
58 | +{ | |
59 | + u32 version; /* the version of this struct */ | |
60 | + size_t subbuf_size; /* sub-buffer size */ | |
61 | + size_t n_subbufs; /* number of sub-buffers per buffer */ | |
62 | + size_t alloc_size; /* total buffer size allocated */ | |
63 | + struct rchan_callbacks *cb; /* client callbacks */ | |
64 | + struct kref kref; /* channel refcount */ | |
65 | + void *private_data; /* for user-defined data */ | |
66 | + size_t last_toobig; /* tried to log event > subbuf size */ | |
67 | + struct rchan_buf *buf[NR_CPUS]; /* per-cpu channel buffers */ | |
68 | +}; | |
69 | + | |
70 | +/* | |
71 | + * Relay channel client callbacks | |
72 | + */ | |
73 | +struct rchan_callbacks | |
74 | +{ | |
75 | + /* | |
76 | + * subbuf_start - called on buffer-switch to a new sub-buffer | |
77 | + * @buf: the channel buffer containing the new sub-buffer | |
78 | + * @subbuf: the start of the new sub-buffer | |
79 | + * @prev_subbuf: the start of the previous sub-buffer | |
80 | + * @prev_padding: unused space at the end of previous sub-buffer | |
81 | + * | |
82 | + * The client should return 1 to continue logging, 0 to stop | |
83 | + * logging. | |
84 | + * | |
85 | + * NOTE: subbuf_start will also be invoked when the buffer is | |
86 | + * created, so that the first sub-buffer can be initialized | |
87 | + * if necessary. In this case, prev_subbuf will be NULL. | |
88 | + * | |
89 | + * NOTE: the client can reserve bytes at the beginning of the new | |
90 | + * sub-buffer by calling subbuf_start_reserve() in this callback. | |
91 | + */ | |
92 | + int (*subbuf_start) (struct rchan_buf *buf, | |
93 | + void *subbuf, | |
94 | + void *prev_subbuf, | |
95 | + size_t prev_padding); | |
96 | + | |
97 | + /* | |
98 | + * buf_mapped - relay buffer mmap notification | |
99 | + * @buf: the channel buffer | |
100 | + * @filp: relay file pointer | |
101 | + * | |
102 | + * Called when a relay file is successfully mmapped | |
103 | + */ | |
104 | + void (*buf_mapped)(struct rchan_buf *buf, | |
105 | + struct file *filp); | |
106 | + | |
107 | + /* | |
108 | + * buf_unmapped - relay buffer unmap notification | |
109 | + * @buf: the channel buffer | |
110 | + * @filp: relay file pointer | |
111 | + * | |
112 | + * Called when a relay file is successfully unmapped | |
113 | + */ | |
114 | + void (*buf_unmapped)(struct rchan_buf *buf, | |
115 | + struct file *filp); | |
116 | + /* | |
117 | + * create_buf_file - create file to represent a relay channel buffer | |
118 | + * @filename: the name of the file to create | |
119 | + * @parent: the parent of the file to create | |
120 | + * @mode: the mode of the file to create | |
121 | + * @buf: the channel buffer | |
122 | + * @is_global: outparam - set non-zero if the buffer should be global | |
123 | + * | |
124 | + * Called during relay_open(), once for each per-cpu buffer, | |
125 | + * to allow the client to create a file to be used to | |
126 | + * represent the corresponding channel buffer. If the file is | |
127 | + * created outside of relay, the parent must also exist in | |
128 | + * that filesystem. | |
129 | + * | |
130 | + * The callback should return the dentry of the file created | |
131 | + * to represent the relay buffer. | |
132 | + * | |
133 | + * Setting the is_global outparam to a non-zero value will | |
134 | + * cause relay_open() to create a single global buffer rather | |
135 | + * than the default set of per-cpu buffers. | |
136 | + * | |
137 | + * See Documentation/filesystems/relayfs.txt for more info. | |
138 | + */ | |
139 | + struct dentry *(*create_buf_file)(const char *filename, | |
140 | + struct dentry *parent, | |
141 | + int mode, | |
142 | + struct rchan_buf *buf, | |
143 | + int *is_global); | |
144 | + | |
145 | + /* | |
146 | + * remove_buf_file - remove file representing a relay channel buffer | |
147 | + * @dentry: the dentry of the file to remove | |
148 | + * | |
149 | + * Called during relay_close(), once for each per-cpu buffer, | |
150 | + * to allow the client to remove a file used to represent a | |
151 | + * channel buffer. | |
152 | + * | |
153 | + * The callback should return 0 if successful, negative if not. | |
154 | + */ | |
155 | + int (*remove_buf_file)(struct dentry *dentry); | |
156 | +}; | |
157 | + | |
158 | +/* | |
159 | + * CONFIG_RELAY kernel API, kernel/relay.c | |
160 | + */ | |
161 | + | |
162 | +struct rchan *relay_open(const char *base_filename, | |
163 | + struct dentry *parent, | |
164 | + size_t subbuf_size, | |
165 | + size_t n_subbufs, | |
166 | + struct rchan_callbacks *cb); | |
167 | +extern void relay_close(struct rchan *chan); | |
168 | +extern void relay_flush(struct rchan *chan); | |
169 | +extern void relay_subbufs_consumed(struct rchan *chan, | |
170 | + unsigned int cpu, | |
171 | + size_t consumed); | |
172 | +extern void relay_reset(struct rchan *chan); | |
173 | +extern int relay_buf_full(struct rchan_buf *buf); | |
174 | + | |
175 | +extern size_t relay_switch_subbuf(struct rchan_buf *buf, | |
176 | + size_t length); | |
177 | + | |
178 | +/** | |
179 | + * relay_write - write data into the channel | |
180 | + * @chan: relay channel | |
181 | + * @data: data to be written | |
182 | + * @length: number of bytes to write | |
183 | + * | |
184 | + * Writes data into the current cpu's channel buffer. | |
185 | + * | |
186 | + * Protects the buffer by disabling interrupts. Use this | |
187 | + * if you might be logging from interrupt context. Try | |
188 | + * __relay_write() if you know you won't be logging from | |
189 | + * interrupt context. | |
190 | + */ | |
191 | +static inline void relay_write(struct rchan *chan, | |
192 | + const void *data, | |
193 | + size_t length) | |
194 | +{ | |
195 | + unsigned long flags; | |
196 | + struct rchan_buf *buf; | |
197 | + | |
198 | + local_irq_save(flags); | |
199 | + buf = chan->buf[smp_processor_id()]; | |
200 | + if (unlikely(buf->offset + length > chan->subbuf_size)) | |
201 | + length = relay_switch_subbuf(buf, length); | |
202 | + memcpy(buf->data + buf->offset, data, length); | |
203 | + buf->offset += length; | |
204 | + local_irq_restore(flags); | |
205 | +} | |
206 | + | |
207 | +/** | |
208 | + * __relay_write - write data into the channel | |
209 | + * @chan: relay channel | |
210 | + * @data: data to be written | |
211 | + * @length: number of bytes to write | |
212 | + * | |
213 | + * Writes data into the current cpu's channel buffer. | |
214 | + * | |
215 | + * Protects the buffer by disabling preemption. Use | |
216 | + * relay_write() if you might be logging from interrupt | |
217 | + * context. | |
218 | + */ | |
219 | +static inline void __relay_write(struct rchan *chan, | |
220 | + const void *data, | |
221 | + size_t length) | |
222 | +{ | |
223 | + struct rchan_buf *buf; | |
224 | + | |
225 | + buf = chan->buf[get_cpu()]; | |
226 | + if (unlikely(buf->offset + length > buf->chan->subbuf_size)) | |
227 | + length = relay_switch_subbuf(buf, length); | |
228 | + memcpy(buf->data + buf->offset, data, length); | |
229 | + buf->offset += length; | |
230 | + put_cpu(); | |
231 | +} | |
232 | + | |
233 | +/** | |
234 | + * relay_reserve - reserve slot in channel buffer | |
235 | + * @chan: relay channel | |
236 | + * @length: number of bytes to reserve | |
237 | + * | |
238 | + * Returns pointer to reserved slot, NULL if full. | |
239 | + * | |
240 | + * Reserves a slot in the current cpu's channel buffer. | |
241 | + * Does not protect the buffer at all - caller must provide | |
242 | + * appropriate synchronization. | |
243 | + */ | |
244 | +static inline void *relay_reserve(struct rchan *chan, size_t length) | |
245 | +{ | |
246 | + void *reserved; | |
247 | + struct rchan_buf *buf = chan->buf[smp_processor_id()]; | |
248 | + | |
249 | + if (unlikely(buf->offset + length > buf->chan->subbuf_size)) { | |
250 | + length = relay_switch_subbuf(buf, length); | |
251 | + if (!length) | |
252 | + return NULL; | |
253 | + } | |
254 | + reserved = buf->data + buf->offset; | |
255 | + buf->offset += length; | |
256 | + | |
257 | + return reserved; | |
258 | +} | |
259 | + | |
260 | +/** | |
261 | + * subbuf_start_reserve - reserve bytes at the start of a sub-buffer | |
262 | + * @buf: relay channel buffer | |
263 | + * @length: number of bytes to reserve | |
264 | + * | |
265 | + * Helper function used to reserve bytes at the beginning of | |
266 | + * a sub-buffer in the subbuf_start() callback. | |
267 | + */ | |
268 | +static inline void subbuf_start_reserve(struct rchan_buf *buf, | |
269 | + size_t length) | |
270 | +{ | |
271 | + BUG_ON(length >= buf->chan->subbuf_size - 1); | |
272 | + buf->offset = length; | |
273 | +} | |
274 | + | |
275 | +/* | |
276 | + * exported relay file operations, kernel/relay.c | |
277 | + */ | |
278 | +extern struct file_operations relay_file_operations; | |
279 | + | |
280 | +#endif /* _LINUX_RELAY_H */ |
init/Kconfig
... | ... | @@ -214,6 +214,17 @@ |
214 | 214 | |
215 | 215 | Say N if unsure. |
216 | 216 | |
217 | +config RELAY | |
218 | + bool "Kernel->user space relay support (formerly relayfs)" | |
219 | + help | |
220 | + This option enables support for relay interface support in | |
221 | + certain file systems (such as debugfs). | |
222 | + It is designed to provide an efficient mechanism for tools and | |
223 | + facilities to relay large amounts of data from kernel space to | |
224 | + user space. | |
225 | + | |
226 | + If unsure, say N. | |
227 | + | |
217 | 228 | source "usr/Kconfig" |
218 | 229 | |
219 | 230 | config UID16 |
kernel/Makefile
... | ... | @@ -34,6 +34,7 @@ |
34 | 34 | obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ |
35 | 35 | obj-$(CONFIG_SECCOMP) += seccomp.o |
36 | 36 | obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o |
37 | +obj-$(CONFIG_RELAY) += relay.o | |
37 | 38 | |
38 | 39 | ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) |
39 | 40 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is |
kernel/relay.c
1 | +/* | |
2 | + * Public API and common code for kernel->userspace relay file support. | |
3 | + * | |
4 | + * See Documentation/filesystems/relayfs.txt for an overview of relayfs. | |
5 | + * | |
6 | + * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp | |
7 | + * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com) | |
8 | + * | |
9 | + * Moved to kernel/relay.c by Paul Mundt, 2006. | |
10 | + * | |
11 | + * This file is released under the GPL. | |
12 | + */ | |
13 | +#include <linux/errno.h> | |
14 | +#include <linux/stddef.h> | |
15 | +#include <linux/slab.h> | |
16 | +#include <linux/module.h> | |
17 | +#include <linux/string.h> | |
18 | +#include <linux/relay.h> | |
19 | +#include <linux/vmalloc.h> | |
20 | +#include <linux/mm.h> | |
21 | + | |
22 | +/* | |
23 | + * close() vm_op implementation for relay file mapping. | |
24 | + */ | |
25 | +static void relay_file_mmap_close(struct vm_area_struct *vma) | |
26 | +{ | |
27 | + struct rchan_buf *buf = vma->vm_private_data; | |
28 | + buf->chan->cb->buf_unmapped(buf, vma->vm_file); | |
29 | +} | |
30 | + | |
31 | +/* | |
32 | + * nopage() vm_op implementation for relay file mapping. | |
33 | + */ | |
34 | +static struct page *relay_buf_nopage(struct vm_area_struct *vma, | |
35 | + unsigned long address, | |
36 | + int *type) | |
37 | +{ | |
38 | + struct page *page; | |
39 | + struct rchan_buf *buf = vma->vm_private_data; | |
40 | + unsigned long offset = address - vma->vm_start; | |
41 | + | |
42 | + if (address > vma->vm_end) | |
43 | + return NOPAGE_SIGBUS; /* Disallow mremap */ | |
44 | + if (!buf) | |
45 | + return NOPAGE_OOM; | |
46 | + | |
47 | + page = vmalloc_to_page(buf->start + offset); | |
48 | + if (!page) | |
49 | + return NOPAGE_OOM; | |
50 | + get_page(page); | |
51 | + | |
52 | + if (type) | |
53 | + *type = VM_FAULT_MINOR; | |
54 | + | |
55 | + return page; | |
56 | +} | |
57 | + | |
58 | +/* | |
59 | + * vm_ops for relay file mappings. | |
60 | + */ | |
61 | +static struct vm_operations_struct relay_file_mmap_ops = { | |
62 | + .nopage = relay_buf_nopage, | |
63 | + .close = relay_file_mmap_close, | |
64 | +}; | |
65 | + | |
66 | +/** | |
67 | + * relay_mmap_buf: - mmap channel buffer to process address space | |
68 | + * @buf: relay channel buffer | |
69 | + * @vma: vm_area_struct describing memory to be mapped | |
70 | + * | |
71 | + * Returns 0 if ok, negative on error | |
72 | + * | |
73 | + * Caller should already have grabbed mmap_sem. | |
74 | + */ | |
75 | +int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma) | |
76 | +{ | |
77 | + unsigned long length = vma->vm_end - vma->vm_start; | |
78 | + struct file *filp = vma->vm_file; | |
79 | + | |
80 | + if (!buf) | |
81 | + return -EBADF; | |
82 | + | |
83 | + if (length != (unsigned long)buf->chan->alloc_size) | |
84 | + return -EINVAL; | |
85 | + | |
86 | + vma->vm_ops = &relay_file_mmap_ops; | |
87 | + vma->vm_private_data = buf; | |
88 | + buf->chan->cb->buf_mapped(buf, filp); | |
89 | + | |
90 | + return 0; | |
91 | +} | |
92 | + | |
93 | +/** | |
94 | + * relay_alloc_buf - allocate a channel buffer | |
95 | + * @buf: the buffer struct | |
96 | + * @size: total size of the buffer | |
97 | + * | |
98 | + * Returns a pointer to the resulting buffer, NULL if unsuccessful | |
99 | + */ | |
100 | +static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size) | |
101 | +{ | |
102 | + void *mem; | |
103 | + unsigned int i, j, n_pages; | |
104 | + | |
105 | + size = PAGE_ALIGN(size); | |
106 | + n_pages = size >> PAGE_SHIFT; | |
107 | + | |
108 | + buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL); | |
109 | + if (!buf->page_array) | |
110 | + return NULL; | |
111 | + | |
112 | + for (i = 0; i < n_pages; i++) { | |
113 | + buf->page_array[i] = alloc_page(GFP_KERNEL); | |
114 | + if (unlikely(!buf->page_array[i])) | |
115 | + goto depopulate; | |
116 | + } | |
117 | + mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL); | |
118 | + if (!mem) | |
119 | + goto depopulate; | |
120 | + | |
121 | + memset(mem, 0, size); | |
122 | + buf->page_count = n_pages; | |
123 | + return mem; | |
124 | + | |
125 | +depopulate: | |
126 | + for (j = 0; j < i; j++) | |
127 | + __free_page(buf->page_array[j]); | |
128 | + kfree(buf->page_array); | |
129 | + return NULL; | |
130 | +} | |
131 | + | |
132 | +/** | |
133 | + * relay_create_buf - allocate and initialize a channel buffer | |
134 | + * @alloc_size: size of the buffer to allocate | |
135 | + * @n_subbufs: number of sub-buffers in the channel | |
136 | + * | |
137 | + * Returns channel buffer if successful, NULL otherwise | |
138 | + */ | |
139 | +struct rchan_buf *relay_create_buf(struct rchan *chan) | |
140 | +{ | |
141 | + struct rchan_buf *buf = kcalloc(1, sizeof(struct rchan_buf), GFP_KERNEL); | |
142 | + if (!buf) | |
143 | + return NULL; | |
144 | + | |
145 | + buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); | |
146 | + if (!buf->padding) | |
147 | + goto free_buf; | |
148 | + | |
149 | + buf->start = relay_alloc_buf(buf, chan->alloc_size); | |
150 | + if (!buf->start) | |
151 | + goto free_buf; | |
152 | + | |
153 | + buf->chan = chan; | |
154 | + kref_get(&buf->chan->kref); | |
155 | + return buf; | |
156 | + | |
157 | +free_buf: | |
158 | + kfree(buf->padding); | |
159 | + kfree(buf); | |
160 | + return NULL; | |
161 | +} | |
162 | + | |
163 | +/** | |
164 | + * relay_destroy_channel - free the channel struct | |
165 | + * | |
166 | + * Should only be called from kref_put(). | |
167 | + */ | |
168 | +void relay_destroy_channel(struct kref *kref) | |
169 | +{ | |
170 | + struct rchan *chan = container_of(kref, struct rchan, kref); | |
171 | + kfree(chan); | |
172 | +} | |
173 | + | |
174 | +/** | |
175 | + * relay_destroy_buf - destroy an rchan_buf struct and associated buffer | |
176 | + * @buf: the buffer struct | |
177 | + */ | |
178 | +void relay_destroy_buf(struct rchan_buf *buf) | |
179 | +{ | |
180 | + struct rchan *chan = buf->chan; | |
181 | + unsigned int i; | |
182 | + | |
183 | + if (likely(buf->start)) { | |
184 | + vunmap(buf->start); | |
185 | + for (i = 0; i < buf->page_count; i++) | |
186 | + __free_page(buf->page_array[i]); | |
187 | + kfree(buf->page_array); | |
188 | + } | |
189 | + kfree(buf->padding); | |
190 | + kfree(buf); | |
191 | + kref_put(&chan->kref, relay_destroy_channel); | |
192 | +} | |
193 | + | |
194 | +/** | |
195 | + * relay_remove_buf - remove a channel buffer | |
196 | + * | |
197 | + * Removes the file from the fileystem, which also frees the | |
198 | + * rchan_buf_struct and the channel buffer. Should only be called from | |
199 | + * kref_put(). | |
200 | + */ | |
201 | +void relay_remove_buf(struct kref *kref) | |
202 | +{ | |
203 | + struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref); | |
204 | + buf->chan->cb->remove_buf_file(buf->dentry); | |
205 | + relay_destroy_buf(buf); | |
206 | +} | |
207 | + | |
208 | +/** | |
209 | + * relay_buf_empty - boolean, is the channel buffer empty? | |
210 | + * @buf: channel buffer | |
211 | + * | |
212 | + * Returns 1 if the buffer is empty, 0 otherwise. | |
213 | + */ | |
214 | +int relay_buf_empty(struct rchan_buf *buf) | |
215 | +{ | |
216 | + return (buf->subbufs_produced - buf->subbufs_consumed) ? 0 : 1; | |
217 | +} | |
218 | +EXPORT_SYMBOL_GPL(relay_buf_empty); | |
219 | + | |
220 | +/** | |
221 | + * relay_buf_full - boolean, is the channel buffer full? | |
222 | + * @buf: channel buffer | |
223 | + * | |
224 | + * Returns 1 if the buffer is full, 0 otherwise. | |
225 | + */ | |
226 | +int relay_buf_full(struct rchan_buf *buf) | |
227 | +{ | |
228 | + size_t ready = buf->subbufs_produced - buf->subbufs_consumed; | |
229 | + return (ready >= buf->chan->n_subbufs) ? 1 : 0; | |
230 | +} | |
231 | +EXPORT_SYMBOL_GPL(relay_buf_full); | |
232 | + | |
233 | +/* | |
234 | + * High-level relay kernel API and associated functions. | |
235 | + */ | |
236 | + | |
237 | +/* | |
238 | + * rchan_callback implementations defining default channel behavior. Used | |
239 | + * in place of corresponding NULL values in client callback struct. | |
240 | + */ | |
241 | + | |
242 | +/* | |
243 | + * subbuf_start() default callback. Does nothing. | |
244 | + */ | |
245 | +static int subbuf_start_default_callback (struct rchan_buf *buf, | |
246 | + void *subbuf, | |
247 | + void *prev_subbuf, | |
248 | + size_t prev_padding) | |
249 | +{ | |
250 | + if (relay_buf_full(buf)) | |
251 | + return 0; | |
252 | + | |
253 | + return 1; | |
254 | +} | |
255 | + | |
256 | +/* | |
257 | + * buf_mapped() default callback. Does nothing. | |
258 | + */ | |
259 | +static void buf_mapped_default_callback(struct rchan_buf *buf, | |
260 | + struct file *filp) | |
261 | +{ | |
262 | +} | |
263 | + | |
264 | +/* | |
265 | + * buf_unmapped() default callback. Does nothing. | |
266 | + */ | |
267 | +static void buf_unmapped_default_callback(struct rchan_buf *buf, | |
268 | + struct file *filp) | |
269 | +{ | |
270 | +} | |
271 | + | |
272 | +/* | |
273 | + * create_buf_file_create() default callback. Does nothing. | |
274 | + */ | |
275 | +static struct dentry *create_buf_file_default_callback(const char *filename, | |
276 | + struct dentry *parent, | |
277 | + int mode, | |
278 | + struct rchan_buf *buf, | |
279 | + int *is_global) | |
280 | +{ | |
281 | + return NULL; | |
282 | +} | |
283 | + | |
284 | +/* | |
285 | + * remove_buf_file() default callback. Does nothing. | |
286 | + */ | |
287 | +static int remove_buf_file_default_callback(struct dentry *dentry) | |
288 | +{ | |
289 | + return -EINVAL; | |
290 | +} | |
291 | + | |
292 | +/* relay channel default callbacks */ | |
293 | +static struct rchan_callbacks default_channel_callbacks = { | |
294 | + .subbuf_start = subbuf_start_default_callback, | |
295 | + .buf_mapped = buf_mapped_default_callback, | |
296 | + .buf_unmapped = buf_unmapped_default_callback, | |
297 | + .create_buf_file = create_buf_file_default_callback, | |
298 | + .remove_buf_file = remove_buf_file_default_callback, | |
299 | +}; | |
300 | + | |
301 | +/** | |
302 | + * wakeup_readers - wake up readers waiting on a channel | |
303 | + * @private: the channel buffer | |
304 | + * | |
305 | + * This is the work function used to defer reader waking. The | |
306 | + * reason waking is deferred is that calling directly from write | |
307 | + * causes problems if you're writing from say the scheduler. | |
308 | + */ | |
309 | +static void wakeup_readers(void *private) | |
310 | +{ | |
311 | + struct rchan_buf *buf = private; | |
312 | + wake_up_interruptible(&buf->read_wait); | |
313 | +} | |
314 | + | |
315 | +/** | |
316 | + * __relay_reset - reset a channel buffer | |
317 | + * @buf: the channel buffer | |
318 | + * @init: 1 if this is a first-time initialization | |
319 | + * | |
320 | + * See relay_reset for description of effect. | |
321 | + */ | |
322 | +static inline void __relay_reset(struct rchan_buf *buf, unsigned int init) | |
323 | +{ | |
324 | + size_t i; | |
325 | + | |
326 | + if (init) { | |
327 | + init_waitqueue_head(&buf->read_wait); | |
328 | + kref_init(&buf->kref); | |
329 | + INIT_WORK(&buf->wake_readers, NULL, NULL); | |
330 | + } else { | |
331 | + cancel_delayed_work(&buf->wake_readers); | |
332 | + flush_scheduled_work(); | |
333 | + } | |
334 | + | |
335 | + buf->subbufs_produced = 0; | |
336 | + buf->subbufs_consumed = 0; | |
337 | + buf->bytes_consumed = 0; | |
338 | + buf->finalized = 0; | |
339 | + buf->data = buf->start; | |
340 | + buf->offset = 0; | |
341 | + | |
342 | + for (i = 0; i < buf->chan->n_subbufs; i++) | |
343 | + buf->padding[i] = 0; | |
344 | + | |
345 | + buf->chan->cb->subbuf_start(buf, buf->data, NULL, 0); | |
346 | +} | |
347 | + | |
348 | +/** | |
349 | + * relay_reset - reset the channel | |
350 | + * @chan: the channel | |
351 | + * | |
352 | + * This has the effect of erasing all data from all channel buffers | |
353 | + * and restarting the channel in its initial state. The buffers | |
354 | + * are not freed, so any mappings are still in effect. | |
355 | + * | |
356 | + * NOTE: Care should be taken that the channel isn't actually | |
357 | + * being used by anything when this call is made. | |
358 | + */ | |
359 | +void relay_reset(struct rchan *chan) | |
360 | +{ | |
361 | + unsigned int i; | |
362 | + struct rchan_buf *prev = NULL; | |
363 | + | |
364 | + if (!chan) | |
365 | + return; | |
366 | + | |
367 | + for (i = 0; i < NR_CPUS; i++) { | |
368 | + if (!chan->buf[i] || chan->buf[i] == prev) | |
369 | + break; | |
370 | + __relay_reset(chan->buf[i], 0); | |
371 | + prev = chan->buf[i]; | |
372 | + } | |
373 | +} | |
374 | +EXPORT_SYMBOL_GPL(relay_reset); | |
375 | + | |
376 | +/** | |
377 | + * relay_open_buf - create a new relay channel buffer | |
378 | + * | |
379 | + * Internal - used by relay_open(). | |
380 | + */ | |
381 | +static struct rchan_buf *relay_open_buf(struct rchan *chan, | |
382 | + const char *filename, | |
383 | + struct dentry *parent, | |
384 | + int *is_global) | |
385 | +{ | |
386 | + struct rchan_buf *buf; | |
387 | + struct dentry *dentry; | |
388 | + | |
389 | + if (*is_global) | |
390 | + return chan->buf[0]; | |
391 | + | |
392 | + buf = relay_create_buf(chan); | |
393 | + if (!buf) | |
394 | + return NULL; | |
395 | + | |
396 | + /* Create file in fs */ | |
397 | + dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR, | |
398 | + buf, is_global); | |
399 | + if (!dentry) { | |
400 | + relay_destroy_buf(buf); | |
401 | + return NULL; | |
402 | + } | |
403 | + | |
404 | + buf->dentry = dentry; | |
405 | + __relay_reset(buf, 1); | |
406 | + | |
407 | + return buf; | |
408 | +} | |
409 | + | |
410 | +/** | |
411 | + * relay_close_buf - close a channel buffer | |
412 | + * @buf: channel buffer | |
413 | + * | |
414 | + * Marks the buffer finalized and restores the default callbacks. | |
415 | + * The channel buffer and channel buffer data structure are then freed | |
416 | + * automatically when the last reference is given up. | |
417 | + */ | |
418 | +static inline void relay_close_buf(struct rchan_buf *buf) | |
419 | +{ | |
420 | + buf->finalized = 1; | |
421 | + cancel_delayed_work(&buf->wake_readers); | |
422 | + flush_scheduled_work(); | |
423 | + kref_put(&buf->kref, relay_remove_buf); | |
424 | +} | |
425 | + | |
426 | +static inline void setup_callbacks(struct rchan *chan, | |
427 | + struct rchan_callbacks *cb) | |
428 | +{ | |
429 | + if (!cb) { | |
430 | + chan->cb = &default_channel_callbacks; | |
431 | + return; | |
432 | + } | |
433 | + | |
434 | + if (!cb->subbuf_start) | |
435 | + cb->subbuf_start = subbuf_start_default_callback; | |
436 | + if (!cb->buf_mapped) | |
437 | + cb->buf_mapped = buf_mapped_default_callback; | |
438 | + if (!cb->buf_unmapped) | |
439 | + cb->buf_unmapped = buf_unmapped_default_callback; | |
440 | + if (!cb->create_buf_file) | |
441 | + cb->create_buf_file = create_buf_file_default_callback; | |
442 | + if (!cb->remove_buf_file) | |
443 | + cb->remove_buf_file = remove_buf_file_default_callback; | |
444 | + chan->cb = cb; | |
445 | +} | |
446 | + | |
447 | +/** | |
448 | + * relay_open - create a new relay channel | |
449 | + * @base_filename: base name of files to create | |
450 | + * @parent: dentry of parent directory, NULL for root directory | |
451 | + * @subbuf_size: size of sub-buffers | |
452 | + * @n_subbufs: number of sub-buffers | |
453 | + * @cb: client callback functions | |
454 | + * | |
455 | + * Returns channel pointer if successful, NULL otherwise. | |
456 | + * | |
457 | + * Creates a channel buffer for each cpu using the sizes and | |
458 | + * attributes specified. The created channel buffer files | |
459 | + * will be named base_filename0...base_filenameN-1. File | |
460 | + * permissions will be S_IRUSR. | |
461 | + */ | |
462 | +struct rchan *relay_open(const char *base_filename, | |
463 | + struct dentry *parent, | |
464 | + size_t subbuf_size, | |
465 | + size_t n_subbufs, | |
466 | + struct rchan_callbacks *cb) | |
467 | +{ | |
468 | + unsigned int i; | |
469 | + struct rchan *chan; | |
470 | + char *tmpname; | |
471 | + int is_global = 0; | |
472 | + | |
473 | + if (!base_filename) | |
474 | + return NULL; | |
475 | + | |
476 | + if (!(subbuf_size && n_subbufs)) | |
477 | + return NULL; | |
478 | + | |
479 | + chan = kcalloc(1, sizeof(struct rchan), GFP_KERNEL); | |
480 | + if (!chan) | |
481 | + return NULL; | |
482 | + | |
483 | + chan->version = RELAYFS_CHANNEL_VERSION; | |
484 | + chan->n_subbufs = n_subbufs; | |
485 | + chan->subbuf_size = subbuf_size; | |
486 | + chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); | |
487 | + setup_callbacks(chan, cb); | |
488 | + kref_init(&chan->kref); | |
489 | + | |
490 | + tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL); | |
491 | + if (!tmpname) | |
492 | + goto free_chan; | |
493 | + | |
494 | + for_each_online_cpu(i) { | |
495 | + sprintf(tmpname, "%s%d", base_filename, i); | |
496 | + chan->buf[i] = relay_open_buf(chan, tmpname, parent, | |
497 | + &is_global); | |
498 | + if (!chan->buf[i]) | |
499 | + goto free_bufs; | |
500 | + | |
501 | + chan->buf[i]->cpu = i; | |
502 | + } | |
503 | + | |
504 | + kfree(tmpname); | |
505 | + return chan; | |
506 | + | |
507 | +free_bufs: | |
508 | + for (i = 0; i < NR_CPUS; i++) { | |
509 | + if (!chan->buf[i]) | |
510 | + break; | |
511 | + relay_close_buf(chan->buf[i]); | |
512 | + if (is_global) | |
513 | + break; | |
514 | + } | |
515 | + kfree(tmpname); | |
516 | + | |
517 | +free_chan: | |
518 | + kref_put(&chan->kref, relay_destroy_channel); | |
519 | + return NULL; | |
520 | +} | |
521 | +EXPORT_SYMBOL_GPL(relay_open); | |
522 | + | |
523 | +/** | |
524 | + * relay_switch_subbuf - switch to a new sub-buffer | |
525 | + * @buf: channel buffer | |
526 | + * @length: size of current event | |
527 | + * | |
528 | + * Returns either the length passed in or 0 if full. | |
529 | + * | |
530 | + * Performs sub-buffer-switch tasks such as invoking callbacks, | |
531 | + * updating padding counts, waking up readers, etc. | |
532 | + */ | |
533 | +size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length) | |
534 | +{ | |
535 | + void *old, *new; | |
536 | + size_t old_subbuf, new_subbuf; | |
537 | + | |
538 | + if (unlikely(length > buf->chan->subbuf_size)) | |
539 | + goto toobig; | |
540 | + | |
541 | + if (buf->offset != buf->chan->subbuf_size + 1) { | |
542 | + buf->prev_padding = buf->chan->subbuf_size - buf->offset; | |
543 | + old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; | |
544 | + buf->padding[old_subbuf] = buf->prev_padding; | |
545 | + buf->subbufs_produced++; | |
546 | + if (waitqueue_active(&buf->read_wait)) { | |
547 | + PREPARE_WORK(&buf->wake_readers, wakeup_readers, buf); | |
548 | + schedule_delayed_work(&buf->wake_readers, 1); | |
549 | + } | |
550 | + } | |
551 | + | |
552 | + old = buf->data; | |
553 | + new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; | |
554 | + new = buf->start + new_subbuf * buf->chan->subbuf_size; | |
555 | + buf->offset = 0; | |
556 | + if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) { | |
557 | + buf->offset = buf->chan->subbuf_size + 1; | |
558 | + return 0; | |
559 | + } | |
560 | + buf->data = new; | |
561 | + buf->padding[new_subbuf] = 0; | |
562 | + | |
563 | + if (unlikely(length + buf->offset > buf->chan->subbuf_size)) | |
564 | + goto toobig; | |
565 | + | |
566 | + return length; | |
567 | + | |
568 | +toobig: | |
569 | + buf->chan->last_toobig = length; | |
570 | + return 0; | |
571 | +} | |
572 | +EXPORT_SYMBOL_GPL(relay_switch_subbuf); | |
573 | + | |
574 | +/** | |
575 | + * relay_subbufs_consumed - update the buffer's sub-buffers-consumed count | |
576 | + * @chan: the channel | |
577 | + * @cpu: the cpu associated with the channel buffer to update | |
578 | + * @subbufs_consumed: number of sub-buffers to add to current buf's count | |
579 | + * | |
580 | + * Adds to the channel buffer's consumed sub-buffer count. | |
581 | + * subbufs_consumed should be the number of sub-buffers newly consumed, | |
582 | + * not the total consumed. | |
583 | + * | |
584 | + * NOTE: kernel clients don't need to call this function if the channel | |
585 | + * mode is 'overwrite'. | |
586 | + */ | |
587 | +void relay_subbufs_consumed(struct rchan *chan, | |
588 | + unsigned int cpu, | |
589 | + size_t subbufs_consumed) | |
590 | +{ | |
591 | + struct rchan_buf *buf; | |
592 | + | |
593 | + if (!chan) | |
594 | + return; | |
595 | + | |
596 | + if (cpu >= NR_CPUS || !chan->buf[cpu]) | |
597 | + return; | |
598 | + | |
599 | + buf = chan->buf[cpu]; | |
600 | + buf->subbufs_consumed += subbufs_consumed; | |
601 | + if (buf->subbufs_consumed > buf->subbufs_produced) | |
602 | + buf->subbufs_consumed = buf->subbufs_produced; | |
603 | +} | |
604 | +EXPORT_SYMBOL_GPL(relay_subbufs_consumed); | |
605 | + | |
606 | +/** | |
607 | + * relay_close - close the channel | |
608 | + * @chan: the channel | |
609 | + * | |
610 | + * Closes all channel buffers and frees the channel. | |
611 | + */ | |
612 | +void relay_close(struct rchan *chan) | |
613 | +{ | |
614 | + unsigned int i; | |
615 | + struct rchan_buf *prev = NULL; | |
616 | + | |
617 | + if (!chan) | |
618 | + return; | |
619 | + | |
620 | + for (i = 0; i < NR_CPUS; i++) { | |
621 | + if (!chan->buf[i] || chan->buf[i] == prev) | |
622 | + break; | |
623 | + relay_close_buf(chan->buf[i]); | |
624 | + prev = chan->buf[i]; | |
625 | + } | |
626 | + | |
627 | + if (chan->last_toobig) | |
628 | + printk(KERN_WARNING "relay: one or more items not logged " | |
629 | + "[item size (%Zd) > sub-buffer size (%Zd)]\n", | |
630 | + chan->last_toobig, chan->subbuf_size); | |
631 | + | |
632 | + kref_put(&chan->kref, relay_destroy_channel); | |
633 | +} | |
634 | +EXPORT_SYMBOL_GPL(relay_close); | |
635 | + | |
636 | +/** | |
637 | + * relay_flush - close the channel | |
638 | + * @chan: the channel | |
639 | + * | |
640 | + * Flushes all channel buffers i.e. forces buffer switch. | |
641 | + */ | |
642 | +void relay_flush(struct rchan *chan) | |
643 | +{ | |
644 | + unsigned int i; | |
645 | + struct rchan_buf *prev = NULL; | |
646 | + | |
647 | + if (!chan) | |
648 | + return; | |
649 | + | |
650 | + for (i = 0; i < NR_CPUS; i++) { | |
651 | + if (!chan->buf[i] || chan->buf[i] == prev) | |
652 | + break; | |
653 | + relay_switch_subbuf(chan->buf[i], 0); | |
654 | + prev = chan->buf[i]; | |
655 | + } | |
656 | +} | |
657 | +EXPORT_SYMBOL_GPL(relay_flush); | |
658 | + | |
659 | +/** | |
660 | + * relay_file_open - open file op for relay files | |
661 | + * @inode: the inode | |
662 | + * @filp: the file | |
663 | + * | |
664 | + * Increments the channel buffer refcount. | |
665 | + */ | |
666 | +static int relay_file_open(struct inode *inode, struct file *filp) | |
667 | +{ | |
668 | + struct rchan_buf *buf = inode->u.generic_ip; | |
669 | + kref_get(&buf->kref); | |
670 | + filp->private_data = buf; | |
671 | + | |
672 | + return 0; | |
673 | +} | |
674 | + | |
675 | +/** | |
676 | + * relay_file_mmap - mmap file op for relay files | |
677 | + * @filp: the file | |
678 | + * @vma: the vma describing what to map | |
679 | + * | |
680 | + * Calls upon relay_mmap_buf to map the file into user space. | |
681 | + */ | |
682 | +static int relay_file_mmap(struct file *filp, struct vm_area_struct *vma) | |
683 | +{ | |
684 | + struct rchan_buf *buf = filp->private_data; | |
685 | + return relay_mmap_buf(buf, vma); | |
686 | +} | |
687 | + | |
688 | +/** | |
689 | + * relay_file_poll - poll file op for relay files | |
690 | + * @filp: the file | |
691 | + * @wait: poll table | |
692 | + * | |
693 | + * Poll implemention. | |
694 | + */ | |
695 | +static unsigned int relay_file_poll(struct file *filp, poll_table *wait) | |
696 | +{ | |
697 | + unsigned int mask = 0; | |
698 | + struct rchan_buf *buf = filp->private_data; | |
699 | + | |
700 | + if (buf->finalized) | |
701 | + return POLLERR; | |
702 | + | |
703 | + if (filp->f_mode & FMODE_READ) { | |
704 | + poll_wait(filp, &buf->read_wait, wait); | |
705 | + if (!relay_buf_empty(buf)) | |
706 | + mask |= POLLIN | POLLRDNORM; | |
707 | + } | |
708 | + | |
709 | + return mask; | |
710 | +} | |
711 | + | |
712 | +/** | |
713 | + * relay_file_release - release file op for relay files | |
714 | + * @inode: the inode | |
715 | + * @filp: the file | |
716 | + * | |
717 | + * Decrements the channel refcount, as the filesystem is | |
718 | + * no longer using it. | |
719 | + */ | |
720 | +static int relay_file_release(struct inode *inode, struct file *filp) | |
721 | +{ | |
722 | + struct rchan_buf *buf = filp->private_data; | |
723 | + kref_put(&buf->kref, relay_remove_buf); | |
724 | + | |
725 | + return 0; | |
726 | +} | |
727 | + | |
728 | +/** | |
729 | + * relay_file_read_consume - update the consumed count for the buffer | |
730 | + */ | |
731 | +static void relay_file_read_consume(struct rchan_buf *buf, | |
732 | + size_t read_pos, | |
733 | + size_t bytes_consumed) | |
734 | +{ | |
735 | + size_t subbuf_size = buf->chan->subbuf_size; | |
736 | + size_t n_subbufs = buf->chan->n_subbufs; | |
737 | + size_t read_subbuf; | |
738 | + | |
739 | + if (buf->bytes_consumed + bytes_consumed > subbuf_size) { | |
740 | + relay_subbufs_consumed(buf->chan, buf->cpu, 1); | |
741 | + buf->bytes_consumed = 0; | |
742 | + } | |
743 | + | |
744 | + buf->bytes_consumed += bytes_consumed; | |
745 | + read_subbuf = read_pos / buf->chan->subbuf_size; | |
746 | + if (buf->bytes_consumed + buf->padding[read_subbuf] == subbuf_size) { | |
747 | + if ((read_subbuf == buf->subbufs_produced % n_subbufs) && | |
748 | + (buf->offset == subbuf_size)) | |
749 | + return; | |
750 | + relay_subbufs_consumed(buf->chan, buf->cpu, 1); | |
751 | + buf->bytes_consumed = 0; | |
752 | + } | |
753 | +} | |
754 | + | |
755 | +/** | |
756 | + * relay_file_read_avail - boolean, are there unconsumed bytes available? | |
757 | + */ | |
758 | +static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos) | |
759 | +{ | |
760 | + size_t bytes_produced, bytes_consumed, write_offset; | |
761 | + size_t subbuf_size = buf->chan->subbuf_size; | |
762 | + size_t n_subbufs = buf->chan->n_subbufs; | |
763 | + size_t produced = buf->subbufs_produced % n_subbufs; | |
764 | + size_t consumed = buf->subbufs_consumed % n_subbufs; | |
765 | + | |
766 | + write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset; | |
767 | + | |
768 | + if (consumed > produced) { | |
769 | + if ((produced > n_subbufs) && | |
770 | + (produced + n_subbufs - consumed <= n_subbufs)) | |
771 | + produced += n_subbufs; | |
772 | + } else if (consumed == produced) { | |
773 | + if (buf->offset > subbuf_size) { | |
774 | + produced += n_subbufs; | |
775 | + if (buf->subbufs_produced == buf->subbufs_consumed) | |
776 | + consumed += n_subbufs; | |
777 | + } | |
778 | + } | |
779 | + | |
780 | + if (buf->offset > subbuf_size) | |
781 | + bytes_produced = (produced - 1) * subbuf_size + write_offset; | |
782 | + else | |
783 | + bytes_produced = produced * subbuf_size + write_offset; | |
784 | + bytes_consumed = consumed * subbuf_size + buf->bytes_consumed; | |
785 | + | |
786 | + if (bytes_produced == bytes_consumed) | |
787 | + return 0; | |
788 | + | |
789 | + relay_file_read_consume(buf, read_pos, 0); | |
790 | + | |
791 | + return 1; | |
792 | +} | |
793 | + | |
794 | +/** | |
795 | + * relay_file_read_subbuf_avail - return bytes available in sub-buffer | |
796 | + */ | |
797 | +static size_t relay_file_read_subbuf_avail(size_t read_pos, | |
798 | + struct rchan_buf *buf) | |
799 | +{ | |
800 | + size_t padding, avail = 0; | |
801 | + size_t read_subbuf, read_offset, write_subbuf, write_offset; | |
802 | + size_t subbuf_size = buf->chan->subbuf_size; | |
803 | + | |
804 | + write_subbuf = (buf->data - buf->start) / subbuf_size; | |
805 | + write_offset = buf->offset > subbuf_size ? subbuf_size : buf->offset; | |
806 | + read_subbuf = read_pos / subbuf_size; | |
807 | + read_offset = read_pos % subbuf_size; | |
808 | + padding = buf->padding[read_subbuf]; | |
809 | + | |
810 | + if (read_subbuf == write_subbuf) { | |
811 | + if (read_offset + padding < write_offset) | |
812 | + avail = write_offset - (read_offset + padding); | |
813 | + } else | |
814 | + avail = (subbuf_size - padding) - read_offset; | |
815 | + | |
816 | + return avail; | |
817 | +} | |
818 | + | |
819 | +/** | |
820 | + * relay_file_read_start_pos - find the first available byte to read | |
821 | + * | |
822 | + * If the read_pos is in the middle of padding, return the | |
823 | + * position of the first actually available byte, otherwise | |
824 | + * return the original value. | |
825 | + */ | |
826 | +static size_t relay_file_read_start_pos(size_t read_pos, | |
827 | + struct rchan_buf *buf) | |
828 | +{ | |
829 | + size_t read_subbuf, padding, padding_start, padding_end; | |
830 | + size_t subbuf_size = buf->chan->subbuf_size; | |
831 | + size_t n_subbufs = buf->chan->n_subbufs; | |
832 | + | |
833 | + read_subbuf = read_pos / subbuf_size; | |
834 | + padding = buf->padding[read_subbuf]; | |
835 | + padding_start = (read_subbuf + 1) * subbuf_size - padding; | |
836 | + padding_end = (read_subbuf + 1) * subbuf_size; | |
837 | + if (read_pos >= padding_start && read_pos < padding_end) { | |
838 | + read_subbuf = (read_subbuf + 1) % n_subbufs; | |
839 | + read_pos = read_subbuf * subbuf_size; | |
840 | + } | |
841 | + | |
842 | + return read_pos; | |
843 | +} | |
844 | + | |
845 | +/** | |
846 | + * relay_file_read_end_pos - return the new read position | |
847 | + */ | |
848 | +static size_t relay_file_read_end_pos(struct rchan_buf *buf, | |
849 | + size_t read_pos, | |
850 | + size_t count) | |
851 | +{ | |
852 | + size_t read_subbuf, padding, end_pos; | |
853 | + size_t subbuf_size = buf->chan->subbuf_size; | |
854 | + size_t n_subbufs = buf->chan->n_subbufs; | |
855 | + | |
856 | + read_subbuf = read_pos / subbuf_size; | |
857 | + padding = buf->padding[read_subbuf]; | |
858 | + if (read_pos % subbuf_size + count + padding == subbuf_size) | |
859 | + end_pos = (read_subbuf + 1) * subbuf_size; | |
860 | + else | |
861 | + end_pos = read_pos + count; | |
862 | + if (end_pos >= subbuf_size * n_subbufs) | |
863 | + end_pos = 0; | |
864 | + | |
865 | + return end_pos; | |
866 | +} | |
867 | + | |
868 | +/** | |
869 | + * relay_file_read - read file op for relay files | |
870 | + * @filp: the file | |
871 | + * @buffer: the userspace buffer | |
872 | + * @count: number of bytes to read | |
873 | + * @ppos: position to read from | |
874 | + * | |
875 | + * Reads count bytes or the number of bytes available in the | |
876 | + * current sub-buffer being read, whichever is smaller. | |
877 | + */ | |
878 | +static ssize_t relay_file_read(struct file *filp, | |
879 | + char __user *buffer, | |
880 | + size_t count, | |
881 | + loff_t *ppos) | |
882 | +{ | |
883 | + struct rchan_buf *buf = filp->private_data; | |
884 | + struct inode *inode = filp->f_dentry->d_inode; | |
885 | + size_t read_start, avail; | |
886 | + ssize_t ret = 0; | |
887 | + void *from; | |
888 | + | |
889 | + mutex_lock(&inode->i_mutex); | |
890 | + if(!relay_file_read_avail(buf, *ppos)) | |
891 | + goto out; | |
892 | + | |
893 | + read_start = relay_file_read_start_pos(*ppos, buf); | |
894 | + avail = relay_file_read_subbuf_avail(read_start, buf); | |
895 | + if (!avail) | |
896 | + goto out; | |
897 | + | |
898 | + from = buf->start + read_start; | |
899 | + ret = count = min(count, avail); | |
900 | + if (copy_to_user(buffer, from, count)) { | |
901 | + ret = -EFAULT; | |
902 | + goto out; | |
903 | + } | |
904 | + relay_file_read_consume(buf, read_start, count); | |
905 | + *ppos = relay_file_read_end_pos(buf, read_start, count); | |
906 | +out: | |
907 | + mutex_unlock(&inode->i_mutex); | |
908 | + return ret; | |
909 | +} | |
910 | + | |
911 | +struct file_operations relay_file_operations = { | |
912 | + .open = relay_file_open, | |
913 | + .poll = relay_file_poll, | |
914 | + .mmap = relay_file_mmap, | |
915 | + .read = relay_file_read, | |
916 | + .llseek = no_llseek, | |
917 | + .release = relay_file_release, | |
918 | +}; | |
919 | +EXPORT_SYMBOL_GPL(relay_file_operations); |