priority-table.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2023 Red Hat
  4. */
  5. #include "priority-table.h"
  6. #include <linux/log2.h>
  7. #include "errors.h"
  8. #include "memory-alloc.h"
  9. #include "permassert.h"
  10. #include "status-codes.h"
  11. /* We use a single 64-bit search vector, so the maximum priority is 63 */
  12. #define MAX_PRIORITY 63
  13. /*
  14. * All the entries with the same priority are queued in a circular list in a bucket for that
  15. * priority. The table is essentially an array of buckets.
  16. */
  17. struct bucket {
  18. /*
  19. * The head of a queue of table entries, all having the same priority
  20. */
  21. struct list_head queue;
  22. /* The priority of all the entries in this bucket */
  23. unsigned int priority;
  24. };
  25. /*
  26. * A priority table is an array of buckets, indexed by priority. New entries are added to the end
  27. * of the queue in the appropriate bucket. The dequeue operation finds the highest-priority
  28. * non-empty bucket by searching a bit vector represented as a single 8-byte word, which is very
  29. * fast with compiler and CPU support.
  30. */
  31. struct priority_table {
  32. /* The maximum priority of entries that may be stored in this table */
  33. unsigned int max_priority;
  34. /* A bit vector flagging all buckets that are currently non-empty */
  35. u64 search_vector;
  36. /* The array of all buckets, indexed by priority */
  37. struct bucket buckets[];
  38. };
  39. /**
  40. * vdo_make_priority_table() - Allocate and initialize a new priority_table.
  41. * @max_priority: The maximum priority value for table entries.
  42. * @table_ptr: A pointer to hold the new table.
  43. *
  44. * Return: VDO_SUCCESS or an error code.
  45. */
  46. int vdo_make_priority_table(unsigned int max_priority, struct priority_table **table_ptr)
  47. {
  48. struct priority_table *table;
  49. int result;
  50. unsigned int priority;
  51. if (max_priority > MAX_PRIORITY)
  52. return UDS_INVALID_ARGUMENT;
  53. result = vdo_allocate_extended(struct priority_table, max_priority + 1,
  54. struct bucket, __func__, &table);
  55. if (result != VDO_SUCCESS)
  56. return result;
  57. for (priority = 0; priority <= max_priority; priority++) {
  58. struct bucket *bucket = &table->buckets[priority];
  59. bucket->priority = priority;
  60. INIT_LIST_HEAD(&bucket->queue);
  61. }
  62. table->max_priority = max_priority;
  63. table->search_vector = 0;
  64. *table_ptr = table;
  65. return VDO_SUCCESS;
  66. }
  67. /**
  68. * vdo_free_priority_table() - Free a priority_table.
  69. * @table: The table to free.
  70. *
  71. * The table does not own the entries stored in it and they are not freed by this call.
  72. */
  73. void vdo_free_priority_table(struct priority_table *table)
  74. {
  75. if (table == NULL)
  76. return;
  77. /*
  78. * Unlink the buckets from any entries still in the table so the entries won't be left with
  79. * dangling pointers to freed memory.
  80. */
  81. vdo_reset_priority_table(table);
  82. vdo_free(table);
  83. }
  84. /**
  85. * vdo_reset_priority_table() - Reset a priority table, leaving it in the same empty state as when
  86. * newly constructed.
  87. * @table: The table to reset.
  88. *
  89. * The table does not own the entries stored in it and they are not freed (or even unlinked from
  90. * each other) by this call.
  91. */
  92. void vdo_reset_priority_table(struct priority_table *table)
  93. {
  94. unsigned int priority;
  95. table->search_vector = 0;
  96. for (priority = 0; priority <= table->max_priority; priority++)
  97. list_del_init(&table->buckets[priority].queue);
  98. }
  99. /**
  100. * vdo_priority_table_enqueue() - Add a new entry to the priority table, appending it to the queue
  101. * for entries with the specified priority.
  102. * @table: The table in which to store the entry.
  103. * @priority: The priority of the entry.
  104. * @entry: The list_head embedded in the entry to store in the table (the caller must have
  105. * initialized it).
  106. */
  107. void vdo_priority_table_enqueue(struct priority_table *table, unsigned int priority,
  108. struct list_head *entry)
  109. {
  110. VDO_ASSERT_LOG_ONLY((priority <= table->max_priority),
  111. "entry priority must be valid for the table");
  112. /* Append the entry to the queue in the specified bucket. */
  113. list_move_tail(entry, &table->buckets[priority].queue);
  114. /* Flag the bucket in the search vector since it must be non-empty. */
  115. table->search_vector |= (1ULL << priority);
  116. }
  117. static inline void mark_bucket_empty(struct priority_table *table, struct bucket *bucket)
  118. {
  119. table->search_vector &= ~(1ULL << bucket->priority);
  120. }
  121. /**
  122. * vdo_priority_table_dequeue() - Find the highest-priority entry in the table, remove it from the
  123. * table, and return it.
  124. * @table: The priority table from which to remove an entry.
  125. *
  126. * If there are multiple entries with the same priority, the one that has been in the table with
  127. * that priority the longest will be returned.
  128. *
  129. * Return: The dequeued entry, or NULL if the table is currently empty.
  130. */
  131. struct list_head *vdo_priority_table_dequeue(struct priority_table *table)
  132. {
  133. struct bucket *bucket;
  134. struct list_head *entry;
  135. int top_priority;
  136. if (table->search_vector == 0) {
  137. /* All buckets are empty. */
  138. return NULL;
  139. }
  140. /*
  141. * Find the highest priority non-empty bucket by finding the highest-order non-zero bit in
  142. * the search vector.
  143. */
  144. top_priority = ilog2(table->search_vector);
  145. /* Dequeue the first entry in the bucket. */
  146. bucket = &table->buckets[top_priority];
  147. entry = bucket->queue.next;
  148. list_del_init(entry);
  149. /* Clear the bit in the search vector if the bucket has been emptied. */
  150. if (list_empty(&bucket->queue))
  151. mark_bucket_empty(table, bucket);
  152. return entry;
  153. }
  154. /**
  155. * vdo_priority_table_remove() - Remove a specified entry from its priority table.
  156. * @table: The table from which to remove the entry.
  157. * @entry: The entry to remove from the table.
  158. */
  159. void vdo_priority_table_remove(struct priority_table *table, struct list_head *entry)
  160. {
  161. struct list_head *next_entry;
  162. /*
  163. * We can't guard against calls where the entry is on a list for a different table, but
  164. * it's easy to deal with an entry not in any table or list.
  165. */
  166. if (list_empty(entry))
  167. return;
  168. /*
  169. * Remove the entry from the bucket list, remembering a pointer to another entry in the
  170. * list.
  171. */
  172. next_entry = entry->next;
  173. list_del_init(entry);
  174. /*
  175. * If the rest of the list is now empty, the next node must be the list head in the bucket
  176. * and we can use it to update the search vector.
  177. */
  178. if (list_empty(next_entry))
  179. mark_bucket_empty(table, list_entry(next_entry, struct bucket, queue));
  180. }
  181. /**
  182. * vdo_is_priority_table_empty() - Return whether the priority table is empty.
  183. * @table: The table to check.
  184. *
  185. * Return: true if the table is empty.
  186. */
  187. bool vdo_is_priority_table_empty(struct priority_table *table)
  188. {
  189. return (table->search_vector == 0);
  190. }