diff options
author | Stefani Seibold <stefani@seibold.net> | 2009-12-21 17:37:31 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-22 17:17:56 -0500 |
commit | a121f24accac1600bf5b6fb1e12eeabdfed7cb1a (patch) | |
tree | 24a3bb527e50304677785dbb390dd5ff838c94e8 /kernel/kfifo.c | |
parent | 37bdfbbfaab47811fcec84dff23c4e8da1a09f9e (diff) |
kfifo: add kfifo_skip, kfifo_from_user and kfifo_to_user
Add kfifo_reset_out() for save lockless discard the fifo output
Add kfifo_skip() to skip a number of output bytes
Add kfifo_from_user() to copy user space data into the fifo
Add kfifo_to_user() to copy fifo data to user space
Signed-off-by: Stefani Seibold <stefani@seibold.net>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Acked-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/kfifo.c')
-rw-r--r-- | kernel/kfifo.c | 139 |
1 files changed, 123 insertions, 16 deletions
diff --git a/kernel/kfifo.c b/kernel/kfifo.c index d659442e73f2..2a78425ef67f 100644 --- a/kernel/kfifo.c +++ b/kernel/kfifo.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/err.h> | 26 | #include <linux/err.h> |
27 | #include <linux/kfifo.h> | 27 | #include <linux/kfifo.h> |
28 | #include <linux/log2.h> | 28 | #include <linux/log2.h> |
29 | #include <linux/uaccess.h> | ||
29 | 30 | ||
30 | static void _kfifo_init(struct kfifo *fifo, unsigned char *buffer, | 31 | static void _kfifo_init(struct kfifo *fifo, unsigned char *buffer, |
31 | unsigned int size) | 32 | unsigned int size) |
@@ -100,6 +101,21 @@ void kfifo_free(struct kfifo *fifo) | |||
100 | EXPORT_SYMBOL(kfifo_free); | 101 | EXPORT_SYMBOL(kfifo_free); |
101 | 102 | ||
102 | /** | 103 | /** |
104 | * kfifo_skip - skip output data | ||
105 | * @fifo: the fifo to be used. | ||
106 | * @len: number of bytes to skip | ||
107 | */ | ||
108 | void kfifo_skip(struct kfifo *fifo, unsigned int len) | ||
109 | { | ||
110 | if (len < kfifo_len(fifo)) { | ||
111 | __kfifo_add_out(fifo, len); | ||
112 | return; | ||
113 | } | ||
114 | kfifo_reset_out(fifo); | ||
115 | } | ||
116 | EXPORT_SYMBOL(kfifo_skip); | ||
117 | |||
118 | /** | ||
103 | * kfifo_in - puts some data into the FIFO | 119 | * kfifo_in - puts some data into the FIFO |
104 | * @fifo: the fifo to be used. | 120 | * @fifo: the fifo to be used. |
105 | * @from: the data to be added. | 121 | * @from: the data to be added. |
@@ -115,6 +131,7 @@ EXPORT_SYMBOL(kfifo_free); | |||
115 | unsigned int kfifo_in(struct kfifo *fifo, | 131 | unsigned int kfifo_in(struct kfifo *fifo, |
116 | const unsigned char *from, unsigned int len) | 132 | const unsigned char *from, unsigned int len) |
117 | { | 133 | { |
134 | unsigned int off; | ||
118 | unsigned int l; | 135 | unsigned int l; |
119 | 136 | ||
120 | len = min(len, fifo->size - fifo->in + fifo->out); | 137 | len = min(len, fifo->size - fifo->in + fifo->out); |
@@ -126,21 +143,16 @@ unsigned int kfifo_in(struct kfifo *fifo, | |||
126 | 143 | ||
127 | smp_mb(); | 144 | smp_mb(); |
128 | 145 | ||
146 | off = __kfifo_off(fifo, fifo->in); | ||
147 | |||
129 | /* first put the data starting from fifo->in to buffer end */ | 148 | /* first put the data starting from fifo->in to buffer end */ |
130 | l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); | 149 | l = min(len, fifo->size - off); |
131 | memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), from, l); | 150 | memcpy(fifo->buffer + off, from, l); |
132 | 151 | ||
133 | /* then put the rest (if any) at the beginning of the buffer */ | 152 | /* then put the rest (if any) at the beginning of the buffer */ |
134 | memcpy(fifo->buffer, from + l, len - l); | 153 | memcpy(fifo->buffer, from + l, len - l); |
135 | 154 | ||
136 | /* | 155 | __kfifo_add_in(fifo, len); |
137 | * Ensure that we add the bytes to the kfifo -before- | ||
138 | * we update the fifo->in index. | ||
139 | */ | ||
140 | |||
141 | smp_wmb(); | ||
142 | |||
143 | fifo->in += len; | ||
144 | 156 | ||
145 | return len; | 157 | return len; |
146 | } | 158 | } |
@@ -161,6 +173,7 @@ EXPORT_SYMBOL(kfifo_in); | |||
161 | unsigned int kfifo_out(struct kfifo *fifo, | 173 | unsigned int kfifo_out(struct kfifo *fifo, |
162 | unsigned char *to, unsigned int len) | 174 | unsigned char *to, unsigned int len) |
163 | { | 175 | { |
176 | unsigned int off; | ||
164 | unsigned int l; | 177 | unsigned int l; |
165 | 178 | ||
166 | len = min(len, fifo->in - fifo->out); | 179 | len = min(len, fifo->in - fifo->out); |
@@ -172,22 +185,116 @@ unsigned int kfifo_out(struct kfifo *fifo, | |||
172 | 185 | ||
173 | smp_rmb(); | 186 | smp_rmb(); |
174 | 187 | ||
188 | off = __kfifo_off(fifo, fifo->out); | ||
189 | |||
175 | /* first get the data from fifo->out until the end of the buffer */ | 190 | /* first get the data from fifo->out until the end of the buffer */ |
176 | l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); | 191 | l = min(len, fifo->size - off); |
177 | memcpy(to, fifo->buffer + (fifo->out & (fifo->size - 1)), l); | 192 | memcpy(to, fifo->buffer + off, l); |
178 | 193 | ||
179 | /* then get the rest (if any) from the beginning of the buffer */ | 194 | /* then get the rest (if any) from the beginning of the buffer */ |
180 | memcpy(to + l, fifo->buffer, len - l); | 195 | memcpy(to + l, fifo->buffer, len - l); |
181 | 196 | ||
197 | __kfifo_add_out(fifo, len); | ||
198 | |||
199 | return len; | ||
200 | } | ||
201 | EXPORT_SYMBOL(kfifo_out); | ||
202 | |||
203 | /** | ||
204 | * kfifo_from_user - puts some data from user space into the FIFO | ||
205 | * @fifo: the fifo to be used. | ||
206 | * @from: pointer to the data to be added. | ||
207 | * @len: the length of the data to be added. | ||
208 | * | ||
209 | * This function copies at most @len bytes from the @from into the | ||
210 | * FIFO depending and returns the number of copied bytes. | ||
211 | * | ||
212 | * Note that with only one concurrent reader and one concurrent | ||
213 | * writer, you don't need extra locking to use these functions. | ||
214 | */ | ||
215 | unsigned int kfifo_from_user(struct kfifo *fifo, | ||
216 | const void __user *from, unsigned int len) | ||
217 | { | ||
218 | unsigned int off; | ||
219 | unsigned int l; | ||
220 | int ret; | ||
221 | |||
222 | len = min(len, fifo->size - fifo->in + fifo->out); | ||
223 | |||
182 | /* | 224 | /* |
183 | * Ensure that we remove the bytes from the kfifo -before- | 225 | * Ensure that we sample the fifo->out index -before- we |
184 | * we update the fifo->out index. | 226 | * start putting bytes into the kfifo. |
185 | */ | 227 | */ |
186 | 228 | ||
187 | smp_mb(); | 229 | smp_mb(); |
188 | 230 | ||
189 | fifo->out += len; | 231 | off = __kfifo_off(fifo, fifo->in); |
232 | |||
233 | /* first put the data starting from fifo->in to buffer end */ | ||
234 | l = min(len, fifo->size - off); | ||
235 | ret = copy_from_user(fifo->buffer + off, from, l); | ||
236 | |||
237 | if (unlikely(ret)) | ||
238 | return l - ret; | ||
239 | |||
240 | /* then put the rest (if any) at the beginning of the buffer */ | ||
241 | ret = copy_from_user(fifo->buffer, from + l, len - l); | ||
242 | |||
243 | if (unlikely(ret)) | ||
244 | return len - ret; | ||
245 | |||
246 | __kfifo_add_in(fifo, len); | ||
190 | 247 | ||
191 | return len; | 248 | return len; |
192 | } | 249 | } |
193 | EXPORT_SYMBOL(kfifo_out); | 250 | EXPORT_SYMBOL(kfifo_from_user); |
251 | |||
252 | /** | ||
253 | * kfifo_to_user - gets data from the FIFO and write it to user space | ||
254 | * @fifo: the fifo to be used. | ||
255 | * @to: where the data must be copied. | ||
256 | * @len: the size of the destination buffer. | ||
257 | * | ||
258 | * This function copies at most @len bytes from the FIFO into the | ||
259 | * @to buffer and returns the number of copied bytes. | ||
260 | * | ||
261 | * Note that with only one concurrent reader and one concurrent | ||
262 | * writer, you don't need extra locking to use these functions. | ||
263 | */ | ||
264 | unsigned int kfifo_to_user(struct kfifo *fifo, | ||
265 | void __user *to, unsigned int len) | ||
266 | { | ||
267 | unsigned int off; | ||
268 | unsigned int l; | ||
269 | int ret; | ||
270 | |||
271 | len = min(len, fifo->in - fifo->out); | ||
272 | |||
273 | /* | ||
274 | * Ensure that we sample the fifo->in index -before- we | ||
275 | * start removing bytes from the kfifo. | ||
276 | */ | ||
277 | |||
278 | smp_rmb(); | ||
279 | |||
280 | off = __kfifo_off(fifo, fifo->out); | ||
281 | |||
282 | /* first get the data from fifo->out until the end of the buffer */ | ||
283 | l = min(len, fifo->size - off); | ||
284 | ret = copy_to_user(to, fifo->buffer + off, l); | ||
285 | |||
286 | if (unlikely(ret)) | ||
287 | return l - ret; | ||
288 | |||
289 | /* then get the rest (if any) from the beginning of the buffer */ | ||
290 | ret = copy_to_user(to + l, fifo->buffer, len - l); | ||
291 | |||
292 | if (unlikely(ret)) | ||
293 | return len - ret; | ||
294 | |||
295 | __kfifo_add_out(fifo, len); | ||
296 | |||
297 | return len; | ||
298 | } | ||
299 | EXPORT_SYMBOL(kfifo_to_user); | ||
300 | |||