]>
Commit | Line | Data |
---|---|---|
e82894f8 TZ |
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 | } | |
1cc956e1 | 112 | mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL); |
e82894f8 TZ |
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); | |
08c541a7 | 188 | buf->chan->cb->remove_buf_file(buf->dentry); |
6625b861 | 189 | relay_destroy_buf(buf); |
e82894f8 | 190 | } |