Plist in Linux -- a two-way linked list with descending priority

Posted by Daggeth on Wed, 23 Feb 2022 10:21:51 +0100

plist

https://elixir.bootlin.com/linux/v4.9.59/source/include/linux/plist.h#L212

*Descending priority sorted double linked list

plist is a list of nodes sorted by priority; The priority of each node is from INT_MIN to INT_MAX (minimum).

The time complexity of inserting is O(k), the time complexity of deleting is O(1), and the time complexity of changing node priority is O(k). Where k is the number of RT priorities in the system. (1≤K≤99)

data structure

//plist head
struct plist_head {
	struct list_head node_list;
};

//plist node
struct plist_node {
	int			prio;
	struct list_head	prio_list;
	struct list_head	node_list;
};

plist is a two-way linked list with priority formed by two two-way circular linked lists. among

  1. Plist per node_ According to the prio value, the node is weighted from small (highest priority) to large (lowest priority)_ List is sorted to form a linked list. Nodes with the same prio value are inserted first.
  2. Nodes with different prio values are in the order of small to large_ List is sorted to form a linked list. Among the nodes with the same prio value, only the node that was first inserted into the linked list is linked to prio_list, other nodes prio_list is empty.

This structure makes it possible to insert nodes through the variable prio_list can quickly find the insertion location without nodes with the same prio variables. Simultaneous node_list is sorted from small to large prio. For nodes with the same prio, the earliest inserted node is the first; When leaving the queue according to priority, only the first node needs to be taken, which can be realized. The high priority gives priority to the pair, and the FIFO function is realized with the same priority.

Macro / function

Initialize plist header and node

initiate static

Create variables and initialize.

/**p
 * PLIST_HEAD_INIT - Statically initialize plist header structure.
 * @head:	struct plist_head variable name
 */
#define PLIST_HEAD_INIT(head)				\
{							\
	.node_list = LIST_HEAD_INIT((head).node_list)	\
}

/**
 * PLIST_HEAD - Define and initialize a plist header.
 * @head:	name for struct plist_head variable
 */
#define PLIST_HEAD(head) \
	struct plist_head head = PLIST_HEAD_INIT(head)

/**
 * PLIST_NODE_INIT - Statically initialize the plistd node structure.
 * @node:	struct plist_node variable name
 * @__prio:	initial node priority
 */
#define PLIST_NODE_INIT(node, __prio)			\
{							\
	.prio  = (__prio),				\
	.prio_list = LIST_HEAD_INIT((node).prio_list),	\
	.node_list = LIST_HEAD_INIT((node).node_list),	\
}

dynamic initialization

Initialize defined variables

/**
 * plist_head_init - Dynamically initialize plist header
 * @head:	&struct plist_head pointer
 */
static inline void
plist_head_init(struct plist_head *head)
{
	INIT_LIST_HEAD(&head->node_list);
}

/**
 * plist_node_init - Initialize plist node dynamically
 * @node:	&struct plist_node pointer
 * @prio:	initial node priority
 */
static inline void plist_node_init(struct plist_node *node, int prio)
{
	node->prio = prio;
	INIT_LIST_HEAD(&node->prio_list);
	INIT_LIST_HEAD(&node->node_list);
}

Insert delete plist node

Insert node

/**
 * plist_add - add @node to @head
 *
 * @node:	&struct plist_node pointer
 * @head:	&struct plist_head pointer
 */
void plist_add(struct plist_node *node, struct plist_head *head)
{
	struct plist_node *first, *iter, *prev = NULL;
	struct list_head *node_next = &head->node_list;

	plist_check_head(head);
	WARN_ON(!plist_node_empty(node));
	WARN_ON(!list_empty(&node->prio_list));

	if (plist_head_empty(head)) //Case 1.
		goto ins_node;

	first = iter = plist_first(head);//list_entry(head->node_list.next,struct plist_node, node_list);

	do {
		if (node->prio < iter->prio) {
			node_next = &iter->node_list;
			break; //prio is less than a node in the linked list; The same as the previous node, case 3; Different from the previous node, case 4;
		}

		prev = iter;
		iter = list_entry(iter->prio_list.next,
				struct plist_node, prio_list);
	} while (iter != first); //After traversal, the prio is the largest, case 2; Prio is the same as the last node, case 3;

	if (!prev || prev->prio != node->prio) //Only when prio is different from the nodes in the linked list, it needs to be inserted into prio_list
		list_add_tail(&node->prio_list, &iter->prio_list);  

ins_node:
	list_add_tail(&node->node_list, node_next); //

	plist_check_head(head);
}

