Skip to content

Commit 47ea0f2

Browse files
committed
afs: Optimise callback breaking by not repeating volume lookup
At the moment, afs_break_callbacks calls afs_break_one_callback() for each separate FID it was given, and the latter looks up the volume individually for each one. However, this is inefficient if two or more FIDs have the same vid as we could reuse the volume. This is complicated by cell aliasing whereby we may have multiple cells sharing a volume and can therefore have multiple callback interests for any particular volume ID. At the moment afs_break_one_callback() scans the entire list of volumes we're getting from a server and breaks the appropriate callback in every matching volume, regardless of cell. This scan is done for every FID. Optimise callback breaking by the following means: (1) Sort the FID list by vid so that all FIDs belonging to the same volume are clumped together. This is done through the use of an indirection table as we cannot do an insertion sort on the afs_callback_break array as we decode FIDs into it as we subsequently also have to decode callback info into it that corresponds by array index only. We also don't really want to bubblesort afterwards if we can avoid it. (2) Sort the server->cb_interests array by vid so that all the matching volumes are grouped together. This permits the scan to stop after finding a record that has a higher vid. (3) When breaking FIDs, we try to keep server->cb_break_lock as long as possible, caching the start point in the array for that volume group as long as possible. It might make sense to add another layer in that list and have a refcounted volume ID anchor that has the matching interests attached to it rather than being in the list. This would allow the lock to be dropped without losing the cursor. Signed-off-by: David Howells <[email protected]>
1 parent 0da0b7f commit 47ea0f2

File tree

3 files changed

+107
-20
lines changed

3 files changed

+107
-20
lines changed

fs/afs/callback.c

Lines changed: 93 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,66 @@
2020
#include <linux/sched.h>
2121
#include "internal.h"
2222

