diff options
-rw-r--r-- | include/linux/kfifo.h | 47 | ||||
-rw-r--r-- | kernel/kfifo.c | 139 |
2 files changed, 170 insertions, 16 deletions
diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index dd53eed3e2af..d3230fb08bc7 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h | |||
@@ -125,6 +125,16 @@ static inline void kfifo_reset(struct kfifo *fifo) | |||
125 | } | 125 | } |
126 | 126 | ||
127 | /** | 127 | /** |
128 | * kfifo_reset_out - skip FIFO contents | ||
129 | * @fifo: the fifo to be emptied. | ||
130 | */ | ||
131 | static inline void kfifo_reset_out(struct kfifo *fifo) | ||
132 | { | ||
133 | smp_mb(); | ||
134 | fifo->out = fifo->in; | ||
135 | } | ||
136 | |||
137 | /** | ||
128 | * kfifo_size - returns the size of the fifo in bytes | 138 | * kfifo_size - returns the size of the fifo in bytes |
129 | * @fifo: the fifo to be used. | 139 | * @fifo: the fifo to be used. |
130 | */ | 140 | */ |
@@ -231,4 +241,41 @@ static inline __must_check unsigned int kfifo_out_locked(struct kfifo *fifo, | |||
231 | return ret; | 241 | return ret; |
232 | } | 242 | } |
233 | 243 | ||
244 | extern void kfifo_skip(struct kfifo *fifo, unsigned int len); | ||
245 | |||
246 | extern __must_check unsigned int kfifo_from_user(struct kfifo *fifo, | ||
247 | const void __user *from, unsigned int n); | ||
248 | |||
249 | extern __must_check unsigned int kfifo_to_user(struct kfifo *fifo, | ||
250 | void __user *to, unsigned int n); | ||
251 | |||
252 | /** | ||
253 | * __kfifo_add_out internal helper function for updating the out offset | ||
254 | */ | ||
255 | static inline void __kfifo_add_out(struct kfifo *fifo, | ||
256 | unsigned int off) | ||
257 | { | ||
258 | smp_mb(); | ||
259 | fifo->out += off; | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * __kfifo_add_in internal helper function for updating the in offset | ||
264 | */ | ||
265 | static inline void __kfifo_add_in(struct kfifo *fifo, | ||
266 | unsigned int off) | ||
267 | { | ||
268 | smp_wmb(); | ||
269 | fifo->in += off; | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * __kfifo_off internal helper function for calculating the index of a | ||
274 | * given offeset | ||
275 | */ | ||
276 | static inline unsigned int __kfifo_off(struct kfifo *fifo, unsigned int off) | ||
277 | { | ||
278 | return off & (fifo->size - 1); | ||
279 | } | ||
280 | |||
234 | #endif | 281 | #endif |
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 | |||