There are four situations for inserting nodes:

  1. node_list list is empty; New node_ Insert list directly into node_list chain header.
  2. node_ The list is not empty, and the prio of the new node is greater than the node_list all nodes in the linked list; New node_ Insert list into node_ The tail of the list, prio_list insert prio_list tail.
  3. node_ The list is not empty, and the new node prio is equal to node_ A node in the list; New node_ Insert node from list_ After the current position of the list, prio_list is empty.
  4. node_ The list is not empty, and the new node prio is less than node_ A node in the list; New node_list insert this node node_ In front of the list, prio_list insert prio_ In front of the list.

Delete node

/**
 * plist_del - Remove a @node from plist.
 *
 * @node:	&struct plist_node pointer - entry to be removed
 * @head:	&struct plist_head pointer - list head
 */
void plist_del(struct plist_node *node, struct plist_head *head)
{
	plist_check_head(head);

	if (!list_empty(&node->prio_list)) {
		if (node->node_list.next != &head->node_list) {
			struct plist_node *next;

			next = list_entry(node->node_list.next,
					struct plist_node, node_list);

			/* add the next plist_node into prio_list */
			if (list_empty(&next->prio_list))
				list_add(&next->prio_list, &node->prio_list); //Scenario 3 a
		} //else case 2
		list_del_init(&node->prio_list);
	} //else case 1

	list_del_init(&node->node_list);

	plist_check_head(head);
}

There are three major cases of deletion:

  1. If you want to delete prio_list is empty (that is, it has the same prio value as other nodes and is not the first node in the queue); Directly from node_list is deleted from the linked list.
  2. If you want to delete prio_ The list is not empty and is a node in the head_ The last of the list; First from prio_ Delete the list and re node_ It's scattered.
  3. If you want to delete prio_ The list is not empty and is not a node in the head_ The last of the list;
    1. If the node of the node to be deleted_ Prio of the next node in the list_ List is empty (the node to be deleted has the same prio value as the next node of node_list). You need to add the next node to prio first_ List, and then delete it in the next step.
    2. First from prio_ Delete the list and re node_list delete.

auxiliary function

/**
 * Judge whether the specified plist header is empty
 * plist_head_empty - return !0 if a plist_head is empty
 * @head:	&struct plist_head pointer
 */
static inline int plist_head_empty(const struct plist_head *head)
{
	return list_empty(&head->node_list);
}

/**
 * Judge whether the specified plist node is empty
 * plist_node_empty - return !0 if plist_node is not on a list
 * @node:	&struct plist_node pointer
 */
static inline int plist_node_empty(const struct plist_node *node)
{
	return list_empty(&node->node_list);
}

/**
 * plist_next - get the next entry in list
 * @pos:	the type * to cursor
 */
#define plist_next(pos) \
	list_next_entry(pos, node_list)

/**
 * plist_prev - get the prev entry in list
 * @pos:	the type * to cursor
 */
#define plist_prev(pos) \
	list_prev_entry(pos, node_list)

/**
 * Get plist first node
 * plist_first - return the first node (and thus, highest priority)
 * @head:	the &struct plist_head pointer
 *
 * Assumes the plist is _not_ empty.
 */
static inline struct plist_node *plist_first(const struct plist_head *head)
{
	return list_entry(head->node_list.next,
			  struct plist_node, node_list);
}

/**
 * Gets the last node of plist.
 * plist_last - return the last node (and thus, lowest priority)
 * @head:	the &struct plist_head pointer
 *
 * Assumes the plist is _not_ empty.
 */
static inline struct plist_node *plist_last(const struct plist_head *head)
{
	return list_entry(head->node_list.prev,
			  struct plist_node, node_list);
}

Topics: Linux data structure linked list