funnel-queue.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /* SPDX-License-Identifier: GPL-2.0-only */
  2. /*
  3. * Copyright 2023 Red Hat
  4. */
  5. #ifndef VDO_FUNNEL_QUEUE_H
  6. #define VDO_FUNNEL_QUEUE_H
  7. #include <linux/atomic.h>
  8. #include <linux/cache.h>
  9. /*
  10. * A funnel queue is a simple (almost) lock-free queue that accepts entries from multiple threads
  11. * (multi-producer) and delivers them to a single thread (single-consumer). "Funnel" is an attempt
  12. * to evoke the image of requests from more than one producer being "funneled down" to a single
  13. * consumer.
  14. *
  15. * This is an unsynchronized but thread-safe data structure when used as intended. There is no
  16. * mechanism to ensure that only one thread is consuming from the queue. If more than one thread
  17. * attempts to consume from the queue, the resulting behavior is undefined. Clients must not
  18. * directly access or manipulate the internals of the queue, which are only exposed for the purpose
  19. * of allowing the very simple enqueue operation to be inlined.
  20. *
  21. * The implementation requires that a funnel_queue_entry structure (a link pointer) is embedded in
  22. * the queue entries, and pointers to those structures are used exclusively by the queue. No macros
  23. * are defined to template the queue, so the offset of the funnel_queue_entry in the records placed
  24. * in the queue must all be the same so the client can derive their structure pointer from the
  25. * entry pointer returned by vdo_funnel_queue_poll().
  26. *
  27. * Callers are wholly responsible for allocating and freeing the entries. Entries may be freed as
  28. * soon as they are returned since this queue is not susceptible to the "ABA problem" present in
  29. * many lock-free data structures. The queue is dynamically allocated to ensure cache-line
  30. * alignment, but no other dynamic allocation is used.
  31. *
  32. * The algorithm is not actually 100% lock-free. There is a single point in vdo_funnel_queue_put()
  33. * at which a preempted producer will prevent the consumers from seeing items added to the queue by
  34. * later producers, and only if the queue is short enough or the consumer fast enough for it to
  35. * reach what was the end of the queue at the time of the preemption.
  36. *
  37. * The consumer function, vdo_funnel_queue_poll(), will return NULL when the queue is empty. To
  38. * wait for data to consume, spin (if safe) or combine the queue with a struct event_count to
  39. * signal the presence of new entries.
  40. */
  41. /* This queue link structure must be embedded in client entries. */
  42. struct funnel_queue_entry {
  43. /* The next (newer) entry in the queue. */
  44. struct funnel_queue_entry *next;
  45. };
  46. /*
  47. * The dynamically allocated queue structure, which is allocated on a cache line boundary so the
  48. * producer and consumer fields in the structure will land on separate cache lines. This should be
  49. * consider opaque but it is exposed here so vdo_funnel_queue_put() can be inlined.
  50. */
  51. struct __aligned(L1_CACHE_BYTES) funnel_queue {
  52. /*
  53. * The producers' end of the queue, an atomically exchanged pointer that will never be
  54. * NULL.
  55. */
  56. struct funnel_queue_entry *newest;
  57. /* The consumer's end of the queue, which is owned by the consumer and never NULL. */
  58. struct funnel_queue_entry *oldest __aligned(L1_CACHE_BYTES);
  59. /* A dummy entry used to provide the non-NULL invariants above. */
  60. struct funnel_queue_entry stub;
  61. };
  62. int __must_check vdo_make_funnel_queue(struct funnel_queue **queue_ptr);
  63. void vdo_free_funnel_queue(struct funnel_queue *queue);
  64. /*
  65. * Put an entry on the end of the queue.
  66. *
  67. * The entry pointer must be to the struct funnel_queue_entry embedded in the caller's data
  68. * structure. The caller must be able to derive the address of the start of their data structure
  69. * from the pointer that passed in here, so every entry in the queue must have the struct
  70. * funnel_queue_entry at the same offset within the client's structure.
  71. */
  72. static inline void vdo_funnel_queue_put(struct funnel_queue *queue,
  73. struct funnel_queue_entry *entry)
  74. {
  75. struct funnel_queue_entry *previous;
  76. /*
  77. * Barrier requirements: All stores relating to the entry ("next" pointer, containing data
  78. * structure fields) must happen before the previous->next store making it visible to the
  79. * consumer. Also, the entry's "next" field initialization to NULL must happen before any
  80. * other producer threads can see the entry (the xchg) and try to update the "next" field.
  81. *
  82. * xchg implements a full barrier.
  83. */
  84. WRITE_ONCE(entry->next, NULL);
  85. previous = xchg(&queue->newest, entry);
  86. /*
  87. * Preemptions between these two statements hide the rest of the queue from the consumer,
  88. * preventing consumption until the following assignment runs.
  89. */
  90. WRITE_ONCE(previous->next, entry);
  91. }
  92. struct funnel_queue_entry *__must_check vdo_funnel_queue_poll(struct funnel_queue *queue);
  93. bool __must_check vdo_is_funnel_queue_empty(struct funnel_queue *queue);
  94. bool __must_check vdo_is_funnel_queue_idle(struct funnel_queue *queue);
  95. #endif /* VDO_FUNNEL_QUEUE_H */