JJJ:网络子系统的软中断

9907 open_softirq(NET_TX_SOFTIRQ, net_tx_action);
9908 open_softirq(NET_RX_SOFTIRQ, net_rx_action);

454 void open_softirq(int nr, void (*action)(struct softirq_action *))
455 {
456 softirq_vec[nr].action = action;
457 }

723 static struct smp_hotplug_thread softirq_threads = {
724 .store = &ksoftirqd,
725 .thread_should_run = ksoftirqd_should_run,
726 .thread_fn = run_ksoftirqd,
727 .thread_comm = “ksoftirqd/%u”,
728 };

内核启动开始
730 static __init int spawn_ksoftirqd(void)
731 {
732 cpuhp_setup_state_nocalls(CPUHP_SOFTIRQ_DEAD, “softirq:dead”, NULL,
733 takeover_tasklets);
//最终会执行.thread_fn = run_ksoftirqd函数
734 BUG_ON(smpboot_register_percpu_thread(&softirq_threads));
735
736 return 0;
737 }

645 static void run_ksoftirqd(unsigned int cpu)
646 {
647 local_irq_disable();
648 if (local_softirq_pending()) {
649 /*
650 * We can safely run softirq on inline stack, as we are not deep
651 * in the task stack here.
652 */
653 __do_softirq(); 此处一次去执行被标记的bit对应的中断action
654 local_irq_enable();
655 cond_resched();
656 return;
657 }
658 local_irq_enable();
659 }

include/linux/interrupt.h:441:#define local_softirq_pending()
(__this_cpu_read(local_softirq_pending_ref))

include/linux/interrupt.h:438:#define local_softirq_pending_ref
irq_stat.__softirq_pending

loopback_xmit
netif_rx
netif_rx_internal
enqueue_to_backlog
____napi_schedule
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
448 void __raise_softirq_irqoff(unsigned int nr)
449 {
450 trace_softirq_raise(nr);
451 or_softirq_pending(1UL << nr);
452 }
443 #define or_softirq_pending(x) (__this_cpu_or(local_softirq_pending_ref, (x)))

249 asmlinkage __visible void __softirq_entry __do_softirq(void)
250 {
251 unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
252 unsigned long old_flags = current->flags;
253 int max_restart = MAX_SOFTIRQ_RESTART;
254 struct softirq_action h;
255 bool in_hardirq;
256 __u32 pending;
257 int softirq_bit;
258
259 /

260 * Mask out PF_MEMALLOC s current task context is borrowed for the
261 * softirq. A softirq handled such as network RX might set PF_MEMALLOC
262 * again if the socket is related to swap
263 /
264 current->flags &= ~PF_MEMALLOC;
265
266 pending = local_softirq_pending();
267 account_irq_enter_time(current);
268
269 __local_bh_disable_ip(RET_IP, SOFTIRQ_OFFSET);
270 in_hardirq = lockdep_softirq_start();
271
272 restart:
273 /
Reset the pending bitmask before enabling irqs */
274 set_softirq_pending(0);
275
276 local_irq_enable();
277
278 h = softirq_vec;
279
280 while ((softirq_bit = ffs(pending))) {
281 unsigned int vec_nr;
282 int prev_count;
283
284 h += softirq_bit - 1;
285
286 vec_nr = h - softirq_vec;
287 prev_count = preempt_count();
288
289 kstat_incr_softirqs_this_cpu(vec_nr);
290
291 trace_softirq_entry(vec_nr);
292 h->action(h); 具体的执行动作,如net_rx_action
293 trace_softirq_exit(vec_nr);
294 if (unlikely(prev_count != preempt_count())) {
295 pr_err(“huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n”,
296 vec_nr, softirq_to_name[vec_nr], h->action,
297 prev_count, preempt_count());
298 preempt_count_set(prev_count);
299 }
300 h++;
301 pending >>= softirq_bit;
302 }
303
304 rcu_bh_qs();
305 local_irq_disable();
306
307 pending = local_softirq_pending();
308 if (pending) {
309 if (time_before(jiffies, end) && !need_resched() &&
310 --max_restart)
311 goto restart;
312
313 wakeup_softirqd();
314 }
315
316 lockdep_softirq_end(in_hardirq);
317 account_irq_exit_time(current);
318 __local_bh_enable(SOFTIRQ_OFFSET);
319 WARN_ON_ONCE(in_interrupt());
320 current_restore_flags(old_flags, PF_MEMALLOC);
321 }

