percpu_freelist.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /* Copyright (c) 2016 Facebook
  2. *
  3. * This program is free software; you can redistribute it and/or
  4. * modify it under the terms of version 2 of the GNU General Public
  5. * License as published by the Free Software Foundation.
  6. */
  7. #include "percpu_freelist.h"
  8. int pcpu_freelist_init(struct pcpu_freelist *s)
  9. {
  10. int cpu;
  11. s->freelist = alloc_percpu(struct pcpu_freelist_head);
  12. if (!s->freelist)
  13. return -ENOMEM;
  14. for_each_possible_cpu(cpu) {
  15. struct pcpu_freelist_head *head = per_cpu_ptr(s->freelist, cpu);
  16. raw_spin_lock_init(&head->lock);
  17. head->first = NULL;
  18. }
  19. return 0;
  20. }
  21. void pcpu_freelist_destroy(struct pcpu_freelist *s)
  22. {
  23. free_percpu(s->freelist);
  24. }
  25. static inline void ___pcpu_freelist_push(struct pcpu_freelist_head *head,
  26. struct pcpu_freelist_node *node)
  27. {
  28. raw_spin_lock(&head->lock);
  29. node->next = head->first;
  30. head->first = node;
  31. raw_spin_unlock(&head->lock);
  32. }
  33. void __pcpu_freelist_push(struct pcpu_freelist *s,
  34. struct pcpu_freelist_node *node)
  35. {
  36. struct pcpu_freelist_head *head = this_cpu_ptr(s->freelist);
  37. ___pcpu_freelist_push(head, node);
  38. }
  39. void pcpu_freelist_push(struct pcpu_freelist *s,
  40. struct pcpu_freelist_node *node)
  41. {
  42. unsigned long flags;
  43. local_irq_save(flags);
  44. __pcpu_freelist_push(s, node);
  45. local_irq_restore(flags);
  46. }
  47. void pcpu_freelist_populate(struct pcpu_freelist *s, void *buf, u32 elem_size,
  48. u32 nr_elems)
  49. {
  50. struct pcpu_freelist_head *head;
  51. unsigned long flags;
  52. int i, cpu, pcpu_entries;
  53. pcpu_entries = nr_elems / num_possible_cpus() + 1;
  54. i = 0;
  55. /* disable irq to workaround lockdep false positive
  56. * in bpf usage pcpu_freelist_populate() will never race
  57. * with pcpu_freelist_push()
  58. */
  59. local_irq_save(flags);
  60. for_each_possible_cpu(cpu) {
  61. again:
  62. head = per_cpu_ptr(s->freelist, cpu);
  63. ___pcpu_freelist_push(head, buf);
  64. i++;
  65. buf += elem_size;
  66. if (i == nr_elems)
  67. break;
  68. if (i % pcpu_entries)
  69. goto again;
  70. }
  71. local_irq_restore(flags);
  72. }
  73. struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
  74. {
  75. struct pcpu_freelist_head *head;
  76. struct pcpu_freelist_node *node;
  77. int orig_cpu, cpu;
  78. orig_cpu = cpu = raw_smp_processor_id();
  79. while (1) {
  80. head = per_cpu_ptr(s->freelist, cpu);
  81. raw_spin_lock(&head->lock);
  82. node = head->first;
  83. if (node) {
  84. head->first = node->next;
  85. raw_spin_unlock(&head->lock);
  86. return node;
  87. }
  88. raw_spin_unlock(&head->lock);
  89. cpu = cpumask_next(cpu, cpu_possible_mask);
  90. if (cpu >= nr_cpu_ids)
  91. cpu = 0;
  92. if (cpu == orig_cpu)
  93. return NULL;
  94. }
  95. }
  96. struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)
  97. {
  98. struct pcpu_freelist_node *ret;
  99. unsigned long flags;
  100. local_irq_save(flags);
  101. ret = __pcpu_freelist_pop(s);
  102. local_irq_restore(flags);
  103. return ret;
  104. }