/* * finish off updating the recorded status of a file * - starts callback expiry timer * - adds to server's callback list */ void afs_vnode_finalise_status_update(afs_vnode_t *vnode, afs_server_t *server, int ret) { afs_server_t *oldserver = NULL; _enter("%p,%p,%d",vnode,server,ret); spin_lock(&vnode->lock); vnode->flags &= ~AFS_VNODE_CHANGED; if (ret==0) { /* adjust the callback timeout appropriately */ afs_kafstimod_add_timer(&vnode->cb_timeout,vnode->cb_expiry*HZ); spin_lock(&afs_cb_hash_lock); list_del(&vnode->cb_hash_link); list_add_tail(&vnode->cb_hash_link,&afs_cb_hash(server,&vnode->fid)); spin_unlock(&afs_cb_hash_lock); /* swap ref to old callback server with that for new callback server */ oldserver = xchg(&vnode->cb_server,server); if (oldserver!=server) { if (oldserver) { spin_lock(&oldserver->cb_lock); list_del_init(&vnode->cb_link); spin_unlock(&oldserver->cb_lock); } afs_get_server(server); spin_lock(&server->cb_lock); list_add_tail(&vnode->cb_link,&server->cb_promises); spin_unlock(&server->cb_lock); } else { /* same server */ oldserver = NULL; } } else if (ret==-ENOENT) { /* the file was deleted - clear the callback timeout */ oldserver = xchg(&vnode->cb_server,NULL); afs_kafstimod_del_timer(&vnode->cb_timeout); _debug("got NOENT from server - marking file deleted"); vnode->flags |= AFS_VNODE_DELETED; } vnode->update_cnt--; spin_unlock(&vnode->lock); wake_up_all(&vnode->update_waitq); afs_put_server(oldserver); _leave(""); } /* end afs_vnode_finalise_status_update() */
/* * insert a vnode into the backing server's vnode tree */ static void afs_install_vnode(struct afs_vnode *vnode, struct afs_server *server) { struct afs_server *old_server = vnode->server; struct afs_vnode *xvnode; struct rb_node *parent, **p; _enter("%p,%p", vnode, server); if (old_server) { spin_lock(&old_server->fs_lock); rb_erase(&vnode->server_rb, &old_server->fs_vnodes); spin_unlock(&old_server->fs_lock); } afs_get_server(server); vnode->server = server; afs_put_server(old_server); /* insert into the server's vnode tree in FID order */ spin_lock(&server->fs_lock); parent = NULL; p = &server->fs_vnodes.rb_node; while (*p) { parent = *p; xvnode = rb_entry(parent, struct afs_vnode, server_rb); if (vnode->fid.vid < xvnode->fid.vid) p = &(*p)->rb_left; else if (vnode->fid.vid > xvnode->fid.vid) p = &(*p)->rb_right; else if (vnode->fid.vnode < xvnode->fid.vnode) p = &(*p)->rb_left; else if (vnode->fid.vnode > xvnode->fid.vnode) p = &(*p)->rb_right; else if (vnode->fid.unique < xvnode->fid.unique) p = &(*p)->rb_left; else if (vnode->fid.unique > xvnode->fid.unique) p = &(*p)->rb_right; else BUG(); /* can't happen unless afs_iget() malfunctions */ } rb_link_node(&vnode->server_rb, parent, p); rb_insert_color(&vnode->server_rb, &server->fs_vnodes); spin_unlock(&server->fs_lock); _leave(""); }
/* * pick a server to use to try accessing this volume * - returns with an elevated usage count on the server chosen */ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode) { struct afs_volume *volume = vnode->volume; struct afs_server *server; int ret, state, loop; _enter("%s", volume->vlocation->vldb.name); /* stick with the server we're already using if we can */ if (vnode->server && vnode->server->fs_state == 0) { afs_get_server(vnode->server); _leave(" = %p [current]", vnode->server); return vnode->server; } down_read(&volume->server_sem); /* handle the no-server case */ if (volume->nservers == 0) { ret = volume->rjservers ? -ENOMEDIUM : -ESTALE; up_read(&volume->server_sem); _leave(" = %d [no servers]", ret); return ERR_PTR(ret); } /* basically, just search the list for the first live server and use * that */ ret = 0; for (loop = 0; loop < volume->nservers; loop++) { server = volume->servers[loop]; state = server->fs_state; _debug("consider %d [%d]", loop, state); switch (state) { /* found an apparently healthy server */ case 0: afs_get_server(server); up_read(&volume->server_sem); _leave(" = %p (picked %08x)", server, ntohl(server->addr.s_addr)); return server; case -ENETUNREACH: if (ret == 0) ret = state; break; case -EHOSTUNREACH: if (ret == 0 || ret == -ENETUNREACH) ret = state; break; case -ECONNREFUSED: if (ret == 0 || ret == -ENETUNREACH || ret == -EHOSTUNREACH) ret = state; break; default: case -EREMOTEIO: if (ret == 0 || ret == -ENETUNREACH || ret == -EHOSTUNREACH || ret == -ECONNREFUSED) ret = state; break; } } /* no available servers * - TODO: handle the no active servers case better */ up_read(&volume->server_sem); _leave(" = %d", ret); return ERR_PTR(ret); }