diff options
-rw-r--r-- | kernel/printk/printk.c | 108 |
1 files changed, 107 insertions, 1 deletions
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 5d81206a572d..040fb948924e 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c | |||
@@ -86,8 +86,15 @@ EXPORT_SYMBOL_GPL(console_drivers); | |||
86 | static struct lockdep_map console_lock_dep_map = { | 86 | static struct lockdep_map console_lock_dep_map = { |
87 | .name = "console_lock" | 87 | .name = "console_lock" |
88 | }; | 88 | }; |
89 | static struct lockdep_map console_owner_dep_map = { | ||
90 | .name = "console_owner" | ||
91 | }; | ||
89 | #endif | 92 | #endif |
90 | 93 | ||
94 | static DEFINE_RAW_SPINLOCK(console_owner_lock); | ||
95 | static struct task_struct *console_owner; | ||
96 | static bool console_waiter; | ||
97 | |||
91 | enum devkmsg_log_bits { | 98 | enum devkmsg_log_bits { |
92 | __DEVKMSG_LOG_BIT_ON = 0, | 99 | __DEVKMSG_LOG_BIT_ON = 0, |
93 | __DEVKMSG_LOG_BIT_OFF, | 100 | __DEVKMSG_LOG_BIT_OFF, |
@@ -1753,8 +1760,56 @@ asmlinkage int vprintk_emit(int facility, int level, | |||
1753 | * semaphore. The release will print out buffers and wake up | 1760 | * semaphore. The release will print out buffers and wake up |
1754 | * /dev/kmsg and syslog() users. | 1761 | * /dev/kmsg and syslog() users. |
1755 | */ | 1762 | */ |
1756 | if (console_trylock()) | 1763 | if (console_trylock()) { |
1757 | console_unlock(); | 1764 | console_unlock(); |
1765 | } else { | ||
1766 | struct task_struct *owner = NULL; | ||
1767 | bool waiter; | ||
1768 | bool spin = false; | ||
1769 | |||
1770 | printk_safe_enter_irqsave(flags); | ||
1771 | |||
1772 | raw_spin_lock(&console_owner_lock); | ||
1773 | owner = READ_ONCE(console_owner); | ||
1774 | waiter = READ_ONCE(console_waiter); | ||
1775 | if (!waiter && owner && owner != current) { | ||
1776 | WRITE_ONCE(console_waiter, true); | ||
1777 | spin = true; | ||
1778 | } | ||
1779 | raw_spin_unlock(&console_owner_lock); | ||
1780 | |||
1781 | /* | ||
1782 | * If there is an active printk() writing to the | ||
1783 | * consoles, instead of having it write our data too, | ||
1784 | * see if we can offload that load from the active | ||
1785 | * printer, and do some printing ourselves. | ||
1786 | * Go into a spin only if there isn't already a waiter | ||
1787 | * spinning, and there is an active printer, and | ||
1788 | * that active printer isn't us (recursive printk?). | ||
1789 | */ | ||
1790 | if (spin) { | ||
1791 | /* We spin waiting for the owner to release us */ | ||
1792 | spin_acquire(&console_owner_dep_map, 0, 0, _THIS_IP_); | ||
1793 | /* Owner will clear console_waiter on hand off */ | ||
1794 | while (READ_ONCE(console_waiter)) | ||
1795 | cpu_relax(); | ||
1796 | |||
1797 | spin_release(&console_owner_dep_map, 1, _THIS_IP_); | ||
1798 | printk_safe_exit_irqrestore(flags); | ||
1799 | |||
1800 | /* | ||
1801 | * The owner passed the console lock to us. | ||
1802 | * Since we did not spin on console lock, annotate | ||
1803 | * this as a trylock. Otherwise lockdep will | ||
1804 | * complain. | ||
1805 | */ | ||
1806 | mutex_acquire(&console_lock_dep_map, 0, 1, _THIS_IP_); | ||
1807 | console_unlock(); | ||
1808 | printk_safe_enter_irqsave(flags); | ||
1809 | } | ||
1810 | printk_safe_exit_irqrestore(flags); | ||
1811 | |||
1812 | } | ||
1758 | } | 1813 | } |
1759 | 1814 | ||
1760 | return printed_len; | 1815 | return printed_len; |
@@ -2141,6 +2196,7 @@ void console_unlock(void) | |||
2141 | static u64 seen_seq; | 2196 | static u64 seen_seq; |
2142 | unsigned long flags; | 2197 | unsigned long flags; |
2143 | bool wake_klogd = false; | 2198 | bool wake_klogd = false; |
2199 | bool waiter = false; | ||
2144 | bool do_cond_resched, retry; | 2200 | bool do_cond_resched, retry; |
2145 | 2201 | ||
2146 | if (console_suspended) { | 2202 | if (console_suspended) { |
@@ -2229,14 +2285,64 @@ skip: | |||
2229 | console_seq++; | 2285 | console_seq++; |
2230 | raw_spin_unlock(&logbuf_lock); | 2286 | raw_spin_unlock(&logbuf_lock); |
2231 | 2287 | ||
2288 | /* | ||
2289 | * While actively printing out messages, if another printk() | ||
2290 | * were to occur on another CPU, it may wait for this one to | ||
2291 | * finish. This task can not be preempted if there is a | ||
2292 | * waiter waiting to take over. | ||
2293 | */ | ||
2294 | raw_spin_lock(&console_owner_lock); | ||
2295 | console_owner = current; | ||
2296 | raw_spin_unlock(&console_owner_lock); | ||
2297 | |||
2298 | /* The waiter may spin on us after setting console_owner */ | ||
2299 | spin_acquire(&console_owner_dep_map, 0, 0, _THIS_IP_); | ||
2300 | |||
2232 | stop_critical_timings(); /* don't trace print latency */ | 2301 | stop_critical_timings(); /* don't trace print latency */ |
2233 | call_console_drivers(ext_text, ext_len, text, len); | 2302 | call_console_drivers(ext_text, ext_len, text, len); |
2234 | start_critical_timings(); | 2303 | start_critical_timings(); |
2304 | |||
2305 | raw_spin_lock(&console_owner_lock); | ||
2306 | waiter = READ_ONCE(console_waiter); | ||
2307 | console_owner = NULL; | ||
2308 | raw_spin_unlock(&console_owner_lock); | ||
2309 | |||
2310 | /* | ||
2311 | * If there is a waiter waiting for us, then pass the | ||
2312 | * rest of the work load over to that waiter. | ||
2313 | */ | ||
2314 | if (waiter) | ||
2315 | break; | ||
2316 | |||
2317 | /* There was no waiter, and nothing will spin on us here */ | ||
2318 | spin_release(&console_owner_dep_map, 1, _THIS_IP_); | ||
2319 | |||
2235 | printk_safe_exit_irqrestore(flags); | 2320 | printk_safe_exit_irqrestore(flags); |
2236 | 2321 | ||
2237 | if (do_cond_resched) | 2322 | if (do_cond_resched) |
2238 | cond_resched(); | 2323 | cond_resched(); |
2239 | } | 2324 | } |
2325 | |||
2326 | /* | ||
2327 | * If there is an active waiter waiting on the console_lock. | ||
2328 | * Pass off the printing to the waiter, and the waiter | ||
2329 | * will continue printing on its CPU, and when all writing | ||
2330 | * has finished, the last printer will wake up klogd. | ||
2331 | */ | ||
2332 | if (waiter) { | ||
2333 | WRITE_ONCE(console_waiter, false); | ||
2334 | /* The waiter is now free to continue */ | ||
2335 | spin_release(&console_owner_dep_map, 1, _THIS_IP_); | ||
2336 | /* | ||
2337 | * Hand off console_lock to waiter. The waiter will perform | ||
2338 | * the up(). After this, the waiter is the console_lock owner. | ||
2339 | */ | ||
2340 | mutex_release(&console_lock_dep_map, 1, _THIS_IP_); | ||
2341 | printk_safe_exit_irqrestore(flags); | ||
2342 | /* Note, if waiter is set, logbuf_lock is not held */ | ||
2343 | return; | ||
2344 | } | ||
2345 | |||
2240 | console_locked = 0; | 2346 | console_locked = 0; |
2241 | 2347 | ||
2242 | /* Release the exclusive_console once it is used */ | 2348 | /* Release the exclusive_console once it is used */ |