Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
bb4f552a
Commit
bb4f552a
authored
Jan 22, 2018
by
Petr Mladek
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-4.16-console-waiter-logic' into for-4.16
parents
51ccbb0a
fd5f7cde
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
166 additions
and
15 deletions
+166
-15
kernel/printk/printk.c
kernel/printk/printk.c
+166
-15
No files found.
kernel/printk/printk.c
View file @
bb4f552a
...
...
@@ -1547,6 +1547,146 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
return
do_syslog
(
type
,
buf
,
len
,
SYSLOG_FROM_READER
);
}
/*
* Special console_lock variants that help to reduce the risk of soft-lockups.
* They allow to pass console_lock to another printk() call using a busy wait.
*/
#ifdef CONFIG_LOCKDEP
static
struct
lockdep_map
console_owner_dep_map
=
{
.
name
=
"console_owner"
};
#endif
static
DEFINE_RAW_SPINLOCK
(
console_owner_lock
);
static
struct
task_struct
*
console_owner
;
static
bool
console_waiter
;
/**
* console_lock_spinning_enable - mark beginning of code where another
* thread might safely busy wait
*
* This basically converts console_lock into a spinlock. This marks
* the section where the console_lock owner can not sleep, because
* there may be a waiter spinning (like a spinlock). Also it must be
* ready to hand over the lock at the end of the section.
*/
static
void
console_lock_spinning_enable
(
void
)
{
raw_spin_lock
(
&
console_owner_lock
);
console_owner
=
current
;
raw_spin_unlock
(
&
console_owner_lock
);
/* The waiter may spin on us after setting console_owner */
spin_acquire
(
&
console_owner_dep_map
,
0
,
0
,
_THIS_IP_
);
}
/**
* console_lock_spinning_disable_and_check - mark end of code where another
* thread was able to busy wait and check if there is a waiter
*
* This is called at the end of the section where spinning is allowed.
* It has two functions. First, it is a signal that it is no longer
* safe to start busy waiting for the lock. Second, it checks if
* there is a busy waiter and passes the lock rights to her.
*
* Important: Callers lose the lock if there was a busy waiter.
* They must not touch items synchronized by console_lock
* in this case.
*
* Return: 1 if the lock rights were passed, 0 otherwise.
*/
static
int
console_lock_spinning_disable_and_check
(
void
)
{
int
waiter
;
raw_spin_lock
(
&
console_owner_lock
);
waiter
=
READ_ONCE
(
console_waiter
);
console_owner
=
NULL
;
raw_spin_unlock
(
&
console_owner_lock
);
if
(
!
waiter
)
{
spin_release
(
&
console_owner_dep_map
,
1
,
_THIS_IP_
);
return
0
;
}
/* The waiter is now free to continue */
WRITE_ONCE
(
console_waiter
,
false
);
spin_release
(
&
console_owner_dep_map
,
1
,
_THIS_IP_
);
/*
* Hand off console_lock to waiter. The waiter will perform
* the up(). After this, the waiter is the console_lock owner.
*/
mutex_release
(
&
console_lock_dep_map
,
1
,
_THIS_IP_
);
return
1
;
}
/**
* console_trylock_spinning - try to get console_lock by busy waiting
*
* This allows to busy wait for the console_lock when the current
* owner is running in specially marked sections. It means that
* the current owner is running and cannot reschedule until it
* is ready to lose the lock.
*
* Return: 1 if we got the lock, 0 othrewise
*/
static
int
console_trylock_spinning
(
void
)
{
struct
task_struct
*
owner
=
NULL
;
bool
waiter
;
bool
spin
=
false
;
unsigned
long
flags
;
if
(
console_trylock
())
return
1
;
printk_safe_enter_irqsave
(
flags
);
raw_spin_lock
(
&
console_owner_lock
);
owner
=
READ_ONCE
(
console_owner
);
waiter
=
READ_ONCE
(
console_waiter
);
if
(
!
waiter
&&
owner
&&
owner
!=
current
)
{
WRITE_ONCE
(
console_waiter
,
true
);
spin
=
true
;
}
raw_spin_unlock
(
&
console_owner_lock
);
/*
* If there is an active printk() writing to the
* consoles, instead of having it write our data too,
* see if we can offload that load from the active
* printer, and do some printing ourselves.
* Go into a spin only if there isn't already a waiter
* spinning, and there is an active printer, and
* that active printer isn't us (recursive printk?).
*/
if
(
!
spin
)
{
printk_safe_exit_irqrestore
(
flags
);
return
0
;
}
/* We spin waiting for the owner to release us */
spin_acquire
(
&
console_owner_dep_map
,
0
,
0
,
_THIS_IP_
);
/* Owner will clear console_waiter on hand off */
while
(
READ_ONCE
(
console_waiter
))
cpu_relax
();
spin_release
(
&
console_owner_dep_map
,
1
,
_THIS_IP_
);
printk_safe_exit_irqrestore
(
flags
);
/*
* The owner passed the console lock to us.
* Since we did not spin on console lock, annotate
* this as a trylock. Otherwise lockdep will
* complain.
*/
mutex_acquire
(
&
console_lock_dep_map
,
0
,
1
,
_THIS_IP_
);
return
1
;
}
/*
* Call the console drivers, asking them to write out
* log_buf[start] to log_buf[end - 1].
...
...
@@ -1752,13 +1892,20 @@ asmlinkage int vprintk_emit(int facility, int level,
/* If called from the scheduler, we can not call up(). */
if
(
!
in_sched
)
{
/*
* Disable preemption to avoid being preempted while holding
* console_sem which would prevent anyone from printing to
* console
*/
preempt_disable
();
/*
* Try to acquire and then immediately release the console
* semaphore. The release will print out buffers and wake up
* /dev/kmsg and syslog() users.
*/
if
(
console_trylock
())
if
(
console_trylock
_spinning
())
console_unlock
();
preempt_enable
();
}
return
printed_len
;
...
...
@@ -1859,6 +2006,8 @@ static ssize_t msg_print_ext_header(char *buf, size_t size,
static
ssize_t
msg_print_ext_body
(
char
*
buf
,
size_t
size
,
char
*
dict
,
size_t
dict_len
,
char
*
text
,
size_t
text_len
)
{
return
0
;
}
static
void
console_lock_spinning_enable
(
void
)
{
}
static
int
console_lock_spinning_disable_and_check
(
void
)
{
return
0
;
}
static
void
call_console_drivers
(
const
char
*
ext_text
,
size_t
ext_len
,
const
char
*
text
,
size_t
len
)
{}
static
size_t
msg_print_text
(
const
struct
printk_log
*
msg
,
...
...
@@ -2084,20 +2233,7 @@ int console_trylock(void)
return
0
;
}
console_locked
=
1
;
/*
* When PREEMPT_COUNT disabled we can't reliably detect if it's
* safe to schedule (e.g. calling printk while holding a spin_lock),
* because preempt_disable()/preempt_enable() are just barriers there
* and preempt_count() is always 0.
*
* RCU read sections have a separate preemption counter when
* PREEMPT_RCU enabled thus we must take extra care and check
* rcu_preempt_depth(), otherwise RCU read sections modify
* preempt_count().
*/
console_may_schedule
=
!
oops_in_progress
&&
preemptible
()
&&
!
rcu_preempt_depth
();
console_may_schedule
=
0
;
return
1
;
}
EXPORT_SYMBOL
(
console_trylock
);
...
...
@@ -2247,14 +2383,29 @@ void console_unlock(void)
console_seq
++
;
raw_spin_unlock
(
&
logbuf_lock
);
/*
* While actively printing out messages, if another printk()
* were to occur on another CPU, it may wait for this one to
* finish. This task can not be preempted if there is a
* waiter waiting to take over.
*/
console_lock_spinning_enable
();
stop_critical_timings
();
/* don't trace print latency */
call_console_drivers
(
ext_text
,
ext_len
,
text
,
len
);
start_critical_timings
();
if
(
console_lock_spinning_disable_and_check
())
{
printk_safe_exit_irqrestore
(
flags
);
return
;
}
printk_safe_exit_irqrestore
(
flags
);
if
(
do_cond_resched
)
cond_resched
();
}
console_locked
=
0
;
/* Release the exclusive_console once it is used */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment