aboutsummaryrefslogtreecommitdiffstats
path: root/fs/afs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2018-06-15 10:24:50 -0400
committerDavid Howells <dhowells@redhat.com>2018-06-15 10:27:09 -0400
commit47ea0f2ebffd400d36ab5946ec8d6d6e08a67d53 (patch)
treec3100b32ecadbc87bcefad4e00079ac52f5c69b4 /fs/afs
parent0da0b7fd73e4f20e1a987dfade0b36bb4813cf10 (diff)
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 <dhowells@redhat.com>
Diffstat (limited to 'fs/afs')
-rw-r--r--fs/afs/callback.c110
-rw-r--r--fs/afs/internal.h15
-rw-r--r--fs/afs/server.c2
3 files changed, 107 insertions, 20 deletions
diff --git a/fs/afs/callback.c b/fs/afs/callback.c
index 571437dcb252..5f261fbf2182 100644
--- a/fs/afs/callback.c
+++ b/fs/afs/callback.c
@@ -21,6 +21,66 @@
21#include "internal.h" 21#include "internal.h"
22 22
23/* 23/*
24 * Create volume and callback interests on a server.
25 */
26static 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;
73found_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
83/*
24 * Set up an interest-in-callbacks record for a volume on a server and 84 * Set up an interest-in-callbacks record for a volume on a server and
25 * register it with the server. 85 * register it with the server.
26 * - Called with vnode->io_lock held. 86 * - Called with vnode->io_lock held.
@@ -77,20 +137,10 @@ again:
77 } 137 }
78 138
79 if (!cbi) { 139 if (!cbi) {
80 new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL); 140 new = afs_create_interest(server, vnode);
81 if (!new) 141 if (!new)
82 return -ENOMEM; 142 return -ENOMEM;
83 143
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
94 write_lock(&slist->lock); 144 write_lock(&slist->lock);
95 if (!entry->cb_interest) { 145 if (!entry->cb_interest) {
96 entry->cb_interest = afs_get_cb_interest(new); 146 entry->cb_interest = afs_get_cb_interest(new);
@@ -126,11 +176,22 @@ again:
126 */ 176 */
127void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) 177void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
128{ 178{
179 struct afs_vol_interest *vi;
180
129 if (cbi && refcount_dec_and_test(&cbi->usage)) { 181 if (cbi && refcount_dec_and_test(&cbi->usage)) {
130 if (!list_empty(&cbi->cb_link)) { 182 if (!hlist_unhashed(&cbi->cb_vlink)) {
131 write_lock(&cbi->server->cb_break_lock); 183 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
133 write_unlock(&cbi->server->cb_break_lock); 193 write_unlock(&cbi->server->cb_break_lock);
194 kfree(vi);
134 afs_put_server(net, cbi->server); 195 afs_put_server(net, cbi->server);
135 } 196 }
136 kfree(cbi); 197 kfree(cbi);
@@ -182,20 +243,34 @@ void afs_break_callback(struct afs_vnode *vnode)
182static void afs_break_one_callback(struct afs_server *server, 243static void afs_break_one_callback(struct afs_server *server,
183 struct afs_fid *fid) 244 struct afs_fid *fid)
184{ 245{
246 struct afs_vol_interest *vi;
185 struct afs_cb_interest *cbi; 247 struct afs_cb_interest *cbi;
186 struct afs_iget_data data; 248 struct afs_iget_data data;
187 struct afs_vnode *vnode; 249 struct afs_vnode *vnode;
188 struct inode *inode; 250 struct inode *inode;
189 251
190 read_lock(&server->cb_break_lock); 252 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;
191 269
192 /* Step through all interested superblocks. There may be more than one 270 /* Step through all interested superblocks. There may be more than one
193 * because of cell aliasing. 271 * because of cell aliasing.
194 */ 272 */
195 list_for_each_entry(cbi, &server->cb_interests, cb_link) { 273 hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
196 if (cbi->vid != fid->vid)
197 continue;
198
199 if (fid->vnode == 0 && fid->unique == 0) { 274 if (fid->vnode == 0 && fid->unique == 0) {
200 /* The callback break applies to an entire volume. */ 275 /* The callback break applies to an entire volume. */
201 struct afs_super_info *as = AFS_FS_S(cbi->sb); 276 struct afs_super_info *as = AFS_FS_S(cbi->sb);
@@ -217,6 +292,7 @@ static void afs_break_one_callback(struct afs_server *server,
217 } 292 }
218 } 293 }
219 294
295out:
220 read_unlock(&server->cb_break_lock); 296 read_unlock(&server->cb_break_lock);
221} 297}
222 298
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 5d8260b4c2b3..9778df135717 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -407,16 +407,27 @@ struct afs_server {
407 rwlock_t fs_lock; /* access lock */ 407 rwlock_t fs_lock; /* access lock */
408 408
409 /* callback promise management */ 409 /* 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 */
411 unsigned cb_s_break; /* Break-everything counter. */ 411 unsigned cb_s_break; /* Break-everything counter. */
412 rwlock_t cb_break_lock; /* Volume finding lock */ 412 rwlock_t cb_break_lock; /* Volume finding lock */
413}; 413};
414 414
415/* 415/*
416 * Volume collation in the server's callback interest list.
417 */
418struct 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
425/*
416 * Interest by a superblock on a server. 426 * Interest by a superblock on a server.
417 */ 427 */
418struct afs_cb_interest { 428struct 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;
420 struct afs_server *server; /* Server on which this interest resides */ 431 struct afs_server *server; /* Server on which this interest resides */
421 struct super_block *sb; /* Superblock on which inodes reside */ 432 struct super_block *sb; /* Superblock on which inodes reside */
422 afs_volid_t vid; /* Volume ID to match */ 433 afs_volid_t vid; /* Volume ID to match */
diff --git a/fs/afs/server.c b/fs/afs/server.c
index 3af4625e2f8c..1d329e6981d5 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -228,7 +228,7 @@ static struct afs_server *afs_alloc_server(struct afs_net *net,
228 server->flags = (1UL << AFS_SERVER_FL_NEW); 228 server->flags = (1UL << AFS_SERVER_FL_NEW);
229 server->update_at = ktime_get_real_seconds() + afs_server_update_delay; 229 server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
230 rwlock_init(&server->fs_lock); 230 rwlock_init(&server->fs_lock);
231 INIT_LIST_HEAD(&server->cb_interests); 231 INIT_HLIST_HEAD(&server->cb_volumes);
232 rwlock_init(&server->cb_break_lock); 232 rwlock_init(&server->cb_break_lock);
233 233
234 afs_inc_servers_outstanding(net); 234 afs_inc_servers_outstanding(net);