/* We use a single 64-bit search vector, so the maximum priority is 63 */ #define MAX_PRIORITY 63
/* * All the entries with the same priority are queued in a circular list in a bucket for that * priority. The table is essentially an array of buckets.
*/ struct bucket { /* * The head of a queue of table entries, all having the same priority
*/ struct list_head queue; /* The priority of all the entries in this bucket */ unsignedint priority;
};
/* * A priority table is an array of buckets, indexed by priority. New entries are added to the end * of the queue in the appropriate bucket. The dequeue operation finds the highest-priority * non-empty bucket by searching a bit vector represented as a single 8-byte word, which is very * fast with compiler and CPU support.
*/ struct priority_table { /* The maximum priority of entries that may be stored in this table */ unsignedint max_priority; /* A bit vector flagging all buckets that are currently non-empty */
u64 search_vector; /* The array of all buckets, indexed by priority */ struct bucket buckets[];
};
/** * vdo_make_priority_table() - Allocate and initialize a new priority_table. * @max_priority: The maximum priority value for table entries. * @table_ptr: A pointer to hold the new table. * * Return: VDO_SUCCESS or an error code.
*/ int vdo_make_priority_table(unsignedint max_priority, struct priority_table **table_ptr)
{ struct priority_table *table; int result; unsignedint priority;
if (max_priority > MAX_PRIORITY) return UDS_INVALID_ARGUMENT;
result = vdo_allocate_extended(struct priority_table, max_priority + 1, struct bucket, __func__, &table); if (result != VDO_SUCCESS) return result;
/** * vdo_free_priority_table() - Free a priority_table. * @table: The table to free. * * The table does not own the entries stored in it and they are not freed by this call.
*/ void vdo_free_priority_table(struct priority_table *table)
{ if (table == NULL) return;
/* * Unlink the buckets from any entries still in the table so the entries won't be left with * dangling pointers to freed memory.
*/
vdo_reset_priority_table(table);
vdo_free(table);
}
/** * vdo_reset_priority_table() - Reset a priority table, leaving it in the same empty state as when * newly constructed. * @table: The table to reset. * * The table does not own the entries stored in it and they are not freed (or even unlinked from * each other) by this call.
*/ void vdo_reset_priority_table(struct priority_table *table)
{ unsignedint priority;
/** * vdo_priority_table_enqueue() - Add a new entry to the priority table, appending it to the queue * for entries with the specified priority. * @table: The table in which to store the entry. * @priority: The priority of the entry. * @entry: The list_head embedded in the entry to store in the table (the caller must have * initialized it).
*/ void vdo_priority_table_enqueue(struct priority_table *table, unsignedint priority, struct list_head *entry)
{
VDO_ASSERT_LOG_ONLY((priority <= table->max_priority), "entry priority must be valid for the table");
/* Append the entry to the queue in the specified bucket. */
list_move_tail(entry, &table->buckets[priority].queue);
/* Flag the bucket in the search vector since it must be non-empty. */
table->search_vector |= (1ULL << priority);
}
/** * vdo_priority_table_dequeue() - Find the highest-priority entry in the table, remove it from the * table, and return it. * @table: The priority table from which to remove an entry. * * If there are multiple entries with the same priority, the one that has been in the table with * that priority the longest will be returned. * * Return: The dequeued entry, or NULL if the table is currently empty.
*/ struct list_head *vdo_priority_table_dequeue(struct priority_table *table)
{ struct bucket *bucket; struct list_head *entry; int top_priority;
if (table->search_vector == 0) { /* All buckets are empty. */ return NULL;
}
/* * Find the highest priority non-empty bucket by finding the highest-order non-zero bit in * the search vector.
*/
top_priority = ilog2(table->search_vector);
/* Dequeue the first entry in the bucket. */
bucket = &table->buckets[top_priority];
entry = bucket->queue.next;
list_del_init(entry);
/* Clear the bit in the search vector if the bucket has been emptied. */ if (list_empty(&bucket->queue))
mark_bucket_empty(table, bucket);
return entry;
}
/** * vdo_priority_table_remove() - Remove a specified entry from its priority table. * @table: The table from which to remove the entry. * @entry: The entry to remove from the table.
*/ void vdo_priority_table_remove(struct priority_table *table, struct list_head *entry)
{ struct list_head *next_entry;
/* * We can't guard against calls where the entry is on a list for a different table, but * it's easy to deal with an entry not in any table or list.
*/ if (list_empty(entry)) return;
/* * Remove the entry from the bucket list, remembering a pointer to another entry in the * list.
*/
next_entry = entry->next;
list_del_init(entry);
/* * If the rest of the list is now empty, the next node must be the list head in the bucket * and we can use it to update the search vector.
*/ if (list_empty(next_entry))
mark_bucket_empty(table, list_entry(next_entry, struct bucket, queue));
}
/** * vdo_is_priority_table_empty() - Return whether the priority table is empty. * @table: The table to check. * * Return: true if the table is empty.
*/ bool vdo_is_priority_table_empty(struct priority_table *table)
{ return (table->search_vector == 0);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.