kfifo.c (12378B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * A generic kernel FIFO implementation 4 * 5 * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net> 6 */ 7 8#include <linux/kernel.h> 9#include <linux/export.h> 10#include <linux/slab.h> 11#include <linux/err.h> 12#include <linux/log2.h> 13#include <linux/uaccess.h> 14#include <linux/kfifo.h> 15 16/* 17 * internal helper to calculate the unused elements in a fifo 18 */ 19static inline unsigned int kfifo_unused(struct __kfifo *fifo) 20{ 21 return (fifo->mask + 1) - (fifo->in - fifo->out); 22} 23 24int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, 25 size_t esize, gfp_t gfp_mask) 26{ 27 /* 28 * round up to the next power of 2, since our 'let the indices 29 * wrap' technique works only in this case. 30 */ 31 size = roundup_pow_of_two(size); 32 33 fifo->in = 0; 34 fifo->out = 0; 35 fifo->esize = esize; 36 37 if (size < 2) { 38 fifo->data = NULL; 39 fifo->mask = 0; 40 return -EINVAL; 41 } 42 43 fifo->data = kmalloc_array(esize, size, gfp_mask); 44 45 if (!fifo->data) { 46 fifo->mask = 0; 47 return -ENOMEM; 48 } 49 fifo->mask = size - 1; 50 51 return 0; 52} 53EXPORT_SYMBOL(__kfifo_alloc); 54 55void __kfifo_free(struct __kfifo *fifo) 56{ 57 kfree(fifo->data); 58 fifo->in = 0; 59 fifo->out = 0; 60 fifo->esize = 0; 61 fifo->data = NULL; 62 fifo->mask = 0; 63} 64EXPORT_SYMBOL(__kfifo_free); 65 66int __kfifo_init(struct __kfifo *fifo, void *buffer, 67 unsigned int size, size_t esize) 68{ 69 size /= esize; 70 71 if (!is_power_of_2(size)) 72 size = rounddown_pow_of_two(size); 73 74 fifo->in = 0; 75 fifo->out = 0; 76 fifo->esize = esize; 77 fifo->data = buffer; 78 79 if (size < 2) { 80 fifo->mask = 0; 81 return -EINVAL; 82 } 83 fifo->mask = size - 1; 84 85 return 0; 86} 87EXPORT_SYMBOL(__kfifo_init); 88 89static void kfifo_copy_in(struct __kfifo *fifo, const void *src, 90 unsigned int len, unsigned int off) 91{ 92 unsigned int size = fifo->mask + 1; 93 unsigned int esize = fifo->esize; 94 unsigned int l; 95 96 off &= fifo->mask; 97 if (esize != 1) { 98 off *= esize; 99 size *= esize; 100 len *= esize; 101 } 102 l = min(len, size - off); 103 104 memcpy(fifo->data + off, src, l); 105 memcpy(fifo->data, src + l, len - l); 106 /* 107 * make sure that the data in the fifo is up to date before 108 * incrementing the fifo->in index counter 109 */ 110 smp_wmb(); 111} 112 113unsigned int __kfifo_in(struct __kfifo *fifo, 114 const void *buf, unsigned int len) 115{ 116 unsigned int l; 117 118 l = kfifo_unused(fifo); 119 if (len > l) 120 len = l; 121 122 kfifo_copy_in(fifo, buf, len, fifo->in); 123 fifo->in += len; 124 return len; 125} 126EXPORT_SYMBOL(__kfifo_in); 127 128static void kfifo_copy_out(struct __kfifo *fifo, void *dst, 129 unsigned int len, unsigned int off) 130{ 131 unsigned int size = fifo->mask + 1; 132 unsigned int esize = fifo->esize; 133 unsigned int l; 134 135 off &= fifo->mask; 136 if (esize != 1) { 137 off *= esize; 138 size *= esize; 139 len *= esize; 140 } 141 l = min(len, size - off); 142 143 memcpy(dst, fifo->data + off, l); 144 memcpy(dst + l, fifo->data, len - l); 145 /* 146 * make sure that the data is copied before 147 * incrementing the fifo->out index counter 148 */ 149 smp_wmb(); 150} 151 152unsigned int __kfifo_out_peek(struct __kfifo *fifo, 153 void *buf, unsigned int len) 154{ 155 unsigned int l; 156 157 l = fifo->in - fifo->out; 158 if (len > l) 159 len = l; 160 161 kfifo_copy_out(fifo, buf, len, fifo->out); 162 return len; 163} 164EXPORT_SYMBOL(__kfifo_out_peek); 165 166unsigned int __kfifo_out(struct __kfifo *fifo, 167 void *buf, unsigned int len) 168{ 169 len = __kfifo_out_peek(fifo, buf, len); 170 fifo->out += len; 171 return len; 172} 173EXPORT_SYMBOL(__kfifo_out); 174 175static unsigned long kfifo_copy_from_user(struct __kfifo *fifo, 176 const void __user *from, unsigned int len, unsigned int off, 177 unsigned int *copied) 178{ 179 unsigned int size = fifo->mask + 1; 180 unsigned int esize = fifo->esize; 181 unsigned int l; 182 unsigned long ret; 183 184 off &= fifo->mask; 185 if (esize != 1) { 186 off *= esize; 187 size *= esize; 188 len *= esize; 189 } 190 l = min(len, size - off); 191 192 ret = copy_from_user(fifo->data + off, from, l); 193 if (unlikely(ret)) 194 ret = DIV_ROUND_UP(ret + len - l, esize); 195 else { 196 ret = copy_from_user(fifo->data, from + l, len - l); 197 if (unlikely(ret)) 198 ret = DIV_ROUND_UP(ret, esize); 199 } 200 /* 201 * make sure that the data in the fifo is up to date before 202 * incrementing the fifo->in index counter 203 */ 204 smp_wmb(); 205 *copied = len - ret * esize; 206 /* return the number of elements which are not copied */ 207 return ret; 208} 209 210int __kfifo_from_user(struct __kfifo *fifo, const void __user *from, 211 unsigned long len, unsigned int *copied) 212{ 213 unsigned int l; 214 unsigned long ret; 215 unsigned int esize = fifo->esize; 216 int err; 217 218 if (esize != 1) 219 len /= esize; 220 221 l = kfifo_unused(fifo); 222 if (len > l) 223 len = l; 224 225 ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied); 226 if (unlikely(ret)) { 227 len -= ret; 228 err = -EFAULT; 229 } else 230 err = 0; 231 fifo->in += len; 232 return err; 233} 234EXPORT_SYMBOL(__kfifo_from_user); 235 236static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to, 237 unsigned int len, unsigned int off, unsigned int *copied) 238{ 239 unsigned int l; 240 unsigned long ret; 241 unsigned int size = fifo->mask + 1; 242 unsigned int esize = fifo->esize; 243 244 off &= fifo->mask; 245 if (esize != 1) { 246 off *= esize; 247 size *= esize; 248 len *= esize; 249 } 250 l = min(len, size - off); 251 252 ret = copy_to_user(to, fifo->data + off, l); 253 if (unlikely(ret)) 254 ret = DIV_ROUND_UP(ret + len - l, esize); 255 else { 256 ret = copy_to_user(to + l, fifo->data, len - l); 257 if (unlikely(ret)) 258 ret = DIV_ROUND_UP(ret, esize); 259 } 260 /* 261 * make sure that the data is copied before 262 * incrementing the fifo->out index counter 263 */ 264 smp_wmb(); 265 *copied = len - ret * esize; 266 /* return the number of elements which are not copied */ 267 return ret; 268} 269 270int __kfifo_to_user(struct __kfifo *fifo, void __user *to, 271 unsigned long len, unsigned int *copied) 272{ 273 unsigned int l; 274 unsigned long ret; 275 unsigned int esize = fifo->esize; 276 int err; 277 278 if (esize != 1) 279 len /= esize; 280 281 l = fifo->in - fifo->out; 282 if (len > l) 283 len = l; 284 ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied); 285 if (unlikely(ret)) { 286 len -= ret; 287 err = -EFAULT; 288 } else 289 err = 0; 290 fifo->out += len; 291 return err; 292} 293EXPORT_SYMBOL(__kfifo_to_user); 294 295static int setup_sgl_buf(struct scatterlist *sgl, void *buf, 296 int nents, unsigned int len) 297{ 298 int n; 299 unsigned int l; 300 unsigned int off; 301 struct page *page; 302 303 if (!nents) 304 return 0; 305 306 if (!len) 307 return 0; 308 309 n = 0; 310 page = virt_to_page(buf); 311 off = offset_in_page(buf); 312 l = 0; 313 314 while (len >= l + PAGE_SIZE - off) { 315 struct page *npage; 316 317 l += PAGE_SIZE; 318 buf += PAGE_SIZE; 319 npage = virt_to_page(buf); 320 if (page_to_phys(page) != page_to_phys(npage) - l) { 321 sg_set_page(sgl, page, l - off, off); 322 sgl = sg_next(sgl); 323 if (++n == nents || sgl == NULL) 324 return n; 325 page = npage; 326 len -= l - off; 327 l = off = 0; 328 } 329 } 330 sg_set_page(sgl, page, len, off); 331 return n + 1; 332} 333 334static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, 335 int nents, unsigned int len, unsigned int off) 336{ 337 unsigned int size = fifo->mask + 1; 338 unsigned int esize = fifo->esize; 339 unsigned int l; 340 unsigned int n; 341 342 off &= fifo->mask; 343 if (esize != 1) { 344 off *= esize; 345 size *= esize; 346 len *= esize; 347 } 348 l = min(len, size - off); 349 350 n = setup_sgl_buf(sgl, fifo->data + off, nents, l); 351 n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l); 352 353 return n; 354} 355 356unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, 357 struct scatterlist *sgl, int nents, unsigned int len) 358{ 359 unsigned int l; 360 361 l = kfifo_unused(fifo); 362 if (len > l) 363 len = l; 364 365 return setup_sgl(fifo, sgl, nents, len, fifo->in); 366} 367EXPORT_SYMBOL(__kfifo_dma_in_prepare); 368 369unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, 370 struct scatterlist *sgl, int nents, unsigned int len) 371{ 372 unsigned int l; 373 374 l = fifo->in - fifo->out; 375 if (len > l) 376 len = l; 377 378 return setup_sgl(fifo, sgl, nents, len, fifo->out); 379} 380EXPORT_SYMBOL(__kfifo_dma_out_prepare); 381 382unsigned int __kfifo_max_r(unsigned int len, size_t recsize) 383{ 384 unsigned int max = (1 << (recsize << 3)) - 1; 385 386 if (len > max) 387 return max; 388 return len; 389} 390EXPORT_SYMBOL(__kfifo_max_r); 391 392#define __KFIFO_PEEK(data, out, mask) \ 393 ((data)[(out) & (mask)]) 394/* 395 * __kfifo_peek_n internal helper function for determinate the length of 396 * the next record in the fifo 397 */ 398static unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize) 399{ 400 unsigned int l; 401 unsigned int mask = fifo->mask; 402 unsigned char *data = fifo->data; 403 404 l = __KFIFO_PEEK(data, fifo->out, mask); 405 406 if (--recsize) 407 l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8; 408 409 return l; 410} 411 412#define __KFIFO_POKE(data, in, mask, val) \ 413 ( \ 414 (data)[(in) & (mask)] = (unsigned char)(val) \ 415 ) 416 417/* 418 * __kfifo_poke_n internal helper function for storing the length of 419 * the record into the fifo 420 */ 421static void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize) 422{ 423 unsigned int mask = fifo->mask; 424 unsigned char *data = fifo->data; 425 426 __KFIFO_POKE(data, fifo->in, mask, n); 427 428 if (recsize > 1) 429 __KFIFO_POKE(data, fifo->in + 1, mask, n >> 8); 430} 431 432unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize) 433{ 434 return __kfifo_peek_n(fifo, recsize); 435} 436EXPORT_SYMBOL(__kfifo_len_r); 437 438unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf, 439 unsigned int len, size_t recsize) 440{ 441 if (len + recsize > kfifo_unused(fifo)) 442 return 0; 443 444 __kfifo_poke_n(fifo, len, recsize); 445 446 kfifo_copy_in(fifo, buf, len, fifo->in + recsize); 447 fifo->in += len + recsize; 448 return len; 449} 450EXPORT_SYMBOL(__kfifo_in_r); 451 452static unsigned int kfifo_out_copy_r(struct __kfifo *fifo, 453 void *buf, unsigned int len, size_t recsize, unsigned int *n) 454{ 455 *n = __kfifo_peek_n(fifo, recsize); 456 457 if (len > *n) 458 len = *n; 459 460 kfifo_copy_out(fifo, buf, len, fifo->out + recsize); 461 return len; 462} 463 464unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, 465 unsigned int len, size_t recsize) 466{ 467 unsigned int n; 468 469 if (fifo->in == fifo->out) 470 return 0; 471 472 return kfifo_out_copy_r(fifo, buf, len, recsize, &n); 473} 474EXPORT_SYMBOL(__kfifo_out_peek_r); 475 476unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf, 477 unsigned int len, size_t recsize) 478{ 479 unsigned int n; 480 481 if (fifo->in == fifo->out) 482 return 0; 483 484 len = kfifo_out_copy_r(fifo, buf, len, recsize, &n); 485 fifo->out += n + recsize; 486 return len; 487} 488EXPORT_SYMBOL(__kfifo_out_r); 489 490void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize) 491{ 492 unsigned int n; 493 494 n = __kfifo_peek_n(fifo, recsize); 495 fifo->out += n + recsize; 496} 497EXPORT_SYMBOL(__kfifo_skip_r); 498 499int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from, 500 unsigned long len, unsigned int *copied, size_t recsize) 501{ 502 unsigned long ret; 503 504 len = __kfifo_max_r(len, recsize); 505 506 if (len + recsize > kfifo_unused(fifo)) { 507 *copied = 0; 508 return 0; 509 } 510 511 __kfifo_poke_n(fifo, len, recsize); 512 513 ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied); 514 if (unlikely(ret)) { 515 *copied = 0; 516 return -EFAULT; 517 } 518 fifo->in += len + recsize; 519 return 0; 520} 521EXPORT_SYMBOL(__kfifo_from_user_r); 522 523int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to, 524 unsigned long len, unsigned int *copied, size_t recsize) 525{ 526 unsigned long ret; 527 unsigned int n; 528 529 if (fifo->in == fifo->out) { 530 *copied = 0; 531 return 0; 532 } 533 534 n = __kfifo_peek_n(fifo, recsize); 535 if (len > n) 536 len = n; 537 538 ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied); 539 if (unlikely(ret)) { 540 *copied = 0; 541 return -EFAULT; 542 } 543 fifo->out += n + recsize; 544 return 0; 545} 546EXPORT_SYMBOL(__kfifo_to_user_r); 547 548unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, 549 struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) 550{ 551 BUG_ON(!nents); 552 553 len = __kfifo_max_r(len, recsize); 554 555 if (len + recsize > kfifo_unused(fifo)) 556 return 0; 557 558 return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize); 559} 560EXPORT_SYMBOL(__kfifo_dma_in_prepare_r); 561 562void __kfifo_dma_in_finish_r(struct __kfifo *fifo, 563 unsigned int len, size_t recsize) 564{ 565 len = __kfifo_max_r(len, recsize); 566 __kfifo_poke_n(fifo, len, recsize); 567 fifo->in += len + recsize; 568} 569EXPORT_SYMBOL(__kfifo_dma_in_finish_r); 570 571unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, 572 struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) 573{ 574 BUG_ON(!nents); 575 576 len = __kfifo_max_r(len, recsize); 577 578 if (len + recsize > fifo->in - fifo->out) 579 return 0; 580 581 return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize); 582} 583EXPORT_SYMBOL(__kfifo_dma_out_prepare_r); 584 585void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize) 586{ 587 unsigned int len; 588 589 len = __kfifo_peek_n(fifo, recsize); 590 fifo->out += len + recsize; 591} 592EXPORT_SYMBOL(__kfifo_dma_out_finish_r);