-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathworkqueue
More file actions
123 lines (94 loc) · 7.12 KB
/
Copy pathworkqueue
File metadata and controls
123 lines (94 loc) · 7.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
workqueue_struct和具体的worker_pool的关联是通过pool_workqueue来实现的,是一种多对多的关系,pool_workqueue就是连接表
workqueue_struct可以创建任意多个,根据具体的属性与worker_pool关联
所有的unbound_pool都存放在unbound_pool_hash,pool->attrs(nice+cpumask)作为hash key
每个cpu有NR_STD_WORKER_POOLS(2)个worker_pool分别对应不同的优先级
ordered_unbound_pool的唯一性约束是通过构造pwq的时候,所有的node都指向default pwq来确保的
lock {
wq->mutex
pwq->pool->lock
}
workqueue建立以后可以通过sysfs接口动态的调整nice
pool的并发控制 {
pool内控制并发,目标是有work要做的时候,一个worker在跑就行了
worker中的work如果suspend的时候,会勾住sched_core的接口,判断是否有其他work并且没有worker在跑,如果满足该条件,启动新的worker
并发的worker执行完成后,会判断当前的running worker是否超过1个,如果是的话,会进入idle状态
}
flush机制 {
增加一个barrier work链接到target work后面
worker遇到linked work的时候,会依次把linked work以及之后的一个work移到scheduled队列里面
如果target work本身已经在worker上面,那么直接把barrier work插入scheduled队列
}
unbound_pool_hash管理所有的unbound pool,通过wq_pool_mutex同步,如果有重复的可以利用,直接pool->refcnt++
wq->numa_pwq_tbl为什么要wq_pool_mutex和wq->mutex同时保护? {
原始的方案只需要wq->mutex
下面的patches引入了wq_pool_mutex {
commit 899a94fe15a8e928277ff0d0402c086fa67fe16f
commit da7f91b2e2c6176f95ca7b538d74dc70c5d11ded
commit d4d3e2579756e3a5f4fbf8eac211f0696e253bcd
commit a0111cf6710bd1b4145ef313d3f4772602af051b
commit f7142ed483f49f9108bea1be0c1afcd5d9098e05
commit 5b95e1af8d17d85a17728f6de7dbff538e6e3c49
}
虽然后续的提交没有指出,但是原始的方案应该是有问题的,从代码分析来看,wq的unbound相关属性是由pool和wq同时决定的,所以在修改的时候必须同时获取两把锁。
cpu online/offline会影响pool,并进一步影响wq的unbound属性
wq_cpumask_store的效果被cpu online完全覆盖了,具体CPU如何影响unbound属性需要继续调查
cpu online | wq_cpumask_store
mutex_lock(&wq_pool_mutex); |
update pool |
| mutex_lock(&wq->mutex);
| numa_pwq_tbl_install
| mutex_unlock(&wq->mutex);
mutex_lock(&wq->mutex); |
numa_pwq_tbl_install |
mutex_unlock(&wq->mutex); |
mutex_unlock(&wq_pool_mutex) |
}
wq_unbound_cpumask
unbound workqueue包含default_pwq和每node pwq
unbound workqueue的max_active是如何保证的?
flush_color < work_color ?
pool {
cpu, per_cpu_pool -> default cpu, unbound pool -> -1
}
work被queue之后会优先放到对应的pool里面,如果pool里面包含的work数量大于等于pwd的max_active数,work会被缓存在pwd的delayed_works中
unbound_pwq install的时候值为1
unbound_pwq uninstall的时候值为-1
unbound_workqueue更新属性时会uninstall old_pwq
insert_work时get_pwq
work完成时调用pwq_dec_nr_in_flight -> put_pwq
unbound pwq对pool的引用计数在pwq_unbound_release_workfn -> put_unbound_pool释放
6d25be5782e48在wq_worker_sleeping中引入了spin_lock_irq
ida_simple_remove -> ida_free
flush的时候,flusher的flush_color设置为当前的wq->work_color,wq->work_color循环进一,wq->flush_color表示下一个需要被flush的序列,一旦work完成,wq->flush_color循环进一
如果wq->work_color无法进一的时候,flusher被缓存到overflow队列,当flush_color进位时,所有的flusher的flush_color都设置为最新的work_color,然后wq->work_color进一
flush_workqueue_prep_pwqs调用时如果flush_color>=0,可以确定pwq中的nr_in_flight[flush_color]一定时work color队列的head
cpu_bound类型的pwq默认ref_cnt = 1,workqueue删除的时候直接释放,pwq引用的cpu_pool
alloc_unbound_pwq时需要获取unbound_pool并增加引用计数
work的生命周期 {
WORK_STRUCT_PENDING,等待timeout触发之后将work入队
-----------------------on queue-------------------------------------------------
WORK_STRUCT_PENDING,pwq的active work容量已满,放在pwq->delayed_works队列
WORK_STRUCT_PENDING,处于pool->worklist
--------------------------------------------------------------------------------
执行中
WORK_STRUCT_PENDING,WORK_OFFQ_CANCELING,执行中&(同步)取消中,这个状态可能持续任意长时间,因为work的执行是允许sleep的
}
cancel work分为一般取消和同步取消,一般取消无需等待work执行完成,同步取消会等待work执行完成(如果正在执行中),并且在此期间标记WORK_STRUCT_PENDING。
pool有一个mayday timeout机制,如果超过一定时间都无法创建新的worker,就唤起worklist里面对应的wq包含rescuer线程的task
rescuer_thread隶属于workqueue,在需要的时候attach到需要支援的pool里面,结束时从对应的pool中detach
worker flag的同步机制是否完善?{
case 1: worker_set_flags和worker_clr_flags的调用context都是worker线程本身,持有pool->lock, worker_enter_idle和worker_leave_idle都持有pool->lock
case 2: unbind_workers, rebind_workers同时持有wq_pool_attach_mutext和pool->lock,修改确保WORKER_NOT_RUNNING这个状态不会变化?比如从unbound -> rebound,这样确保case4是安全的
case 3: worker_attach_to_pool,worker_detach_from_pool持有wq_pool_attach_mutext,对于非rescuer,分别在初始化和消亡的时候调用,无需考虑,对于rescuer,针对pool是associate的情况更新flag
case 4: wq_worker_running, wq_worker_sleeping无锁读,主要用于work中途sleep和worker自身的睡眠(这种情况flag设为prepara,肯定不会被考虑为running),天然和case1, case3互斥
}
pwq的max_active特性收到用户调整和freeze的同时管控,所以需要同时持有wq_pool_mutex和wq->mutex,但是workqueue_set_max_active只持有了wq->mutex,没有获取wq_pool_mutex
rescuer free了两次
sysfs应该增加对workqueue结构的引用,否则destroy_workqueue的时候,没法确保sysfs已经不再应用workqueue,比如强制open一个workqueue的sysfs节点,然后等到workqueue销毁之后再操作,是否会有问题?
unbound_workers中的schedule到底有什么用? {
unbound_workers调用的时候,per_cpu的worker是否已经migrate到了其他cpu,如果是的话,那么就存在并发的可能性了
但是目前看cpu关闭的时候先调用的workqueu_offline_cpu,然后才是调用sched_cpu_dying->migrate_tasks, so what to do?
sched_cpu_deactivate之后,task再次被调度的时候,会被migrate到其他cpu么?
有没有这样的可能性,cpu offline导致了task在其他cpu上运行,online触发,但是task还没有完全迁移过来,这时候再度触发offline,task还是有可能没有迁移到local cpu
确保WORKER_UNBOUND和nr_running的操作顺序?
}