// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2023 Red Hat
*/
#include "priority-table.h"
#include <linux/log2.h>
#include "errors.h"
#include "memory-alloc.h"
#include "permassert.h"
#include "status-codes.h"
/* 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 */
unsigned int 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 */
unsigned int 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(unsigned int max_priority, struct priority_table **table_ptr)
{
struct priority_table *table;
int result;
unsigned int 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;
for (priority = 0 ; priority <= max_priority; priority++) {
struct bucket *bucket = &table->buckets[priority];
bucket->priority = priority;
INIT_LIST_HEAD(&bucket->queue);
}
table->max_priority = max_priority;
table->search_vector = 0 ;
*table_ptr = table;
return VDO_SUCCESS;
}
/**
* 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)
{
unsigned int priority;
table->search_vector = 0 ;
for (priority = 0 ; priority <= table->max_priority; priority++)
list_del_init(&table->buckets[priority].queue);
}
/**
* 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, unsigned int 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 |= (1 ULL << priority);
}
static inline void mark_bucket_empty(struct priority_table *table, struct bucket *bucket)
{
table->search_vector &= ~(1 ULL << bucket->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 );
}
Messung V0.5 in Prozent C=89 H=94 G=91
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland