| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * linux/kernel/softirq.c |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 1992 Linus Torvalds |
|---|
| 5 | | - * |
|---|
| 6 | | - * Distribute under GPLv2. |
|---|
| 7 | 6 | * |
|---|
| 8 | 7 | * Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903) |
|---|
| 9 | 8 | */ |
|---|
| .. | .. |
|---|
| 29 | 28 | |
|---|
| 30 | 29 | #define CREATE_TRACE_POINTS |
|---|
| 31 | 30 | #include <trace/events/irq.h> |
|---|
| 31 | + |
|---|
| 32 | +EXPORT_TRACEPOINT_SYMBOL_GPL(irq_handler_entry); |
|---|
| 33 | +EXPORT_TRACEPOINT_SYMBOL_GPL(irq_handler_exit); |
|---|
| 32 | 34 | |
|---|
| 33 | 35 | /* |
|---|
| 34 | 36 | - No shared variables, all the data are CPU local. |
|---|
| .. | .. |
|---|
| 56 | 58 | static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; |
|---|
| 57 | 59 | |
|---|
| 58 | 60 | DEFINE_PER_CPU(struct task_struct *, ksoftirqd); |
|---|
| 61 | +EXPORT_PER_CPU_SYMBOL_GPL(ksoftirqd); |
|---|
| 62 | + |
|---|
| 63 | +/* |
|---|
| 64 | + * active_softirqs -- per cpu, a mask of softirqs that are being handled, |
|---|
| 65 | + * with the expectation that approximate answers are acceptable and therefore |
|---|
| 66 | + * no synchronization. |
|---|
| 67 | + */ |
|---|
| 68 | +DEFINE_PER_CPU(__u32, active_softirqs); |
|---|
| 59 | 69 | |
|---|
| 60 | 70 | const char * const softirq_to_name[NR_SOFTIRQS] = { |
|---|
| 61 | 71 | "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "IRQ_POLL", |
|---|
| .. | .. |
|---|
| 78 | 88 | } |
|---|
| 79 | 89 | |
|---|
| 80 | 90 | /* |
|---|
| 81 | | - * If ksoftirqd is scheduled, we do not want to process pending softirqs |
|---|
| 82 | | - * right now. Let ksoftirqd handle this at its own rate, to get fairness, |
|---|
| 83 | | - * unless we're doing some of the synchronous softirqs. |
|---|
| 84 | | - */ |
|---|
| 85 | | -#define SOFTIRQ_NOW_MASK ((1 << HI_SOFTIRQ) | (1 << TASKLET_SOFTIRQ)) |
|---|
| 86 | | -static bool ksoftirqd_running(unsigned long pending) |
|---|
| 87 | | -{ |
|---|
| 88 | | - struct task_struct *tsk = __this_cpu_read(ksoftirqd); |
|---|
| 89 | | - |
|---|
| 90 | | - if (pending & SOFTIRQ_NOW_MASK) |
|---|
| 91 | | - return false; |
|---|
| 92 | | - return tsk && (tsk->state == TASK_RUNNING); |
|---|
| 93 | | -} |
|---|
| 94 | | - |
|---|
| 95 | | -/* |
|---|
| 96 | 91 | * preempt_count and SOFTIRQ_OFFSET usage: |
|---|
| 97 | 92 | * - preempt_count is changed by SOFTIRQ_OFFSET on entering or leaving |
|---|
| 98 | 93 | * softirq processing. |
|---|
| .. | .. |
|---|
| 107 | 102 | * where hardirqs are disabled legitimately: |
|---|
| 108 | 103 | */ |
|---|
| 109 | 104 | #ifdef CONFIG_TRACE_IRQFLAGS |
|---|
| 105 | + |
|---|
| 106 | +DEFINE_PER_CPU(int, hardirqs_enabled); |
|---|
| 107 | +DEFINE_PER_CPU(int, hardirq_context); |
|---|
| 108 | +EXPORT_PER_CPU_SYMBOL_GPL(hardirqs_enabled); |
|---|
| 109 | +EXPORT_PER_CPU_SYMBOL_GPL(hardirq_context); |
|---|
| 110 | + |
|---|
| 110 | 111 | void __local_bh_disable_ip(unsigned long ip, unsigned int cnt) |
|---|
| 111 | 112 | { |
|---|
| 112 | 113 | unsigned long flags; |
|---|
| .. | .. |
|---|
| 126 | 127 | * Were softirqs turned off above: |
|---|
| 127 | 128 | */ |
|---|
| 128 | 129 | if (softirq_count() == (cnt & SOFTIRQ_MASK)) |
|---|
| 129 | | - trace_softirqs_off(ip); |
|---|
| 130 | + lockdep_softirqs_off(ip); |
|---|
| 130 | 131 | raw_local_irq_restore(flags); |
|---|
| 131 | 132 | |
|---|
| 132 | 133 | if (preempt_count() == cnt) { |
|---|
| .. | .. |
|---|
| 147 | 148 | trace_preempt_on(CALLER_ADDR0, get_lock_parent_ip()); |
|---|
| 148 | 149 | |
|---|
| 149 | 150 | if (softirq_count() == (cnt & SOFTIRQ_MASK)) |
|---|
| 150 | | - trace_softirqs_on(_RET_IP_); |
|---|
| 151 | + lockdep_softirqs_on(_RET_IP_); |
|---|
| 151 | 152 | |
|---|
| 152 | 153 | __preempt_count_sub(cnt); |
|---|
| 153 | 154 | } |
|---|
| .. | .. |
|---|
| 174 | 175 | * Are softirqs going to be turned on now: |
|---|
| 175 | 176 | */ |
|---|
| 176 | 177 | if (softirq_count() == SOFTIRQ_DISABLE_OFFSET) |
|---|
| 177 | | - trace_softirqs_on(ip); |
|---|
| 178 | + lockdep_softirqs_on(ip); |
|---|
| 178 | 179 | /* |
|---|
| 179 | 180 | * Keep preemption disabled until we are done with |
|---|
| 180 | 181 | * softirq processing: |
|---|
| .. | .. |
|---|
| 224 | 225 | { |
|---|
| 225 | 226 | bool in_hardirq = false; |
|---|
| 226 | 227 | |
|---|
| 227 | | - if (trace_hardirq_context(current)) { |
|---|
| 228 | + if (lockdep_hardirq_context()) { |
|---|
| 228 | 229 | in_hardirq = true; |
|---|
| 229 | | - trace_hardirq_exit(); |
|---|
| 230 | + lockdep_hardirq_exit(); |
|---|
| 230 | 231 | } |
|---|
| 231 | 232 | |
|---|
| 232 | 233 | lockdep_softirq_enter(); |
|---|
| .. | .. |
|---|
| 239 | 240 | lockdep_softirq_exit(); |
|---|
| 240 | 241 | |
|---|
| 241 | 242 | if (in_hardirq) |
|---|
| 242 | | - trace_hardirq_enter(); |
|---|
| 243 | + lockdep_hardirq_enter(); |
|---|
| 243 | 244 | } |
|---|
| 244 | 245 | #else |
|---|
| 245 | 246 | static inline bool lockdep_softirq_start(void) { return false; } |
|---|
| 246 | 247 | static inline void lockdep_softirq_end(bool in_hardirq) { } |
|---|
| 247 | 248 | #endif |
|---|
| 249 | + |
|---|
| 250 | +#define softirq_deferred_for_rt(pending) \ |
|---|
| 251 | +({ \ |
|---|
| 252 | + __u32 deferred = 0; \ |
|---|
| 253 | + if (cpupri_check_rt()) { \ |
|---|
| 254 | + deferred = pending & LONG_SOFTIRQ_MASK; \ |
|---|
| 255 | + pending &= ~LONG_SOFTIRQ_MASK; \ |
|---|
| 256 | + } \ |
|---|
| 257 | + deferred; \ |
|---|
| 258 | +}) |
|---|
| 248 | 259 | |
|---|
| 249 | 260 | asmlinkage __visible void __softirq_entry __do_softirq(void) |
|---|
| 250 | 261 | { |
|---|
| .. | .. |
|---|
| 253 | 264 | int max_restart = MAX_SOFTIRQ_RESTART; |
|---|
| 254 | 265 | struct softirq_action *h; |
|---|
| 255 | 266 | bool in_hardirq; |
|---|
| 267 | + __u32 deferred; |
|---|
| 256 | 268 | __u32 pending; |
|---|
| 257 | 269 | int softirq_bit; |
|---|
| 258 | 270 | |
|---|
| 259 | 271 | /* |
|---|
| 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 |
|---|
| 272 | + * Mask out PF_MEMALLOC as the current task context is borrowed for the |
|---|
| 273 | + * softirq. A softirq handled, such as network RX, might set PF_MEMALLOC |
|---|
| 274 | + * again if the socket is related to swapping. |
|---|
| 263 | 275 | */ |
|---|
| 264 | 276 | current->flags &= ~PF_MEMALLOC; |
|---|
| 265 | 277 | |
|---|
| 266 | 278 | pending = local_softirq_pending(); |
|---|
| 279 | + deferred = softirq_deferred_for_rt(pending); |
|---|
| 267 | 280 | account_irq_enter_time(current); |
|---|
| 268 | | - |
|---|
| 269 | 281 | __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET); |
|---|
| 270 | 282 | in_hardirq = lockdep_softirq_start(); |
|---|
| 271 | 283 | |
|---|
| 272 | 284 | restart: |
|---|
| 273 | 285 | /* Reset the pending bitmask before enabling irqs */ |
|---|
| 274 | | - set_softirq_pending(0); |
|---|
| 286 | + set_softirq_pending(deferred); |
|---|
| 287 | + __this_cpu_write(active_softirqs, pending); |
|---|
| 275 | 288 | |
|---|
| 276 | 289 | local_irq_enable(); |
|---|
| 277 | 290 | |
|---|
| .. | .. |
|---|
| 301 | 314 | pending >>= softirq_bit; |
|---|
| 302 | 315 | } |
|---|
| 303 | 316 | |
|---|
| 304 | | - rcu_bh_qs(); |
|---|
| 317 | + __this_cpu_write(active_softirqs, 0); |
|---|
| 318 | + if (__this_cpu_read(ksoftirqd) == current) |
|---|
| 319 | + rcu_softirq_qs(); |
|---|
| 305 | 320 | local_irq_disable(); |
|---|
| 306 | 321 | |
|---|
| 307 | 322 | pending = local_softirq_pending(); |
|---|
| 323 | + deferred = softirq_deferred_for_rt(pending); |
|---|
| 324 | + |
|---|
| 308 | 325 | if (pending) { |
|---|
| 309 | 326 | if (time_before(jiffies, end) && !need_resched() && |
|---|
| 310 | 327 | --max_restart) |
|---|
| 311 | 328 | goto restart; |
|---|
| 312 | 329 | |
|---|
| 330 | +#ifndef CONFIG_RT_SOFTINT_OPTIMIZATION |
|---|
| 313 | 331 | wakeup_softirqd(); |
|---|
| 332 | +#endif |
|---|
| 314 | 333 | } |
|---|
| 315 | 334 | |
|---|
| 335 | +#ifdef CONFIG_RT_SOFTINT_OPTIMIZATION |
|---|
| 336 | + if (pending | deferred) |
|---|
| 337 | + wakeup_softirqd(); |
|---|
| 338 | +#endif |
|---|
| 316 | 339 | lockdep_softirq_end(in_hardirq); |
|---|
| 317 | 340 | account_irq_exit_time(current); |
|---|
| 318 | 341 | __local_bh_enable(SOFTIRQ_OFFSET); |
|---|
| .. | .. |
|---|
| 332 | 355 | |
|---|
| 333 | 356 | pending = local_softirq_pending(); |
|---|
| 334 | 357 | |
|---|
| 335 | | - if (pending && !ksoftirqd_running(pending)) |
|---|
| 358 | + if (pending) |
|---|
| 336 | 359 | do_softirq_own_stack(); |
|---|
| 337 | 360 | |
|---|
| 338 | 361 | local_irq_restore(flags); |
|---|
| 339 | 362 | } |
|---|
| 340 | 363 | |
|---|
| 341 | | -/* |
|---|
| 342 | | - * Enter an interrupt context. |
|---|
| 364 | +/** |
|---|
| 365 | + * irq_enter_rcu - Enter an interrupt context with RCU watching |
|---|
| 343 | 366 | */ |
|---|
| 344 | | -void irq_enter(void) |
|---|
| 367 | +void irq_enter_rcu(void) |
|---|
| 345 | 368 | { |
|---|
| 346 | | - rcu_irq_enter(); |
|---|
| 347 | 369 | if (is_idle_task(current) && !in_interrupt()) { |
|---|
| 348 | 370 | /* |
|---|
| 349 | 371 | * Prevent raise_softirq from needlessly waking up ksoftirqd |
|---|
| .. | .. |
|---|
| 353 | 375 | tick_irq_enter(); |
|---|
| 354 | 376 | _local_bh_enable(); |
|---|
| 355 | 377 | } |
|---|
| 356 | | - |
|---|
| 357 | 378 | __irq_enter(); |
|---|
| 379 | +} |
|---|
| 380 | + |
|---|
| 381 | +/** |
|---|
| 382 | + * irq_enter - Enter an interrupt context including RCU update |
|---|
| 383 | + */ |
|---|
| 384 | +void irq_enter(void) |
|---|
| 385 | +{ |
|---|
| 386 | + rcu_irq_enter(); |
|---|
| 387 | + irq_enter_rcu(); |
|---|
| 358 | 388 | } |
|---|
| 359 | 389 | |
|---|
| 360 | 390 | static inline void invoke_softirq(void) |
|---|
| 361 | 391 | { |
|---|
| 362 | | - if (ksoftirqd_running(local_softirq_pending())) |
|---|
| 363 | | - return; |
|---|
| 364 | | - |
|---|
| 365 | 392 | if (!force_irqthreads) { |
|---|
| 366 | 393 | #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK |
|---|
| 367 | 394 | /* |
|---|
| .. | .. |
|---|
| 396 | 423 | #endif |
|---|
| 397 | 424 | } |
|---|
| 398 | 425 | |
|---|
| 399 | | -/* |
|---|
| 400 | | - * Exit an interrupt context. Process softirqs if needed and possible: |
|---|
| 401 | | - */ |
|---|
| 402 | | -void irq_exit(void) |
|---|
| 426 | +static inline void __irq_exit_rcu(void) |
|---|
| 403 | 427 | { |
|---|
| 404 | 428 | #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED |
|---|
| 405 | 429 | local_irq_disable(); |
|---|
| .. | .. |
|---|
| 412 | 436 | invoke_softirq(); |
|---|
| 413 | 437 | |
|---|
| 414 | 438 | tick_irq_exit(); |
|---|
| 439 | +} |
|---|
| 440 | + |
|---|
| 441 | +/** |
|---|
| 442 | + * irq_exit_rcu() - Exit an interrupt context without updating RCU |
|---|
| 443 | + * |
|---|
| 444 | + * Also processes softirqs if needed and possible. |
|---|
| 445 | + */ |
|---|
| 446 | +void irq_exit_rcu(void) |
|---|
| 447 | +{ |
|---|
| 448 | + __irq_exit_rcu(); |
|---|
| 449 | + /* must be last! */ |
|---|
| 450 | + lockdep_hardirq_exit(); |
|---|
| 451 | +} |
|---|
| 452 | + |
|---|
| 453 | +/** |
|---|
| 454 | + * irq_exit - Exit an interrupt context, update RCU and lockdep |
|---|
| 455 | + * |
|---|
| 456 | + * Also processes softirqs if needed and possible. |
|---|
| 457 | + */ |
|---|
| 458 | +void irq_exit(void) |
|---|
| 459 | +{ |
|---|
| 460 | + __irq_exit_rcu(); |
|---|
| 415 | 461 | rcu_irq_exit(); |
|---|
| 416 | | - trace_hardirq_exit(); /* must be last! */ |
|---|
| 462 | + /* must be last! */ |
|---|
| 463 | + lockdep_hardirq_exit(); |
|---|
| 417 | 464 | } |
|---|
| 418 | 465 | |
|---|
| 419 | 466 | /* |
|---|
| .. | .. |
|---|
| 447 | 494 | |
|---|
| 448 | 495 | void __raise_softirq_irqoff(unsigned int nr) |
|---|
| 449 | 496 | { |
|---|
| 497 | + lockdep_assert_irqs_disabled(); |
|---|
| 450 | 498 | trace_softirq_raise(nr); |
|---|
| 451 | 499 | or_softirq_pending(1UL << nr); |
|---|
| 452 | 500 | } |
|---|
| .. | .. |
|---|
| 519 | 567 | if (!test_and_clear_bit(TASKLET_STATE_SCHED, |
|---|
| 520 | 568 | &t->state)) |
|---|
| 521 | 569 | BUG(); |
|---|
| 522 | | - t->func(t->data); |
|---|
| 570 | + if (t->use_callback) { |
|---|
| 571 | + trace_tasklet_entry(t->callback); |
|---|
| 572 | + t->callback(t); |
|---|
| 573 | + trace_tasklet_exit(t->callback); |
|---|
| 574 | + } else { |
|---|
| 575 | + trace_tasklet_entry(t->func); |
|---|
| 576 | + t->func(t->data); |
|---|
| 577 | + trace_tasklet_exit(t->func); |
|---|
| 578 | + } |
|---|
| 523 | 579 | tasklet_unlock(t); |
|---|
| 524 | 580 | continue; |
|---|
| 525 | 581 | } |
|---|
| .. | .. |
|---|
| 545 | 601 | tasklet_action_common(a, this_cpu_ptr(&tasklet_hi_vec), HI_SOFTIRQ); |
|---|
| 546 | 602 | } |
|---|
| 547 | 603 | |
|---|
| 604 | +void tasklet_setup(struct tasklet_struct *t, |
|---|
| 605 | + void (*callback)(struct tasklet_struct *)) |
|---|
| 606 | +{ |
|---|
| 607 | + t->next = NULL; |
|---|
| 608 | + t->state = 0; |
|---|
| 609 | + atomic_set(&t->count, 0); |
|---|
| 610 | + t->callback = callback; |
|---|
| 611 | + t->use_callback = true; |
|---|
| 612 | + t->data = 0; |
|---|
| 613 | +} |
|---|
| 614 | +EXPORT_SYMBOL(tasklet_setup); |
|---|
| 615 | + |
|---|
| 548 | 616 | void tasklet_init(struct tasklet_struct *t, |
|---|
| 549 | 617 | void (*func)(unsigned long), unsigned long data) |
|---|
| 550 | 618 | { |
|---|
| .. | .. |
|---|
| 552 | 620 | t->state = 0; |
|---|
| 553 | 621 | atomic_set(&t->count, 0); |
|---|
| 554 | 622 | t->func = func; |
|---|
| 623 | + t->use_callback = false; |
|---|
| 555 | 624 | t->data = data; |
|---|
| 556 | 625 | } |
|---|
| 557 | 626 | EXPORT_SYMBOL(tasklet_init); |
|---|
| .. | .. |
|---|
| 570 | 639 | clear_bit(TASKLET_STATE_SCHED, &t->state); |
|---|
| 571 | 640 | } |
|---|
| 572 | 641 | EXPORT_SYMBOL(tasklet_kill); |
|---|
| 573 | | - |
|---|
| 574 | | -/* |
|---|
| 575 | | - * tasklet_hrtimer |
|---|
| 576 | | - */ |
|---|
| 577 | | - |
|---|
| 578 | | -/* |
|---|
| 579 | | - * The trampoline is called when the hrtimer expires. It schedules a tasklet |
|---|
| 580 | | - * to run __tasklet_hrtimer_trampoline() which in turn will call the intended |
|---|
| 581 | | - * hrtimer callback, but from softirq context. |
|---|
| 582 | | - */ |
|---|
| 583 | | -static enum hrtimer_restart __hrtimer_tasklet_trampoline(struct hrtimer *timer) |
|---|
| 584 | | -{ |
|---|
| 585 | | - struct tasklet_hrtimer *ttimer = |
|---|
| 586 | | - container_of(timer, struct tasklet_hrtimer, timer); |
|---|
| 587 | | - |
|---|
| 588 | | - tasklet_hi_schedule(&ttimer->tasklet); |
|---|
| 589 | | - return HRTIMER_NORESTART; |
|---|
| 590 | | -} |
|---|
| 591 | | - |
|---|
| 592 | | -/* |
|---|
| 593 | | - * Helper function which calls the hrtimer callback from |
|---|
| 594 | | - * tasklet/softirq context |
|---|
| 595 | | - */ |
|---|
| 596 | | -static void __tasklet_hrtimer_trampoline(unsigned long data) |
|---|
| 597 | | -{ |
|---|
| 598 | | - struct tasklet_hrtimer *ttimer = (void *)data; |
|---|
| 599 | | - enum hrtimer_restart restart; |
|---|
| 600 | | - |
|---|
| 601 | | - restart = ttimer->function(&ttimer->timer); |
|---|
| 602 | | - if (restart != HRTIMER_NORESTART) |
|---|
| 603 | | - hrtimer_restart(&ttimer->timer); |
|---|
| 604 | | -} |
|---|
| 605 | | - |
|---|
| 606 | | -/** |
|---|
| 607 | | - * tasklet_hrtimer_init - Init a tasklet/hrtimer combo for softirq callbacks |
|---|
| 608 | | - * @ttimer: tasklet_hrtimer which is initialized |
|---|
| 609 | | - * @function: hrtimer callback function which gets called from softirq context |
|---|
| 610 | | - * @which_clock: clock id (CLOCK_MONOTONIC/CLOCK_REALTIME) |
|---|
| 611 | | - * @mode: hrtimer mode (HRTIMER_MODE_ABS/HRTIMER_MODE_REL) |
|---|
| 612 | | - */ |
|---|
| 613 | | -void tasklet_hrtimer_init(struct tasklet_hrtimer *ttimer, |
|---|
| 614 | | - enum hrtimer_restart (*function)(struct hrtimer *), |
|---|
| 615 | | - clockid_t which_clock, enum hrtimer_mode mode) |
|---|
| 616 | | -{ |
|---|
| 617 | | - hrtimer_init(&ttimer->timer, which_clock, mode); |
|---|
| 618 | | - ttimer->timer.function = __hrtimer_tasklet_trampoline; |
|---|
| 619 | | - tasklet_init(&ttimer->tasklet, __tasklet_hrtimer_trampoline, |
|---|
| 620 | | - (unsigned long)ttimer); |
|---|
| 621 | | - ttimer->function = function; |
|---|
| 622 | | -} |
|---|
| 623 | | -EXPORT_SYMBOL_GPL(tasklet_hrtimer_init); |
|---|
| 624 | 642 | |
|---|
| 625 | 643 | void __init softirq_init(void) |
|---|
| 626 | 644 | { |
|---|
| .. | .. |
|---|
| 699 | 717 | /* Find end, append list for that CPU. */ |
|---|
| 700 | 718 | if (&per_cpu(tasklet_vec, cpu).head != per_cpu(tasklet_vec, cpu).tail) { |
|---|
| 701 | 719 | *__this_cpu_read(tasklet_vec.tail) = per_cpu(tasklet_vec, cpu).head; |
|---|
| 702 | | - this_cpu_write(tasklet_vec.tail, per_cpu(tasklet_vec, cpu).tail); |
|---|
| 720 | + __this_cpu_write(tasklet_vec.tail, per_cpu(tasklet_vec, cpu).tail); |
|---|
| 703 | 721 | per_cpu(tasklet_vec, cpu).head = NULL; |
|---|
| 704 | 722 | per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head; |
|---|
| 705 | 723 | } |
|---|