struct klist {
spinlock_t k_lock;
struct list_head k_list;
void (*get)(struct klist_node *);
void (*put)(struct klist_node *);
} __attribute__ ((aligned (sizeof(void *))));
struct klist_node {
void *n_klist; /* never access directly */
struct list_head n_node;
struct kref n_ref;
};
struct klist_iter {
struct klist *i_klist;
struct klist_node *i_cur;
};
struct klist_waiter {
struct list_head list;
struct klist_node *node;
struct task_struct *process;
int woken;
};
//=============================================================
// 将 new 插在 head 和 head->next 之间
static inline void list_add(struct list_head *new, struct list_head *head){
__list_add(new, head, head->next);
}
//判断一个节点是否是第一个的方式如下
static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
/**
* list_is_first -- tests whether @list is the first entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_first(const struct list_head *list,
const struct list_head *head)
{
return list->prev == head;
}
//=============================================================
// 将 new 插在 head->prev 和 head 之间
// 那不就是最后一个嘛
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
//判断一个节点是否是最后一个的方式如下
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
//=============================================================
【初始化-klist_init】
/**
* klist_init - Initialize a klist structure.
* @k: The klist we're initializing.
* @get: The get function for the embedding object (NULL if none)
* @put: The put function for the embedding object (NULL if none)
*
* Initialises the klist structure. If the klist_node structures are
* going to be embedded in refcounted objects (necessary for safe
* deletion) then the get/put arguments are used to initialise
* functions that take and release references on the embedding
* objects.
*/
void klist_init(struct klist *k, void (*get)(struct klist_node *),
void (*put)(struct klist_node *))
{
INIT_LIST_HEAD(&k->k_list);
spin_lock_init(&k->k_lock);
k->get = get;
k->put = put;
}
//=============================================================
【增加-klist_add_head】
// 下面统称 节点:klist_node,头部:klist
/**
* klist_add_head - Initialize a klist_node and add it to front.
* @n: node we're adding.
* @k: klist it's going on.
*/
void klist_add_head(struct klist_node *n, struct klist *k)
{
klist_node_init(k, n); // 设置节点的成员
add_head(k, n); // 将节点添加到头部的管理列表最前面
}
EXPORT_SYMBOL_GPL(klist_add_head);
static void klist_node_init(struct klist *k, struct klist_node *n)
{
INIT_LIST_HEAD(&n->n_node); // 初始化节点的 list_head
kref_init(&n->n_ref); // 节点的ref值设为1
knode_set_klist(n, k); // 节点的n_node插入头部的k_list最前面
if (k->get)
k->get(n);
}
static inline void kref_init(struct kref *kref){
refcount_set(&kref->refcount, 1);
}
static void knode_set_klist(struct klist_node *knode, struct klist *klist)
{
knode->n_klist = klist;
/* no knode deserves to start its life dead */
// 没有一个节点生而为死
// knode_dead is (unsigned long)knode->n_klist & KNODE_DEAD
WARN_ON(knode_dead(knode));
}
static void add_head(struct klist *k, struct klist_node *n)
{
spin_lock(&k->k_lock);
list_add(&n->n_node, &k->k_list); // 将节点的n_node插入头部的k_list中
spin_unlock(&k->k_lock);
}
//=============================================================
【增加-klist_add_behind】
/**
* klist_add_behind - Init a klist_node and add it after an existing node
* @n: node we're adding.
* @pos: node to put @n after
*/
void klist_add_behind(struct klist_node *n, struct klist_node *pos)
{
struct klist *k = knode_klist(pos);
klist_node_init(k, n);
spin_lock(&k->k_lock);
list_add(&n->n_node, &pos->n_node); // 将节点的n_node插入pos的后面
spin_unlock(&k->k_lock);
}
EXPORT_SYMBOL_GPL(klist_add_behind);
// 取一个knode指向的klist时,要把最低位去掉
// 最低位用于标记dead
static struct klist *knode_klist(struct klist_node *knode){
return (struct klist *)((unsigned long)knode->n_klist & KNODE_KLIST_MASK);
}
// Use the lowest bit of n_klist to mark deleted nodes and exclude dead ones from iteration.
#define KNODE_DEAD 1LU
#define KNODE_KLIST_MASK ~KNODE_DEAD
//=============================================================
【增加-klist_add_before】
/**
* klist_add_before - Init a klist_node and add it before an existing node
* @n: node we're adding.
* @pos: node to put @n after
*/
void klist_add_before(struct klist_node *n, struct klist_node *pos)
{
struct klist *k = knode_klist(pos);
klist_node_init(k, n);
spin_lock(&k->k_lock);
list_add_tail(&n->n_node, &pos->n_node);
spin_unlock(&k->k_lock);
}
EXPORT_SYMBOL_GPL(klist_add_before);
//=============================================================
【增加-klist_add_tail】
/**
* klist_add_tail - Initialize a klist_node and add it to back.
* @n: node we're adding.
* @k: klist it's going on.
*/
void klist_add_tail(struct klist_node *n, struct klist *k)
{
klist_node_init(k, n);
add_tail(k, n);
}
EXPORT_SYMBOL_GPL(klist_add_tail);
//=============================================================
【遍历-klist_prev】
/**
* klist_prev - Ante up prev node in list.
* @i: Iterator structure.
*
* First grab list lock. Decrement the reference count of the previous
* node, if there was one. Grab the prev node, increment its reference
* count, drop the lock, and return that prev node.
*/
// 每次迭代的时候要把前一个节点的计数递减,然后把下一个节点的计数递增
// 这些变量的名字起的真好
struct klist_node *klist_prev(struct klist_iter *i)
{
void (*put)(struct klist_node *) = i->i_klist->put;
struct klist_node *last = i->i_cur;
struct klist_node *prev;
unsigned long flags;
spin_lock_irqsave(&i->i_klist->k_lock, flags);
if (last) {
prev = to_klist_node(last->n_node.prev);
if (!klist_dec_and_del(last))
put = NULL;
} else
prev = to_klist_node(i->i_klist->k_list.prev);
i->i_cur = NULL;
while (prev != to_klist_node(&i->i_klist->k_list)) {
if (likely(!knode_dead(prev))) { //很有可能没有死
kref_get(&prev->n_ref);
i->i_cur = prev;
break;
}
prev = to_klist_node(prev->n_node.prev);
}
spin_unlock_irqrestore(&i->i_klist->k_lock, flags);
if (put && last)
put(last);
return i->i_cur;
}
EXPORT_SYMBOL_GPL(klist_prev);
//=============================================================
【遍历-klist_next】
/**
* klist_next - Ante up next node in list.
* @i: Iterator structure.
*
* First grab list lock. Decrement the reference count of the previous
* node, if there was one. Grab the next node, increment its reference
* count, drop the lock, and return that next node.
*/
// 每次迭代的时候要把前一个节点的计数递减,然后把下一个节点的计数递增
struct klist_node *klist_next(struct klist_iter *i)
{
void (*put)(struct klist_node *) = i->i_klist->put;
struct klist_node *last = i->i_cur;
struct klist_node *next;
unsigned long flags;
spin_lock_irqsave(&i->i_klist->k_lock, flags);
if (last) {
next = to_klist_node(last->n_node.next);
if (!klist_dec_and_del(last))
put = NULL;
} else
next = to_klist_node(i->i_klist->k_list.next);
i->i_cur = NULL;
while (next != to_klist_node(&i->i_klist->k_list)) {
if (likely(!knode_dead(next))) {
kref_get(&next->n_ref);
i->i_cur = next;
break;
}
next = to_klist_node(next->n_node.next);
}
spin_unlock_irqrestore(&i->i_klist->k_lock, flags);
if (put && last)
put(last);
return i->i_cur;
}
EXPORT_SYMBOL_GPL(klist_next);
//======================================================================
【删除-klist_del】
// 这个是递减计数,试图remove
/**
* klist_del - Decrement the reference count of node and try to remove.
* @n: node we're deleting.
*/
void klist_del(struct klist_node *n)
{
klist_put(n, true);
}
EXPORT_SYMBOL_GPL(klist_del);
static void klist_put(struct klist_node *n, bool kill)
{
struct klist *k = knode_klist(n);
void (*put)(struct klist_node *) = k->put;
spin_lock(&k->k_lock);
if (kill)
knode_kill(n); // 将该 klist_node 的 n_klist 指针的值标记为 DEAD
if (!klist_dec_and_del(n)) // 引用计数减1,如果计数为0,则执行 klist_release 操作
put = NULL;
spin_unlock(&k->k_lock);
if (put) // 只有在 klist_node 的计数减到0之后,才执行 klist 的 put 操作
put(n);
}
static void knode_kill(struct klist_node *knode)
{
/* and no knode should die twice ever either, see we're very humane */
// 死一次足矣
WARN_ON(knode_dead(knode));
// knode->n_klist 该指针指向 klist
// *((unsigned long *)&(knode->n_klist)) |= KNODE_DEAD;
// knode->n_klist 是 knode 指针所指的结构体中 n_klist 的值 (该值是管该knode的klist的地址)
// 此时该值以 void *的数据存放(因为 knode->n_klist 的类型是 void *)
// 然后对上述值取地址,将地址强转为 unsigned long *
// 再解引用,那不就是将之前的值从原来类型(void *)转为 unsigned long 类型了
*(unsigned long *)&knode->n_klist |= KNODE_DEAD;
// 举例如下:
// klist 的地址 0x12345678
// knode 的地址 0x00001212
// knode 中 n_klist指针的地址为 0x00001212 ,n_klist指针的类型为 (void *) ,n_klist的值为 0x12345678
// 目前 knode 中 n_klist指针的值 0x12345678 值不可修改?(该值是一个void类型的地址)
// &knode->n_klist 就是 0x00001212
// (unsigned long *)&knode->n_klist 就是 (unsigned long *)0x00001212
// 那么对应 n_klist指针的类型目前变为 (unsigned long *),n_klist的值不变
// *(unsigned long *)&knode->n_klist 就是 0x12345678
// 目前 knode 中 n_klist指针的值 0x12345678 值可修改(该值是一个unsigned long类型的地址)
// *(unsigned long *)&knode->n_klist |= KNODE_DEAD 就是 (unsigned long)0x12345679
// 所以 knode 中 n_klist指针的地址为 0x00001212,指针的值为 0x12345679
// klist 的地址依旧没变,变的是 knode 中 n_klist指针 的值
}
static int klist_dec_and_del(struct klist_node *n){
return kref_put(&n->n_ref, klist_release); // 自带灭火器
}
/**
* kref_put - decrement refcount for object.
* @kref: object.
* @release: pointer to the function that will clean up the object when the
* last reference to the object is released.
* This pointer is required, and it is not acceptable to pass kfree
* in as this function.
*
* Decrement the refcount, and if 0, call release().
* Return 1 if the object was removed, otherwise return 0. Beware, if this
* function returns 0, you still can not count on the kref from remaining in
* memory. Only use the return value if you want to see if the kref is now
* gone, not present.
*/
// 递减引用计数,如果为0,则调用 release函数
// 返回1表示该节点已经被清理
// 返回0表示仅仅递减了引用计数
static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
if (refcount_dec_and_test(&kref->refcount)) {
release(kref);
return 1;
}
return 0;
}
static void klist_release(struct kref *kref)
{
struct klist_waiter *waiter, *tmp;
struct klist_node *n = container_of(kref, struct klist_node, n_ref);
WARN_ON(!knode_dead(n));
list_del(&n->n_node); //从管理链表中移除
spin_lock(&klist_remove_lock);
// 遍历等待列表,看哪个 waiter 等待在该 klist_node 上
// 如果有,则将该 waiter 从等待链表删除,将该 waiter 标记为 1
// 然后唤醒该 waiter 所在的 process
list_for_each_entry_safe(waiter, tmp, &klist_remove_waiters, list) {
if (waiter->node != n)
continue;
list_del(&waiter->list);
waiter->woken = 1;
mb();
wake_up_process(waiter->process);
}
spin_unlock(&klist_remove_lock);
knode_set_klist(n, NULL);
}
//======================================================================
【删除-klist_remove】
// 这个是递减计数,一直等到它“灭亡”
**
* klist_remove - Decrement the refcount of node and wait for it to go away.
* @n: node we're removing.
*/
void klist_remove(struct klist_node *n)
{
struct klist_waiter waiter;
waiter.node = n;
waiter.process = current;
waiter.woken = 0;
spin_lock(&klist_remove_lock);
list_add(&waiter.list, &klist_remove_waiters); // 将该进程添加到移除队列klist_remove_waiters
spin_unlock(&klist_remove_lock);
klist_del(n);
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (waiter.woken)
break;
schedule();
}
__set_current_state(TASK_RUNNING);
}
EXPORT_SYMBOL_GPL(klist_remove);
// 将前面的各个接口都整理进 klist_remove
// 才能发现这个接口设计的美妙
void klist_remove(struct klist_node *n)
{
struct klist_waiter waiter;
waiter.node = n;
waiter.process = current;
waiter.woken = 0;
spin_lock(&klist_remove_lock);
list_add(&waiter.list, &klist_remove_waiters); // 将该进程添加到移除队列klist_remove_waiters
spin_unlock(&klist_remove_lock);
struct klist *k = knode_klist(n);
void (*put)(struct klist_node *) = k->put;
spin_lock(&k->k_lock);
knode_kill(n); // 将该 klist_node 的 n_klist 指针的值标记为 DEAD
if (refcount_dec_and_test(&kref->refcount)) { // 计数减1,计数为0则执行 klist_release 操作
struct klist_waiter *waiter, *tmp;
struct klist_node *n = container_of(kref, struct klist_node, n_ref);
WARN_ON(!knode_dead(n));
list_del(&n->n_node); //从管理链表中移除
spin_lock(&klist_remove_lock);
// 遍历等待列表,看哪个 waiter 等待在该 klist_node 上
// 如果有,则将该 waiter 从等待链表删除,将该 waiter 标记为 1
// 然后唤醒该 waiter 所在的 process
list_for_each_entry_safe(waiter, tmp, &klist_remove_waiters, list) {
if (waiter->node != n)
continue;
list_del(&waiter->list);
waiter->woken = 1;
mb();
wake_up_process(waiter->process); // 唤醒等待在该删除node上的waiter process
}
spin_unlock(&klist_remove_lock);
knode_set_klist(n, NULL);
} else {
put = NULL;
}
spin_unlock(&k->k_lock);
if (put) // 只有在 klist_node 的计数减到0之后,才执行 klist 的 put 操作
put(n);
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);
// woken 主要用在这里,如果前面计数不为0,则调用 klist_remove 的进程都会跑进这里
// 只有计数减为0的进程才会将“等待删除该节点的进程”的waiter的woken置为1
// woken 用的真是奇妙
if (waiter.woken)
break;
schedule();
}
__set_current_state(TASK_RUNNING);
}
//======================================================================
【判断一个节点是否归属于某个klist】
/**
* klist_node_attached - Say whether a node is bound to a list or not.
* @n: Node that we're testing.
*/
int klist_node_attached(struct klist_node *n)
{
return (n->n_klist != NULL);
}
EXPORT_SYMBOL_GPL(klist_node_attached);
//======================================================================
【klist_iter的初始化-klist_iter_init_node和klist_iter_init】
/**
* klist_iter_init_node - Initialize a klist_iter structure.
* @k: klist we're iterating.
* @i: klist_iter we're filling.
* @n: node to start with.
*
* Similar to klist_iter_init(), but starts the action off with @n,
* instead of with the list head.
*/
// 初始化一个 klist_iter 指针所指的结构体
// 这个接口:传入的变量有 klist_node ,但是内容中会判断迭代的 klist_node 的计数是否为0
// 为0则代表该 klist_node 并没有初始化完成或者已被删除,因此 i->i_cur = NULL
// kref_get_unless_zero 用于无锁的原子操作
void klist_iter_init_node(struct klist *k, struct klist_iter *i,
struct klist_node *n)
{
i->i_klist = k;
i->i_cur = NULL;
if (n && kref_get_unless_zero(&n->n_ref))
i->i_cur = n;
}
EXPORT_SYMBOL_GPL(klist_iter_init_node);
/**
* klist_iter_init - Iniitalize a klist_iter structure.
* @k: klist we're iterating.
* @i: klist_iter structure we're filling.
*
* Similar to klist_iter_init_node(), but start with the list head.
*/
// 从 list head 开始初始化一个 klist_iter
void klist_iter_init(struct klist *k, struct klist_iter *i)
{
klist_iter_init_node(k, i, NULL);
}
EXPORT_SYMBOL_GPL(klist_iter_init);
/**
* kref_get_unless_zero - Increment refcount for object unless it is zero.
* @kref: object.
*
* Return non-zero if the increment succeeded. Otherwise return 0.
*
* This function is intended to simplify locking around refcounting for
* objects that can be looked up from a lookup structure, and which are
* removed from that lookup structure in the object destructor.
* Operations on such objects require at least a read lock around
* lookup + kref_get, and a write lock around kref_put + remove from lookup
* structure. Furthermore, RCU implementations become extremely tricky.
* With a lookup followed by a kref_get_unless_zero *with return value check*
* locking in the kref_put path can be deferred to the actual removal from
* the lookup structure and RCU lookups become trivial.
*/
static inline int __must_check kref_get_unless_zero(struct kref *kref){
return refcount_inc_not_zero(&kref->refcount);
}
static inline __must_check bool refcount_inc_not_zero(refcount_t *r){
return __refcount_add_not_zero(1, r, NULL);
}
// 该函数有阉割
static inline __must_check bool __refcount_add_not_zero(int i, refcount_t *r) {
int old = refcount_read(r);
do {
if (!old)
break;
} while (!atomic_try_cmpxchg_relaxed(&r->refs, &old, old + i));
// 原子操作,当第二次读变量的值和第一次old相同,则把新值赋值给该变量
return old;
}
//======================================================================
【klist_iter的退出-klist_iter_exit】
/**
* klist_iter_exit - Finish a list iteration.
* @i: Iterator structure.
*
* Must be called when done iterating over list, as it decrements the
* refcount of the current node. Necessary in case iteration exited before
* the end of the list was reached, and always good form.
*/
// 迭代结束后,要用该接口
// 如果该 iter 指向某个 klist_node ,则该 klist_node 的引用计数要被减1
// 当迭代还没到达 list 的尾部就退出,调用该接口是一个好习惯
void klist_iter_exit(struct klist_iter *i)
{
if (i->i_cur) {
klist_put(i->i_cur, false);
i->i_cur = NULL;
}
}
EXPORT_SYMBOL_GPL(klist_iter_exit);
// 该 klist_put 的第二个参数为 false ,代表不用标记 klist_node->n_klist 的值为 DEAD
// klist_node->n_klist 的值标记为 DEAD 后,就不能通过 klist_node 来访问它的头klist了
static void klist_put(struct klist_node *n, bool false)
{
struct klist *k = knode_klist(n);
void (*put)(struct klist_node *) = k->put;
spin_lock(&k->k_lock);
if (!klist_dec_and_del(n)) // 引用计数减1,如果计数为0,则执行 klist_release 操作
put = NULL;
spin_unlock(&k->k_lock);
if (put) // 只有在 klist_node 的计数减到0之后,才执行 klist 的 put 操作
put(n);
}
//======================================================================
【通过 klist_node中的成员list_head来获取结构体地址klist_node】
// list_head* 指向 list_head 的地址,所以实参得是一个 list_head结构体 的地址
// klist_node实例的结构体中,包含一个 list_head结构体
// 所以可以通过 klist_node实例 中的成员 list_head的地址,进行 container_of 计算
// 来获取 klist_node实例 的地址
static struct klist_node *to_klist_node(struct list_head *n){
return container_of(n, struct klist_node, n_node);
}
//======================================================================
【附件:修改void指针的值】
#include <stdio.h>
void main() {
int hello = 12;
void *tmp;
printf("hello addr %p, hello value %d\n", &hello, hello);
tmp = &hello;
*(unsigned long *)&tmp +=1;
printf("tmp addr %p, tmp value %p\n", &tmp, tmp);
}
root@ubuntu:/home/kiss/study/test36# ./test
hello addr 0x7ffee496d8ac, hello value 12
tmp addr 0x7ffee496d8b0, tmp value 0x7ffee496d8ad
//======================================================================
为什么要写它?
本人觉得它是内核开发出来的一个精巧的链表管理,搜索整个kernel 5.15,用的模块很少,但bus总线有用,还好提供了一个示例。