282 /**
283 * smpboot_register_percpu_thread - Register a per_cpu thread related
284 * to hotplug
285 * @plug_thread: Hotplug thread descriptor
286 *
287 * Creates and starts the threads on all online cpus.
288 */
289 int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread)
290 {
291 unsigned int cpu;
292 int ret = 0;
293
294 get_online_cpus();
295 mutex_lock(&smpboot_threads_lock);
296 for_each_online_cpu(cpu) {
297 ret = __smpboot_create_thread(plug_thread, cpu);对每个cpu上都执行这个函数
298 if (ret) {
299 smpboot_destroy_threads(plug_thread);
300 goto out;
301 }
302 smpboot_unpark_thread(plug_thread, cpu);
303 }
304 list_add(&plug_thread->list, &hotplug_threads);
305 out:
306 mutex_unlock(&smpboot_threads_lock);
307 put_online_cpus();
308 return ret;
309 }

169 static int
170 __smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)

171 {
172 struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
173 struct smpboot_thread_data *td;
174
175 if (tsk)
176 return 0;
177
178 td = kzalloc_node(sizeof(td), GFP_KERNEL, cpu_to_node(cpu));
179 if (!td)
180 return -ENOMEM;
181 td->cpu = cpu;
182 td->ht = ht;
183
184 tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,
185 ht->thread_comm); 重点是这个
186 if (IS_ERR(tsk)) {
187 kfree(td);
188 return PTR_ERR(tsk);
189 }
190 kthread_set_per_cpu(tsk, cpu);
191 /

192 * Park the thread so that it could start right on the CPU
193 * when it is available.
194 */
195 kthread_park(tsk);
196 get_task_struct(tsk);
197 per_cpu_ptr(ht->store, cpu) = tsk;
198 if (ht->create) {
199 /

200 * Make sure that the task has actually scheduled out
201 * into park position, before calling the create
202 * callback. At least the migration thread callback
203 * requires that the task is off the runqueue.
204 */
205 if (!wait_task_inactive(tsk, TASK_PARKED))
206 WARN_ON(1);
207 else
208 ht->create(cpu);
209 }
210 return 0;
211 }

452 struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
453 void *data, unsigned int cpu,
454 const char *namefmt)
455 {
456 struct task_struct p;
457
458 p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt,
459 cpu);
460 if (IS_ERR§)
461 return p;
462 kthread_bind(p, cpu);
463 /
CPU hotplug need to bind once again when unparking the thread. */
464 to_kthread§->cpu = cpu;
465 return p;
466 }

384 struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
385 void *data, int node,
386 const char namefmt[],
387 …)
388 {
389 struct task_struct *task;
390 va_list args;
391
392 va_start(args, namefmt);
393 task = __kthread_create_on_node(threadfn, data, node, namefmt, args);
394 va_end(args);
395
396 return task;
397 }

