diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/afs/callback.c | 110 | ||||
| -rw-r--r-- | fs/afs/internal.h | 15 | ||||
| -rw-r--r-- | fs/afs/server.c | 2 |
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 | */ | ||
| 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 | |||
| 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 | */ |
| 127 | void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) | 177 | void 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) | |||
| 182 | static void afs_break_one_callback(struct afs_server *server, | 243 | static 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 | ||
| 295 | out: | ||
| 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 | */ | ||
| 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 | |||
| 425 | /* | ||
| 416 | * Interest by a superblock on a server. | 426 | * Interest by a superblock on a server. |
| 417 | */ | 427 | */ |
| 418 | struct afs_cb_interest { | 428 | 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; | ||
| 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); |
