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 | |
| 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>
| -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 | |||
