summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0006-printk-rb-add-blocking-reader-support.patch
blob: 41378d01ea75277508487a5c8c9aac76799f58b9 (plain)
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
From: John Ogness <john.ogness@linutronix.de>
Date: Tue, 12 Feb 2019 15:29:44 +0100
Subject: [PATCH 06/25] printk-rb: add blocking reader support
Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.0/older/patches-5.0.7-rt5.tar.xz

Add a blocking read function for readers. An irq_work function is
used to signal the wait queue so that write notification can
be triggered from any context.

Signed-off-by: John Ogness <john.ogness@linutronix.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 include/linux/printk_ringbuffer.h |   20 +++++++++++++
 lib/printk_ringbuffer.c           |   55 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+)

--- a/include/linux/printk_ringbuffer.h
+++ b/include/linux/printk_ringbuffer.h
@@ -2,8 +2,10 @@
 #ifndef _LINUX_PRINTK_RINGBUFFER_H
 #define _LINUX_PRINTK_RINGBUFFER_H
 
+#include <linux/irq_work.h>
 #include <linux/atomic.h>
 #include <linux/percpu.h>
+#include <linux/wait.h>
 
 struct prb_cpulock {
 	atomic_t owner;
@@ -22,6 +24,10 @@ struct printk_ringbuffer {
 
 	struct prb_cpulock *cpulock;
 	atomic_t ctx;
+
+	struct wait_queue_head *wq;
+	atomic_long_t wq_counter;
+	struct irq_work *wq_work;
 };
 
 struct prb_entry {
@@ -59,6 +65,15 @@ struct prb_iterator {
 #define DECLARE_STATIC_PRINTKRB(name, szbits, cpulockptr)		\
 static char _##name##_buffer[1 << (szbits)]				\
 	__aligned(__alignof__(long));					\
+static DECLARE_WAIT_QUEUE_HEAD(_##name##_wait);				\
+static void _##name##_wake_work_func(struct irq_work *irq_work)		\
+{									\
+	wake_up_interruptible_all(&_##name##_wait);			\
+}									\
+static struct irq_work _##name##_wake_work = {				\
+	.func = _##name##_wake_work_func,				\
+	.flags = IRQ_WORK_LAZY,						\
+};									\
 static struct printk_ringbuffer name = {				\
 	.buffer = &_##name##_buffer[0],					\
 	.size_bits = szbits,						\
@@ -68,6 +83,9 @@ static struct printk_ringbuffer name = {
 	.reserve = ATOMIC_LONG_INIT(-111 * sizeof(long)),		\
 	.cpulock = cpulockptr,						\
 	.ctx = ATOMIC_INIT(0),						\
+	.wq = &_##name##_wait,						\
+	.wq_counter = ATOMIC_LONG_INIT(0),				\
+	.wq_work = &_##name##_wake_work,				\
 }
 
 /* writer interface */
@@ -80,6 +98,8 @@ void prb_iter_init(struct prb_iterator *
 		   u64 *seq);
 void prb_iter_copy(struct prb_iterator *dest, struct prb_iterator *src);
 int prb_iter_next(struct prb_iterator *iter, char *buf, int size, u64 *seq);
+int prb_iter_wait_next(struct prb_iterator *iter, char *buf, int size,
+		       u64 *seq);
 int prb_iter_data(struct prb_iterator *iter, char *buf, int size, u64 *seq);
 
 /* utility functions */
--- a/lib/printk_ringbuffer.c
+++ b/lib/printk_ringbuffer.c
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <linux/sched.h>
 #include <linux/smp.h>
 #include <linux/string.h>
 #include <linux/errno.h>
@@ -154,6 +155,7 @@ static bool push_tail(struct printk_ring
 void prb_commit(struct prb_handle *h)
 {
 	struct printk_ringbuffer *rb = h->rb;
+	bool changed = false;
 	struct prb_entry *e;
 	unsigned long head;
 	unsigned long res;
@@ -175,6 +177,7 @@ void prb_commit(struct prb_handle *h)
 			}
 			e->seq = ++rb->seq;
 			head += e->size;
+			changed = true;
 		}
 		atomic_long_set_release(&rb->head, res);
 		atomic_dec(&rb->ctx);
@@ -185,6 +188,18 @@ void prb_commit(struct prb_handle *h)
 	}
 
 	prb_unlock(rb->cpulock, h->cpu);
+
+	if (changed) {
+		atomic_long_inc(&rb->wq_counter);
+		if (wq_has_sleeper(rb->wq)) {
+#ifdef CONFIG_IRQ_WORK
+			irq_work_queue(rb->wq_work);
+#else
+			if (!in_nmi())
+				wake_up_interruptible_all(rb->wq);
+#endif
+		}
+	}
 }
 
 /*
@@ -437,3 +452,43 @@ int prb_iter_next(struct prb_iterator *i
 
 	return 1;
 }
+
+/*
+ * prb_iter_wait_next: Advance to the next record, blocking if none available.
+ * @iter: Iterator tracking the current position.
+ * @buf: A buffer to store the data of the next record. May be NULL.
+ * @size: The size of @buf. (Ignored if @buf is NULL.)
+ * @seq: The sequence number of the next record. May be NULL.
+ *
+ * If a next record is already available, this function works like
+ * prb_iter_next(). Otherwise block interruptible until a next record is
+ * available.
+ *
+ * When a next record is available, @iter is advanced and (if specified)
+ * the data and/or sequence number of that record are provided.
+ *
+ * This function might sleep.
+ *
+ * Returns 1 if @iter was advanced, -EINVAL if @iter is now invalid, or
+ * -ERESTARTSYS if interrupted by a signal.
+ */
+int prb_iter_wait_next(struct prb_iterator *iter, char *buf, int size, u64 *seq)
+{
+	unsigned long last_seen;
+	int ret;
+
+	for (;;) {
+		last_seen = atomic_long_read(&iter->rb->wq_counter);
+
+		ret = prb_iter_next(iter, buf, size, seq);
+		if (ret != 0)
+			break;
+
+		ret = wait_event_interruptible(*iter->rb->wq,
+			last_seen != atomic_long_read(&iter->rb->wq_counter));
+		if (ret < 0)
+			break;
+	}
+
+	return ret;
+}