297 static __printf(4, 0)
298 struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
299 void *data, int node,
300 const char namefmt[],
301 va_list args)
302 {
303 DECLARE_COMPLETION_ONSTACK(done);
304 struct task_struct *task;
305 struct kthread_create_info *create = kmalloc(sizeof(create),
306 GFP_KERNEL);
307
308 if (!create)
309 return ERR_PTR(-ENOMEM);
310 create->threadfn = threadfn; 就是 smpboot_thread_fn
311 create->data = data;
312 create->node = node;
313 create->done = &done;
314
315 spin_lock(&kthread_create_lock);
316 list_add_tail(&create->list, &kthread_create_list);在kthreadd中会遍历这个链表依次执行threadfn
317 spin_unlock(&kthread_create_lock);
318
319 wake_up_process(kthreadd_task); 执行kthreadd函数
320 /

321 * Wait for completion in killable state, for I might be chosen by
322 * the OOM killer while kthreadd is trying to allocate memory for
323 * new kernel thread.
324 /
325 if (unlikely(wait_for_completion_killable(&done))) {
326 /

327 * If I was SIGKILLed before kthreadd (or new kernel thread)
328 * calls complete(), leave the cleanup of this structure to
329 * that thread.
330 /
331 if (xchg(&create->done, NULL))
332 return ERR_PTR(-EINTR);
333 /

334 * kthreadd (or new kernel thread) will call complete()
335 * shortly.
336 /
337 wait_for_completion(&done);
338 }
339 task = create->result;
340 if (!IS_ERR(task)) {
341 static const struct sched_param param = { .sched_priority = 0 };
342 char name[TASK_COMM_LEN];
343
344 /

345 * task is already visible to other tasks, so updating
346 * COMM must be protected.
347 /
348 vsnprintf(name, sizeof(name), namefmt, args);
349 set_task_comm(task, name);
350 /

351 * root may have changed our (kthreadd’s) priority or CPU mask.
352 * The kernel thread should not inherit these properties.
353 */
354 sched_setscheduler_nocheck(task, SCHED_NORMAL, &param);
355 set_cpus_allowed_ptr(task, cpu_all_mask);
356 }
357 kfree(create);
358 return task;
359 }
kthreadd_task 是指向内核线程 kthreadd 的进程描述符(task_struct 结构体实例)的一个全局变量。
kthreadd 是内核启动时创建的第一个内核线程,具有进程ID(PID)2,
其父进程ID(PPID)为0,即它是从 swapper 进程(又称为idle进程)派生出来的。
相关代码片段如下
420 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
421 rcu_read_lock();
422 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);

find_task_by_pid_ns用于查找指定命名空间(namespace)下具有指定进程ID(pid)的任务(即进程)结构体
(task_struct)实例。

598 int kthreadd(void *unused)
599 {
600 struct task_struct tsk = current;
601
602 /
Setup a clean context for our children to inherit. */
603 set_task_comm(tsk, “kthreadd”);
604 ignore_signals(tsk);
605 set_cpus_allowed_ptr(tsk, cpu_all_mask);
606 set_mems_allowed(node_states[N_MEMORY]);
607
608 current->flags |= PF_NOFREEZE;
609 cgroup_init_kthreadd();
610
611 for (;😉 {
612 set_current_state(TASK_INTERRUPTIBLE);
613 if (list_empty(&kthread_create_list))
614 schedule();
615 __set_current_state(TASK_RUNNING);
616
617 spin_lock(&kthread_create_lock);
618 while (!list_empty(&kthread_create_list)) {
619 struct kthread_create_info *create;
620
621 create = list_entry(kthread_create_list.next,
622 struct kthread_create_info, list);
623 list_del_init(&create->list);
624 spin_unlock(&kthread_create_lock);
625
626 create_kthread(create); 此处创建具体的线程
627
628 spin_lock(&kthread_create_lock);
629 }
630 spin_unlock(&kthread_create_lock);
631 }
632
633 return 0;
634 }

275 static void create_kthread(struct kthread_create_info create)
276 {
277 int pid;
278
279 #ifdef CONFIG_NUMA
280 current->pref_node_fork = create->node;
281 #endif
282 /
We want our own signal handler (we take no signals by default). /
283 pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD); 自动运行
284 if (pid < 0) {
285 /
If user was SIGKILLed, I release the structure. */
286 struct completion *done = xchg(&create->done, NULL);
287
288 if (!done) {
289 kfree(create);
290 return;
291 }
292 create->result = ERR_PTR(pid);
293 complete(done);
294 }
295 }
使用kernel_thread函数创建内核线程时,实际上已经安排了新线程执行所需的函数及其参数。
当_do_fork系统调用成功创建了一个新的内核线程后,新线程在创建完成后会立即开始执行传入的函数指针fn。

