aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/kfifo.c
diff options
context:
space:
mode:
authorStefani Seibold <stefani@seibold.net>2009-12-21 17:37:31 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-22 17:17:56 -0500
commita121f24accac1600bf5b6fb1e12eeabdfed7cb1a (patch)
tree24a3bb527e50304677785dbb390dd5ff838c94e8 /kernel/kfifo.c
parent37bdfbbfaab47811fcec84dff23c4e8da1a09f9e (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.c139
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
30static void _kfifo_init(struct kfifo *fifo, unsigned char *buffer, 31static 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)
100EXPORT_SYMBOL(kfifo_free); 101EXPORT_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 */
108void 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}
116EXPORT_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);
115unsigned int kfifo_in(struct kfifo *fifo, 131unsigned 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);
161unsigned int kfifo_out(struct kfifo *fifo, 173unsigned 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}
201EXPORT_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 */
215unsigned 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}
193EXPORT_SYMBOL(kfifo_out); 250EXPORT_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 */
264unsigned 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}
299EXPORT_SYMBOL(kfifo_to_user);
300