Overhaul permit caching in AFS by making it per-vnode and sharing permit lists where possible. When most of the fileserver operations are called, they return a status structure indicating the (revised) details of the vnode or vnodes involved in the operation. This includes the access mark derived from the ACL (named CallerAccess in the protocol definition file). This is cacheable and if the ACL changes, the server will tell us that it is breaking the callback promise, at which point we can discard the currently cached permits. With this patch, the afs_permits structure has, at the end, an array of { key, CallerAccess } elements, sorted by key pointer. This is then cached in a hash table so that it can be shared between vnodes with the same access permits. Permit lists can only be shared if they contain the exact same set of key->CallerAccess mappings. Note that that table is global rather than being per-net_ns. If the keys in a permit list cross net_ns boundaries, there is no problem sharing the cached permits, since the permits are just integer masks. Since permit lists pin keys, the permit cache also makes it easier for a future patch to find all occurrences of a key and remove them by means of setting the afs_permits::invalidated flag and then clearing the appropriate key pointer. In such an event, memory barriers will need adding. Lastly, the permit caching is skipped if the server has sent either a vnode-specific or an entire-server callback since the start of the operation. Signed-off-by: David Howells <dhowells@redhat.com>
848 lines
20 KiB
C
848 lines
20 KiB
C
/* AFS vnode management
|
|
*
|
|
* Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/sched.h>
|
|
#include "internal.h"
|
|
|
|
/*
|
|
* Handle remote file deletion.
|
|
*/
|
|
static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
|
|
{
|
|
struct afs_cb_interest *cbi = vnode->cb_interest;
|
|
|
|
_enter("{%p}", cbi);
|
|
|
|
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
|
|
|
if (cbi) {
|
|
vnode->cb_interest = NULL;
|
|
afs_put_cb_interest(afs_v2net(vnode), cbi);
|
|
}
|
|
|
|
_leave("");
|
|
}
|
|
|
|
/*
|
|
* finish off updating the recorded status of a file after a successful
|
|
* operation completion
|
|
* - starts callback expiry timer
|
|
* - adds to server's callback list
|
|
*/
|
|
void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
|
|
struct afs_server *server)
|
|
{
|
|
struct afs_server *oldserver = NULL;
|
|
|
|
_enter("%p,%p", vnode, server);
|
|
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
|
|
wake_up_all(&vnode->update_waitq);
|
|
afs_put_server(afs_v2net(vnode), oldserver);
|
|
_leave("");
|
|
}
|
|
|
|
/*
|
|
* finish off updating the recorded status of a file after an operation failed
|
|
*/
|
|
static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret)
|
|
{
|
|
_enter("{%x:%u},%d", vnode->fid.vid, vnode->fid.vnode, ret);
|
|
|
|
spin_lock(&vnode->lock);
|
|
|
|
if (ret == -ENOENT) {
|
|
/* the file was deleted on the server */
|
|
_debug("got NOENT from server - marking file deleted");
|
|
afs_vnode_deleted_remotely(vnode);
|
|
}
|
|
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
|
|
wake_up_all(&vnode->update_waitq);
|
|
_leave("");
|
|
}
|
|
|
|
/*
|
|
* fetch file status from the volume
|
|
* - don't issue a fetch if:
|
|
* - the changed bit is not set and there's a valid callback
|
|
* - there are any outstanding ops that will fetch the status
|
|
* - TODO implement local caching
|
|
*/
|
|
int afs_vnode_fetch_status(struct afs_vnode *vnode, struct key *key, bool force)
|
|
{
|
|
struct afs_server *server;
|
|
unsigned int cb_break = 0;
|
|
int ret;
|
|
|
|
DECLARE_WAITQUEUE(myself, current);
|
|
|
|
_enter("%s,{%x:%u.%u,S=%lx},%u",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
|
|
vnode->flags,
|
|
force);
|
|
|
|
if (!force && test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
|
_leave(" [unchanged]");
|
|
return 0;
|
|
}
|
|
|
|
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
|
|
_leave(" [deleted]");
|
|
return -ENOENT;
|
|
}
|
|
|
|
cb_break = vnode->cb_break + vnode->cb_s_break;
|
|
|
|
spin_lock(&vnode->lock);
|
|
|
|
if (!force && test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
|
spin_unlock(&vnode->lock);
|
|
_leave(" [unchanged]");
|
|
return 0;
|
|
}
|
|
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
|
|
if (vnode->update_cnt > 0) {
|
|
/* someone else started a fetch */
|
|
_debug("wait on fetch %d", vnode->update_cnt);
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
ASSERT(myself.func != NULL);
|
|
add_wait_queue(&vnode->update_waitq, &myself);
|
|
|
|
/* wait for the status to be updated */
|
|
for (;;) {
|
|
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
|
|
break;
|
|
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
|
|
break;
|
|
|
|
/* check to see if it got updated and invalidated all
|
|
* before we saw it */
|
|
if (vnode->update_cnt == 0) {
|
|
remove_wait_queue(&vnode->update_waitq,
|
|
&myself);
|
|
set_current_state(TASK_RUNNING);
|
|
goto get_anyway;
|
|
}
|
|
|
|
spin_unlock(&vnode->lock);
|
|
|
|
schedule();
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
spin_lock(&vnode->lock);
|
|
}
|
|
|
|
remove_wait_queue(&vnode->update_waitq, &myself);
|
|
spin_unlock(&vnode->lock);
|
|
set_current_state(TASK_RUNNING);
|
|
|
|
return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
|
|
-ENOENT : 0;
|
|
}
|
|
|
|
get_anyway:
|
|
/* okay... we're going to have to initiate the op */
|
|
vnode->update_cnt++;
|
|
|
|
spin_unlock(&vnode->lock);
|
|
|
|
/* merge AFS status fetches and clear outstanding callback on this
|
|
* vnode */
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %p{%pIS}",
|
|
server, &server->addr.transport);
|
|
|
|
ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
|
|
false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
_debug("adjust");
|
|
afs_cache_permit(vnode, key, cb_break);
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
} else {
|
|
_debug("failed [%d]", ret);
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
}
|
|
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
|
|
_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* fetch file data from the volume
|
|
* - TODO implement caching
|
|
*/
|
|
int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
|
|
struct afs_read *desc)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x,,,",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key));
|
|
|
|
/* this op will fetch the status */
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt++;
|
|
spin_unlock(&vnode->lock);
|
|
|
|
/* merge in AFS status fetches and clear outstanding callback on this
|
|
* vnode */
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_fetch_data(server, key, vnode, desc,
|
|
false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
} else {
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
}
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* make a file or a directory
|
|
*/
|
|
int afs_vnode_create(struct afs_vnode *vnode, struct key *key,
|
|
const char *name, umode_t mode, struct afs_fid *newfid,
|
|
struct afs_file_status *newstatus,
|
|
struct afs_callback *newcb, struct afs_server **_server)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x,%s,,",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key),
|
|
name);
|
|
|
|
/* this op will fetch the status on the directory we're creating in */
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt++;
|
|
spin_unlock(&vnode->lock);
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_create(server, key, vnode, name, mode, newfid,
|
|
newstatus, newcb, false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
*_server = server;
|
|
} else {
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
*_server = NULL;
|
|
}
|
|
|
|
_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* remove a file or directory
|
|
*/
|
|
int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name,
|
|
bool isdir)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x,%s",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key),
|
|
name);
|
|
|
|
/* this op will fetch the status on the directory we're removing from */
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt++;
|
|
spin_unlock(&vnode->lock);
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_remove(server, key, vnode, name, isdir,
|
|
false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
} else {
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
}
|
|
|
|
_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* create a hard link
|
|
*/
|
|
int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
|
|
struct key *key, const char *name)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%s{%x:%u.%u},%x,%s",
|
|
dvnode->volume->vlocation->vldb.name,
|
|
dvnode->fid.vid,
|
|
dvnode->fid.vnode,
|
|
dvnode->fid.unique,
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key),
|
|
name);
|
|
|
|
/* this op will fetch the status on the directory we're removing from */
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt++;
|
|
spin_unlock(&vnode->lock);
|
|
spin_lock(&dvnode->lock);
|
|
dvnode->update_cnt++;
|
|
spin_unlock(&dvnode->lock);
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(dvnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_link(server, key, dvnode, vnode, name,
|
|
false);
|
|
|
|
} while (!afs_volume_release_fileserver(dvnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
afs_vnode_finalise_status_update(dvnode, server);
|
|
afs_put_server(afs_v2net(dvnode), server);
|
|
} else {
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
afs_vnode_status_update_failed(dvnode, ret);
|
|
}
|
|
|
|
_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
spin_lock(&dvnode->lock);
|
|
dvnode->update_cnt--;
|
|
ASSERTCMP(dvnode->update_cnt, >=, 0);
|
|
spin_unlock(&dvnode->lock);
|
|
_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* create a symbolic link
|
|
*/
|
|
int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key,
|
|
const char *name, const char *content,
|
|
struct afs_fid *newfid,
|
|
struct afs_file_status *newstatus,
|
|
struct afs_server **_server)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x,%s,%s,,,",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key),
|
|
name, content);
|
|
|
|
/* this op will fetch the status on the directory we're creating in */
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt++;
|
|
spin_unlock(&vnode->lock);
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_symlink(server, key, vnode, name, content,
|
|
newfid, newstatus, false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
*_server = server;
|
|
} else {
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
*_server = NULL;
|
|
}
|
|
|
|
_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* rename a file
|
|
*/
|
|
int afs_vnode_rename(struct afs_vnode *orig_dvnode,
|
|
struct afs_vnode *new_dvnode,
|
|
struct key *key,
|
|
const char *orig_name,
|
|
const char *new_name)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%s{%u,%u,%u},%x,%s,%s",
|
|
orig_dvnode->volume->vlocation->vldb.name,
|
|
orig_dvnode->fid.vid,
|
|
orig_dvnode->fid.vnode,
|
|
orig_dvnode->fid.unique,
|
|
new_dvnode->volume->vlocation->vldb.name,
|
|
new_dvnode->fid.vid,
|
|
new_dvnode->fid.vnode,
|
|
new_dvnode->fid.unique,
|
|
key_serial(key),
|
|
orig_name,
|
|
new_name);
|
|
|
|
/* this op will fetch the status on both the directories we're dealing
|
|
* with */
|
|
spin_lock(&orig_dvnode->lock);
|
|
orig_dvnode->update_cnt++;
|
|
spin_unlock(&orig_dvnode->lock);
|
|
if (new_dvnode != orig_dvnode) {
|
|
spin_lock(&new_dvnode->lock);
|
|
new_dvnode->update_cnt++;
|
|
spin_unlock(&new_dvnode->lock);
|
|
}
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(orig_dvnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_rename(server, key, orig_dvnode, orig_name,
|
|
new_dvnode, new_name, false);
|
|
|
|
} while (!afs_volume_release_fileserver(orig_dvnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(orig_dvnode, server);
|
|
if (new_dvnode != orig_dvnode)
|
|
afs_vnode_finalise_status_update(new_dvnode, server);
|
|
afs_put_server(afs_v2net(orig_dvnode), server);
|
|
} else {
|
|
afs_vnode_status_update_failed(orig_dvnode, ret);
|
|
if (new_dvnode != orig_dvnode)
|
|
afs_vnode_status_update_failed(new_dvnode, ret);
|
|
}
|
|
|
|
_leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&orig_dvnode->lock);
|
|
orig_dvnode->update_cnt--;
|
|
ASSERTCMP(orig_dvnode->update_cnt, >=, 0);
|
|
spin_unlock(&orig_dvnode->lock);
|
|
if (new_dvnode != orig_dvnode) {
|
|
spin_lock(&new_dvnode->lock);
|
|
new_dvnode->update_cnt--;
|
|
ASSERTCMP(new_dvnode->update_cnt, >=, 0);
|
|
spin_unlock(&new_dvnode->lock);
|
|
}
|
|
_leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* write to a file
|
|
*/
|
|
int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last,
|
|
unsigned offset, unsigned to)
|
|
{
|
|
struct afs_server *server;
|
|
struct afs_vnode *vnode = wb->vnode;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x,%lx,%lx,%x,%x",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(wb->key),
|
|
first, last, offset, to);
|
|
|
|
/* this op will fetch the status */
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt++;
|
|
spin_unlock(&vnode->lock);
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_store_data(server, wb, first, last, offset, to,
|
|
false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
} else {
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
}
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* set the attributes on a file
|
|
*/
|
|
int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key,
|
|
struct iattr *attr)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key));
|
|
|
|
/* this op will fetch the status */
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt++;
|
|
spin_unlock(&vnode->lock);
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_setattr(server, key, vnode, attr, false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0) {
|
|
afs_vnode_finalise_status_update(vnode, server);
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
} else {
|
|
afs_vnode_status_update_failed(vnode, ret);
|
|
}
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
|
|
no_server:
|
|
spin_lock(&vnode->lock);
|
|
vnode->update_cnt--;
|
|
ASSERTCMP(vnode->update_cnt, >=, 0);
|
|
spin_unlock(&vnode->lock);
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* get the status of a volume
|
|
*/
|
|
int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key,
|
|
struct afs_volume_status *vs)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x,",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key));
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_get_volume_status(server, key, vnode, vs, false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0)
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
|
|
no_server:
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* get a lock on a file
|
|
*/
|
|
int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key,
|
|
afs_lock_type_t type)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x,%u",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key), type);
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_set_lock(server, key, vnode, type, false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0)
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
|
|
no_server:
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* extend a lock on a file
|
|
*/
|
|
int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key));
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_extend_lock(server, key, vnode, false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0)
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
|
|
no_server:
|
|
return PTR_ERR(server);
|
|
}
|
|
|
|
/*
|
|
* release a lock on a file
|
|
*/
|
|
int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key)
|
|
{
|
|
struct afs_server *server;
|
|
int ret;
|
|
|
|
_enter("%s{%x:%u.%u},%x",
|
|
vnode->volume->vlocation->vldb.name,
|
|
vnode->fid.vid,
|
|
vnode->fid.vnode,
|
|
vnode->fid.unique,
|
|
key_serial(key));
|
|
|
|
do {
|
|
/* pick a server to query */
|
|
server = afs_volume_pick_fileserver(vnode);
|
|
if (IS_ERR(server))
|
|
goto no_server;
|
|
|
|
_debug("USING SERVER: %pIS\n", &server->addr.transport);
|
|
|
|
ret = afs_fs_release_lock(server, key, vnode, false);
|
|
|
|
} while (!afs_volume_release_fileserver(vnode, server, ret));
|
|
|
|
/* adjust the flags */
|
|
if (ret == 0)
|
|
afs_put_server(afs_v2net(vnode), server);
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
|
|
no_server:
|
|
return PTR_ERR(server);
|
|
}
|