因此,在调用kernel_thread创建内核线程后,无需再额外调用函数来运行它。
新创建的内核线程会按照我们传递的参数fn所指定的函数自动开始执行。
这里的fn是你想要新线程执行的函数,而arg则是传递给那个函数的参数。

2408 /*
2409 * Create a kernel thread.
2410 */
2411 pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
2412 {
2413 return _do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
2414 (unsigned long)arg, NULL, NULL, 0);
2415 }
复制当前线程的上下文,产生一个新的线程,继续向下执行

kthread在kernel_thread创建的新的内核线程去执行
213 static int kthread(void _create)
214 {
215 /
Copy data: it’s on kthread’s stack */
216 struct kthread_create_info *create = _create;
217 int (*threadfn)(void *data) = create->threadfn;
218 void *data = create->data;
219 struct completion *done;
220 struct kthread *self;
221 int ret;
222
223 self = kzalloc(sizeof(self), GFP_KERNEL);
224 set_kthread_struct(self);
225
226 /
If user was SIGKILLed, I release the structure. /
227 done = xchg(&create->done, NULL);
228 if (!done) {
229 kfree(create);
230 do_exit(-EINTR);
231 }
232
233 if (!self) {
234 create->result = ERR_PTR(-ENOMEM);
235 complete(done);
236 do_exit(-ENOMEM);
237 }
238
239 self->data = data;
240 init_completion(&self->exited);
241 init_completion(&self->parked);
242 current->vfork_done = &self->exited;
243
244 /
OK, tell user we’re spawned, wait for stop or wakeup /
245 __set_current_state(TASK_UNINTERRUPTIBLE);
246 create->result = current;
247 /

248 * Thread is going to call schedule(), do not preempt it,
249 * or the creator may spend more time in wait_task_inactive().
250 */
251 preempt_disable();
252 complete(done);
253 schedule_preempt_disabled();
254 preempt_enable();
255
256 ret = -EINTR;
257 if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
258 cgroup_kthread_ready();
259 __kthread_parkme(self);
260 ret = threadfn(data); 就是执行 smpboot_thread_fn
261 }
262 do_exit(ret);
263 }

106 static int smpboot_thread_fn(void *data)
107 {
108 struct smpboot_thread_data *td = data;
109 struct smp_hotplug_thread ht = td->ht;
110
111 while (1) {
112 set_current_state(TASK_INTERRUPTIBLE);
113 preempt_disable();
114 if (kthread_should_stop()) {
115 __set_current_state(TASK_RUNNING);
116 preempt_enable();
117 /
cleanup must mirror setup /
118 if (ht->cleanup && td->status != HP_THREAD_NONE)
119 ht->cleanup(td->cpu, cpu_online(td->cpu));
120 kfree(td);
121 return 0;
122 }
123
124 if (kthread_should_park()) {
125 __set_current_state(TASK_RUNNING);
126 preempt_enable();
127 if (ht->park && td->status == HP_THREAD_ACTIVE) {
128 BUG_ON(td->cpu != smp_processor_id());
129 ht->park(td->cpu);
130 td->status = HP_THREAD_PARKED;
131 }
132 kthread_parkme();
133 /
We might have been woken for stop /
134 continue;
135 }
136
137 BUG_ON(td->cpu != smp_processor_id());
138
139 /
Check for state change setup */
140 switch (td->status) {
141 case HP_THREAD_NONE:
142 __set_current_state(TASK_RUNNING);
143 preempt_enable();
144 if (ht->setup)
145 ht->setup(td->cpu);
146 td->status = HP_THREAD_ACTIVE;
147 continue;
148
149 case HP_THREAD_PARKED:
150 __set_current_state(TASK_RUNNING);
151 preempt_enable();
152 if (ht->unpark)
153 ht->unpark(td->cpu);
154 td->status = HP_THREAD_ACTIVE;
155 continue;
156 }
157
158 if (!ht->thread_should_run(td->cpu)) {
159 preempt_enable_no_resched();
160 schedule();
161 } else {
162 __set_current_state(TASK_RUNNING);
163 preempt_enable();
164 ht->thread_fn(td->cpu); 执行 run_ksoftirqd
165 }
166 }
167 }

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值