23+
/*
24+
* Create volume and callback interests on a server.
25+
*/
26+
static struct afs_cb_interest *afs_create_interest(struct afs_server *server,
27+
struct afs_vnode *vnode)
28+
{
29+
struct afs_vol_interest *new_vi, *vi;
30+
struct afs_cb_interest *new;
31+
struct hlist_node **pp;
32+
33+
new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL);
34+
if (!new_vi)
35+
return NULL;
36+
37+
new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
38+
if (!new) {
39+
kfree(new_vi);
40+
return NULL;
41+
}
42+
43+
new_vi->usage = 1;
44+
new_vi->vid = vnode->volume->vid;
45+
INIT_HLIST_NODE(&new_vi->srv_link);
46+
INIT_HLIST_HEAD(&new_vi->cb_interests);
47+
48+
refcount_set(&new->usage, 1);
49+
new->sb = vnode->vfs_inode.i_sb;
50+
new->vid = vnode->volume->vid;
51+
new->server = afs_get_server(server);
52+
INIT_HLIST_NODE(&new->cb_vlink);
53+
54+
write_lock(&server->cb_break_lock);
55+
56+
for (pp = &server->cb_volumes.first; *pp; pp = &(*pp)->next) {
57+
vi = hlist_entry(*pp, struct afs_vol_interest, srv_link);
58+
if (vi->vid < new_vi->vid)
59+
continue;
60+
if (vi->vid > new_vi->vid)
61+
break;
62+
vi->usage++;
63+
goto found_vi;
64+
}
65+
66+
new_vi->srv_link.pprev = pp;
67+
new_vi->srv_link.next = *pp;
68+
if (*pp)
69+
(*pp)->pprev = &new_vi->srv_link.next;
70+
*pp = &new_vi->srv_link;
71+
vi = new_vi;
72+
new_vi = NULL;
73+
found_vi:
74+
75+
new->vol_interest = vi;
76+
hlist_add_head(&new->cb_vlink, &vi->cb_interests);
77+
78+
write_unlock(&server->cb_break_lock);
79+
kfree(new_vi);
80+
return new;
81+
}
82+
2383
/*
2484
* Set up an interest-in-callbacks record for a volume on a server and
2585
* register it with the server.
@@ -77,20 +137,10 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
77137
}
78138

79139
if (!cbi) {
80-
new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
140+
new = afs_create_interest(server, vnode);
81141
if (!new)
82142
return -ENOMEM;
83143

84-
refcount_set(&new->usage, 1);
85-
new->sb = vnode->vfs_inode.i_sb;
86-
new->vid = vnode->volume->vid;
87-
new->server = afs_get_server(server);
88-
INIT_LIST_HEAD(&new->cb_link);
89-
90-
write_lock(&server->cb_break_lock);
91-
list_add_tail(&new->cb_link, &server->cb_interests);
92-
write_unlock(&server->cb_break_lock);
93-
94144
write_lock(&slist->lock);
95145
if (!entry->cb_interest) {
96146
entry->cb_interest = afs_get_cb_interest(new);
@@ -126,11 +176,22 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
126176
*/
127177
void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
128178
{
179+
struct afs_vol_interest *vi;
180+
129181
if (cbi && refcount_dec_and_test(&cbi->usage)) {
130-
if (!list_empty(&cbi->cb_link)) {
182+
if (!hlist_unhashed(&cbi->cb_vlink)) {
131183
write_lock(&cbi->server->cb_break_lock);
132-
list_del_init(&cbi->cb_link);
184+
185+
hlist_del_init(&cbi->cb_vlink);
186+
vi = cbi->vol_interest;
187+
cbi->vol_interest = NULL;
188+
if (--vi->usage == 0)
189+
hlist_del(&vi->srv_link);
190+
else
191+
vi = NULL;
192+
133193
write_unlock(&cbi->server->cb_break_lock);
194+
kfree(vi);
134195
afs_put_server(net, cbi->server);
135196
}
136197
kfree(cbi);
@@ -182,20 +243,34 @@ void afs_break_callback(struct afs_vnode *vnode)
182243
static void afs_break_one_callback(struct afs_server *server,
183244
struct afs_fid *fid)
184245
{
246+
struct afs_vol_interest *vi;
185247
struct afs_cb_interest *cbi;
186248
struct afs_iget_data data;
187249
struct afs_vnode *vnode;
188250
struct inode *inode;
189251

190252
read_lock(&server->cb_break_lock);
253+
hlist_for_each_entry(vi, &server->cb_volumes, srv_link) {
254+
if (vi->vid < fid->vid)
255+
continue;
256+
if (vi->vid > fid->vid) {
257+
vi = NULL;
258+
break;
259+
}
260+
//atomic_inc(&vi->usage);
261+
break;
262+
}
263+
264+
/* TODO: Find all matching volumes if we couldn't match the server and
265+
* break them anyway.
266+
*/
267+
if (!vi)
268+
goto out;
191269

192270
/* Step through all interested superblocks. There may be more than one
193271
* because of cell aliasing.
194272
*/
195-
list_for_each_entry(cbi, &server->cb_interests, cb_link) {
196-
if (cbi->vid != fid->vid)
197-
continue;
198-
273+
hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
199274
if (fid->vnode == 0 && fid->unique == 0) {
200275
/* The callback break applies to an entire volume. */
201276
struct afs_super_info *as = AFS_FS_S(cbi->sb);
@@ -217,6 +292,7 @@ static void afs_break_one_callback(struct afs_server *server,
217292
}
218293
}
219294

295+
out:
220296
read_unlock(&server->cb_break_lock);
221297
}
222298

fs/afs/internal.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,16 +407,27 @@ struct afs_server {
407407
rwlock_t fs_lock; /* access lock */
408408

409409
/* callback promise management */
410-
struct list_head cb_interests; /* List of superblocks using this server */
410+
struct hlist_head cb_volumes; /* List of volume interests on this server */
411411
unsigned cb_s_break; /* Break-everything counter. */
412412
rwlock_t cb_break_lock; /* Volume finding lock */
413413
};
414414

415+
/*
416+
* Volume collation in the server's callback interest list.
417+
*/
418+
struct afs_vol_interest {
419+
struct hlist_node srv_link; /* Link in server->cb_volumes */
420+
struct hlist_head cb_interests; /* List of callback interests on the server */
421+
afs_volid_t vid; /* Volume ID to match */
422+
unsigned int usage;
423+
};
424+
415425
/*
416426
* Interest by a superblock on a server.
417427
*/
418428
struct afs_cb_interest {
419-
struct list_head cb_link; /* Link in server->cb_interests */
429+
struct hlist_node cb_vlink; /* Link in vol_interest->cb_interests */
430+
struct afs_vol_interest *vol_interest;
420431
struct afs_server *server; /* Server on which this interest resides */
421432
struct super_block *sb; /* Superblock on which inodes reside */
422433
afs_volid_t vid; /* Volume ID to match */

fs/afs/server.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ static struct afs_server *afs_alloc_server(struct afs_net *net,
228228
server->flags = (1UL << AFS_SERVER_FL_NEW);
229229
server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
230230
rwlock_init(&server->fs_lock);
231-
INIT_LIST_HEAD(&server->cb_interests);
231+
INIT_HLIST_HEAD(&server->cb_volumes);
232232
rwlock_init(&server->cb_break_lock);
233233

234234
afs_inc_servers_outstanding(net);

0 commit comments

Comments
 (0)