Skip to content

Commit 5d45e5e

Browse files
committed
Use RCU list for notifiers
1 parent a815cb3 commit 5d45e5e

File tree

3 files changed

+53
-85
lines changed

3 files changed

+53
-85
lines changed

module/include/linux/surface_aggregator/controller.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ typedef u32 (*ssam_notifier_fn_t)(struct ssam_event_notifier *nf,
653653
/**
654654
* struct ssam_notifier_block - Base notifier block for SSAM event
655655
* notifications.
656-
* @next: The next notifier block in order of priority.
656+
* @node: The node for the list of notifiers.
657657
* @fn: The callback function of this notifier. This function takes the
658658
* respective notifier block and event as input and should return
659659
* a notifier value, which can either be obtained from the flags
@@ -666,7 +666,7 @@ typedef u32 (*ssam_notifier_fn_t)(struct ssam_event_notifier *nf,
666666
* priority) callbacks.
667667
*/
668668
struct ssam_notifier_block {
669-
struct ssam_notifier_block __rcu *next;
669+
struct list_head node;
670670
ssam_notifier_fn_t fn;
671671
int priority;
672672
};

module/src/controller.c

+49-80
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/limits.h>
1515
#include <linux/list.h>
1616
#include <linux/mutex.h>
17+
#include <linux/rculist.h>
1718
#include <linux/rbtree.h>
1819
#include <linux/rwsem.h>
1920
#include <linux/serdev.h>
@@ -145,133 +146,96 @@ static bool ssam_event_matches_notifier(const struct ssam_event_notifier *n,
145146
*/
146147
static int ssam_nfblk_call_chain(struct ssam_nf_head *nh, struct ssam_event *event)
147148
{
148-
struct ssam_notifier_block *nb, *next_nb;
149149
struct ssam_event_notifier *nf;
150150
int ret = 0, idx;
151151

152152
idx = srcu_read_lock(&nh->srcu);
153153

154-
nb = rcu_dereference_raw(nh->head);
155-
while (nb) {
156-
nf = container_of(nb, struct ssam_event_notifier, base);
157-
next_nb = rcu_dereference_raw(nb->next);
158-
154+
list_for_each_entry_rcu(nf, &nh->head, base.node) {
159155
if (ssam_event_matches_notifier(nf, event)) {
160-
ret = (ret & SSAM_NOTIF_STATE_MASK) | nb->fn(nf, event);
156+
ret = (ret & SSAM_NOTIF_STATE_MASK) | nf->base.fn(nf, event);
161157
if (ret & SSAM_NOTIF_STOP)
162158
break;
163159
}
164-
165-
nb = next_nb;
166160
}
167161

168162
srcu_read_unlock(&nh->srcu, idx);
169163
return ret;
170164
}
171165

172166
/**
173-
* __ssam_nfblk_insert() - Insert a new notifier block into the given notifier
167+
* ssam_nfblk_insert() - Insert a new notifier block into the given notifier
174168
* list.
175169
* @nh: The notifier head into which the block should be inserted.
176170
* @nb: The notifier block to add.
177171
*
178172
* Note: This function must be synchronized by the caller with respect to other
179-
* insert and/or remove calls.
173+
* insert, find, and/or remove calls.
180174
*
181175
* Return: Returns zero on success, %-EEXIST if the notifier block has already
182176
* been registered.
183177
*/
184-
static int __ssam_nfblk_insert(struct ssam_nf_head *nh, struct ssam_notifier_block *nb)
178+
static int ssam_nfblk_insert(struct ssam_nf_head *nh, struct ssam_notifier_block *nb)
185179
{
186-
struct ssam_notifier_block **link = &nh->head;
180+
struct ssam_notifier_block *p;
181+
struct list_head *h;
187182

188-
while (*link) {
189-
if (unlikely(*link == nb)) {
183+
/* Runs under lock, no need for RCU variant. */
184+
list_for_each(h, &nh->head) {
185+
p = list_entry(h, struct ssam_notifier_block, node);
186+
187+
if (unlikely(p == nb)) {
190188
WARN(1, "double register detected");
191189
return -EEXIST;
192190
}
193191

194-
if (nb->priority > (*link)->priority)
192+
if (nb->priority > p->priority)
195193
break;
196-
197-
link = &((*link)->next);
198194
}
199195

200-
nb->next = *link;
201-
rcu_assign_pointer(*link, nb);
202-
196+
list_add_tail_rcu(&nb->node, h);
203197
return 0;
204198
}
205199

206200
/**
207-
* __ssam_nfblk_find_link() - Find a notifier block link on the given list.
208-
* @nh: The notifier head on which the search should be conducted.
201+
* ssam_nfblk_find() - Check if a notifier block is registered on the given
202+
* notifier head.
203+
* list.
204+
* @nh: The notifier head on which to search.
209205
* @nb: The notifier block to search for.
210206
*
211-
* Note: This function must be synchronized by the caller with respect to
212-
* insert and/or remove calls.
207+
* Note: This function must be synchronized by the caller with respect to other
208+
* insert, find, and/or remove calls.
213209
*
214-
* Return: Returns a pointer to the link (i.e. pointer pointing) to the given
215-
* notifier block, from the previous node in the list, or %NULL if the given
216-
* notifier block is not contained in the notifier list.
210+
* Return: Returns true if the given notifier block is registered on the given
211+
* notifier head, false otherwise.
217212
*/
218-
static struct ssam_notifier_block **__ssam_nfblk_find_link(
219-
struct ssam_nf_head *nh, struct ssam_notifier_block *nb)
213+
static bool ssam_nfblk_find(struct ssam_nf_head *nh, struct ssam_notifier_block *nb)
220214
{
221-
struct ssam_notifier_block **link = &nh->head;
222-
223-
while (*link) {
224-
if (*link == nb)
225-
return link;
215+
struct ssam_notifier_block *p;
226216

227-
link = &((*link)->next);
217+
/* Runs under lock, no need for RCU variant. */
218+
list_for_each_entry(p, &nh->head, node) {
219+
if (p == nb)
220+
return true;
228221
}
229222

230-
return NULL;
223+
return false;
231224
}
232225

233226
/**
234-
* __ssam_nfblk_erase() - Erase a notifier block link in the given notifier
235-
* list.
236-
* @link: The link to be erased.
227+
* ssam_nfblk_remove() - Remove a notifier block from its notifier list.
228+
* @nb: The notifier block to be removed.
237229
*
238230
* Note: This function must be synchronized by the caller with respect to
239-
* other insert and/or remove/erase/find calls. The caller _must_ ensure SRCU
231+
* other insert, find and/or remove calls. The caller _must_ ensure SRCU
240232
* synchronization by calling synchronize_srcu() with ``nh->srcu`` after
241233
* leaving the critical section, to ensure that the removed notifier block is
242234
* not in use any more.
243235
*/
244-
static void __ssam_nfblk_erase(struct ssam_notifier_block **link)
236+
static void ssam_nfblk_remove(struct ssam_notifier_block *nb)
245237
{
246-
rcu_assign_pointer(*link, (*link)->next);
247-
}
248-
249-
250-
/**
251-
* __ssam_nfblk_remove() - Remove a notifier block from the given notifier list.
252-
* @nh: The notifier head from which the block should be removed.
253-
* @nb: The notifier block to remove.
254-
*
255-
* Note: This function must be synchronized by the caller with respect to
256-
* other insert and/or remove calls. On success, the caller *must* ensure SRCU
257-
* synchronization by calling synchronize_srcu() with ``nh->srcu`` after
258-
* leaving the critical section, to ensure that the removed notifier block is
259-
* not in use any more.
260-
*
261-
* Return: Returns zero on success, %-ENOENT if the specified notifier block
262-
* could not be found on the notifier list.
263-
*/
264-
static int __ssam_nfblk_remove(struct ssam_nf_head *nh,
265-
struct ssam_notifier_block *nb)
266-
{
267-
struct ssam_notifier_block **link;
268-
269-
link = __ssam_nfblk_find_link(nh, nb);
270-
if (!link)
271-
return -ENOENT;
272-
273-
__ssam_nfblk_erase(link);
274-
return 0;
238+
list_del_rcu(&nb->node);
275239
}
276240

277241
/**
@@ -286,7 +250,7 @@ static int ssam_nf_head_init(struct ssam_nf_head *nh)
286250
if (status)
287251
return status;
288252

289-
nh->head = NULL;
253+
INIT_LIST_HEAD(&nh->head);
290254
return 0;
291255
}
292256

@@ -2153,7 +2117,7 @@ int ssam_notifier_register(struct ssam_controller *ctrl,
21532117
n->event.reg.target_category, n->event.id.target_category,
21542118
n->event.id.instance, entry->refcount);
21552119

2156-
status = __ssam_nfblk_insert(nf_head, &n->base);
2120+
status = ssam_nfblk_insert(nf_head, &n->base);
21572121
if (status) {
21582122
entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
21592123
if (entry->refcount == 0)
@@ -2167,7 +2131,7 @@ int ssam_notifier_register(struct ssam_controller *ctrl,
21672131
status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id,
21682132
n->event.flags);
21692133
if (status) {
2170-
__ssam_nfblk_remove(nf_head, &n->base);
2134+
ssam_nfblk_remove(&n->base);
21712135
kfree(ssam_nf_refcount_dec(nf, n->event.reg, n->event.id));
21722136
mutex_unlock(&nf->lock);
21732137
synchronize_srcu(&nf_head->srcu);
@@ -2206,7 +2170,6 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
22062170
struct ssam_event_notifier *n)
22072171
{
22082172
u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
2209-
struct ssam_notifier_block **link;
22102173
struct ssam_nf_refcount_entry *entry;
22112174
struct ssam_nf_head *nf_head;
22122175
struct ssam_nf *nf;
@@ -2220,16 +2183,21 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
22202183

22212184
mutex_lock(&nf->lock);
22222185

2223-
link = __ssam_nfblk_find_link(nf_head, &n->base);
2224-
if (!link) {
2186+
if (!ssam_nfblk_find(nf_head, &n->base)) {
22252187
mutex_unlock(&nf->lock);
22262188
return -ENOENT;
22272189
}
22282190

22292191
entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
22302192
if (WARN_ON(!entry)) {
2231-
mutex_unlock(&nf->lock);
2232-
return -ENOENT;
2193+
/*
2194+
* If this does not return an entry, there's a logic error
2195+
* somewhere: The notifier block is registered, but the event
2196+
* refcount entry is not there. Remove the notifier block
2197+
* anyways.
2198+
*/
2199+
status = -ENOENT;
2200+
goto remove;
22332201
}
22342202

22352203
ssam_dbg(ctrl, "disabling event (reg: 0x%02x, tc: 0x%02x, iid: 0x%02x, rc: %d)\n",
@@ -2249,7 +2217,8 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
22492217
kfree(entry);
22502218
}
22512219

2252-
__ssam_nfblk_erase(link);
2220+
remove:
2221+
ssam_nfblk_remove(&n->base);
22532222
mutex_unlock(&nf->lock);
22542223
synchronize_srcu(&nf_head->srcu);
22552224

module/src/controller.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,11 @@ struct ssh_rqid_counter {
4949
/**
5050
* struct ssam_nf_head - Notifier head for SSAM events.
5151
* @srcu: The SRCU struct for synchronization.
52-
* @head: Head-pointer for the single-linked list of notifier blocks registered
53-
* under this head.
52+
* @head: List-head for notifier blocks registered under this head.
5453
*/
5554
struct ssam_nf_head {
5655
struct srcu_struct srcu;
57-
struct ssam_notifier_block __rcu *head;
56+
struct list_head head;
5857
};
5958

6059
/**

0 commit comments

Comments
 (0)