Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
28
fs/afs/Makefile
Normal file
28
fs/afs/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Makefile for Red Hat Linux AFS client.
|
||||
#
|
||||
|
||||
#CFLAGS += -finstrument-functions
|
||||
|
||||
kafs-objs := \
|
||||
callback.o \
|
||||
cell.o \
|
||||
cmservice.o \
|
||||
dir.o \
|
||||
file.o \
|
||||
fsclient.o \
|
||||
inode.o \
|
||||
kafsasyncd.o \
|
||||
kafstimod.o \
|
||||
main.o \
|
||||
misc.o \
|
||||
mntpt.o \
|
||||
proc.o \
|
||||
server.o \
|
||||
super.o \
|
||||
vlclient.o \
|
||||
vlocation.o \
|
||||
vnode.o \
|
||||
volume.o
|
||||
|
||||
obj-$(CONFIG_AFS_FS) := kafs.o
|
27
fs/afs/cache.h
Normal file
27
fs/afs/cache.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* cache.h: AFS local cache management interface
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_CACHE_H
|
||||
#define _LINUX_AFS_CACHE_H
|
||||
|
||||
#undef AFS_CACHING_SUPPORT
|
||||
|
||||
#include <linux/mm.h>
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
#include <linux/cachefs.h>
|
||||
#endif
|
||||
#include "types.h"
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _LINUX_AFS_CACHE_H */
|
168
fs/afs/callback.c
Normal file
168
fs/afs/callback.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2002 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This software may be freely redistributed under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
|
||||
* David Howells <dhowells@redhat.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include "server.h"
|
||||
#include "vnode.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* allow the fileserver to request callback state (re-)initialisation
|
||||
*/
|
||||
int SRXAFSCM_InitCallBackState(struct afs_server *server)
|
||||
{
|
||||
struct list_head callbacks;
|
||||
|
||||
_enter("%p", server);
|
||||
|
||||
INIT_LIST_HEAD(&callbacks);
|
||||
|
||||
/* transfer the callback list from the server to a temp holding area */
|
||||
spin_lock(&server->cb_lock);
|
||||
|
||||
list_add(&callbacks, &server->cb_promises);
|
||||
list_del_init(&server->cb_promises);
|
||||
|
||||
/* munch our way through the list, grabbing the inode, dropping all the
|
||||
* locks and regetting them in the right order
|
||||
*/
|
||||
while (!list_empty(&callbacks)) {
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
|
||||
vnode = list_entry(callbacks.next, struct afs_vnode, cb_link);
|
||||
list_del_init(&vnode->cb_link);
|
||||
|
||||
/* try and grab the inode - may fail */
|
||||
inode = igrab(AFS_VNODE_TO_I(vnode));
|
||||
if (inode) {
|
||||
int release = 0;
|
||||
|
||||
spin_unlock(&server->cb_lock);
|
||||
spin_lock(&vnode->lock);
|
||||
|
||||
if (vnode->cb_server == server) {
|
||||
vnode->cb_server = NULL;
|
||||
afs_kafstimod_del_timer(&vnode->cb_timeout);
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
list_del_init(&vnode->cb_hash_link);
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
release = 1;
|
||||
}
|
||||
|
||||
spin_unlock(&vnode->lock);
|
||||
|
||||
iput(inode);
|
||||
afs_put_server(server);
|
||||
|
||||
spin_lock(&server->cb_lock);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&server->cb_lock);
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
} /* end SRXAFSCM_InitCallBackState() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* allow the fileserver to break callback promises
|
||||
*/
|
||||
int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
|
||||
struct afs_callback callbacks[])
|
||||
{
|
||||
_enter("%p,%u,", server, count);
|
||||
|
||||
for (; count > 0; callbacks++, count--) {
|
||||
struct afs_vnode *vnode = NULL;
|
||||
struct inode *inode = NULL;
|
||||
int valid = 0;
|
||||
|
||||
_debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
|
||||
callbacks->fid.vid,
|
||||
callbacks->fid.vnode,
|
||||
callbacks->fid.unique,
|
||||
callbacks->version,
|
||||
callbacks->expiry,
|
||||
callbacks->type
|
||||
);
|
||||
|
||||
/* find the inode for this fid */
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
|
||||
list_for_each_entry(vnode,
|
||||
&afs_cb_hash(server, &callbacks->fid),
|
||||
cb_hash_link) {
|
||||
if (memcmp(&vnode->fid, &callbacks->fid,
|
||||
sizeof(struct afs_fid)) != 0)
|
||||
continue;
|
||||
|
||||
/* right vnode, but is it same server? */
|
||||
if (vnode->cb_server != server)
|
||||
break; /* no */
|
||||
|
||||
/* try and nail the inode down */
|
||||
inode = igrab(AFS_VNODE_TO_I(vnode));
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
|
||||
if (inode) {
|
||||
/* we've found the record for this vnode */
|
||||
spin_lock(&vnode->lock);
|
||||
if (vnode->cb_server == server) {
|
||||
/* the callback _is_ on the calling server */
|
||||
vnode->cb_server = NULL;
|
||||
valid = 1;
|
||||
|
||||
afs_kafstimod_del_timer(&vnode->cb_timeout);
|
||||
vnode->flags |= AFS_VNODE_CHANGED;
|
||||
|
||||
spin_lock(&server->cb_lock);
|
||||
list_del_init(&vnode->cb_link);
|
||||
spin_unlock(&server->cb_lock);
|
||||
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
list_del_init(&vnode->cb_hash_link);
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
}
|
||||
spin_unlock(&vnode->lock);
|
||||
|
||||
if (valid) {
|
||||
invalidate_remote_inode(inode);
|
||||
afs_put_server(server);
|
||||
}
|
||||
iput(inode);
|
||||
}
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
} /* end SRXAFSCM_CallBack() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* allow the fileserver to see if the cache manager is still alive
|
||||
*/
|
||||
int SRXAFSCM_Probe(struct afs_server *server)
|
||||
{
|
||||
_debug("SRXAFSCM_Probe(%p)\n", server);
|
||||
return 0;
|
||||
} /* end SRXAFSCM_Probe() */
|
569
fs/afs/cell.c
Normal file
569
fs/afs/cell.c
Normal file
@@ -0,0 +1,569 @@
|
||||
/* cell.c: AFS cell and server record management
|
||||
*
|
||||
* Copyright (C) 2002 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/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <rxrpc/peer.h>
|
||||
#include <rxrpc/connection.h>
|
||||
#include "volume.h"
|
||||
#include "cell.h"
|
||||
#include "server.h"
|
||||
#include "transport.h"
|
||||
#include "vlclient.h"
|
||||
#include "kafstimod.h"
|
||||
#include "super.h"
|
||||
#include "internal.h"
|
||||
|
||||
DECLARE_RWSEM(afs_proc_cells_sem);
|
||||
LIST_HEAD(afs_proc_cells);
|
||||
|
||||
static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells);
|
||||
static DEFINE_RWLOCK(afs_cells_lock);
|
||||
static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
|
||||
static struct afs_cell *afs_cell_root;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_cell_cache_match(void *target,
|
||||
const void *entry);
|
||||
static void afs_cell_cache_update(void *source, void *entry);
|
||||
|
||||
struct cachefs_index_def afs_cache_cell_index_def = {
|
||||
.name = "cell_ix",
|
||||
.data_size = sizeof(struct afs_cache_cell),
|
||||
.keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
|
||||
.match = afs_cell_cache_match,
|
||||
.update = afs_cell_cache_update,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* create a cell record
|
||||
* - "name" is the name of the cell
|
||||
* - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
|
||||
*/
|
||||
int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
char *next;
|
||||
int ret;
|
||||
|
||||
_enter("%s", name);
|
||||
|
||||
BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
|
||||
|
||||
/* allocate and initialise a cell record */
|
||||
cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
|
||||
if (!cell) {
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
down_write(&afs_cells_sem);
|
||||
|
||||
memset(cell, 0, sizeof(struct afs_cell));
|
||||
atomic_set(&cell->usage, 0);
|
||||
|
||||
INIT_LIST_HEAD(&cell->link);
|
||||
|
||||
rwlock_init(&cell->sv_lock);
|
||||
INIT_LIST_HEAD(&cell->sv_list);
|
||||
INIT_LIST_HEAD(&cell->sv_graveyard);
|
||||
spin_lock_init(&cell->sv_gylock);
|
||||
|
||||
init_rwsem(&cell->vl_sem);
|
||||
INIT_LIST_HEAD(&cell->vl_list);
|
||||
INIT_LIST_HEAD(&cell->vl_graveyard);
|
||||
spin_lock_init(&cell->vl_gylock);
|
||||
|
||||
strcpy(cell->name,name);
|
||||
|
||||
/* fill in the VL server list from the rest of the string */
|
||||
ret = -EINVAL;
|
||||
do {
|
||||
unsigned a, b, c, d;
|
||||
|
||||
next = strchr(vllist, ':');
|
||||
if (next)
|
||||
*next++ = 0;
|
||||
|
||||
if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
|
||||
goto badaddr;
|
||||
|
||||
if (a > 255 || b > 255 || c > 255 || d > 255)
|
||||
goto badaddr;
|
||||
|
||||
cell->vl_addrs[cell->vl_naddrs++].s_addr =
|
||||
htonl((a << 24) | (b << 16) | (c << 8) | d);
|
||||
|
||||
if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS)
|
||||
break;
|
||||
|
||||
} while(vllist = next, vllist);
|
||||
|
||||
/* add a proc dir for this cell */
|
||||
ret = afs_proc_cell_setup(cell);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
/* put it up for caching */
|
||||
cachefs_acquire_cookie(afs_cache_netfs.primary_index,
|
||||
&afs_vlocation_cache_index_def,
|
||||
cell,
|
||||
&cell->cache);
|
||||
#endif
|
||||
|
||||
/* add to the cell lists */
|
||||
write_lock(&afs_cells_lock);
|
||||
list_add_tail(&cell->link, &afs_cells);
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
down_write(&afs_proc_cells_sem);
|
||||
list_add_tail(&cell->proc_link, &afs_proc_cells);
|
||||
up_write(&afs_proc_cells_sem);
|
||||
|
||||
*_cell = cell;
|
||||
up_write(&afs_cells_sem);
|
||||
|
||||
_leave(" = 0 (%p)", cell);
|
||||
return 0;
|
||||
|
||||
badaddr:
|
||||
printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist);
|
||||
error:
|
||||
up_write(&afs_cells_sem);
|
||||
kfree(cell);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_cell_create() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* initialise the cell database from module parameters
|
||||
*/
|
||||
int afs_cell_init(char *rootcell)
|
||||
{
|
||||
struct afs_cell *old_root, *new_root;
|
||||
char *cp;
|
||||
int ret;
|
||||
|
||||
_enter("");
|
||||
|
||||
if (!rootcell) {
|
||||
/* module is loaded with no parameters, or built statically.
|
||||
* - in the future we might initialize cell DB here.
|
||||
*/
|
||||
_leave(" = 0 (but no root)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cp = strchr(rootcell, ':');
|
||||
if (!cp) {
|
||||
printk(KERN_ERR "kAFS: no VL server IP addresses specified\n");
|
||||
_leave(" = %d (no colon)", -EINVAL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* allocate a cell record for the root cell */
|
||||
*cp++ = 0;
|
||||
ret = afs_cell_create(rootcell, cp, &new_root);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* as afs_put_cell() takes locks by itself, we have to do
|
||||
* a little gymnastics to be race-free.
|
||||
*/
|
||||
afs_get_cell(new_root);
|
||||
|
||||
write_lock(&afs_cells_lock);
|
||||
while (afs_cell_root) {
|
||||
old_root = afs_cell_root;
|
||||
afs_cell_root = NULL;
|
||||
write_unlock(&afs_cells_lock);
|
||||
afs_put_cell(old_root);
|
||||
write_lock(&afs_cells_lock);
|
||||
}
|
||||
afs_cell_root = new_root;
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_cell_init() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* lookup a cell record
|
||||
*/
|
||||
int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
int ret;
|
||||
|
||||
_enter("\"%*.*s\",", namesz, namesz, name ? name : "");
|
||||
|
||||
*_cell = NULL;
|
||||
|
||||
if (name) {
|
||||
/* if the cell was named, look for it in the cell record list */
|
||||
ret = -ENOENT;
|
||||
cell = NULL;
|
||||
read_lock(&afs_cells_lock);
|
||||
|
||||
list_for_each_entry(cell, &afs_cells, link) {
|
||||
if (strncmp(cell->name, name, namesz) == 0) {
|
||||
afs_get_cell(cell);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
cell = NULL;
|
||||
found:
|
||||
|
||||
read_unlock(&afs_cells_lock);
|
||||
|
||||
if (cell)
|
||||
ret = 0;
|
||||
}
|
||||
else {
|
||||
read_lock(&afs_cells_lock);
|
||||
|
||||
cell = afs_cell_root;
|
||||
if (!cell) {
|
||||
/* this should not happen unless user tries to mount
|
||||
* when root cell is not set. Return an impossibly
|
||||
* bizzare errno to alert the user. Things like
|
||||
* ENOENT might be "more appropriate" but they happen
|
||||
* for other reasons.
|
||||
*/
|
||||
ret = -EDESTADDRREQ;
|
||||
}
|
||||
else {
|
||||
afs_get_cell(cell);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
read_unlock(&afs_cells_lock);
|
||||
}
|
||||
|
||||
*_cell = cell;
|
||||
_leave(" = %d (%p)", ret, cell);
|
||||
return ret;
|
||||
|
||||
} /* end afs_cell_lookup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* try and get a cell record
|
||||
*/
|
||||
struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
|
||||
write_lock(&afs_cells_lock);
|
||||
|
||||
cell = *_cell;
|
||||
if (cell && !list_empty(&cell->link))
|
||||
afs_get_cell(cell);
|
||||
else
|
||||
cell = NULL;
|
||||
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
return cell;
|
||||
} /* end afs_get_cell_maybe() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* destroy a cell record
|
||||
*/
|
||||
void afs_put_cell(struct afs_cell *cell)
|
||||
{
|
||||
if (!cell)
|
||||
return;
|
||||
|
||||
_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
|
||||
|
||||
/* sanity check */
|
||||
BUG_ON(atomic_read(&cell->usage) <= 0);
|
||||
|
||||
/* to prevent a race, the decrement and the dequeue must be effectively
|
||||
* atomic */
|
||||
write_lock(&afs_cells_lock);
|
||||
|
||||
if (likely(!atomic_dec_and_test(&cell->usage))) {
|
||||
write_unlock(&afs_cells_lock);
|
||||
_leave("");
|
||||
return;
|
||||
}
|
||||
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
BUG_ON(!list_empty(&cell->sv_list));
|
||||
BUG_ON(!list_empty(&cell->sv_graveyard));
|
||||
BUG_ON(!list_empty(&cell->vl_list));
|
||||
BUG_ON(!list_empty(&cell->vl_graveyard));
|
||||
|
||||
_leave(" [unused]");
|
||||
} /* end afs_put_cell() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* destroy a cell record
|
||||
*/
|
||||
static void afs_cell_destroy(struct afs_cell *cell)
|
||||
{
|
||||
_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
|
||||
|
||||
/* to prevent a race, the decrement and the dequeue must be effectively
|
||||
* atomic */
|
||||
write_lock(&afs_cells_lock);
|
||||
|
||||
/* sanity check */
|
||||
BUG_ON(atomic_read(&cell->usage) != 0);
|
||||
|
||||
list_del_init(&cell->link);
|
||||
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
down_write(&afs_cells_sem);
|
||||
|
||||
afs_proc_cell_remove(cell);
|
||||
|
||||
down_write(&afs_proc_cells_sem);
|
||||
list_del_init(&cell->proc_link);
|
||||
up_write(&afs_proc_cells_sem);
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_relinquish_cookie(cell->cache, 0);
|
||||
#endif
|
||||
|
||||
up_write(&afs_cells_sem);
|
||||
|
||||
BUG_ON(!list_empty(&cell->sv_list));
|
||||
BUG_ON(!list_empty(&cell->sv_graveyard));
|
||||
BUG_ON(!list_empty(&cell->vl_list));
|
||||
BUG_ON(!list_empty(&cell->vl_graveyard));
|
||||
|
||||
/* finish cleaning up the cell */
|
||||
kfree(cell);
|
||||
|
||||
_leave(" [destroyed]");
|
||||
} /* end afs_cell_destroy() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* lookup the server record corresponding to an Rx RPC peer
|
||||
*/
|
||||
int afs_server_find_by_peer(const struct rxrpc_peer *peer,
|
||||
struct afs_server **_server)
|
||||
{
|
||||
struct afs_server *server;
|
||||
struct afs_cell *cell;
|
||||
|
||||
_enter("%p{a=%08x},", peer, ntohl(peer->addr.s_addr));
|
||||
|
||||
/* search the cell list */
|
||||
read_lock(&afs_cells_lock);
|
||||
|
||||
list_for_each_entry(cell, &afs_cells, link) {
|
||||
|
||||
_debug("? cell %s",cell->name);
|
||||
|
||||
write_lock(&cell->sv_lock);
|
||||
|
||||
/* check the active list */
|
||||
list_for_each_entry(server, &cell->sv_list, link) {
|
||||
_debug("?? server %08x", ntohl(server->addr.s_addr));
|
||||
|
||||
if (memcmp(&server->addr, &peer->addr,
|
||||
sizeof(struct in_addr)) == 0)
|
||||
goto found_server;
|
||||
}
|
||||
|
||||
/* check the inactive list */
|
||||
spin_lock(&cell->sv_gylock);
|
||||
list_for_each_entry(server, &cell->sv_graveyard, link) {
|
||||
_debug("?? dead server %08x",
|
||||
ntohl(server->addr.s_addr));
|
||||
|
||||
if (memcmp(&server->addr, &peer->addr,
|
||||
sizeof(struct in_addr)) == 0)
|
||||
goto found_dead_server;
|
||||
}
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
|
||||
write_unlock(&cell->sv_lock);
|
||||
}
|
||||
read_unlock(&afs_cells_lock);
|
||||
|
||||
_leave(" = -ENOENT");
|
||||
return -ENOENT;
|
||||
|
||||
/* we found it in the graveyard - resurrect it */
|
||||
found_dead_server:
|
||||
list_del(&server->link);
|
||||
list_add_tail(&server->link, &cell->sv_list);
|
||||
afs_get_server(server);
|
||||
afs_kafstimod_del_timer(&server->timeout);
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
goto success;
|
||||
|
||||
/* we found it - increment its ref count and return it */
|
||||
found_server:
|
||||
afs_get_server(server);
|
||||
|
||||
success:
|
||||
write_unlock(&cell->sv_lock);
|
||||
read_unlock(&afs_cells_lock);
|
||||
|
||||
*_server = server;
|
||||
_leave(" = 0 (s=%p c=%p)", server, cell);
|
||||
return 0;
|
||||
|
||||
} /* end afs_server_find_by_peer() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* purge in-memory cell database on module unload or afs_init() failure
|
||||
* - the timeout daemon is stopped before calling this
|
||||
*/
|
||||
void afs_cell_purge(void)
|
||||
{
|
||||
struct afs_vlocation *vlocation;
|
||||
struct afs_cell *cell;
|
||||
|
||||
_enter("");
|
||||
|
||||
afs_put_cell(afs_cell_root);
|
||||
|
||||
while (!list_empty(&afs_cells)) {
|
||||
cell = NULL;
|
||||
|
||||
/* remove the next cell from the front of the list */
|
||||
write_lock(&afs_cells_lock);
|
||||
|
||||
if (!list_empty(&afs_cells)) {
|
||||
cell = list_entry(afs_cells.next,
|
||||
struct afs_cell, link);
|
||||
list_del_init(&cell->link);
|
||||
}
|
||||
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
if (cell) {
|
||||
_debug("PURGING CELL %s (%d)",
|
||||
cell->name, atomic_read(&cell->usage));
|
||||
|
||||
BUG_ON(!list_empty(&cell->sv_list));
|
||||
BUG_ON(!list_empty(&cell->vl_list));
|
||||
|
||||
/* purge the cell's VL graveyard list */
|
||||
_debug(" - clearing VL graveyard");
|
||||
|
||||
spin_lock(&cell->vl_gylock);
|
||||
|
||||
while (!list_empty(&cell->vl_graveyard)) {
|
||||
vlocation = list_entry(cell->vl_graveyard.next,
|
||||
struct afs_vlocation,
|
||||
link);
|
||||
list_del_init(&vlocation->link);
|
||||
|
||||
afs_kafstimod_del_timer(&vlocation->timeout);
|
||||
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
afs_vlocation_do_timeout(vlocation);
|
||||
/* TODO: race if move to use krxtimod instead
|
||||
* of kafstimod */
|
||||
|
||||
spin_lock(&cell->vl_gylock);
|
||||
}
|
||||
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
/* purge the cell's server graveyard list */
|
||||
_debug(" - clearing server graveyard");
|
||||
|
||||
spin_lock(&cell->sv_gylock);
|
||||
|
||||
while (!list_empty(&cell->sv_graveyard)) {
|
||||
struct afs_server *server;
|
||||
|
||||
server = list_entry(cell->sv_graveyard.next,
|
||||
struct afs_server, link);
|
||||
list_del_init(&server->link);
|
||||
|
||||
afs_kafstimod_del_timer(&server->timeout);
|
||||
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
|
||||
afs_server_do_timeout(server);
|
||||
|
||||
spin_lock(&cell->sv_gylock);
|
||||
}
|
||||
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
|
||||
/* now the cell should be left with no references */
|
||||
afs_cell_destroy(cell);
|
||||
}
|
||||
}
|
||||
|
||||
_leave("");
|
||||
} /* end afs_cell_purge() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* match a cell record obtained from the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_cell_cache_match(void *target,
|
||||
const void *entry)
|
||||
{
|
||||
const struct afs_cache_cell *ccell = entry;
|
||||
struct afs_cell *cell = target;
|
||||
|
||||
_enter("{%s},{%s}", ccell->name, cell->name);
|
||||
|
||||
if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) {
|
||||
_leave(" = SUCCESS");
|
||||
return CACHEFS_MATCH_SUCCESS;
|
||||
}
|
||||
|
||||
_leave(" = FAILED");
|
||||
return CACHEFS_MATCH_FAILED;
|
||||
} /* end afs_cell_cache_match() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* update a cell record in the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static void afs_cell_cache_update(void *source, void *entry)
|
||||
{
|
||||
struct afs_cache_cell *ccell = entry;
|
||||
struct afs_cell *cell = source;
|
||||
|
||||
_enter("%p,%p", source, entry);
|
||||
|
||||
strncpy(ccell->name, cell->name, sizeof(ccell->name));
|
||||
|
||||
memcpy(ccell->vl_servers,
|
||||
cell->vl_addrs,
|
||||
min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs)));
|
||||
|
||||
} /* end afs_cell_cache_update() */
|
||||
#endif
|
78
fs/afs/cell.h
Normal file
78
fs/afs/cell.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* cell.h: AFS cell record
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_CELL_H
|
||||
#define _LINUX_AFS_CELL_H
|
||||
|
||||
#include "types.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define AFS_CELL_MAX_ADDRS 15
|
||||
|
||||
extern volatile int afs_cells_being_purged; /* T when cells are being purged by rmmod */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* entry in the cached cell catalogue
|
||||
*/
|
||||
struct afs_cache_cell
|
||||
{
|
||||
char name[64]; /* cell name (padded with NULs) */
|
||||
struct in_addr vl_servers[15]; /* cached cell VL servers */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS cell record
|
||||
*/
|
||||
struct afs_cell
|
||||
{
|
||||
atomic_t usage;
|
||||
struct list_head link; /* main cell list link */
|
||||
struct list_head proc_link; /* /proc cell list link */
|
||||
struct proc_dir_entry *proc_dir; /* /proc dir for this cell */
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct cachefs_cookie *cache; /* caching cookie */
|
||||
#endif
|
||||
|
||||
/* server record management */
|
||||
rwlock_t sv_lock; /* active server list lock */
|
||||
struct list_head sv_list; /* active server list */
|
||||
struct list_head sv_graveyard; /* inactive server list */
|
||||
spinlock_t sv_gylock; /* inactive server list lock */
|
||||
|
||||
/* volume location record management */
|
||||
struct rw_semaphore vl_sem; /* volume management serialisation semaphore */
|
||||
struct list_head vl_list; /* cell's active VL record list */
|
||||
struct list_head vl_graveyard; /* cell's inactive VL record list */
|
||||
spinlock_t vl_gylock; /* graveyard lock */
|
||||
unsigned short vl_naddrs; /* number of VL servers in addr list */
|
||||
unsigned short vl_curr_svix; /* current server index */
|
||||
struct in_addr vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */
|
||||
|
||||
char name[0]; /* cell name - must go last */
|
||||
};
|
||||
|
||||
extern int afs_cell_init(char *rootcell);
|
||||
|
||||
extern int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell);
|
||||
|
||||
extern int afs_cell_lookup(const char *name, unsigned nmsize, struct afs_cell **_cell);
|
||||
|
||||
#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
|
||||
|
||||
extern struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell);
|
||||
|
||||
extern void afs_put_cell(struct afs_cell *cell);
|
||||
|
||||
extern void afs_cell_purge(void);
|
||||
|
||||
#endif /* _LINUX_AFS_CELL_H */
|
652
fs/afs/cmservice.c
Normal file
652
fs/afs/cmservice.c
Normal file
@@ -0,0 +1,652 @@
|
||||
/* cmservice.c: AFS Cache Manager Service
|
||||
*
|
||||
* Copyright (C) 2002 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/completion.h>
|
||||
#include "server.h"
|
||||
#include "cell.h"
|
||||
#include "transport.h"
|
||||
#include <rxrpc/rxrpc.h>
|
||||
#include <rxrpc/transport.h>
|
||||
#include <rxrpc/connection.h>
|
||||
#include <rxrpc/call.h>
|
||||
#include "cmservice.h"
|
||||
#include "internal.h"
|
||||
|
||||
static unsigned afscm_usage; /* AFS cache manager usage count */
|
||||
static struct rw_semaphore afscm_sem; /* AFS cache manager start/stop semaphore */
|
||||
|
||||
static int afscm_new_call(struct rxrpc_call *call);
|
||||
static void afscm_attention(struct rxrpc_call *call);
|
||||
static void afscm_error(struct rxrpc_call *call);
|
||||
static void afscm_aemap(struct rxrpc_call *call);
|
||||
|
||||
static void _SRXAFSCM_CallBack(struct rxrpc_call *call);
|
||||
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call);
|
||||
static void _SRXAFSCM_Probe(struct rxrpc_call *call);
|
||||
|
||||
typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call);
|
||||
|
||||
static const struct rxrpc_operation AFSCM_ops[] = {
|
||||
{
|
||||
.id = 204,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "CallBack",
|
||||
.user = _SRXAFSCM_CallBack,
|
||||
},
|
||||
{
|
||||
.id = 205,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "InitCallBackState",
|
||||
.user = _SRXAFSCM_InitCallBackState,
|
||||
},
|
||||
{
|
||||
.id = 206,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "Probe",
|
||||
.user = _SRXAFSCM_Probe,
|
||||
},
|
||||
#if 0
|
||||
{
|
||||
.id = 207,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetLock",
|
||||
.user = _SRXAFSCM_GetLock,
|
||||
},
|
||||
{
|
||||
.id = 208,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetCE",
|
||||
.user = _SRXAFSCM_GetCE,
|
||||
},
|
||||
{
|
||||
.id = 209,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetXStatsVersion",
|
||||
.user = _SRXAFSCM_GetXStatsVersion,
|
||||
},
|
||||
{
|
||||
.id = 210,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetXStats",
|
||||
.user = _SRXAFSCM_GetXStats,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct rxrpc_service AFSCM_service = {
|
||||
.name = "AFS/CM",
|
||||
.owner = THIS_MODULE,
|
||||
.link = LIST_HEAD_INIT(AFSCM_service.link),
|
||||
.new_call = afscm_new_call,
|
||||
.service_id = 1,
|
||||
.attn_func = afscm_attention,
|
||||
.error_func = afscm_error,
|
||||
.aemap_func = afscm_aemap,
|
||||
.ops_begin = &AFSCM_ops[0],
|
||||
.ops_end = &AFSCM_ops[sizeof(AFSCM_ops) / sizeof(AFSCM_ops[0])],
|
||||
};
|
||||
|
||||
static DECLARE_COMPLETION(kafscmd_alive);
|
||||
static DECLARE_COMPLETION(kafscmd_dead);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq);
|
||||
static LIST_HEAD(kafscmd_attention_list);
|
||||
static LIST_HEAD(afscm_calls);
|
||||
static DEFINE_SPINLOCK(afscm_calls_lock);
|
||||
static DEFINE_SPINLOCK(kafscmd_attention_lock);
|
||||
static int kafscmd_die;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS Cache Manager kernel thread
|
||||
*/
|
||||
static int kafscmd(void *arg)
|
||||
{
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
struct rxrpc_call *call;
|
||||
_SRXAFSCM_xxxx_t func;
|
||||
int die;
|
||||
|
||||
printk("kAFS: Started kafscmd %d\n", current->pid);
|
||||
|
||||
daemonize("kafscmd");
|
||||
|
||||
complete(&kafscmd_alive);
|
||||
|
||||
/* loop around looking for things to attend to */
|
||||
do {
|
||||
if (list_empty(&kafscmd_attention_list)) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&kafscmd_sleepq, &myself);
|
||||
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (!list_empty(&kafscmd_attention_list) ||
|
||||
signal_pending(current) ||
|
||||
kafscmd_die)
|
||||
break;
|
||||
|
||||
schedule();
|
||||
}
|
||||
|
||||
remove_wait_queue(&kafscmd_sleepq, &myself);
|
||||
set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
die = kafscmd_die;
|
||||
|
||||
/* dequeue the next call requiring attention */
|
||||
call = NULL;
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
|
||||
if (!list_empty(&kafscmd_attention_list)) {
|
||||
call = list_entry(kafscmd_attention_list.next,
|
||||
struct rxrpc_call,
|
||||
app_attn_link);
|
||||
list_del_init(&call->app_attn_link);
|
||||
die = 0;
|
||||
}
|
||||
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
if (call) {
|
||||
/* act upon it */
|
||||
_debug("@@@ Begin Attend Call %p", call);
|
||||
|
||||
func = call->app_user;
|
||||
if (func)
|
||||
func(call);
|
||||
|
||||
rxrpc_put_call(call);
|
||||
|
||||
_debug("@@@ End Attend Call %p", call);
|
||||
}
|
||||
|
||||
} while(!die);
|
||||
|
||||
/* and that's all */
|
||||
complete_and_exit(&kafscmd_dead, 0);
|
||||
|
||||
} /* end kafscmd() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle a call coming in to the cache manager
|
||||
* - if I want to keep the call, I must increment its usage count
|
||||
* - the return value will be negated and passed back in an abort packet if
|
||||
* non-zero
|
||||
* - serialised by virtue of there only being one krxiod
|
||||
*/
|
||||
static int afscm_new_call(struct rxrpc_call *call)
|
||||
{
|
||||
_enter("%p{cid=%u u=%d}",
|
||||
call, ntohl(call->call_id), atomic_read(&call->usage));
|
||||
|
||||
rxrpc_get_call(call);
|
||||
|
||||
/* add to my current call list */
|
||||
spin_lock(&afscm_calls_lock);
|
||||
list_add(&call->app_link,&afscm_calls);
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
} /* end afscm_new_call() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* queue on the kafscmd queue for attention
|
||||
*/
|
||||
static void afscm_attention(struct rxrpc_call *call)
|
||||
{
|
||||
_enter("%p{cid=%u u=%d}",
|
||||
call, ntohl(call->call_id), atomic_read(&call->usage));
|
||||
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
|
||||
if (list_empty(&call->app_attn_link)) {
|
||||
list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
|
||||
rxrpc_get_call(call);
|
||||
}
|
||||
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
wake_up(&kafscmd_sleepq);
|
||||
|
||||
_leave(" {u=%d}", atomic_read(&call->usage));
|
||||
} /* end afscm_attention() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle my call being aborted
|
||||
* - clean up, dequeue and put my ref to the call
|
||||
*/
|
||||
static void afscm_error(struct rxrpc_call *call)
|
||||
{
|
||||
int removed;
|
||||
|
||||
_enter("%p{est=%s ac=%u er=%d}",
|
||||
call,
|
||||
rxrpc_call_error_states[call->app_err_state],
|
||||
call->app_abort_code,
|
||||
call->app_errno);
|
||||
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
|
||||
if (list_empty(&call->app_attn_link)) {
|
||||
list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
|
||||
rxrpc_get_call(call);
|
||||
}
|
||||
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
removed = 0;
|
||||
spin_lock(&afscm_calls_lock);
|
||||
if (!list_empty(&call->app_link)) {
|
||||
list_del_init(&call->app_link);
|
||||
removed = 1;
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
if (removed)
|
||||
rxrpc_put_call(call);
|
||||
|
||||
wake_up(&kafscmd_sleepq);
|
||||
|
||||
_leave("");
|
||||
} /* end afscm_error() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* map afs abort codes to/from Linux error codes
|
||||
* - called with call->lock held
|
||||
*/
|
||||
static void afscm_aemap(struct rxrpc_call *call)
|
||||
{
|
||||
switch (call->app_err_state) {
|
||||
case RXRPC_ESTATE_LOCAL_ABORT:
|
||||
call->app_abort_code = -call->app_errno;
|
||||
break;
|
||||
case RXRPC_ESTATE_PEER_ABORT:
|
||||
call->app_errno = -ECONNABORTED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} /* end afscm_aemap() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* start the cache manager service if not already started
|
||||
*/
|
||||
int afscm_start(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
down_write(&afscm_sem);
|
||||
if (!afscm_usage) {
|
||||
ret = kernel_thread(kafscmd, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
wait_for_completion(&kafscmd_alive);
|
||||
|
||||
ret = rxrpc_add_service(afs_transport, &AFSCM_service);
|
||||
if (ret < 0)
|
||||
goto kill;
|
||||
|
||||
afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
|
||||
afs_mntpt_expiry_timeout * HZ);
|
||||
}
|
||||
|
||||
afscm_usage++;
|
||||
up_write(&afscm_sem);
|
||||
|
||||
return 0;
|
||||
|
||||
kill:
|
||||
kafscmd_die = 1;
|
||||
wake_up(&kafscmd_sleepq);
|
||||
wait_for_completion(&kafscmd_dead);
|
||||
|
||||
out:
|
||||
up_write(&afscm_sem);
|
||||
return ret;
|
||||
|
||||
} /* end afscm_start() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* stop the cache manager service
|
||||
*/
|
||||
void afscm_stop(void)
|
||||
{
|
||||
struct rxrpc_call *call;
|
||||
|
||||
down_write(&afscm_sem);
|
||||
|
||||
BUG_ON(afscm_usage == 0);
|
||||
afscm_usage--;
|
||||
|
||||
if (afscm_usage == 0) {
|
||||
/* don't want more incoming calls */
|
||||
rxrpc_del_service(afs_transport, &AFSCM_service);
|
||||
|
||||
/* abort any calls I've still got open (the afscm_error() will
|
||||
* dequeue them) */
|
||||
spin_lock(&afscm_calls_lock);
|
||||
while (!list_empty(&afscm_calls)) {
|
||||
call = list_entry(afscm_calls.next,
|
||||
struct rxrpc_call,
|
||||
app_link);
|
||||
|
||||
list_del_init(&call->app_link);
|
||||
rxrpc_get_call(call);
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and
|
||||
* put */
|
||||
|
||||
_debug("nuking active call %08x.%d",
|
||||
ntohl(call->conn->conn_id),
|
||||
ntohl(call->call_id));
|
||||
rxrpc_put_call(call);
|
||||
rxrpc_put_call(call);
|
||||
|
||||
spin_lock(&afscm_calls_lock);
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
/* get rid of my daemon */
|
||||
kafscmd_die = 1;
|
||||
wake_up(&kafscmd_sleepq);
|
||||
wait_for_completion(&kafscmd_dead);
|
||||
|
||||
/* dispose of any calls waiting for attention */
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
while (!list_empty(&kafscmd_attention_list)) {
|
||||
call = list_entry(kafscmd_attention_list.next,
|
||||
struct rxrpc_call,
|
||||
app_attn_link);
|
||||
|
||||
list_del_init(&call->app_attn_link);
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
rxrpc_put_call(call);
|
||||
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
}
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
afs_kafstimod_del_timer(&afs_mntpt_expiry_timer);
|
||||
}
|
||||
|
||||
up_write(&afscm_sem);
|
||||
|
||||
} /* end afscm_stop() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle the fileserver breaking a set of callbacks
|
||||
*/
|
||||
static void _SRXAFSCM_CallBack(struct rxrpc_call *call)
|
||||
{
|
||||
struct afs_server *server;
|
||||
size_t count, qty, tmp;
|
||||
int ret = 0, removed;
|
||||
|
||||
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
|
||||
|
||||
server = afs_server_get_from_peer(call->conn->peer);
|
||||
|
||||
switch (call->app_call_state) {
|
||||
/* we've received the last packet
|
||||
* - drain all the data from the call and send the reply
|
||||
*/
|
||||
case RXRPC_CSTATE_SRVR_GOT_ARGS:
|
||||
ret = -EBADMSG;
|
||||
qty = call->app_ready_qty;
|
||||
if (qty < 8 || qty > 50 * (6 * 4) + 8)
|
||||
break;
|
||||
|
||||
{
|
||||
struct afs_callback *cb, *pcb;
|
||||
int loop;
|
||||
__be32 *fp, *bp;
|
||||
|
||||
fp = rxrpc_call_alloc_scratch(call, qty);
|
||||
|
||||
/* drag the entire argument block out to the scratch
|
||||
* space */
|
||||
ret = rxrpc_call_read_data(call, fp, qty, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/* and unmarshall the parameter block */
|
||||
ret = -EBADMSG;
|
||||
count = ntohl(*fp++);
|
||||
if (count>AFSCBMAX ||
|
||||
(count * (3 * 4) + 8 != qty &&
|
||||
count * (6 * 4) + 8 != qty))
|
||||
break;
|
||||
|
||||
bp = fp + count*3;
|
||||
tmp = ntohl(*bp++);
|
||||
if (tmp > 0 && tmp != count)
|
||||
break;
|
||||
if (tmp == 0)
|
||||
bp = NULL;
|
||||
|
||||
pcb = cb = rxrpc_call_alloc_scratch_s(
|
||||
call, struct afs_callback);
|
||||
|
||||
for (loop = count - 1; loop >= 0; loop--) {
|
||||
pcb->fid.vid = ntohl(*fp++);
|
||||
pcb->fid.vnode = ntohl(*fp++);
|
||||
pcb->fid.unique = ntohl(*fp++);
|
||||
if (bp) {
|
||||
pcb->version = ntohl(*bp++);
|
||||
pcb->expiry = ntohl(*bp++);
|
||||
pcb->type = ntohl(*bp++);
|
||||
}
|
||||
else {
|
||||
pcb->version = 0;
|
||||
pcb->expiry = 0;
|
||||
pcb->type = AFSCM_CB_UNTYPED;
|
||||
}
|
||||
pcb++;
|
||||
}
|
||||
|
||||
/* invoke the actual service routine */
|
||||
ret = SRXAFSCM_CallBack(server, count, cb);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* send the reply */
|
||||
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
|
||||
GFP_KERNEL, 0, &count);
|
||||
if (ret < 0)
|
||||
break;
|
||||
break;
|
||||
|
||||
/* operation complete */
|
||||
case RXRPC_CSTATE_COMPLETE:
|
||||
call->app_user = NULL;
|
||||
removed = 0;
|
||||
spin_lock(&afscm_calls_lock);
|
||||
if (!list_empty(&call->app_link)) {
|
||||
list_del_init(&call->app_link);
|
||||
removed = 1;
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
if (removed)
|
||||
rxrpc_put_call(call);
|
||||
break;
|
||||
|
||||
/* operation terminated on error */
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
call->app_user = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
rxrpc_call_abort(call, ret);
|
||||
|
||||
afs_put_server(server);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
|
||||
} /* end _SRXAFSCM_CallBack() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle the fileserver asking us to initialise our callback state
|
||||
*/
|
||||
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call)
|
||||
{
|
||||
struct afs_server *server;
|
||||
size_t count;
|
||||
int ret = 0, removed;
|
||||
|
||||
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
|
||||
|
||||
server = afs_server_get_from_peer(call->conn->peer);
|
||||
|
||||
switch (call->app_call_state) {
|
||||
/* we've received the last packet - drain all the data from the
|
||||
* call */
|
||||
case RXRPC_CSTATE_SRVR_GOT_ARGS:
|
||||
/* shouldn't be any args */
|
||||
ret = -EBADMSG;
|
||||
break;
|
||||
|
||||
/* send the reply when asked for it */
|
||||
case RXRPC_CSTATE_SRVR_SND_REPLY:
|
||||
/* invoke the actual service routine */
|
||||
ret = SRXAFSCM_InitCallBackState(server);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
|
||||
GFP_KERNEL, 0, &count);
|
||||
if (ret < 0)
|
||||
break;
|
||||
break;
|
||||
|
||||
/* operation complete */
|
||||
case RXRPC_CSTATE_COMPLETE:
|
||||
call->app_user = NULL;
|
||||
removed = 0;
|
||||
spin_lock(&afscm_calls_lock);
|
||||
if (!list_empty(&call->app_link)) {
|
||||
list_del_init(&call->app_link);
|
||||
removed = 1;
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
if (removed)
|
||||
rxrpc_put_call(call);
|
||||
break;
|
||||
|
||||
/* operation terminated on error */
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
call->app_user = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
rxrpc_call_abort(call, ret);
|
||||
|
||||
afs_put_server(server);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
|
||||
} /* end _SRXAFSCM_InitCallBackState() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle a probe from a fileserver
|
||||
*/
|
||||
static void _SRXAFSCM_Probe(struct rxrpc_call *call)
|
||||
{
|
||||
struct afs_server *server;
|
||||
size_t count;
|
||||
int ret = 0, removed;
|
||||
|
||||
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
|
||||
|
||||
server = afs_server_get_from_peer(call->conn->peer);
|
||||
|
||||
switch (call->app_call_state) {
|
||||
/* we've received the last packet - drain all the data from the
|
||||
* call */
|
||||
case RXRPC_CSTATE_SRVR_GOT_ARGS:
|
||||
/* shouldn't be any args */
|
||||
ret = -EBADMSG;
|
||||
break;
|
||||
|
||||
/* send the reply when asked for it */
|
||||
case RXRPC_CSTATE_SRVR_SND_REPLY:
|
||||
/* invoke the actual service routine */
|
||||
ret = SRXAFSCM_Probe(server);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
|
||||
GFP_KERNEL, 0, &count);
|
||||
if (ret < 0)
|
||||
break;
|
||||
break;
|
||||
|
||||
/* operation complete */
|
||||
case RXRPC_CSTATE_COMPLETE:
|
||||
call->app_user = NULL;
|
||||
removed = 0;
|
||||
spin_lock(&afscm_calls_lock);
|
||||
if (!list_empty(&call->app_link)) {
|
||||
list_del_init(&call->app_link);
|
||||
removed = 1;
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
if (removed)
|
||||
rxrpc_put_call(call);
|
||||
break;
|
||||
|
||||
/* operation terminated on error */
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
call->app_user = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
rxrpc_call_abort(call, ret);
|
||||
|
||||
afs_put_server(server);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
|
||||
} /* end _SRXAFSCM_Probe() */
|
29
fs/afs/cmservice.h
Normal file
29
fs/afs/cmservice.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* cmservice.h: AFS Cache Manager Service declarations
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_CMSERVICE_H
|
||||
#define _LINUX_AFS_CMSERVICE_H
|
||||
|
||||
#include <rxrpc/transport.h>
|
||||
#include "types.h"
|
||||
|
||||
/* cache manager start/stop */
|
||||
extern int afscm_start(void);
|
||||
extern void afscm_stop(void);
|
||||
|
||||
/* cache manager server functions */
|
||||
extern int SRXAFSCM_InitCallBackState(struct afs_server *server);
|
||||
extern int SRXAFSCM_CallBack(struct afs_server *server,
|
||||
size_t count,
|
||||
struct afs_callback callbacks[]);
|
||||
extern int SRXAFSCM_Probe(struct afs_server *server);
|
||||
|
||||
#endif /* _LINUX_AFS_CMSERVICE_H */
|
666
fs/afs/dir.c
Normal file
666
fs/afs/dir.c
Normal file
@@ -0,0 +1,666 @@
|
||||
/* dir.c: AFS filesystem directory handling
|
||||
*
|
||||
* Copyright (C) 2002 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/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include "vnode.h"
|
||||
#include "volume.h"
|
||||
#include <rxrpc/call.h>
|
||||
#include "super.h"
|
||||
#include "internal.h"
|
||||
|
||||
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct nameidata *nd);
|
||||
static int afs_dir_open(struct inode *inode, struct file *file);
|
||||
static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir);
|
||||
static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
|
||||
static int afs_d_delete(struct dentry *dentry);
|
||||
static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
|
||||
loff_t fpos, ino_t ino, unsigned dtype);
|
||||
|
||||
struct file_operations afs_dir_file_operations = {
|
||||
.open = afs_dir_open,
|
||||
.readdir = afs_dir_readdir,
|
||||
};
|
||||
|
||||
struct inode_operations afs_dir_inode_operations = {
|
||||
.lookup = afs_dir_lookup,
|
||||
.getattr = afs_inode_getattr,
|
||||
#if 0 /* TODO */
|
||||
.create = afs_dir_create,
|
||||
.link = afs_dir_link,
|
||||
.unlink = afs_dir_unlink,
|
||||
.symlink = afs_dir_symlink,
|
||||
.mkdir = afs_dir_mkdir,
|
||||
.rmdir = afs_dir_rmdir,
|
||||
.mknod = afs_dir_mknod,
|
||||
.rename = afs_dir_rename,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct dentry_operations afs_fs_dentry_operations = {
|
||||
.d_revalidate = afs_d_revalidate,
|
||||
.d_delete = afs_d_delete,
|
||||
};
|
||||
|
||||
#define AFS_DIR_HASHTBL_SIZE 128
|
||||
#define AFS_DIR_DIRENT_SIZE 32
|
||||
#define AFS_DIRENT_PER_BLOCK 64
|
||||
|
||||
union afs_dirent {
|
||||
struct {
|
||||
uint8_t valid;
|
||||
uint8_t unused[1];
|
||||
__be16 hash_next;
|
||||
__be32 vnode;
|
||||
__be32 unique;
|
||||
uint8_t name[16];
|
||||
uint8_t overflow[4]; /* if any char of the name (inc
|
||||
* NUL) reaches here, consume
|
||||
* the next dirent too */
|
||||
} u;
|
||||
uint8_t extended_name[32];
|
||||
};
|
||||
|
||||
/* AFS directory page header (one at the beginning of every 2048-byte chunk) */
|
||||
struct afs_dir_pagehdr {
|
||||
__be16 npages;
|
||||
__be16 magic;
|
||||
#define AFS_DIR_MAGIC htons(1234)
|
||||
uint8_t nentries;
|
||||
uint8_t bitmap[8];
|
||||
uint8_t pad[19];
|
||||
};
|
||||
|
||||
/* directory block layout */
|
||||
union afs_dir_block {
|
||||
|
||||
struct afs_dir_pagehdr pagehdr;
|
||||
|
||||
struct {
|
||||
struct afs_dir_pagehdr pagehdr;
|
||||
uint8_t alloc_ctrs[128];
|
||||
/* dir hash table */
|
||||
uint16_t hashtable[AFS_DIR_HASHTBL_SIZE];
|
||||
} hdr;
|
||||
|
||||
union afs_dirent dirents[AFS_DIRENT_PER_BLOCK];
|
||||
};
|
||||
|
||||
/* layout on a linux VM page */
|
||||
struct afs_dir_page {
|
||||
union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)];
|
||||
};
|
||||
|
||||
struct afs_dir_lookup_cookie {
|
||||
struct afs_fid fid;
|
||||
const char *name;
|
||||
size_t nlen;
|
||||
int found;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* check that a directory page is valid
|
||||
*/
|
||||
static inline void afs_dir_check_page(struct inode *dir, struct page *page)
|
||||
{
|
||||
struct afs_dir_page *dbuf;
|
||||
loff_t latter;
|
||||
int tmp, qty;
|
||||
|
||||
#if 0
|
||||
/* check the page count */
|
||||
qty = desc.size / sizeof(dbuf->blocks[0]);
|
||||
if (qty == 0)
|
||||
goto error;
|
||||
|
||||
if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) {
|
||||
printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",
|
||||
__FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages));
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* determine how many magic numbers there should be in this page */
|
||||
latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT);
|
||||
if (latter >= PAGE_SIZE)
|
||||
qty = PAGE_SIZE;
|
||||
else
|
||||
qty = latter;
|
||||
qty /= sizeof(union afs_dir_block);
|
||||
|
||||
/* check them */
|
||||
dbuf = page_address(page);
|
||||
for (tmp = 0; tmp < qty; tmp++) {
|
||||
if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) {
|
||||
printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n",
|
||||
__FUNCTION__, dir->i_ino, tmp, qty,
|
||||
ntohs(dbuf->blocks[tmp].pagehdr.magic));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
SetPageChecked(page);
|
||||
return;
|
||||
|
||||
error:
|
||||
SetPageChecked(page);
|
||||
SetPageError(page);
|
||||
|
||||
} /* end afs_dir_check_page() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* discard a page cached in the pagecache
|
||||
*/
|
||||
static inline void afs_dir_put_page(struct page *page)
|
||||
{
|
||||
kunmap(page);
|
||||
page_cache_release(page);
|
||||
|
||||
} /* end afs_dir_put_page() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* get a page into the pagecache
|
||||
*/
|
||||
static struct page *afs_dir_get_page(struct inode *dir, unsigned long index)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
_enter("{%lu},%lu", dir->i_ino, index);
|
||||
|
||||
page = read_cache_page(dir->i_mapping,index,
|
||||
(filler_t *) dir->i_mapping->a_ops->readpage,
|
||||
NULL);
|
||||
if (!IS_ERR(page)) {
|
||||
wait_on_page_locked(page);
|
||||
kmap(page);
|
||||
if (!PageUptodate(page))
|
||||
goto fail;
|
||||
if (!PageChecked(page))
|
||||
afs_dir_check_page(dir, page);
|
||||
if (PageError(page))
|
||||
goto fail;
|
||||
}
|
||||
return page;
|
||||
|
||||
fail:
|
||||
afs_dir_put_page(page);
|
||||
return ERR_PTR(-EIO);
|
||||
} /* end afs_dir_get_page() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* open an AFS directory file
|
||||
*/
|
||||
static int afs_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
_enter("{%lu}", inode->i_ino);
|
||||
|
||||
BUG_ON(sizeof(union afs_dir_block) != 2048);
|
||||
BUG_ON(sizeof(union afs_dirent) != 32);
|
||||
|
||||
if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED)
|
||||
return -ENOENT;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
} /* end afs_dir_open() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* deal with one block in an AFS directory
|
||||
*/
|
||||
static int afs_dir_iterate_block(unsigned *fpos,
|
||||
union afs_dir_block *block,
|
||||
unsigned blkoff,
|
||||
void *cookie,
|
||||
filldir_t filldir)
|
||||
{
|
||||
union afs_dirent *dire;
|
||||
unsigned offset, next, curr;
|
||||
size_t nlen;
|
||||
int tmp, ret;
|
||||
|
||||
_enter("%u,%x,%p,,",*fpos,blkoff,block);
|
||||
|
||||
curr = (*fpos - blkoff) / sizeof(union afs_dirent);
|
||||
|
||||
/* walk through the block, an entry at a time */
|
||||
for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries;
|
||||
offset < AFS_DIRENT_PER_BLOCK;
|
||||
offset = next
|
||||
) {
|
||||
next = offset + 1;
|
||||
|
||||
/* skip entries marked unused in the bitmap */
|
||||
if (!(block->pagehdr.bitmap[offset / 8] &
|
||||
(1 << (offset % 8)))) {
|
||||
_debug("ENT[%Zu.%u]: unused\n",
|
||||
blkoff / sizeof(union afs_dir_block), offset);
|
||||
if (offset >= curr)
|
||||
*fpos = blkoff +
|
||||
next * sizeof(union afs_dirent);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* got a valid entry */
|
||||
dire = &block->dirents[offset];
|
||||
nlen = strnlen(dire->u.name,
|
||||
sizeof(*block) -
|
||||
offset * sizeof(union afs_dirent));
|
||||
|
||||
_debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n",
|
||||
blkoff / sizeof(union afs_dir_block), offset,
|
||||
(offset < curr ? "skip" : "fill"),
|
||||
nlen, dire->u.name);
|
||||
|
||||
/* work out where the next possible entry is */
|
||||
for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) {
|
||||
if (next >= AFS_DIRENT_PER_BLOCK) {
|
||||
_debug("ENT[%Zu.%u]:"
|
||||
" %u travelled beyond end dir block"
|
||||
" (len %u/%Zu)\n",
|
||||
blkoff / sizeof(union afs_dir_block),
|
||||
offset, next, tmp, nlen);
|
||||
return -EIO;
|
||||
}
|
||||
if (!(block->pagehdr.bitmap[next / 8] &
|
||||
(1 << (next % 8)))) {
|
||||
_debug("ENT[%Zu.%u]:"
|
||||
" %u unmarked extension (len %u/%Zu)\n",
|
||||
blkoff / sizeof(union afs_dir_block),
|
||||
offset, next, tmp, nlen);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
_debug("ENT[%Zu.%u]: ext %u/%Zu\n",
|
||||
blkoff / sizeof(union afs_dir_block),
|
||||
next, tmp, nlen);
|
||||
next++;
|
||||
}
|
||||
|
||||
/* skip if starts before the current position */
|
||||
if (offset < curr)
|
||||
continue;
|
||||
|
||||
/* found the next entry */
|
||||
ret = filldir(cookie,
|
||||
dire->u.name,
|
||||
nlen,
|
||||
blkoff + offset * sizeof(union afs_dirent),
|
||||
ntohl(dire->u.vnode),
|
||||
filldir == afs_dir_lookup_filldir ?
|
||||
ntohl(dire->u.unique) : DT_UNKNOWN);
|
||||
if (ret < 0) {
|
||||
_leave(" = 0 [full]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*fpos = blkoff + next * sizeof(union afs_dirent);
|
||||
}
|
||||
|
||||
_leave(" = 1 [more]");
|
||||
return 1;
|
||||
} /* end afs_dir_iterate_block() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* read an AFS directory
|
||||
*/
|
||||
static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
|
||||
filldir_t filldir)
|
||||
{
|
||||
union afs_dir_block *dblock;
|
||||
struct afs_dir_page *dbuf;
|
||||
struct page *page;
|
||||
unsigned blkoff, limit;
|
||||
int ret;
|
||||
|
||||
_enter("{%lu},%u,,", dir->i_ino, *fpos);
|
||||
|
||||
if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
|
||||
_leave(" = -ESTALE");
|
||||
return -ESTALE;
|
||||
}
|
||||
|
||||
/* round the file position up to the next entry boundary */
|
||||
*fpos += sizeof(union afs_dirent) - 1;
|
||||
*fpos &= ~(sizeof(union afs_dirent) - 1);
|
||||
|
||||
/* walk through the blocks in sequence */
|
||||
ret = 0;
|
||||
while (*fpos < dir->i_size) {
|
||||
blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1);
|
||||
|
||||
/* fetch the appropriate page from the directory */
|
||||
page = afs_dir_get_page(dir, blkoff / PAGE_SIZE);
|
||||
if (IS_ERR(page)) {
|
||||
ret = PTR_ERR(page);
|
||||
break;
|
||||
}
|
||||
|
||||
limit = blkoff & ~(PAGE_SIZE - 1);
|
||||
|
||||
dbuf = page_address(page);
|
||||
|
||||
/* deal with the individual blocks stashed on this page */
|
||||
do {
|
||||
dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) /
|
||||
sizeof(union afs_dir_block)];
|
||||
ret = afs_dir_iterate_block(fpos, dblock, blkoff,
|
||||
cookie, filldir);
|
||||
if (ret != 1) {
|
||||
afs_dir_put_page(page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
blkoff += sizeof(union afs_dir_block);
|
||||
|
||||
} while (*fpos < dir->i_size && blkoff < limit);
|
||||
|
||||
afs_dir_put_page(page);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_dir_iterate() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* read an AFS directory
|
||||
*/
|
||||
static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
|
||||
{
|
||||
unsigned fpos;
|
||||
int ret;
|
||||
|
||||
_enter("{%Ld,{%lu}}", file->f_pos, file->f_dentry->d_inode->i_ino);
|
||||
|
||||
fpos = file->f_pos;
|
||||
ret = afs_dir_iterate(file->f_dentry->d_inode, &fpos, cookie, filldir);
|
||||
file->f_pos = fpos;
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_dir_readdir() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* search the directory for a name
|
||||
* - if afs_dir_iterate_block() spots this function, it'll pass the FID
|
||||
* uniquifier through dtype
|
||||
*/
|
||||
static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
|
||||
loff_t fpos, ino_t ino, unsigned dtype)
|
||||
{
|
||||
struct afs_dir_lookup_cookie *cookie = _cookie;
|
||||
|
||||
_enter("{%s,%Zu},%s,%u,,%lu,%u",
|
||||
cookie->name, cookie->nlen, name, nlen, ino, dtype);
|
||||
|
||||
if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) {
|
||||
_leave(" = 0 [no]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cookie->fid.vnode = ino;
|
||||
cookie->fid.unique = dtype;
|
||||
cookie->found = 1;
|
||||
|
||||
_leave(" = -1 [found]");
|
||||
return -1;
|
||||
} /* end afs_dir_lookup_filldir() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* look up an entry in a directory
|
||||
*/
|
||||
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
struct afs_dir_lookup_cookie cookie;
|
||||
struct afs_super_info *as;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
unsigned fpos;
|
||||
int ret;
|
||||
|
||||
_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);
|
||||
|
||||
/* insanity checks first */
|
||||
BUG_ON(sizeof(union afs_dir_block) != 2048);
|
||||
BUG_ON(sizeof(union afs_dirent) != 32);
|
||||
|
||||
if (dentry->d_name.len > 255) {
|
||||
_leave(" = -ENAMETOOLONG");
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
}
|
||||
|
||||
vnode = AFS_FS_I(dir);
|
||||
if (vnode->flags & AFS_VNODE_DELETED) {
|
||||
_leave(" = -ESTALE");
|
||||
return ERR_PTR(-ESTALE);
|
||||
}
|
||||
|
||||
as = dir->i_sb->s_fs_info;
|
||||
|
||||
/* search the directory */
|
||||
cookie.name = dentry->d_name.name;
|
||||
cookie.nlen = dentry->d_name.len;
|
||||
cookie.fid.vid = as->volume->vid;
|
||||
cookie.found = 0;
|
||||
|
||||
fpos = 0;
|
||||
ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = -ENOENT;
|
||||
if (!cookie.found) {
|
||||
_leave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* instantiate the dentry */
|
||||
ret = afs_iget(dir->i_sb, &cookie.fid, &inode);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
dentry->d_op = &afs_fs_dentry_operations;
|
||||
dentry->d_fsdata = (void *) (unsigned long) vnode->status.version;
|
||||
|
||||
d_add(dentry, inode);
|
||||
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }",
|
||||
cookie.fid.vnode,
|
||||
cookie.fid.unique,
|
||||
dentry->d_inode->i_ino,
|
||||
dentry->d_inode->i_version);
|
||||
|
||||
return NULL;
|
||||
} /* end afs_dir_lookup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* check that a dentry lookup hit has found a valid entry
|
||||
* - NOTE! the hit can be a negative hit too, so we can't assume we have an
|
||||
* inode
|
||||
* (derived from nfs_lookup_revalidate)
|
||||
*/
|
||||
static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct afs_dir_lookup_cookie cookie;
|
||||
struct dentry *parent;
|
||||
struct inode *inode, *dir;
|
||||
unsigned fpos;
|
||||
int ret;
|
||||
|
||||
_enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name);
|
||||
|
||||
/* lock down the parent dentry so we can peer at it */
|
||||
parent = dget_parent(dentry->d_parent);
|
||||
|
||||
dir = parent->d_inode;
|
||||
inode = dentry->d_inode;
|
||||
|
||||
/* handle a negative dentry */
|
||||
if (!inode)
|
||||
goto out_bad;
|
||||
|
||||
/* handle a bad inode */
|
||||
if (is_bad_inode(inode)) {
|
||||
printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
/* force a full look up if the parent directory changed since last the
|
||||
* server was consulted
|
||||
* - otherwise this inode must still exist, even if the inode details
|
||||
* themselves have changed
|
||||
*/
|
||||
if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED)
|
||||
afs_vnode_fetch_status(AFS_FS_I(dir));
|
||||
|
||||
if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
|
||||
_debug("%s: parent dir deleted", dentry->d_name.name);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) {
|
||||
_debug("%s: file already deleted", dentry->d_name.name);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if ((unsigned long) dentry->d_fsdata !=
|
||||
(unsigned long) AFS_FS_I(dir)->status.version) {
|
||||
_debug("%s: parent changed %lu -> %u",
|
||||
dentry->d_name.name,
|
||||
(unsigned long) dentry->d_fsdata,
|
||||
(unsigned) AFS_FS_I(dir)->status.version);
|
||||
|
||||
/* search the directory for this vnode */
|
||||
cookie.name = dentry->d_name.name;
|
||||
cookie.nlen = dentry->d_name.len;
|
||||
cookie.fid.vid = AFS_FS_I(inode)->volume->vid;
|
||||
cookie.found = 0;
|
||||
|
||||
fpos = 0;
|
||||
ret = afs_dir_iterate(dir, &fpos, &cookie,
|
||||
afs_dir_lookup_filldir);
|
||||
if (ret < 0) {
|
||||
_debug("failed to iterate dir %s: %d",
|
||||
parent->d_name.name, ret);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if (!cookie.found) {
|
||||
_debug("%s: dirent not found", dentry->d_name.name);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
/* if the vnode ID has changed, then the dirent points to a
|
||||
* different file */
|
||||
if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) {
|
||||
_debug("%s: dirent changed", dentry->d_name.name);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
/* if the vnode ID uniqifier has changed, then the file has
|
||||
* been deleted */
|
||||
if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) {
|
||||
_debug("%s: file deleted (uq %u -> %u I:%lu)",
|
||||
dentry->d_name.name,
|
||||
cookie.fid.unique,
|
||||
AFS_FS_I(inode)->fid.unique,
|
||||
inode->i_version);
|
||||
spin_lock(&AFS_FS_I(inode)->lock);
|
||||
AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED;
|
||||
spin_unlock(&AFS_FS_I(inode)->lock);
|
||||
invalidate_remote_inode(inode);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
dentry->d_fsdata =
|
||||
(void *) (unsigned long) AFS_FS_I(dir)->status.version;
|
||||
}
|
||||
|
||||
out_valid:
|
||||
dput(parent);
|
||||
_leave(" = 1 [valid]");
|
||||
return 1;
|
||||
|
||||
/* the dirent, if it exists, now points to a different vnode */
|
||||
not_found:
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_NFSFS_RENAMED;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
out_bad:
|
||||
if (inode) {
|
||||
/* don't unhash if we have submounts */
|
||||
if (have_submounts(dentry))
|
||||
goto out_valid;
|
||||
}
|
||||
|
||||
shrink_dcache_parent(dentry);
|
||||
|
||||
_debug("dropping dentry %s/%s",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
d_drop(dentry);
|
||||
|
||||
dput(parent);
|
||||
|
||||
_leave(" = 0 [bad]");
|
||||
return 0;
|
||||
} /* end afs_d_revalidate() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
|
||||
* sleep)
|
||||
* - called from dput() when d_count is going to 0.
|
||||
* - return 1 to request dentry be unhashed, 0 otherwise
|
||||
*/
|
||||
static int afs_d_delete(struct dentry *dentry)
|
||||
{
|
||||
_enter("%s", dentry->d_name.name);
|
||||
|
||||
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
|
||||
goto zap;
|
||||
|
||||
if (dentry->d_inode) {
|
||||
if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED)
|
||||
goto zap;
|
||||
}
|
||||
|
||||
_leave(" = 0 [keep]");
|
||||
return 0;
|
||||
|
||||
zap:
|
||||
_leave(" = 1 [zap]");
|
||||
return 1;
|
||||
} /* end afs_d_delete() */
|
34
fs/afs/errors.h
Normal file
34
fs/afs/errors.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* errors.h: AFS abort/error codes
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_ERRORS_H
|
||||
#define _LINUX_AFS_ERRORS_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* file server abort codes */
|
||||
typedef enum {
|
||||
VSALVAGE = 101, /* volume needs salvaging */
|
||||
VNOVNODE = 102, /* no such file/dir (vnode) */
|
||||
VNOVOL = 103, /* no such volume or volume unavailable */
|
||||
VVOLEXISTS = 104, /* volume name already exists */
|
||||
VNOSERVICE = 105, /* volume not currently in service */
|
||||
VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */
|
||||
VONLINE = 107, /* volume is already online */
|
||||
VDISKFULL = 108, /* disk partition is full */
|
||||
VOVERQUOTA = 109, /* volume's maximum quota exceeded */
|
||||
VBUSY = 110, /* volume is temporarily unavailable */
|
||||
VMOVED = 111, /* volume moved to new server - ask this FS where */
|
||||
} afs_rxfs_abort_t;
|
||||
|
||||
extern int afs_abort_to_error(int abortcode);
|
||||
|
||||
#endif /* _LINUX_AFS_ERRORS_H */
|
305
fs/afs/file.c
Normal file
305
fs/afs/file.c
Normal file
@@ -0,0 +1,305 @@
|
||||
/* file.c: AFS filesystem file handling
|
||||
*
|
||||
* Copyright (C) 2002 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/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include "volume.h"
|
||||
#include "vnode.h"
|
||||
#include <rxrpc/call.h>
|
||||
#include "internal.h"
|
||||
|
||||
#if 0
|
||||
static int afs_file_open(struct inode *inode, struct file *file);
|
||||
static int afs_file_release(struct inode *inode, struct file *file);
|
||||
#endif
|
||||
|
||||
static int afs_file_readpage(struct file *file, struct page *page);
|
||||
static int afs_file_invalidatepage(struct page *page, unsigned long offset);
|
||||
static int afs_file_releasepage(struct page *page, int gfp_flags);
|
||||
|
||||
static ssize_t afs_file_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off);
|
||||
|
||||
struct inode_operations afs_file_inode_operations = {
|
||||
.getattr = afs_inode_getattr,
|
||||
};
|
||||
|
||||
struct file_operations afs_file_file_operations = {
|
||||
.read = generic_file_read,
|
||||
.write = afs_file_write,
|
||||
.mmap = generic_file_mmap,
|
||||
#if 0
|
||||
.open = afs_file_open,
|
||||
.release = afs_file_release,
|
||||
.fsync = afs_file_fsync,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct address_space_operations afs_fs_aops = {
|
||||
.readpage = afs_file_readpage,
|
||||
.sync_page = block_sync_page,
|
||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
||||
.releasepage = afs_file_releasepage,
|
||||
.invalidatepage = afs_file_invalidatepage,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS file write
|
||||
*/
|
||||
static ssize_t afs_file_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
|
||||
vnode = AFS_FS_I(file->f_dentry->d_inode);
|
||||
if (vnode->flags & AFS_VNODE_DELETED)
|
||||
return -ESTALE;
|
||||
|
||||
return -EIO;
|
||||
} /* end afs_file_write() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* deal with notification that a page was read from the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static void afs_file_readpage_read_complete(void *cookie_data,
|
||||
struct page *page,
|
||||
void *data,
|
||||
int error)
|
||||
{
|
||||
_enter("%p,%p,%p,%d", cookie_data, page, data, error);
|
||||
|
||||
if (error)
|
||||
SetPageError(page);
|
||||
else
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
|
||||
} /* end afs_file_readpage_read_complete() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* deal with notification that a page was written to the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static void afs_file_readpage_write_complete(void *cookie_data,
|
||||
struct page *page,
|
||||
void *data,
|
||||
int error)
|
||||
{
|
||||
_enter("%p,%p,%p,%d", cookie_data, page, data, error);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
} /* end afs_file_readpage_write_complete() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS read page from file (or symlink)
|
||||
*/
|
||||
static int afs_file_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
struct afs_rxfs_fetch_descriptor desc;
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct cachefs_page *pageio;
|
||||
#endif
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
inode = page->mapping->host;
|
||||
|
||||
_enter("{%lu},{%lu}", inode->i_ino, page->index);
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
if (!PageLocked(page))
|
||||
PAGE_BUG(page);
|
||||
|
||||
ret = -ESTALE;
|
||||
if (vnode->flags & AFS_VNODE_DELETED)
|
||||
goto error;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* is it cached? */
|
||||
ret = cachefs_read_or_alloc_page(vnode->cache,
|
||||
page,
|
||||
afs_file_readpage_read_complete,
|
||||
NULL,
|
||||
GFP_KERNEL);
|
||||
#else
|
||||
ret = -ENOBUFS;
|
||||
#endif
|
||||
|
||||
switch (ret) {
|
||||
/* read BIO submitted and wb-journal entry found */
|
||||
case 1:
|
||||
BUG(); // TODO - handle wb-journal match
|
||||
|
||||
/* read BIO submitted (page in cache) */
|
||||
case 0:
|
||||
break;
|
||||
|
||||
/* no page available in cache */
|
||||
case -ENOBUFS:
|
||||
case -ENODATA:
|
||||
default:
|
||||
desc.fid = vnode->fid;
|
||||
desc.offset = page->index << PAGE_CACHE_SHIFT;
|
||||
desc.size = min((size_t) (inode->i_size - desc.offset),
|
||||
(size_t) PAGE_SIZE);
|
||||
desc.buffer = kmap(page);
|
||||
|
||||
clear_page(desc.buffer);
|
||||
|
||||
/* read the contents of the file from the server into the
|
||||
* page */
|
||||
ret = afs_vnode_fetch_data(vnode, &desc);
|
||||
kunmap(page);
|
||||
if (ret < 0) {
|
||||
if (ret==-ENOENT) {
|
||||
_debug("got NOENT from server"
|
||||
" - marking file deleted and stale");
|
||||
vnode->flags |= AFS_VNODE_DELETED;
|
||||
ret = -ESTALE;
|
||||
}
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_uncache_page(vnode->cache, page);
|
||||
#endif
|
||||
goto error;
|
||||
}
|
||||
|
||||
SetPageUptodate(page);
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
if (cachefs_write_page(vnode->cache,
|
||||
page,
|
||||
afs_file_readpage_write_complete,
|
||||
NULL,
|
||||
GFP_KERNEL) != 0
|
||||
) {
|
||||
cachefs_uncache_page(vnode->cache, page);
|
||||
unlock_page(page);
|
||||
}
|
||||
#else
|
||||
unlock_page(page);
|
||||
#endif
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
SetPageError(page);
|
||||
unlock_page(page);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_file_readpage() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* get a page cookie for the specified page
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
int afs_cache_get_page_cookie(struct page *page,
|
||||
struct cachefs_page **_page_cookie)
|
||||
{
|
||||
int ret;
|
||||
|
||||
_enter("");
|
||||
ret = cachefs_page_get_private(page,_page_cookie, GFP_NOIO);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_cache_get_page_cookie() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* invalidate part or all of a page
|
||||
*/
|
||||
static int afs_file_invalidatepage(struct page *page, unsigned long offset)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
_enter("{%lu},%lu", page->index, offset);
|
||||
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
if (PagePrivate(page)) {
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
|
||||
cachefs_uncache_page(vnode->cache,page);
|
||||
#endif
|
||||
|
||||
/* We release buffers only if the entire page is being
|
||||
* invalidated.
|
||||
* The get_block cached value has been unconditionally
|
||||
* invalidated, so real IO is not possible anymore.
|
||||
*/
|
||||
if (offset == 0) {
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
ret = 0;
|
||||
if (!PageWriteback(page))
|
||||
ret = page->mapping->a_ops->releasepage(page,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_file_invalidatepage() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* release a page and cleanup its private data
|
||||
*/
|
||||
static int afs_file_releasepage(struct page *page, int gfp_flags)
|
||||
{
|
||||
struct cachefs_page *pageio;
|
||||
|
||||
_enter("{%lu},%x", page->index, gfp_flags);
|
||||
|
||||
if (PagePrivate(page)) {
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
|
||||
cachefs_uncache_page(vnode->cache, page);
|
||||
#endif
|
||||
|
||||
pageio = (struct cachefs_page *) page->private;
|
||||
page->private = 0;
|
||||
ClearPagePrivate(page);
|
||||
|
||||
if (pageio)
|
||||
kfree(pageio);
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
} /* end afs_file_releasepage() */
|
837
fs/afs/fsclient.c
Normal file
837
fs/afs/fsclient.c
Normal file
@@ -0,0 +1,837 @@
|
||||
/* fsclient.c: AFS File Server client stubs
|
||||
*
|
||||
* Copyright (C) 2002 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/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <rxrpc/rxrpc.h>
|
||||
#include <rxrpc/transport.h>
|
||||
#include <rxrpc/connection.h>
|
||||
#include <rxrpc/call.h>
|
||||
#include "fsclient.h"
|
||||
#include "cmservice.h"
|
||||
#include "vnode.h"
|
||||
#include "server.h"
|
||||
#include "errors.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define FSFETCHSTATUS 132 /* AFS Fetch file status */
|
||||
#define FSFETCHDATA 130 /* AFS Fetch file data */
|
||||
#define FSGIVEUPCALLBACKS 147 /* AFS Discard callback promises */
|
||||
#define FSGETVOLUMEINFO 148 /* AFS Get root volume information */
|
||||
#define FSGETROOTVOLUME 151 /* AFS Get root volume name */
|
||||
#define FSLOOKUP 161 /* AFS lookup file in directory */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* map afs abort codes to/from Linux error codes
|
||||
* - called with call->lock held
|
||||
*/
|
||||
static void afs_rxfs_aemap(struct rxrpc_call *call)
|
||||
{
|
||||
switch (call->app_err_state) {
|
||||
case RXRPC_ESTATE_LOCAL_ABORT:
|
||||
call->app_abort_code = -call->app_errno;
|
||||
break;
|
||||
case RXRPC_ESTATE_PEER_ABORT:
|
||||
call->app_errno = afs_abort_to_error(call->app_abort_code);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} /* end afs_rxfs_aemap() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* get the root volume name from a fileserver
|
||||
* - this operation doesn't seem to work correctly in OpenAFS server 1.2.2
|
||||
*/
|
||||
#if 0
|
||||
int afs_rxfs_get_root_volume(struct afs_server *server,
|
||||
char *buf, size_t *buflen)
|
||||
{
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[2];
|
||||
size_t sent;
|
||||
int ret;
|
||||
u32 param[1];
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
kenter("%p,%p,%u",server, buf, *buflen);
|
||||
|
||||
/* get hold of the fileserver connection */
|
||||
ret = afs_server_get_fsconn(server, &conn);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = FSGETROOTVOLUME;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
param[0] = htonl(FSGETROOTVOLUME);
|
||||
|
||||
piov[0].iov_len = sizeof(param);
|
||||
piov[0].iov_base = param;
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY ||
|
||||
signal_pending(current))
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
ret = -EINTR;
|
||||
if (signal_pending(current))
|
||||
goto abort;
|
||||
|
||||
switch (call->app_call_state) {
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
ret = call->app_errno;
|
||||
kdebug("Got Error: %d", ret);
|
||||
goto out_unwait;
|
||||
|
||||
case RXRPC_CSTATE_CLNT_GOT_REPLY:
|
||||
/* read the reply */
|
||||
kdebug("Got Reply: qty=%d", call->app_ready_qty);
|
||||
|
||||
ret = -EBADMSG;
|
||||
if (call->app_ready_qty <= 4)
|
||||
goto abort;
|
||||
|
||||
ret = rxrpc_call_read_data(call, NULL, call->app_ready_qty, 0);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
#if 0
|
||||
/* unmarshall the reply */
|
||||
bp = buffer;
|
||||
for (loop = 0; loop < 65; loop++)
|
||||
entry->name[loop] = ntohl(*bp++);
|
||||
entry->name[64] = 0;
|
||||
|
||||
entry->type = ntohl(*bp++);
|
||||
entry->num_servers = ntohl(*bp++);
|
||||
|
||||
for (loop = 0; loop < 8; loop++)
|
||||
entry->servers[loop].addr.s_addr = *bp++;
|
||||
|
||||
for (loop = 0; loop < 8; loop++)
|
||||
entry->servers[loop].partition = ntohl(*bp++);
|
||||
|
||||
for (loop = 0; loop < 8; loop++)
|
||||
entry->servers[loop].flags = ntohl(*bp++);
|
||||
|
||||
for (loop = 0; loop < 3; loop++)
|
||||
entry->volume_ids[loop] = ntohl(*bp++);
|
||||
|
||||
entry->clone_id = ntohl(*bp++);
|
||||
entry->flags = ntohl(*bp);
|
||||
#endif
|
||||
|
||||
/* success */
|
||||
ret = 0;
|
||||
goto out_unwait;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
afs_server_release_fsconn(server, conn);
|
||||
out:
|
||||
kleave("");
|
||||
return ret;
|
||||
} /* end afs_rxfs_get_root_volume() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* get information about a volume
|
||||
*/
|
||||
#if 0
|
||||
int afs_rxfs_get_volume_info(struct afs_server *server,
|
||||
const char *name,
|
||||
struct afs_volume_info *vinfo)
|
||||
{
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[3];
|
||||
size_t sent;
|
||||
int ret;
|
||||
u32 param[2], *bp, zero;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
_enter("%p,%s,%p", server, name, vinfo);
|
||||
|
||||
/* get hold of the fileserver connection */
|
||||
ret = afs_server_get_fsconn(server, &conn);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = FSGETVOLUMEINFO;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
piov[1].iov_len = strlen(name);
|
||||
piov[1].iov_base = (char *) name;
|
||||
|
||||
zero = 0;
|
||||
piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3;
|
||||
piov[2].iov_base = &zero;
|
||||
|
||||
param[0] = htonl(FSGETVOLUMEINFO);
|
||||
param[1] = htonl(piov[1].iov_len);
|
||||
|
||||
piov[0].iov_len = sizeof(param);
|
||||
piov[0].iov_base = param;
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
bp = rxrpc_call_alloc_scratch(call, 64);
|
||||
|
||||
ret = rxrpc_call_read_data(call, bp, 64,
|
||||
RXRPC_CALL_READ_BLOCK |
|
||||
RXRPC_CALL_READ_ALL);
|
||||
if (ret < 0) {
|
||||
if (ret == -ECONNABORTED) {
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
}
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* unmarshall the reply */
|
||||
vinfo->vid = ntohl(*bp++);
|
||||
vinfo->type = ntohl(*bp++);
|
||||
|
||||
vinfo->type_vids[0] = ntohl(*bp++);
|
||||
vinfo->type_vids[1] = ntohl(*bp++);
|
||||
vinfo->type_vids[2] = ntohl(*bp++);
|
||||
vinfo->type_vids[3] = ntohl(*bp++);
|
||||
vinfo->type_vids[4] = ntohl(*bp++);
|
||||
|
||||
vinfo->nservers = ntohl(*bp++);
|
||||
vinfo->servers[0].addr.s_addr = *bp++;
|
||||
vinfo->servers[1].addr.s_addr = *bp++;
|
||||
vinfo->servers[2].addr.s_addr = *bp++;
|
||||
vinfo->servers[3].addr.s_addr = *bp++;
|
||||
vinfo->servers[4].addr.s_addr = *bp++;
|
||||
vinfo->servers[5].addr.s_addr = *bp++;
|
||||
vinfo->servers[6].addr.s_addr = *bp++;
|
||||
vinfo->servers[7].addr.s_addr = *bp++;
|
||||
|
||||
ret = -EBADMSG;
|
||||
if (vinfo->nservers > 8)
|
||||
goto abort;
|
||||
|
||||
/* success */
|
||||
ret = 0;
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
afs_server_release_fsconn(server, conn);
|
||||
out:
|
||||
_leave("");
|
||||
return ret;
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
goto out_unwait;
|
||||
|
||||
} /* end afs_rxfs_get_volume_info() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* fetch the status information for a file
|
||||
*/
|
||||
int afs_rxfs_fetch_file_status(struct afs_server *server,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_volsync *volsync)
|
||||
{
|
||||
struct afs_server_callslot callslot;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[1];
|
||||
size_t sent;
|
||||
int ret;
|
||||
__be32 *bp;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
_enter("%p,{%u,%u,%u}",
|
||||
server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
|
||||
|
||||
/* get hold of the fileserver connection */
|
||||
ret = afs_server_request_callslot(server, &callslot);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap,
|
||||
&call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = FSFETCHSTATUS;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = rxrpc_call_alloc_scratch(call, 16);
|
||||
bp[0] = htonl(FSFETCHSTATUS);
|
||||
bp[1] = htonl(vnode->fid.vid);
|
||||
bp[2] = htonl(vnode->fid.vnode);
|
||||
bp[3] = htonl(vnode->fid.unique);
|
||||
|
||||
piov[0].iov_len = 16;
|
||||
piov[0].iov_base = bp;
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
bp = rxrpc_call_alloc_scratch(call, 120);
|
||||
|
||||
ret = rxrpc_call_read_data(call, bp, 120,
|
||||
RXRPC_CALL_READ_BLOCK |
|
||||
RXRPC_CALL_READ_ALL);
|
||||
if (ret < 0) {
|
||||
if (ret == -ECONNABORTED) {
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
}
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* unmarshall the reply */
|
||||
vnode->status.if_version = ntohl(*bp++);
|
||||
vnode->status.type = ntohl(*bp++);
|
||||
vnode->status.nlink = ntohl(*bp++);
|
||||
vnode->status.size = ntohl(*bp++);
|
||||
vnode->status.version = ntohl(*bp++);
|
||||
vnode->status.author = ntohl(*bp++);
|
||||
vnode->status.owner = ntohl(*bp++);
|
||||
vnode->status.caller_access = ntohl(*bp++);
|
||||
vnode->status.anon_access = ntohl(*bp++);
|
||||
vnode->status.mode = ntohl(*bp++);
|
||||
vnode->status.parent.vid = vnode->fid.vid;
|
||||
vnode->status.parent.vnode = ntohl(*bp++);
|
||||
vnode->status.parent.unique = ntohl(*bp++);
|
||||
bp++; /* seg size */
|
||||
vnode->status.mtime_client = ntohl(*bp++);
|
||||
vnode->status.mtime_server = ntohl(*bp++);
|
||||
bp++; /* group */
|
||||
bp++; /* sync counter */
|
||||
vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
|
||||
bp++; /* spare2 */
|
||||
bp++; /* spare3 */
|
||||
bp++; /* spare4 */
|
||||
|
||||
vnode->cb_version = ntohl(*bp++);
|
||||
vnode->cb_expiry = ntohl(*bp++);
|
||||
vnode->cb_type = ntohl(*bp++);
|
||||
|
||||
if (volsync) {
|
||||
volsync->creation = ntohl(*bp++);
|
||||
bp++; /* spare2 */
|
||||
bp++; /* spare3 */
|
||||
bp++; /* spare4 */
|
||||
bp++; /* spare5 */
|
||||
bp++; /* spare6 */
|
||||
}
|
||||
|
||||
/* success */
|
||||
ret = 0;
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
afs_server_release_callslot(server, &callslot);
|
||||
out:
|
||||
_leave("");
|
||||
return ret;
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
goto out_unwait;
|
||||
} /* end afs_rxfs_fetch_file_status() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* fetch the contents of a file or directory
|
||||
*/
|
||||
int afs_rxfs_fetch_file_data(struct afs_server *server,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_rxfs_fetch_descriptor *desc,
|
||||
struct afs_volsync *volsync)
|
||||
{
|
||||
struct afs_server_callslot callslot;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[1];
|
||||
size_t sent;
|
||||
int ret;
|
||||
__be32 *bp;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
_enter("%p,{fid={%u,%u,%u},sz=%Zu,of=%lu}",
|
||||
server,
|
||||
desc->fid.vid,
|
||||
desc->fid.vnode,
|
||||
desc->fid.unique,
|
||||
desc->size,
|
||||
desc->offset);
|
||||
|
||||
/* get hold of the fileserver connection */
|
||||
ret = afs_server_request_callslot(server, &callslot);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = FSFETCHDATA;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = rxrpc_call_alloc_scratch(call, 24);
|
||||
bp[0] = htonl(FSFETCHDATA);
|
||||
bp[1] = htonl(desc->fid.vid);
|
||||
bp[2] = htonl(desc->fid.vnode);
|
||||
bp[3] = htonl(desc->fid.unique);
|
||||
bp[4] = htonl(desc->offset);
|
||||
bp[5] = htonl(desc->size);
|
||||
|
||||
piov[0].iov_len = 24;
|
||||
piov[0].iov_base = bp;
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the data count to arrive */
|
||||
ret = rxrpc_call_read_data(call, bp, 4, RXRPC_CALL_READ_BLOCK);
|
||||
if (ret < 0)
|
||||
goto read_failed;
|
||||
|
||||
desc->actual = ntohl(bp[0]);
|
||||
if (desc->actual != desc->size) {
|
||||
ret = -EBADMSG;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* call the app to read the actual data */
|
||||
rxrpc_call_reset_scratch(call);
|
||||
|
||||
ret = rxrpc_call_read_data(call, desc->buffer, desc->actual,
|
||||
RXRPC_CALL_READ_BLOCK);
|
||||
if (ret < 0)
|
||||
goto read_failed;
|
||||
|
||||
/* wait for the rest of the reply to completely arrive */
|
||||
rxrpc_call_reset_scratch(call);
|
||||
bp = rxrpc_call_alloc_scratch(call, 120);
|
||||
|
||||
ret = rxrpc_call_read_data(call, bp, 120,
|
||||
RXRPC_CALL_READ_BLOCK |
|
||||
RXRPC_CALL_READ_ALL);
|
||||
if (ret < 0)
|
||||
goto read_failed;
|
||||
|
||||
/* unmarshall the reply */
|
||||
vnode->status.if_version = ntohl(*bp++);
|
||||
vnode->status.type = ntohl(*bp++);
|
||||
vnode->status.nlink = ntohl(*bp++);
|
||||
vnode->status.size = ntohl(*bp++);
|
||||
vnode->status.version = ntohl(*bp++);
|
||||
vnode->status.author = ntohl(*bp++);
|
||||
vnode->status.owner = ntohl(*bp++);
|
||||
vnode->status.caller_access = ntohl(*bp++);
|
||||
vnode->status.anon_access = ntohl(*bp++);
|
||||
vnode->status.mode = ntohl(*bp++);
|
||||
vnode->status.parent.vid = desc->fid.vid;
|
||||
vnode->status.parent.vnode = ntohl(*bp++);
|
||||
vnode->status.parent.unique = ntohl(*bp++);
|
||||
bp++; /* seg size */
|
||||
vnode->status.mtime_client = ntohl(*bp++);
|
||||
vnode->status.mtime_server = ntohl(*bp++);
|
||||
bp++; /* group */
|
||||
bp++; /* sync counter */
|
||||
vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
|
||||
bp++; /* spare2 */
|
||||
bp++; /* spare3 */
|
||||
bp++; /* spare4 */
|
||||
|
||||
vnode->cb_version = ntohl(*bp++);
|
||||
vnode->cb_expiry = ntohl(*bp++);
|
||||
vnode->cb_type = ntohl(*bp++);
|
||||
|
||||
if (volsync) {
|
||||
volsync->creation = ntohl(*bp++);
|
||||
bp++; /* spare2 */
|
||||
bp++; /* spare3 */
|
||||
bp++; /* spare4 */
|
||||
bp++; /* spare5 */
|
||||
bp++; /* spare6 */
|
||||
}
|
||||
|
||||
/* success */
|
||||
ret = 0;
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq,&myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
afs_server_release_callslot(server, &callslot);
|
||||
out:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
read_failed:
|
||||
if (ret == -ECONNABORTED) {
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
}
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
goto out_unwait;
|
||||
|
||||
} /* end afs_rxfs_fetch_file_data() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* ask the AFS fileserver to discard a callback request on a file
|
||||
*/
|
||||
int afs_rxfs_give_up_callback(struct afs_server *server,
|
||||
struct afs_vnode *vnode)
|
||||
{
|
||||
struct afs_server_callslot callslot;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[1];
|
||||
size_t sent;
|
||||
int ret;
|
||||
__be32 *bp;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
_enter("%p,{%u,%u,%u}",
|
||||
server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
|
||||
|
||||
/* get hold of the fileserver connection */
|
||||
ret = afs_server_request_callslot(server, &callslot);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = FSGIVEUPCALLBACKS;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = rxrpc_call_alloc_scratch(call, (1 + 4 + 4) * 4);
|
||||
|
||||
piov[0].iov_len = (1 + 4 + 4) * 4;
|
||||
piov[0].iov_base = bp;
|
||||
|
||||
*bp++ = htonl(FSGIVEUPCALLBACKS);
|
||||
*bp++ = htonl(1);
|
||||
*bp++ = htonl(vnode->fid.vid);
|
||||
*bp++ = htonl(vnode->fid.vnode);
|
||||
*bp++ = htonl(vnode->fid.unique);
|
||||
*bp++ = htonl(1);
|
||||
*bp++ = htonl(vnode->cb_version);
|
||||
*bp++ = htonl(vnode->cb_expiry);
|
||||
*bp++ = htonl(vnode->cb_type);
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY ||
|
||||
signal_pending(current))
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
ret = -EINTR;
|
||||
if (signal_pending(current))
|
||||
goto abort;
|
||||
|
||||
switch (call->app_call_state) {
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
|
||||
case RXRPC_CSTATE_CLNT_GOT_REPLY:
|
||||
ret = 0;
|
||||
goto out_unwait;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
afs_server_release_callslot(server, &callslot);
|
||||
out:
|
||||
_leave("");
|
||||
return ret;
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
goto out_unwait;
|
||||
} /* end afs_rxfs_give_up_callback() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* look a filename up in a directory
|
||||
* - this operation doesn't seem to work correctly in OpenAFS server 1.2.2
|
||||
*/
|
||||
#if 0
|
||||
int afs_rxfs_lookup(struct afs_server *server,
|
||||
struct afs_vnode *dir,
|
||||
const char *filename,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_volsync *volsync)
|
||||
{
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[3];
|
||||
size_t sent;
|
||||
int ret;
|
||||
u32 *bp, zero;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
kenter("%p,{%u,%u,%u},%s",
|
||||
server, fid->vid, fid->vnode, fid->unique, filename);
|
||||
|
||||
/* get hold of the fileserver connection */
|
||||
ret = afs_server_get_fsconn(server, &conn);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = FSLOOKUP;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq,&myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = rxrpc_call_alloc_scratch(call, 20);
|
||||
|
||||
zero = 0;
|
||||
|
||||
piov[0].iov_len = 20;
|
||||
piov[0].iov_base = bp;
|
||||
piov[1].iov_len = strlen(filename);
|
||||
piov[1].iov_base = (char *) filename;
|
||||
piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3;
|
||||
piov[2].iov_base = &zero;
|
||||
|
||||
*bp++ = htonl(FSLOOKUP);
|
||||
*bp++ = htonl(dirfid->vid);
|
||||
*bp++ = htonl(dirfid->vnode);
|
||||
*bp++ = htonl(dirfid->unique);
|
||||
*bp++ = htonl(piov[1].iov_len);
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
bp = rxrpc_call_alloc_scratch(call, 220);
|
||||
|
||||
ret = rxrpc_call_read_data(call, bp, 220,
|
||||
RXRPC_CALL_READ_BLOCK |
|
||||
RXRPC_CALL_READ_ALL);
|
||||
if (ret < 0) {
|
||||
if (ret == -ECONNABORTED) {
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
}
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* unmarshall the reply */
|
||||
fid->vid = ntohl(*bp++);
|
||||
fid->vnode = ntohl(*bp++);
|
||||
fid->unique = ntohl(*bp++);
|
||||
|
||||
vnode->status.if_version = ntohl(*bp++);
|
||||
vnode->status.type = ntohl(*bp++);
|
||||
vnode->status.nlink = ntohl(*bp++);
|
||||
vnode->status.size = ntohl(*bp++);
|
||||
vnode->status.version = ntohl(*bp++);
|
||||
vnode->status.author = ntohl(*bp++);
|
||||
vnode->status.owner = ntohl(*bp++);
|
||||
vnode->status.caller_access = ntohl(*bp++);
|
||||
vnode->status.anon_access = ntohl(*bp++);
|
||||
vnode->status.mode = ntohl(*bp++);
|
||||
vnode->status.parent.vid = dirfid->vid;
|
||||
vnode->status.parent.vnode = ntohl(*bp++);
|
||||
vnode->status.parent.unique = ntohl(*bp++);
|
||||
bp++; /* seg size */
|
||||
vnode->status.mtime_client = ntohl(*bp++);
|
||||
vnode->status.mtime_server = ntohl(*bp++);
|
||||
bp++; /* group */
|
||||
bp++; /* sync counter */
|
||||
vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
|
||||
bp++; /* spare2 */
|
||||
bp++; /* spare3 */
|
||||
bp++; /* spare4 */
|
||||
|
||||
dir->status.if_version = ntohl(*bp++);
|
||||
dir->status.type = ntohl(*bp++);
|
||||
dir->status.nlink = ntohl(*bp++);
|
||||
dir->status.size = ntohl(*bp++);
|
||||
dir->status.version = ntohl(*bp++);
|
||||
dir->status.author = ntohl(*bp++);
|
||||
dir->status.owner = ntohl(*bp++);
|
||||
dir->status.caller_access = ntohl(*bp++);
|
||||
dir->status.anon_access = ntohl(*bp++);
|
||||
dir->status.mode = ntohl(*bp++);
|
||||
dir->status.parent.vid = dirfid->vid;
|
||||
dir->status.parent.vnode = ntohl(*bp++);
|
||||
dir->status.parent.unique = ntohl(*bp++);
|
||||
bp++; /* seg size */
|
||||
dir->status.mtime_client = ntohl(*bp++);
|
||||
dir->status.mtime_server = ntohl(*bp++);
|
||||
bp++; /* group */
|
||||
bp++; /* sync counter */
|
||||
dir->status.version |= ((unsigned long long) ntohl(*bp++)) << 32;
|
||||
bp++; /* spare2 */
|
||||
bp++; /* spare3 */
|
||||
bp++; /* spare4 */
|
||||
|
||||
callback->fid = *fid;
|
||||
callback->version = ntohl(*bp++);
|
||||
callback->expiry = ntohl(*bp++);
|
||||
callback->type = ntohl(*bp++);
|
||||
|
||||
if (volsync) {
|
||||
volsync->creation = ntohl(*bp++);
|
||||
bp++; /* spare2 */
|
||||
bp++; /* spare3 */
|
||||
bp++; /* spare4 */
|
||||
bp++; /* spare5 */
|
||||
bp++; /* spare6 */
|
||||
}
|
||||
|
||||
/* success */
|
||||
ret = 0;
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
afs_server_release_fsconn(server, conn);
|
||||
out:
|
||||
kleave("");
|
||||
return ret;
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
goto out_unwait;
|
||||
} /* end afs_rxfs_lookup() */
|
||||
#endif
|
54
fs/afs/fsclient.h
Normal file
54
fs/afs/fsclient.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* fsclient.h: AFS File Server client stub declarations
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_FSCLIENT_H
|
||||
#define _LINUX_AFS_FSCLIENT_H
|
||||
|
||||
#include "server.h"
|
||||
|
||||
extern int afs_rxfs_get_volume_info(struct afs_server *server,
|
||||
const char *name,
|
||||
struct afs_volume_info *vinfo);
|
||||
|
||||
extern int afs_rxfs_fetch_file_status(struct afs_server *server,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_volsync *volsync);
|
||||
|
||||
struct afs_rxfs_fetch_descriptor {
|
||||
struct afs_fid fid; /* file ID to fetch */
|
||||
size_t size; /* total number of bytes to fetch */
|
||||
off_t offset; /* offset in file to start from */
|
||||
void *buffer; /* read buffer */
|
||||
size_t actual; /* actual size sent back by server */
|
||||
};
|
||||
|
||||
extern int afs_rxfs_fetch_file_data(struct afs_server *server,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_rxfs_fetch_descriptor *desc,
|
||||
struct afs_volsync *volsync);
|
||||
|
||||
extern int afs_rxfs_give_up_callback(struct afs_server *server,
|
||||
struct afs_vnode *vnode);
|
||||
|
||||
/* this doesn't appear to work in OpenAFS server */
|
||||
extern int afs_rxfs_lookup(struct afs_server *server,
|
||||
struct afs_vnode *dir,
|
||||
const char *filename,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_volsync *volsync);
|
||||
|
||||
/* this is apparently mis-implemented in OpenAFS server */
|
||||
extern int afs_rxfs_get_root_volume(struct afs_server *server,
|
||||
char *buf,
|
||||
size_t *buflen);
|
||||
|
||||
|
||||
#endif /* _LINUX_AFS_FSCLIENT_H */
|
287
fs/afs/inode.c
Normal file
287
fs/afs/inode.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (c) 2002 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This software may be freely redistributed under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
|
||||
* David Howells <dhowells@redhat.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "volume.h"
|
||||
#include "vnode.h"
|
||||
#include "super.h"
|
||||
#include "internal.h"
|
||||
|
||||
struct afs_iget_data {
|
||||
struct afs_fid fid;
|
||||
struct afs_volume *volume; /* volume on which resides */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* map the AFS file status to the inode member variables
|
||||
*/
|
||||
static int afs_inode_map_status(struct afs_vnode *vnode)
|
||||
{
|
||||
struct inode *inode = AFS_VNODE_TO_I(vnode);
|
||||
|
||||
_debug("FS: ft=%d lk=%d sz=%Zu ver=%Lu mod=%hu",
|
||||
vnode->status.type,
|
||||
vnode->status.nlink,
|
||||
vnode->status.size,
|
||||
vnode->status.version,
|
||||
vnode->status.mode);
|
||||
|
||||
switch (vnode->status.type) {
|
||||
case AFS_FTYPE_FILE:
|
||||
inode->i_mode = S_IFREG | vnode->status.mode;
|
||||
inode->i_op = &afs_file_inode_operations;
|
||||
inode->i_fop = &afs_file_file_operations;
|
||||
break;
|
||||
case AFS_FTYPE_DIR:
|
||||
inode->i_mode = S_IFDIR | vnode->status.mode;
|
||||
inode->i_op = &afs_dir_inode_operations;
|
||||
inode->i_fop = &afs_dir_file_operations;
|
||||
break;
|
||||
case AFS_FTYPE_SYMLINK:
|
||||
inode->i_mode = S_IFLNK | vnode->status.mode;
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
break;
|
||||
default:
|
||||
printk("kAFS: AFS vnode with undefined type\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
inode->i_nlink = vnode->status.nlink;
|
||||
inode->i_uid = vnode->status.owner;
|
||||
inode->i_gid = 0;
|
||||
inode->i_size = vnode->status.size;
|
||||
inode->i_ctime.tv_sec = vnode->status.mtime_server;
|
||||
inode->i_ctime.tv_nsec = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime;
|
||||
inode->i_blksize = PAGE_CACHE_SIZE;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_version = vnode->fid.unique;
|
||||
inode->i_mapping->a_ops = &afs_fs_aops;
|
||||
|
||||
/* check to see whether a symbolic link is really a mountpoint */
|
||||
if (vnode->status.type == AFS_FTYPE_SYMLINK) {
|
||||
afs_mntpt_check_symlink(vnode);
|
||||
|
||||
if (vnode->flags & AFS_VNODE_MOUNTPOINT) {
|
||||
inode->i_mode = S_IFDIR | vnode->status.mode;
|
||||
inode->i_op = &afs_mntpt_inode_operations;
|
||||
inode->i_fop = &afs_mntpt_file_operations;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} /* end afs_inode_map_status() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* attempt to fetch the status of an inode, coelescing multiple simultaneous
|
||||
* fetches
|
||||
*/
|
||||
static int afs_inode_fetch_status(struct inode *inode)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
int ret;
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
ret = afs_vnode_fetch_status(vnode);
|
||||
|
||||
if (ret == 0)
|
||||
ret = afs_inode_map_status(vnode);
|
||||
|
||||
return ret;
|
||||
|
||||
} /* end afs_inode_fetch_status() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* iget5() comparator
|
||||
*/
|
||||
static int afs_iget5_test(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_iget_data *data = opaque;
|
||||
|
||||
return inode->i_ino == data->fid.vnode &&
|
||||
inode->i_version == data->fid.unique;
|
||||
} /* end afs_iget5_test() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* iget5() inode initialiser
|
||||
*/
|
||||
static int afs_iget5_set(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_iget_data *data = opaque;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
|
||||
inode->i_ino = data->fid.vnode;
|
||||
inode->i_version = data->fid.unique;
|
||||
vnode->fid = data->fid;
|
||||
vnode->volume = data->volume;
|
||||
|
||||
return 0;
|
||||
} /* end afs_iget5_set() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* inode retrieval
|
||||
*/
|
||||
inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
|
||||
struct inode **_inode)
|
||||
{
|
||||
struct afs_iget_data data = { .fid = *fid };
|
||||
struct afs_super_info *as;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
_enter(",{%u,%u,%u},,", fid->vid, fid->vnode, fid->unique);
|
||||
|
||||
as = sb->s_fs_info;
|
||||
data.volume = as->volume;
|
||||
|
||||
inode = iget5_locked(sb, fid->vnode, afs_iget5_test, afs_iget5_set,
|
||||
&data);
|
||||
if (!inode) {
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
/* deal with an existing inode */
|
||||
if (!(inode->i_state & I_NEW)) {
|
||||
ret = afs_vnode_fetch_status(vnode);
|
||||
if (ret==0)
|
||||
*_inode = inode;
|
||||
else
|
||||
iput(inode);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
/* set up caching before reading the status, as fetch-status reads the
|
||||
* first page of symlinks to see if they're really mntpts */
|
||||
cachefs_acquire_cookie(vnode->volume->cache,
|
||||
NULL,
|
||||
vnode,
|
||||
&vnode->cache);
|
||||
#endif
|
||||
|
||||
/* okay... it's a new inode */
|
||||
inode->i_flags |= S_NOATIME;
|
||||
vnode->flags |= AFS_VNODE_CHANGED;
|
||||
ret = afs_inode_fetch_status(inode);
|
||||
if (ret<0)
|
||||
goto bad_inode;
|
||||
|
||||
/* success */
|
||||
unlock_new_inode(inode);
|
||||
|
||||
*_inode = inode;
|
||||
_leave(" = 0 [CB { v=%u x=%lu t=%u }]",
|
||||
vnode->cb_version,
|
||||
vnode->cb_timeout.timo_jif,
|
||||
vnode->cb_type);
|
||||
return 0;
|
||||
|
||||
/* failure */
|
||||
bad_inode:
|
||||
make_bad_inode(inode);
|
||||
unlock_new_inode(inode);
|
||||
iput(inode);
|
||||
|
||||
_leave(" = %d [bad]", ret);
|
||||
return ret;
|
||||
} /* end afs_iget() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* read the attributes of an inode
|
||||
*/
|
||||
int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
struct kstat *stat)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
|
||||
_enter("{ ino=%lu v=%lu }", inode->i_ino, inode->i_version);
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
ret = afs_inode_fetch_status(inode);
|
||||
if (ret == -ENOENT) {
|
||||
_leave(" = %d [%d %p]",
|
||||
ret, atomic_read(&dentry->d_count), dentry->d_inode);
|
||||
return ret;
|
||||
}
|
||||
else if (ret < 0) {
|
||||
make_bad_inode(inode);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* transfer attributes from the inode structure to the stat
|
||||
* structure */
|
||||
generic_fillattr(inode, stat);
|
||||
|
||||
_leave(" = 0 CB { v=%u x=%u t=%u }",
|
||||
vnode->cb_version,
|
||||
vnode->cb_expiry,
|
||||
vnode->cb_type);
|
||||
|
||||
return 0;
|
||||
} /* end afs_inode_getattr() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clear an AFS inode
|
||||
*/
|
||||
void afs_clear_inode(struct inode *inode)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
_enter("ino=%lu { vn=%08x v=%u x=%u t=%u }",
|
||||
inode->i_ino,
|
||||
vnode->fid.vnode,
|
||||
vnode->cb_version,
|
||||
vnode->cb_expiry,
|
||||
vnode->cb_type
|
||||
);
|
||||
|
||||
BUG_ON(inode->i_ino != vnode->fid.vnode);
|
||||
|
||||
afs_vnode_give_up_callback(vnode);
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_relinquish_cookie(vnode->cache, 0);
|
||||
vnode->cache = NULL;
|
||||
#endif
|
||||
|
||||
_leave("");
|
||||
} /* end afs_clear_inode() */
|
140
fs/afs/internal.h
Normal file
140
fs/afs/internal.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/* internal.h: internal AFS stuff
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef AFS_INTERNAL_H
|
||||
#define AFS_INTERNAL_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
/*
|
||||
* debug tracing
|
||||
*/
|
||||
#define kenter(FMT, a...) printk("==> %s("FMT")\n",__FUNCTION__ , ## a)
|
||||
#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ## a)
|
||||
#define kdebug(FMT, a...) printk(FMT"\n" , ## a)
|
||||
#define kproto(FMT, a...) printk("### "FMT"\n" , ## a)
|
||||
#define knet(FMT, a...) printk(FMT"\n" , ## a)
|
||||
|
||||
#ifdef __KDEBUG
|
||||
#define _enter(FMT, a...) kenter(FMT , ## a)
|
||||
#define _leave(FMT, a...) kleave(FMT , ## a)
|
||||
#define _debug(FMT, a...) kdebug(FMT , ## a)
|
||||
#define _proto(FMT, a...) kproto(FMT , ## a)
|
||||
#define _net(FMT, a...) knet(FMT , ## a)
|
||||
#else
|
||||
#define _enter(FMT, a...) do { } while(0)
|
||||
#define _leave(FMT, a...) do { } while(0)
|
||||
#define _debug(FMT, a...) do { } while(0)
|
||||
#define _proto(FMT, a...) do { } while(0)
|
||||
#define _net(FMT, a...) do { } while(0)
|
||||
#endif
|
||||
|
||||
static inline void afs_discard_my_signals(void)
|
||||
{
|
||||
while (signal_pending(current)) {
|
||||
siginfo_t sinfo;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
dequeue_signal(current,¤t->blocked, &sinfo);
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cell.c
|
||||
*/
|
||||
extern struct rw_semaphore afs_proc_cells_sem;
|
||||
extern struct list_head afs_proc_cells;
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
extern struct cachefs_index_def afs_cache_cell_index_def;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* dir.c
|
||||
*/
|
||||
extern struct inode_operations afs_dir_inode_operations;
|
||||
extern struct file_operations afs_dir_file_operations;
|
||||
|
||||
/*
|
||||
* file.c
|
||||
*/
|
||||
extern struct address_space_operations afs_fs_aops;
|
||||
extern struct inode_operations afs_file_inode_operations;
|
||||
extern struct file_operations afs_file_file_operations;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
extern int afs_cache_get_page_cookie(struct page *page,
|
||||
struct cachefs_page **_page_cookie);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* inode.c
|
||||
*/
|
||||
extern int afs_iget(struct super_block *sb, struct afs_fid *fid,
|
||||
struct inode **_inode);
|
||||
extern int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
struct kstat *stat);
|
||||
extern void afs_clear_inode(struct inode *inode);
|
||||
|
||||
/*
|
||||
* key_afs.c
|
||||
*/
|
||||
#ifdef CONFIG_KEYS
|
||||
extern int afs_key_register(void);
|
||||
extern void afs_key_unregister(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* main.c
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
extern struct cachefs_netfs afs_cache_netfs;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* mntpt.c
|
||||
*/
|
||||
extern struct inode_operations afs_mntpt_inode_operations;
|
||||
extern struct file_operations afs_mntpt_file_operations;
|
||||
extern struct afs_timer afs_mntpt_expiry_timer;
|
||||
extern struct afs_timer_ops afs_mntpt_expiry_timer_ops;
|
||||
extern unsigned long afs_mntpt_expiry_timeout;
|
||||
|
||||
extern int afs_mntpt_check_symlink(struct afs_vnode *vnode);
|
||||
|
||||
/*
|
||||
* super.c
|
||||
*/
|
||||
extern int afs_fs_init(void);
|
||||
extern void afs_fs_exit(void);
|
||||
|
||||
#define AFS_CB_HASH_COUNT (PAGE_SIZE / sizeof(struct list_head))
|
||||
|
||||
extern struct list_head afs_cb_hash_tbl[];
|
||||
extern spinlock_t afs_cb_hash_lock;
|
||||
|
||||
#define afs_cb_hash(SRV,FID) \
|
||||
afs_cb_hash_tbl[((unsigned long)(SRV) + \
|
||||
(FID)->vid + (FID)->vnode + (FID)->unique) % \
|
||||
AFS_CB_HASH_COUNT]
|
||||
|
||||
/*
|
||||
* proc.c
|
||||
*/
|
||||
extern int afs_proc_init(void);
|
||||
extern void afs_proc_cleanup(void);
|
||||
extern int afs_proc_cell_setup(struct afs_cell *cell);
|
||||
extern void afs_proc_cell_remove(struct afs_cell *cell);
|
||||
|
||||
#endif /* AFS_INTERNAL_H */
|
257
fs/afs/kafsasyncd.c
Normal file
257
fs/afs/kafsasyncd.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/* kafsasyncd.c: AFS asynchronous operation daemon
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*
|
||||
*
|
||||
* The AFS async daemon is used to the following:
|
||||
* - probe "dead" servers to see whether they've come back to life yet.
|
||||
* - probe "live" servers that we haven't talked to for a while to see if they are better
|
||||
* candidates for serving than what we're currently using
|
||||
* - poll volume location servers to keep up to date volume location lists
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/completion.h>
|
||||
#include "cell.h"
|
||||
#include "server.h"
|
||||
#include "volume.h"
|
||||
#include "kafsasyncd.h"
|
||||
#include "kafstimod.h"
|
||||
#include <rxrpc/call.h>
|
||||
#include <asm/errno.h>
|
||||
#include "internal.h"
|
||||
|
||||
static DECLARE_COMPLETION(kafsasyncd_alive);
|
||||
static DECLARE_COMPLETION(kafsasyncd_dead);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(kafsasyncd_sleepq);
|
||||
static struct task_struct *kafsasyncd_task;
|
||||
static int kafsasyncd_die;
|
||||
|
||||
static int kafsasyncd(void *arg);
|
||||
|
||||
static LIST_HEAD(kafsasyncd_async_attnq);
|
||||
static LIST_HEAD(kafsasyncd_async_busyq);
|
||||
static DEFINE_SPINLOCK(kafsasyncd_async_lock);
|
||||
|
||||
static void kafsasyncd_null_call_attn_func(struct rxrpc_call *call)
|
||||
{
|
||||
}
|
||||
|
||||
static void kafsasyncd_null_call_error_func(struct rxrpc_call *call)
|
||||
{
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* start the async daemon
|
||||
*/
|
||||
int afs_kafsasyncd_start(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kernel_thread(kafsasyncd, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wait_for_completion(&kafsasyncd_alive);
|
||||
|
||||
return ret;
|
||||
} /* end afs_kafsasyncd_start() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* stop the async daemon
|
||||
*/
|
||||
void afs_kafsasyncd_stop(void)
|
||||
{
|
||||
/* get rid of my daemon */
|
||||
kafsasyncd_die = 1;
|
||||
wake_up(&kafsasyncd_sleepq);
|
||||
wait_for_completion(&kafsasyncd_dead);
|
||||
|
||||
} /* end afs_kafsasyncd_stop() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* probing daemon
|
||||
*/
|
||||
static int kafsasyncd(void *arg)
|
||||
{
|
||||
struct afs_async_op *op;
|
||||
int die;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
kafsasyncd_task = current;
|
||||
|
||||
printk("kAFS: Started kafsasyncd %d\n", current->pid);
|
||||
|
||||
daemonize("kafsasyncd");
|
||||
|
||||
complete(&kafsasyncd_alive);
|
||||
|
||||
/* loop around looking for things to attend to */
|
||||
do {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&kafsasyncd_sleepq, &myself);
|
||||
|
||||
for (;;) {
|
||||
if (!list_empty(&kafsasyncd_async_attnq) ||
|
||||
signal_pending(current) ||
|
||||
kafsasyncd_die)
|
||||
break;
|
||||
|
||||
schedule();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
remove_wait_queue(&kafsasyncd_sleepq, &myself);
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
try_to_freeze(PF_FREEZE);
|
||||
|
||||
/* discard pending signals */
|
||||
afs_discard_my_signals();
|
||||
|
||||
die = kafsasyncd_die;
|
||||
|
||||
/* deal with the next asynchronous operation requiring
|
||||
* attention */
|
||||
if (!list_empty(&kafsasyncd_async_attnq)) {
|
||||
struct afs_async_op *op;
|
||||
|
||||
_debug("@@@ Begin Asynchronous Operation");
|
||||
|
||||
op = NULL;
|
||||
spin_lock(&kafsasyncd_async_lock);
|
||||
|
||||
if (!list_empty(&kafsasyncd_async_attnq)) {
|
||||
op = list_entry(kafsasyncd_async_attnq.next,
|
||||
struct afs_async_op, link);
|
||||
list_del(&op->link);
|
||||
list_add_tail(&op->link,
|
||||
&kafsasyncd_async_busyq);
|
||||
}
|
||||
|
||||
spin_unlock(&kafsasyncd_async_lock);
|
||||
|
||||
_debug("@@@ Operation %p {%p}\n",
|
||||
op, op ? op->ops : NULL);
|
||||
|
||||
if (op)
|
||||
op->ops->attend(op);
|
||||
|
||||
_debug("@@@ End Asynchronous Operation");
|
||||
}
|
||||
|
||||
} while(!die);
|
||||
|
||||
/* need to kill all outstanding asynchronous operations before
|
||||
* exiting */
|
||||
kafsasyncd_task = NULL;
|
||||
spin_lock(&kafsasyncd_async_lock);
|
||||
|
||||
/* fold the busy and attention queues together */
|
||||
list_splice_init(&kafsasyncd_async_busyq,
|
||||
&kafsasyncd_async_attnq);
|
||||
|
||||
/* dequeue kafsasyncd from all their wait queues */
|
||||
list_for_each_entry(op, &kafsasyncd_async_attnq, link) {
|
||||
op->call->app_attn_func = kafsasyncd_null_call_attn_func;
|
||||
op->call->app_error_func = kafsasyncd_null_call_error_func;
|
||||
remove_wait_queue(&op->call->waitq, &op->waiter);
|
||||
}
|
||||
|
||||
spin_unlock(&kafsasyncd_async_lock);
|
||||
|
||||
/* abort all the operations */
|
||||
while (!list_empty(&kafsasyncd_async_attnq)) {
|
||||
op = list_entry(kafsasyncd_async_attnq.next, struct afs_async_op, link);
|
||||
list_del_init(&op->link);
|
||||
|
||||
rxrpc_call_abort(op->call, -EIO);
|
||||
rxrpc_put_call(op->call);
|
||||
op->call = NULL;
|
||||
|
||||
op->ops->discard(op);
|
||||
}
|
||||
|
||||
/* and that's all */
|
||||
_leave("");
|
||||
complete_and_exit(&kafsasyncd_dead, 0);
|
||||
|
||||
} /* end kafsasyncd() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* begin an operation
|
||||
* - place operation on busy queue
|
||||
*/
|
||||
void afs_kafsasyncd_begin_op(struct afs_async_op *op)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
spin_lock(&kafsasyncd_async_lock);
|
||||
|
||||
init_waitqueue_entry(&op->waiter, kafsasyncd_task);
|
||||
add_wait_queue(&op->call->waitq, &op->waiter);
|
||||
|
||||
list_del(&op->link);
|
||||
list_add_tail(&op->link, &kafsasyncd_async_busyq);
|
||||
|
||||
spin_unlock(&kafsasyncd_async_lock);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_kafsasyncd_begin_op() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* request attention for an operation
|
||||
* - move to attention queue
|
||||
*/
|
||||
void afs_kafsasyncd_attend_op(struct afs_async_op *op)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
spin_lock(&kafsasyncd_async_lock);
|
||||
|
||||
list_del(&op->link);
|
||||
list_add_tail(&op->link, &kafsasyncd_async_attnq);
|
||||
|
||||
spin_unlock(&kafsasyncd_async_lock);
|
||||
|
||||
wake_up(&kafsasyncd_sleepq);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_kafsasyncd_attend_op() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* terminate an operation
|
||||
* - remove from either queue
|
||||
*/
|
||||
void afs_kafsasyncd_terminate_op(struct afs_async_op *op)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
spin_lock(&kafsasyncd_async_lock);
|
||||
|
||||
if (!list_empty(&op->link)) {
|
||||
list_del_init(&op->link);
|
||||
remove_wait_queue(&op->call->waitq, &op->waiter);
|
||||
}
|
||||
|
||||
spin_unlock(&kafsasyncd_async_lock);
|
||||
|
||||
wake_up(&kafsasyncd_sleepq);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_kafsasyncd_terminate_op() */
|
52
fs/afs/kafsasyncd.h
Normal file
52
fs/afs/kafsasyncd.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* kafsasyncd.h: AFS asynchronous operation daemon
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_KAFSASYNCD_H
|
||||
#define _LINUX_AFS_KAFSASYNCD_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct afs_async_op;
|
||||
|
||||
struct afs_async_op_ops {
|
||||
void (*attend)(struct afs_async_op *op);
|
||||
void (*discard)(struct afs_async_op *op);
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* asynchronous operation record
|
||||
*/
|
||||
struct afs_async_op
|
||||
{
|
||||
struct list_head link;
|
||||
struct afs_server *server; /* server being contacted */
|
||||
struct rxrpc_call *call; /* RxRPC call performing op */
|
||||
wait_queue_t waiter; /* wait queue for kafsasyncd */
|
||||
const struct afs_async_op_ops *ops; /* operations */
|
||||
};
|
||||
|
||||
static inline void afs_async_op_init(struct afs_async_op *op,
|
||||
const struct afs_async_op_ops *ops)
|
||||
{
|
||||
INIT_LIST_HEAD(&op->link);
|
||||
op->call = NULL;
|
||||
op->ops = ops;
|
||||
}
|
||||
|
||||
extern int afs_kafsasyncd_start(void);
|
||||
extern void afs_kafsasyncd_stop(void);
|
||||
|
||||
extern void afs_kafsasyncd_begin_op(struct afs_async_op *op);
|
||||
extern void afs_kafsasyncd_attend_op(struct afs_async_op *op);
|
||||
extern void afs_kafsasyncd_terminate_op(struct afs_async_op *op);
|
||||
|
||||
#endif /* _LINUX_AFS_KAFSASYNCD_H */
|
204
fs/afs/kafstimod.c
Normal file
204
fs/afs/kafstimod.c
Normal file
@@ -0,0 +1,204 @@
|
||||
/* kafstimod.c: AFS timeout daemon
|
||||
*
|
||||
* Copyright (C) 2002 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/completion.h>
|
||||
#include "cell.h"
|
||||
#include "volume.h"
|
||||
#include "kafstimod.h"
|
||||
#include <asm/errno.h>
|
||||
#include "internal.h"
|
||||
|
||||
static DECLARE_COMPLETION(kafstimod_alive);
|
||||
static DECLARE_COMPLETION(kafstimod_dead);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(kafstimod_sleepq);
|
||||
static int kafstimod_die;
|
||||
|
||||
static LIST_HEAD(kafstimod_list);
|
||||
static DEFINE_SPINLOCK(kafstimod_lock);
|
||||
|
||||
static int kafstimod(void *arg);
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* start the timeout daemon
|
||||
*/
|
||||
int afs_kafstimod_start(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kernel_thread(kafstimod, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wait_for_completion(&kafstimod_alive);
|
||||
|
||||
return ret;
|
||||
} /* end afs_kafstimod_start() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* stop the timeout daemon
|
||||
*/
|
||||
void afs_kafstimod_stop(void)
|
||||
{
|
||||
/* get rid of my daemon */
|
||||
kafstimod_die = 1;
|
||||
wake_up(&kafstimod_sleepq);
|
||||
wait_for_completion(&kafstimod_dead);
|
||||
|
||||
} /* end afs_kafstimod_stop() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* timeout processing daemon
|
||||
*/
|
||||
static int kafstimod(void *arg)
|
||||
{
|
||||
struct afs_timer *timer;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
printk("kAFS: Started kafstimod %d\n", current->pid);
|
||||
|
||||
daemonize("kafstimod");
|
||||
|
||||
complete(&kafstimod_alive);
|
||||
|
||||
/* loop around looking for things to attend to */
|
||||
loop:
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&kafstimod_sleepq, &myself);
|
||||
|
||||
for (;;) {
|
||||
unsigned long jif;
|
||||
signed long timeout;
|
||||
|
||||
/* deal with the server being asked to die */
|
||||
if (kafstimod_die) {
|
||||
remove_wait_queue(&kafstimod_sleepq, &myself);
|
||||
_leave("");
|
||||
complete_and_exit(&kafstimod_dead, 0);
|
||||
}
|
||||
|
||||
try_to_freeze(PF_FREEZE);
|
||||
|
||||
/* discard pending signals */
|
||||
afs_discard_my_signals();
|
||||
|
||||
/* work out the time to elapse before the next event */
|
||||
spin_lock(&kafstimod_lock);
|
||||
if (list_empty(&kafstimod_list)) {
|
||||
timeout = MAX_SCHEDULE_TIMEOUT;
|
||||
}
|
||||
else {
|
||||
timer = list_entry(kafstimod_list.next,
|
||||
struct afs_timer, link);
|
||||
timeout = timer->timo_jif;
|
||||
jif = jiffies;
|
||||
|
||||
if (time_before_eq((unsigned long) timeout, jif))
|
||||
goto immediate;
|
||||
|
||||
else {
|
||||
timeout = (long) timeout - (long) jiffies;
|
||||
}
|
||||
}
|
||||
spin_unlock(&kafstimod_lock);
|
||||
|
||||
schedule_timeout(timeout);
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
/* the thing on the front of the queue needs processing
|
||||
* - we come here with the lock held and timer pointing to the expired
|
||||
* entry
|
||||
*/
|
||||
immediate:
|
||||
remove_wait_queue(&kafstimod_sleepq, &myself);
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
_debug("@@@ Begin Timeout of %p", timer);
|
||||
|
||||
/* dequeue the timer */
|
||||
list_del_init(&timer->link);
|
||||
spin_unlock(&kafstimod_lock);
|
||||
|
||||
/* call the timeout function */
|
||||
timer->ops->timed_out(timer);
|
||||
|
||||
_debug("@@@ End Timeout");
|
||||
goto loop;
|
||||
|
||||
} /* end kafstimod() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* (re-)queue a timer
|
||||
*/
|
||||
void afs_kafstimod_add_timer(struct afs_timer *timer, unsigned long timeout)
|
||||
{
|
||||
struct afs_timer *ptimer;
|
||||
struct list_head *_p;
|
||||
|
||||
_enter("%p,%lu", timer, timeout);
|
||||
|
||||
spin_lock(&kafstimod_lock);
|
||||
|
||||
list_del(&timer->link);
|
||||
|
||||
/* the timer was deferred or reset - put it back in the queue at the
|
||||
* right place */
|
||||
timer->timo_jif = jiffies + timeout;
|
||||
|
||||
list_for_each(_p, &kafstimod_list) {
|
||||
ptimer = list_entry(_p, struct afs_timer, link);
|
||||
if (time_before(timer->timo_jif, ptimer->timo_jif))
|
||||
break;
|
||||
}
|
||||
|
||||
list_add_tail(&timer->link, _p); /* insert before stopping point */
|
||||
|
||||
spin_unlock(&kafstimod_lock);
|
||||
|
||||
wake_up(&kafstimod_sleepq);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_kafstimod_add_timer() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* dequeue a timer
|
||||
* - returns 0 if the timer was deleted or -ENOENT if it wasn't queued
|
||||
*/
|
||||
int afs_kafstimod_del_timer(struct afs_timer *timer)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
_enter("%p", timer);
|
||||
|
||||
spin_lock(&kafstimod_lock);
|
||||
|
||||
if (list_empty(&timer->link))
|
||||
ret = -ENOENT;
|
||||
else
|
||||
list_del_init(&timer->link);
|
||||
|
||||
spin_unlock(&kafstimod_lock);
|
||||
|
||||
wake_up(&kafstimod_sleepq);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_kafstimod_del_timer() */
|
49
fs/afs/kafstimod.h
Normal file
49
fs/afs/kafstimod.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* kafstimod.h: AFS timeout daemon
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_KAFSTIMOD_H
|
||||
#define _LINUX_AFS_KAFSTIMOD_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct afs_timer;
|
||||
|
||||
struct afs_timer_ops {
|
||||
/* called when the front of the timer queue has timed out */
|
||||
void (*timed_out)(struct afs_timer *timer);
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS timer/timeout record
|
||||
*/
|
||||
struct afs_timer
|
||||
{
|
||||
struct list_head link; /* link in timer queue */
|
||||
unsigned long timo_jif; /* timeout time */
|
||||
const struct afs_timer_ops *ops; /* timeout expiry function */
|
||||
};
|
||||
|
||||
static inline void afs_timer_init(struct afs_timer *timer,
|
||||
const struct afs_timer_ops *ops)
|
||||
{
|
||||
INIT_LIST_HEAD(&timer->link);
|
||||
timer->ops = ops;
|
||||
}
|
||||
|
||||
extern int afs_kafstimod_start(void);
|
||||
extern void afs_kafstimod_stop(void);
|
||||
|
||||
extern void afs_kafstimod_add_timer(struct afs_timer *timer,
|
||||
unsigned long timeout);
|
||||
extern int afs_kafstimod_del_timer(struct afs_timer *timer);
|
||||
|
||||
#endif /* _LINUX_AFS_KAFSTIMOD_H */
|
286
fs/afs/main.c
Normal file
286
fs/afs/main.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/* main.c: AFS client file system
|
||||
*
|
||||
* Copyright (C) 2002 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/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/completion.h>
|
||||
#include <rxrpc/rxrpc.h>
|
||||
#include <rxrpc/transport.h>
|
||||
#include <rxrpc/call.h>
|
||||
#include <rxrpc/peer.h>
|
||||
#include "cache.h"
|
||||
#include "cell.h"
|
||||
#include "server.h"
|
||||
#include "fsclient.h"
|
||||
#include "cmservice.h"
|
||||
#include "kafstimod.h"
|
||||
#include "kafsasyncd.h"
|
||||
#include "internal.h"
|
||||
|
||||
struct rxrpc_transport *afs_transport;
|
||||
|
||||
static int afs_adding_peer(struct rxrpc_peer *peer);
|
||||
static void afs_discarding_peer(struct rxrpc_peer *peer);
|
||||
|
||||
|
||||
MODULE_DESCRIPTION("AFS Client File System");
|
||||
MODULE_AUTHOR("Red Hat, Inc.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static char *rootcell;
|
||||
|
||||
module_param(rootcell, charp, 0);
|
||||
MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
|
||||
|
||||
|
||||
static struct rxrpc_peer_ops afs_peer_ops = {
|
||||
.adding = afs_adding_peer,
|
||||
.discarding = afs_discarding_peer,
|
||||
};
|
||||
|
||||
struct list_head afs_cb_hash_tbl[AFS_CB_HASH_COUNT];
|
||||
DEFINE_SPINLOCK(afs_cb_hash_lock);
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static struct cachefs_netfs_operations afs_cache_ops = {
|
||||
.get_page_cookie = afs_cache_get_page_cookie,
|
||||
};
|
||||
|
||||
struct cachefs_netfs afs_cache_netfs = {
|
||||
.name = "afs",
|
||||
.version = 0,
|
||||
.ops = &afs_cache_ops,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* initialise the AFS client FS module
|
||||
*/
|
||||
static int __init afs_init(void)
|
||||
{
|
||||
int loop, ret;
|
||||
|
||||
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n");
|
||||
|
||||
/* initialise the callback hash table */
|
||||
spin_lock_init(&afs_cb_hash_lock);
|
||||
for (loop = AFS_CB_HASH_COUNT - 1; loop >= 0; loop--)
|
||||
INIT_LIST_HEAD(&afs_cb_hash_tbl[loop]);
|
||||
|
||||
/* register the /proc stuff */
|
||||
ret = afs_proc_init();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
/* we want to be able to cache */
|
||||
ret = cachefs_register_netfs(&afs_cache_netfs,
|
||||
&afs_cache_cell_index_def);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KEYS_TURNED_OFF
|
||||
ret = afs_key_register();
|
||||
if (ret < 0)
|
||||
goto error_cache;
|
||||
#endif
|
||||
|
||||
/* initialise the cell DB */
|
||||
ret = afs_cell_init(rootcell);
|
||||
if (ret < 0)
|
||||
goto error_keys;
|
||||
|
||||
/* start the timeout daemon */
|
||||
ret = afs_kafstimod_start();
|
||||
if (ret < 0)
|
||||
goto error_keys;
|
||||
|
||||
/* start the async operation daemon */
|
||||
ret = afs_kafsasyncd_start();
|
||||
if (ret < 0)
|
||||
goto error_kafstimod;
|
||||
|
||||
/* create the RxRPC transport */
|
||||
ret = rxrpc_create_transport(7001, &afs_transport);
|
||||
if (ret < 0)
|
||||
goto error_kafsasyncd;
|
||||
|
||||
afs_transport->peer_ops = &afs_peer_ops;
|
||||
|
||||
/* register the filesystems */
|
||||
ret = afs_fs_init();
|
||||
if (ret < 0)
|
||||
goto error_transport;
|
||||
|
||||
return ret;
|
||||
|
||||
error_transport:
|
||||
rxrpc_put_transport(afs_transport);
|
||||
error_kafsasyncd:
|
||||
afs_kafsasyncd_stop();
|
||||
error_kafstimod:
|
||||
afs_kafstimod_stop();
|
||||
error_keys:
|
||||
#ifdef CONFIG_KEYS_TURNED_OFF
|
||||
afs_key_unregister();
|
||||
error_cache:
|
||||
#endif
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_unregister_netfs(&afs_cache_netfs);
|
||||
error:
|
||||
#endif
|
||||
afs_cell_purge();
|
||||
afs_proc_cleanup();
|
||||
printk(KERN_ERR "kAFS: failed to register: %d\n", ret);
|
||||
return ret;
|
||||
} /* end afs_init() */
|
||||
|
||||
/* XXX late_initcall is kludgy, but the only alternative seems to create
|
||||
* a transport upon the first mount, which is worse. Or is it?
|
||||
*/
|
||||
late_initcall(afs_init); /* must be called after net/ to create socket */
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clean up on module removal
|
||||
*/
|
||||
static void __exit afs_exit(void)
|
||||
{
|
||||
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
|
||||
|
||||
afs_fs_exit();
|
||||
rxrpc_put_transport(afs_transport);
|
||||
afs_kafstimod_stop();
|
||||
afs_kafsasyncd_stop();
|
||||
afs_cell_purge();
|
||||
#ifdef CONFIG_KEYS_TURNED_OFF
|
||||
afs_key_unregister();
|
||||
#endif
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_unregister_netfs(&afs_cache_netfs);
|
||||
#endif
|
||||
afs_proc_cleanup();
|
||||
|
||||
} /* end afs_exit() */
|
||||
|
||||
module_exit(afs_exit);
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* notification that new peer record is being added
|
||||
* - called from krxsecd
|
||||
* - return an error to induce an abort
|
||||
* - mustn't sleep (caller holds an rwlock)
|
||||
*/
|
||||
static int afs_adding_peer(struct rxrpc_peer *peer)
|
||||
{
|
||||
struct afs_server *server;
|
||||
int ret;
|
||||
|
||||
_debug("kAFS: Adding new peer %08x\n", ntohl(peer->addr.s_addr));
|
||||
|
||||
/* determine which server the peer resides in (if any) */
|
||||
ret = afs_server_find_by_peer(peer, &server);
|
||||
if (ret < 0)
|
||||
return ret; /* none that we recognise, so abort */
|
||||
|
||||
_debug("Server %p{u=%d}\n", server, atomic_read(&server->usage));
|
||||
|
||||
_debug("Cell %p{u=%d}\n",
|
||||
server->cell, atomic_read(&server->cell->usage));
|
||||
|
||||
/* cross-point the structs under a global lock */
|
||||
spin_lock(&afs_server_peer_lock);
|
||||
peer->user = server;
|
||||
server->peer = peer;
|
||||
spin_unlock(&afs_server_peer_lock);
|
||||
|
||||
afs_put_server(server);
|
||||
|
||||
return 0;
|
||||
} /* end afs_adding_peer() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* notification that a peer record is being discarded
|
||||
* - called from krxiod or krxsecd
|
||||
*/
|
||||
static void afs_discarding_peer(struct rxrpc_peer *peer)
|
||||
{
|
||||
struct afs_server *server;
|
||||
|
||||
_enter("%p",peer);
|
||||
|
||||
_debug("Discarding peer %08x (rtt=%lu.%lumS)\n",
|
||||
ntohl(peer->addr.s_addr),
|
||||
(long) (peer->rtt / 1000),
|
||||
(long) (peer->rtt % 1000));
|
||||
|
||||
/* uncross-point the structs under a global lock */
|
||||
spin_lock(&afs_server_peer_lock);
|
||||
server = peer->user;
|
||||
if (server) {
|
||||
peer->user = NULL;
|
||||
server->peer = NULL;
|
||||
}
|
||||
spin_unlock(&afs_server_peer_lock);
|
||||
|
||||
_leave("");
|
||||
|
||||
} /* end afs_discarding_peer() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clear the dead space between task_struct and kernel stack
|
||||
* - called by supplying -finstrument-functions to gcc
|
||||
*/
|
||||
#if 0
|
||||
void __cyg_profile_func_enter (void *this_fn, void *call_site)
|
||||
__attribute__((no_instrument_function));
|
||||
|
||||
void __cyg_profile_func_enter (void *this_fn, void *call_site)
|
||||
{
|
||||
asm volatile(" movl %%esp,%%edi \n"
|
||||
" andl %0,%%edi \n"
|
||||
" addl %1,%%edi \n"
|
||||
" movl %%esp,%%ecx \n"
|
||||
" subl %%edi,%%ecx \n"
|
||||
" shrl $2,%%ecx \n"
|
||||
" movl $0xedededed,%%eax \n"
|
||||
" rep stosl \n"
|
||||
:
|
||||
: "i"(~(THREAD_SIZE - 1)), "i"(sizeof(struct thread_info))
|
||||
: "eax", "ecx", "edi", "memory", "cc"
|
||||
);
|
||||
}
|
||||
|
||||
void __cyg_profile_func_exit(void *this_fn, void *call_site)
|
||||
__attribute__((no_instrument_function));
|
||||
|
||||
void __cyg_profile_func_exit(void *this_fn, void *call_site)
|
||||
{
|
||||
asm volatile(" movl %%esp,%%edi \n"
|
||||
" andl %0,%%edi \n"
|
||||
" addl %1,%%edi \n"
|
||||
" movl %%esp,%%ecx \n"
|
||||
" subl %%edi,%%ecx \n"
|
||||
" shrl $2,%%ecx \n"
|
||||
" movl $0xdadadada,%%eax \n"
|
||||
" rep stosl \n"
|
||||
:
|
||||
: "i"(~(THREAD_SIZE - 1)), "i"(sizeof(struct thread_info))
|
||||
: "eax", "ecx", "edi", "memory", "cc"
|
||||
);
|
||||
}
|
||||
#endif
|
39
fs/afs/misc.c
Normal file
39
fs/afs/misc.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/* misc.c: miscellaneous bits
|
||||
*
|
||||
* Copyright (C) 2002 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/errno.h>
|
||||
#include "errors.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* convert an AFS abort code to a Linux error number
|
||||
*/
|
||||
int afs_abort_to_error(int abortcode)
|
||||
{
|
||||
switch (abortcode) {
|
||||
case VSALVAGE: return -EIO;
|
||||
case VNOVNODE: return -ENOENT;
|
||||
case VNOVOL: return -ENXIO;
|
||||
case VVOLEXISTS: return -EEXIST;
|
||||
case VNOSERVICE: return -EIO;
|
||||
case VOFFLINE: return -ENOENT;
|
||||
case VONLINE: return -EEXIST;
|
||||
case VDISKFULL: return -ENOSPC;
|
||||
case VOVERQUOTA: return -EDQUOT;
|
||||
case VBUSY: return -EBUSY;
|
||||
case VMOVED: return -ENXIO;
|
||||
default: return -EIO;
|
||||
}
|
||||
|
||||
} /* end afs_abort_to_error() */
|
287
fs/afs/mntpt.c
Normal file
287
fs/afs/mntpt.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/* mntpt.c: mountpoint management
|
||||
*
|
||||
* Copyright (C) 2002 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/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/namespace.h>
|
||||
#include "super.h"
|
||||
#include "cell.h"
|
||||
#include "volume.h"
|
||||
#include "vnode.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
static struct dentry *afs_mntpt_lookup(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
struct nameidata *nd);
|
||||
static int afs_mntpt_open(struct inode *inode, struct file *file);
|
||||
static int afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
|
||||
|
||||
struct file_operations afs_mntpt_file_operations = {
|
||||
.open = afs_mntpt_open,
|
||||
};
|
||||
|
||||
struct inode_operations afs_mntpt_inode_operations = {
|
||||
.lookup = afs_mntpt_lookup,
|
||||
.follow_link = afs_mntpt_follow_link,
|
||||
.readlink = page_readlink,
|
||||
.getattr = afs_inode_getattr,
|
||||
};
|
||||
|
||||
static LIST_HEAD(afs_vfsmounts);
|
||||
|
||||
static void afs_mntpt_expiry_timed_out(struct afs_timer *timer);
|
||||
|
||||
struct afs_timer_ops afs_mntpt_expiry_timer_ops = {
|
||||
.timed_out = afs_mntpt_expiry_timed_out,
|
||||
};
|
||||
|
||||
struct afs_timer afs_mntpt_expiry_timer;
|
||||
|
||||
unsigned long afs_mntpt_expiry_timeout = 20;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* check a symbolic link to see whether it actually encodes a mountpoint
|
||||
* - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately
|
||||
*/
|
||||
int afs_mntpt_check_symlink(struct afs_vnode *vnode)
|
||||
{
|
||||
struct page *page;
|
||||
filler_t *filler;
|
||||
size_t size;
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
_enter("{%u,%u}", vnode->fid.vnode, vnode->fid.unique);
|
||||
|
||||
/* read the contents of the symlink into the pagecache */
|
||||
filler = (filler_t *) AFS_VNODE_TO_I(vnode)->i_mapping->a_ops->readpage;
|
||||
|
||||
page = read_cache_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0,
|
||||
filler, NULL);
|
||||
if (IS_ERR(page)) {
|
||||
ret = PTR_ERR(page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
wait_on_page_locked(page);
|
||||
buf = kmap(page);
|
||||
if (!PageUptodate(page))
|
||||
goto out_free;
|
||||
if (PageError(page))
|
||||
goto out_free;
|
||||
|
||||
/* examine the symlink's contents */
|
||||
size = vnode->status.size;
|
||||
_debug("symlink to %*.*s", size, (int) size, buf);
|
||||
|
||||
if (size > 2 &&
|
||||
(buf[0] == '%' || buf[0] == '#') &&
|
||||
buf[size - 1] == '.'
|
||||
) {
|
||||
_debug("symlink is a mountpoint");
|
||||
spin_lock(&vnode->lock);
|
||||
vnode->flags |= AFS_VNODE_MOUNTPOINT;
|
||||
spin_unlock(&vnode->lock);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out_free:
|
||||
kunmap(page);
|
||||
page_cache_release(page);
|
||||
out:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_mntpt_check_symlink() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* no valid lookup procedure on this sort of dir
|
||||
*/
|
||||
static struct dentry *afs_mntpt_lookup(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
kenter("%p,%p{%p{%s},%s}",
|
||||
dir,
|
||||
dentry,
|
||||
dentry->d_parent,
|
||||
dentry->d_parent ?
|
||||
dentry->d_parent->d_name.name : (const unsigned char *) "",
|
||||
dentry->d_name.name);
|
||||
|
||||
return ERR_PTR(-EREMOTE);
|
||||
} /* end afs_mntpt_lookup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* no valid open procedure on this sort of dir
|
||||
*/
|
||||
static int afs_mntpt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
kenter("%p,%p{%p{%s},%s}",
|
||||
inode, file,
|
||||
file->f_dentry->d_parent,
|
||||
file->f_dentry->d_parent ?
|
||||
file->f_dentry->d_parent->d_name.name :
|
||||
(const unsigned char *) "",
|
||||
file->f_dentry->d_name.name);
|
||||
|
||||
return -EREMOTE;
|
||||
} /* end afs_mntpt_open() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* create a vfsmount to be automounted
|
||||
*/
|
||||
static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
|
||||
{
|
||||
struct afs_super_info *super;
|
||||
struct vfsmount *mnt;
|
||||
struct page *page = NULL;
|
||||
size_t size;
|
||||
char *buf, *devname = NULL, *options = NULL;
|
||||
filler_t *filler;
|
||||
int ret;
|
||||
|
||||
kenter("{%s}", mntpt->d_name.name);
|
||||
|
||||
BUG_ON(!mntpt->d_inode);
|
||||
|
||||
ret = -EINVAL;
|
||||
size = mntpt->d_inode->i_size;
|
||||
if (size > PAGE_SIZE - 1)
|
||||
goto error;
|
||||
|
||||
ret = -ENOMEM;
|
||||
devname = (char *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!devname)
|
||||
goto error;
|
||||
|
||||
options = (char *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!options)
|
||||
goto error;
|
||||
|
||||
/* read the contents of the AFS special symlink */
|
||||
filler = (filler_t *)mntpt->d_inode->i_mapping->a_ops->readpage;
|
||||
|
||||
page = read_cache_page(mntpt->d_inode->i_mapping, 0, filler, NULL);
|
||||
if (IS_ERR(page)) {
|
||||
ret = PTR_ERR(page);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
wait_on_page_locked(page);
|
||||
if (!PageUptodate(page) || PageError(page))
|
||||
goto error;
|
||||
|
||||
buf = kmap(page);
|
||||
memcpy(devname, buf, size);
|
||||
kunmap(page);
|
||||
page_cache_release(page);
|
||||
page = NULL;
|
||||
|
||||
/* work out what options we want */
|
||||
super = AFS_FS_S(mntpt->d_sb);
|
||||
memcpy(options, "cell=", 5);
|
||||
strcpy(options + 5, super->volume->cell->name);
|
||||
if (super->volume->type == AFSVL_RWVOL)
|
||||
strcat(options, ",rwpath");
|
||||
|
||||
/* try and do the mount */
|
||||
kdebug("--- attempting mount %s -o %s ---", devname, options);
|
||||
mnt = do_kern_mount("afs", 0, devname, options);
|
||||
kdebug("--- mount result %p ---", mnt);
|
||||
|
||||
free_page((unsigned long) devname);
|
||||
free_page((unsigned long) options);
|
||||
kleave(" = %p", mnt);
|
||||
return mnt;
|
||||
|
||||
error:
|
||||
if (page)
|
||||
page_cache_release(page);
|
||||
if (devname)
|
||||
free_page((unsigned long) devname);
|
||||
if (options)
|
||||
free_page((unsigned long) options);
|
||||
kleave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
} /* end afs_mntpt_do_automount() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* follow a link from a mountpoint directory, thus causing it to be mounted
|
||||
*/
|
||||
static int afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct vfsmount *newmnt;
|
||||
struct dentry *old_dentry;
|
||||
int err;
|
||||
|
||||
kenter("%p{%s},{%s:%p{%s}}",
|
||||
dentry,
|
||||
dentry->d_name.name,
|
||||
nd->mnt->mnt_devname,
|
||||
dentry,
|
||||
nd->dentry->d_name.name);
|
||||
|
||||
newmnt = afs_mntpt_do_automount(dentry);
|
||||
if (IS_ERR(newmnt)) {
|
||||
path_release(nd);
|
||||
return PTR_ERR(newmnt);
|
||||
}
|
||||
|
||||
old_dentry = nd->dentry;
|
||||
nd->dentry = dentry;
|
||||
err = do_add_mount(newmnt, nd, 0, &afs_vfsmounts);
|
||||
nd->dentry = old_dentry;
|
||||
|
||||
path_release(nd);
|
||||
|
||||
if (!err) {
|
||||
mntget(newmnt);
|
||||
nd->mnt = newmnt;
|
||||
dget(newmnt->mnt_root);
|
||||
nd->dentry = newmnt->mnt_root;
|
||||
}
|
||||
|
||||
kleave(" = %d", err);
|
||||
return err;
|
||||
} /* end afs_mntpt_follow_link() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle mountpoint expiry timer going off
|
||||
*/
|
||||
static void afs_mntpt_expiry_timed_out(struct afs_timer *timer)
|
||||
{
|
||||
kenter("");
|
||||
|
||||
mark_mounts_for_expiry(&afs_vfsmounts);
|
||||
|
||||
afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
|
||||
afs_mntpt_expiry_timeout * HZ);
|
||||
|
||||
kleave("");
|
||||
} /* end afs_mntpt_expiry_timed_out() */
|
23
fs/afs/mount.h
Normal file
23
fs/afs/mount.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* mount.h: mount parameters
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_MOUNT_H
|
||||
#define _LINUX_AFS_MOUNT_H
|
||||
|
||||
struct afs_mountdata {
|
||||
const char *volume; /* name of volume */
|
||||
const char *cell; /* name of cell containing volume */
|
||||
const char *cache; /* name of cache block device */
|
||||
size_t nservers; /* number of server addresses listed */
|
||||
uint32_t servers[10]; /* IP addresses of servers in this cell */
|
||||
};
|
||||
|
||||
#endif /* _LINUX_AFS_MOUNT_H */
|
857
fs/afs/proc.c
Normal file
857
fs/afs/proc.c
Normal file
@@ -0,0 +1,857 @@
|
||||
/* proc.c: /proc interface for AFS
|
||||
*
|
||||
* Copyright (C) 2002 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/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "cell.h"
|
||||
#include "volume.h"
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
||||
static struct proc_dir_entry *proc_afs;
|
||||
|
||||
|
||||
static int afs_proc_cells_open(struct inode *inode, struct file *file);
|
||||
static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos);
|
||||
static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos);
|
||||
static void afs_proc_cells_stop(struct seq_file *p, void *v);
|
||||
static int afs_proc_cells_show(struct seq_file *m, void *v);
|
||||
static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *_pos);
|
||||
|
||||
static struct seq_operations afs_proc_cells_ops = {
|
||||
.start = afs_proc_cells_start,
|
||||
.next = afs_proc_cells_next,
|
||||
.stop = afs_proc_cells_stop,
|
||||
.show = afs_proc_cells_show,
|
||||
};
|
||||
|
||||
static struct file_operations afs_proc_cells_fops = {
|
||||
.open = afs_proc_cells_open,
|
||||
.read = seq_read,
|
||||
.write = afs_proc_cells_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static int afs_proc_rootcell_open(struct inode *inode, struct file *file);
|
||||
static int afs_proc_rootcell_release(struct inode *inode, struct file *file);
|
||||
static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *_pos);
|
||||
static ssize_t afs_proc_rootcell_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t size, loff_t *_pos);
|
||||
|
||||
static struct file_operations afs_proc_rootcell_fops = {
|
||||
.open = afs_proc_rootcell_open,
|
||||
.read = afs_proc_rootcell_read,
|
||||
.write = afs_proc_rootcell_write,
|
||||
.llseek = no_llseek,
|
||||
.release = afs_proc_rootcell_release
|
||||
};
|
||||
|
||||
static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file);
|
||||
static int afs_proc_cell_volumes_release(struct inode *inode,
|
||||
struct file *file);
|
||||
static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos);
|
||||
static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
|
||||
loff_t *pos);
|
||||
static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v);
|
||||
static int afs_proc_cell_volumes_show(struct seq_file *m, void *v);
|
||||
|
||||
static struct seq_operations afs_proc_cell_volumes_ops = {
|
||||
.start = afs_proc_cell_volumes_start,
|
||||
.next = afs_proc_cell_volumes_next,
|
||||
.stop = afs_proc_cell_volumes_stop,
|
||||
.show = afs_proc_cell_volumes_show,
|
||||
};
|
||||
|
||||
static struct file_operations afs_proc_cell_volumes_fops = {
|
||||
.open = afs_proc_cell_volumes_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = afs_proc_cell_volumes_release,
|
||||
};
|
||||
|
||||
static int afs_proc_cell_vlservers_open(struct inode *inode,
|
||||
struct file *file);
|
||||
static int afs_proc_cell_vlservers_release(struct inode *inode,
|
||||
struct file *file);
|
||||
static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos);
|
||||
static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
|
||||
loff_t *pos);
|
||||
static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v);
|
||||
static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v);
|
||||
|
||||
static struct seq_operations afs_proc_cell_vlservers_ops = {
|
||||
.start = afs_proc_cell_vlservers_start,
|
||||
.next = afs_proc_cell_vlservers_next,
|
||||
.stop = afs_proc_cell_vlservers_stop,
|
||||
.show = afs_proc_cell_vlservers_show,
|
||||
};
|
||||
|
||||
static struct file_operations afs_proc_cell_vlservers_fops = {
|
||||
.open = afs_proc_cell_vlservers_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = afs_proc_cell_vlservers_release,
|
||||
};
|
||||
|
||||
static int afs_proc_cell_servers_open(struct inode *inode, struct file *file);
|
||||
static int afs_proc_cell_servers_release(struct inode *inode,
|
||||
struct file *file);
|
||||
static void *afs_proc_cell_servers_start(struct seq_file *p, loff_t *pos);
|
||||
static void *afs_proc_cell_servers_next(struct seq_file *p, void *v,
|
||||
loff_t *pos);
|
||||
static void afs_proc_cell_servers_stop(struct seq_file *p, void *v);
|
||||
static int afs_proc_cell_servers_show(struct seq_file *m, void *v);
|
||||
|
||||
static struct seq_operations afs_proc_cell_servers_ops = {
|
||||
.start = afs_proc_cell_servers_start,
|
||||
.next = afs_proc_cell_servers_next,
|
||||
.stop = afs_proc_cell_servers_stop,
|
||||
.show = afs_proc_cell_servers_show,
|
||||
};
|
||||
|
||||
static struct file_operations afs_proc_cell_servers_fops = {
|
||||
.open = afs_proc_cell_servers_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = afs_proc_cell_servers_release,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* initialise the /proc/fs/afs/ directory
|
||||
*/
|
||||
int afs_proc_init(void)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
_enter("");
|
||||
|
||||
proc_afs = proc_mkdir("fs/afs", NULL);
|
||||
if (!proc_afs)
|
||||
goto error;
|
||||
proc_afs->owner = THIS_MODULE;
|
||||
|
||||
p = create_proc_entry("cells", 0, proc_afs);
|
||||
if (!p)
|
||||
goto error_proc;
|
||||
p->proc_fops = &afs_proc_cells_fops;
|
||||
p->owner = THIS_MODULE;
|
||||
|
||||
p = create_proc_entry("rootcell", 0, proc_afs);
|
||||
if (!p)
|
||||
goto error_cells;
|
||||
p->proc_fops = &afs_proc_rootcell_fops;
|
||||
p->owner = THIS_MODULE;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
error_cells:
|
||||
remove_proc_entry("cells", proc_afs);
|
||||
error_proc:
|
||||
remove_proc_entry("fs/afs", NULL);
|
||||
error:
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
|
||||
} /* end afs_proc_init() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clean up the /proc/fs/afs/ directory
|
||||
*/
|
||||
void afs_proc_cleanup(void)
|
||||
{
|
||||
remove_proc_entry("cells", proc_afs);
|
||||
|
||||
remove_proc_entry("fs/afs", NULL);
|
||||
|
||||
} /* end afs_proc_cleanup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* open "/proc/fs/afs/cells" which provides a summary of extant cells
|
||||
*/
|
||||
static int afs_proc_cells_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *m;
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &afs_proc_cells_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
m = file->private_data;
|
||||
m->private = PDE(inode)->data;
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cells_open() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* set up the iterator to start reading from the cells list and return the
|
||||
* first item
|
||||
*/
|
||||
static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
|
||||
{
|
||||
struct list_head *_p;
|
||||
loff_t pos = *_pos;
|
||||
|
||||
/* lock the list against modification */
|
||||
down_read(&afs_proc_cells_sem);
|
||||
|
||||
/* allow for the header line */
|
||||
if (!pos)
|
||||
return (void *) 1;
|
||||
pos--;
|
||||
|
||||
/* find the n'th element in the list */
|
||||
list_for_each(_p, &afs_proc_cells)
|
||||
if (!pos--)
|
||||
break;
|
||||
|
||||
return _p != &afs_proc_cells ? _p : NULL;
|
||||
} /* end afs_proc_cells_start() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* move to next cell in cells list
|
||||
*/
|
||||
static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos)
|
||||
{
|
||||
struct list_head *_p;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
_p = v;
|
||||
_p = v == (void *) 1 ? afs_proc_cells.next : _p->next;
|
||||
|
||||
return _p != &afs_proc_cells ? _p : NULL;
|
||||
} /* end afs_proc_cells_next() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clean up after reading from the cells list
|
||||
*/
|
||||
static void afs_proc_cells_stop(struct seq_file *p, void *v)
|
||||
{
|
||||
up_read(&afs_proc_cells_sem);
|
||||
|
||||
} /* end afs_proc_cells_stop() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* display a header line followed by a load of cell lines
|
||||
*/
|
||||
static int afs_proc_cells_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
|
||||
|
||||
/* display header on line 1 */
|
||||
if (v == (void *) 1) {
|
||||
seq_puts(m, "USE NAME\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* display one cell per line on subsequent lines */
|
||||
seq_printf(m, "%3d %s\n", atomic_read(&cell->usage), cell->name);
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cells_show() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle writes to /proc/fs/afs/cells
|
||||
* - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
|
||||
*/
|
||||
static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *_pos)
|
||||
{
|
||||
char *kbuf, *name, *args;
|
||||
int ret;
|
||||
|
||||
/* start by dragging the command into memory */
|
||||
if (size <= 1 || size >= PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
kbuf = kmalloc(size + 1, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(kbuf, buf, size) != 0)
|
||||
goto done;
|
||||
kbuf[size] = 0;
|
||||
|
||||
/* trim to first NL */
|
||||
name = memchr(kbuf, '\n', size);
|
||||
if (name)
|
||||
*name = 0;
|
||||
|
||||
/* split into command, name and argslist */
|
||||
name = strchr(kbuf, ' ');
|
||||
if (!name)
|
||||
goto inval;
|
||||
do {
|
||||
*name++ = 0;
|
||||
} while(*name == ' ');
|
||||
if (!*name)
|
||||
goto inval;
|
||||
|
||||
args = strchr(name, ' ');
|
||||
if (!args)
|
||||
goto inval;
|
||||
do {
|
||||
*args++ = 0;
|
||||
} while(*args == ' ');
|
||||
if (!*args)
|
||||
goto inval;
|
||||
|
||||
/* determine command to perform */
|
||||
_debug("cmd=%s name=%s args=%s", kbuf, name, args);
|
||||
|
||||
if (strcmp(kbuf, "add") == 0) {
|
||||
struct afs_cell *cell;
|
||||
ret = afs_cell_create(name, args, &cell);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
printk("kAFS: Added new cell '%s'\n", name);
|
||||
}
|
||||
else {
|
||||
goto inval;
|
||||
}
|
||||
|
||||
ret = size;
|
||||
|
||||
done:
|
||||
kfree(kbuf);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
inval:
|
||||
ret = -EINVAL;
|
||||
printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
|
||||
goto done;
|
||||
} /* end afs_proc_cells_write() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* Stubs for /proc/fs/afs/rootcell
|
||||
*/
|
||||
static int afs_proc_rootcell_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int afs_proc_rootcell_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *_pos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle writes to /proc/fs/afs/rootcell
|
||||
* - to initialize rootcell: echo "cell.name:192.168.231.14"
|
||||
*/
|
||||
static ssize_t afs_proc_rootcell_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t size, loff_t *_pos)
|
||||
{
|
||||
char *kbuf, *s;
|
||||
int ret;
|
||||
|
||||
/* start by dragging the command into memory */
|
||||
if (size <= 1 || size >= PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
ret = -ENOMEM;
|
||||
kbuf = kmalloc(size + 1, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
goto nomem;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(kbuf, buf, size) != 0)
|
||||
goto infault;
|
||||
kbuf[size] = 0;
|
||||
|
||||
/* trim to first NL */
|
||||
s = memchr(kbuf, '\n', size);
|
||||
if (s)
|
||||
*s = 0;
|
||||
|
||||
/* determine command to perform */
|
||||
_debug("rootcell=%s", kbuf);
|
||||
|
||||
ret = afs_cell_init(kbuf);
|
||||
if (ret >= 0)
|
||||
ret = size; /* consume everything, always */
|
||||
|
||||
infault:
|
||||
kfree(kbuf);
|
||||
nomem:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_proc_rootcell_write() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* initialise /proc/fs/afs/<cell>/
|
||||
*/
|
||||
int afs_proc_cell_setup(struct afs_cell *cell)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
_enter("%p{%s}", cell, cell->name);
|
||||
|
||||
cell->proc_dir = proc_mkdir(cell->name, proc_afs);
|
||||
if (!cell->proc_dir)
|
||||
return -ENOMEM;
|
||||
|
||||
p = create_proc_entry("servers", 0, cell->proc_dir);
|
||||
if (!p)
|
||||
goto error_proc;
|
||||
p->proc_fops = &afs_proc_cell_servers_fops;
|
||||
p->owner = THIS_MODULE;
|
||||
p->data = cell;
|
||||
|
||||
p = create_proc_entry("vlservers", 0, cell->proc_dir);
|
||||
if (!p)
|
||||
goto error_servers;
|
||||
p->proc_fops = &afs_proc_cell_vlservers_fops;
|
||||
p->owner = THIS_MODULE;
|
||||
p->data = cell;
|
||||
|
||||
p = create_proc_entry("volumes", 0, cell->proc_dir);
|
||||
if (!p)
|
||||
goto error_vlservers;
|
||||
p->proc_fops = &afs_proc_cell_volumes_fops;
|
||||
p->owner = THIS_MODULE;
|
||||
p->data = cell;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
error_vlservers:
|
||||
remove_proc_entry("vlservers", cell->proc_dir);
|
||||
error_servers:
|
||||
remove_proc_entry("servers", cell->proc_dir);
|
||||
error_proc:
|
||||
remove_proc_entry(cell->name, proc_afs);
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
} /* end afs_proc_cell_setup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* remove /proc/fs/afs/<cell>/
|
||||
*/
|
||||
void afs_proc_cell_remove(struct afs_cell *cell)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
remove_proc_entry("volumes", cell->proc_dir);
|
||||
remove_proc_entry("vlservers", cell->proc_dir);
|
||||
remove_proc_entry("servers", cell->proc_dir);
|
||||
remove_proc_entry(cell->name, proc_afs);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_proc_cell_remove() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* open "/proc/fs/afs/<cell>/volumes" which provides a summary of extant cells
|
||||
*/
|
||||
static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
struct seq_file *m;
|
||||
int ret;
|
||||
|
||||
cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
|
||||
if (!cell)
|
||||
return -ENOENT;
|
||||
|
||||
ret = seq_open(file, &afs_proc_cell_volumes_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
m = file->private_data;
|
||||
m->private = cell;
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cell_volumes_open() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* close the file and release the ref to the cell
|
||||
*/
|
||||
static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct afs_cell *cell = PDE(inode)->data;
|
||||
int ret;
|
||||
|
||||
ret = seq_release(inode,file);
|
||||
|
||||
afs_put_cell(cell);
|
||||
|
||||
return ret;
|
||||
} /* end afs_proc_cell_volumes_release() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* set up the iterator to start reading from the cells list and return the
|
||||
* first item
|
||||
*/
|
||||
static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
|
||||
{
|
||||
struct list_head *_p;
|
||||
struct afs_cell *cell = m->private;
|
||||
loff_t pos = *_pos;
|
||||
|
||||
_enter("cell=%p pos=%Ld", cell, *_pos);
|
||||
|
||||
/* lock the list against modification */
|
||||
down_read(&cell->vl_sem);
|
||||
|
||||
/* allow for the header line */
|
||||
if (!pos)
|
||||
return (void *) 1;
|
||||
pos--;
|
||||
|
||||
/* find the n'th element in the list */
|
||||
list_for_each(_p, &cell->vl_list)
|
||||
if (!pos--)
|
||||
break;
|
||||
|
||||
return _p != &cell->vl_list ? _p : NULL;
|
||||
} /* end afs_proc_cell_volumes_start() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* move to next cell in cells list
|
||||
*/
|
||||
static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
|
||||
loff_t *_pos)
|
||||
{
|
||||
struct list_head *_p;
|
||||
struct afs_cell *cell = p->private;
|
||||
|
||||
_enter("cell=%p pos=%Ld", cell, *_pos);
|
||||
|
||||
(*_pos)++;
|
||||
|
||||
_p = v;
|
||||
_p = v == (void *) 1 ? cell->vl_list.next : _p->next;
|
||||
|
||||
return _p != &cell->vl_list ? _p : NULL;
|
||||
} /* end afs_proc_cell_volumes_next() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clean up after reading from the cells list
|
||||
*/
|
||||
static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
|
||||
{
|
||||
struct afs_cell *cell = p->private;
|
||||
|
||||
up_read(&cell->vl_sem);
|
||||
|
||||
} /* end afs_proc_cell_volumes_stop() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* display a header line followed by a load of volume lines
|
||||
*/
|
||||
static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct afs_vlocation *vlocation =
|
||||
list_entry(v, struct afs_vlocation, link);
|
||||
|
||||
/* display header on line 1 */
|
||||
if (v == (void *) 1) {
|
||||
seq_puts(m, "USE VLID[0] VLID[1] VLID[2] NAME\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* display one cell per line on subsequent lines */
|
||||
seq_printf(m, "%3d %08x %08x %08x %s\n",
|
||||
atomic_read(&vlocation->usage),
|
||||
vlocation->vldb.vid[0],
|
||||
vlocation->vldb.vid[1],
|
||||
vlocation->vldb.vid[2],
|
||||
vlocation->vldb.name
|
||||
);
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cell_volumes_show() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* open "/proc/fs/afs/<cell>/vlservers" which provides a list of volume
|
||||
* location server
|
||||
*/
|
||||
static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
struct seq_file *m;
|
||||
int ret;
|
||||
|
||||
cell = afs_get_cell_maybe((struct afs_cell**)&PDE(inode)->data);
|
||||
if (!cell)
|
||||
return -ENOENT;
|
||||
|
||||
ret = seq_open(file,&afs_proc_cell_vlservers_ops);
|
||||
if (ret<0)
|
||||
return ret;
|
||||
|
||||
m = file->private_data;
|
||||
m->private = cell;
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cell_vlservers_open() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* close the file and release the ref to the cell
|
||||
*/
|
||||
static int afs_proc_cell_vlservers_release(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct afs_cell *cell = PDE(inode)->data;
|
||||
int ret;
|
||||
|
||||
ret = seq_release(inode,file);
|
||||
|
||||
afs_put_cell(cell);
|
||||
|
||||
return ret;
|
||||
} /* end afs_proc_cell_vlservers_release() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* set up the iterator to start reading from the cells list and return the
|
||||
* first item
|
||||
*/
|
||||
static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
|
||||
{
|
||||
struct afs_cell *cell = m->private;
|
||||
loff_t pos = *_pos;
|
||||
|
||||
_enter("cell=%p pos=%Ld", cell, *_pos);
|
||||
|
||||
/* lock the list against modification */
|
||||
down_read(&cell->vl_sem);
|
||||
|
||||
/* allow for the header line */
|
||||
if (!pos)
|
||||
return (void *) 1;
|
||||
pos--;
|
||||
|
||||
if (pos >= cell->vl_naddrs)
|
||||
return NULL;
|
||||
|
||||
return &cell->vl_addrs[pos];
|
||||
} /* end afs_proc_cell_vlservers_start() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* move to next cell in cells list
|
||||
*/
|
||||
static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
|
||||
loff_t *_pos)
|
||||
{
|
||||
struct afs_cell *cell = p->private;
|
||||
loff_t pos;
|
||||
|
||||
_enter("cell=%p{nad=%u} pos=%Ld", cell, cell->vl_naddrs, *_pos);
|
||||
|
||||
pos = *_pos;
|
||||
(*_pos)++;
|
||||
if (pos >= cell->vl_naddrs)
|
||||
return NULL;
|
||||
|
||||
return &cell->vl_addrs[pos];
|
||||
} /* end afs_proc_cell_vlservers_next() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clean up after reading from the cells list
|
||||
*/
|
||||
static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
|
||||
{
|
||||
struct afs_cell *cell = p->private;
|
||||
|
||||
up_read(&cell->vl_sem);
|
||||
|
||||
} /* end afs_proc_cell_vlservers_stop() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* display a header line followed by a load of volume lines
|
||||
*/
|
||||
static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct in_addr *addr = v;
|
||||
|
||||
/* display header on line 1 */
|
||||
if (v == (struct in_addr *) 1) {
|
||||
seq_puts(m, "ADDRESS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* display one cell per line on subsequent lines */
|
||||
seq_printf(m, "%u.%u.%u.%u\n", NIPQUAD(addr->s_addr));
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cell_vlservers_show() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* open "/proc/fs/afs/<cell>/servers" which provides a summary of active
|
||||
* servers
|
||||
*/
|
||||
static int afs_proc_cell_servers_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
struct seq_file *m;
|
||||
int ret;
|
||||
|
||||
cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
|
||||
if (!cell)
|
||||
return -ENOENT;
|
||||
|
||||
ret = seq_open(file, &afs_proc_cell_servers_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
m = file->private_data;
|
||||
m->private = cell;
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cell_servers_open() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* close the file and release the ref to the cell
|
||||
*/
|
||||
static int afs_proc_cell_servers_release(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct afs_cell *cell = PDE(inode)->data;
|
||||
int ret;
|
||||
|
||||
ret = seq_release(inode, file);
|
||||
|
||||
afs_put_cell(cell);
|
||||
|
||||
return ret;
|
||||
} /* end afs_proc_cell_servers_release() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* set up the iterator to start reading from the cells list and return the
|
||||
* first item
|
||||
*/
|
||||
static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
|
||||
{
|
||||
struct list_head *_p;
|
||||
struct afs_cell *cell = m->private;
|
||||
loff_t pos = *_pos;
|
||||
|
||||
_enter("cell=%p pos=%Ld", cell, *_pos);
|
||||
|
||||
/* lock the list against modification */
|
||||
read_lock(&cell->sv_lock);
|
||||
|
||||
/* allow for the header line */
|
||||
if (!pos)
|
||||
return (void *) 1;
|
||||
pos--;
|
||||
|
||||
/* find the n'th element in the list */
|
||||
list_for_each(_p, &cell->sv_list)
|
||||
if (!pos--)
|
||||
break;
|
||||
|
||||
return _p != &cell->sv_list ? _p : NULL;
|
||||
} /* end afs_proc_cell_servers_start() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* move to next cell in cells list
|
||||
*/
|
||||
static void *afs_proc_cell_servers_next(struct seq_file *p, void *v,
|
||||
loff_t *_pos)
|
||||
{
|
||||
struct list_head *_p;
|
||||
struct afs_cell *cell = p->private;
|
||||
|
||||
_enter("cell=%p pos=%Ld", cell, *_pos);
|
||||
|
||||
(*_pos)++;
|
||||
|
||||
_p = v;
|
||||
_p = v == (void *) 1 ? cell->sv_list.next : _p->next;
|
||||
|
||||
return _p != &cell->sv_list ? _p : NULL;
|
||||
} /* end afs_proc_cell_servers_next() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clean up after reading from the cells list
|
||||
*/
|
||||
static void afs_proc_cell_servers_stop(struct seq_file *p, void *v)
|
||||
{
|
||||
struct afs_cell *cell = p->private;
|
||||
|
||||
read_unlock(&cell->sv_lock);
|
||||
|
||||
} /* end afs_proc_cell_servers_stop() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* display a header line followed by a load of volume lines
|
||||
*/
|
||||
static int afs_proc_cell_servers_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct afs_server *server = list_entry(v, struct afs_server, link);
|
||||
char ipaddr[20];
|
||||
|
||||
/* display header on line 1 */
|
||||
if (v == (void *) 1) {
|
||||
seq_puts(m, "USE ADDR STATE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* display one cell per line on subsequent lines */
|
||||
sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(server->addr));
|
||||
seq_printf(m, "%3d %-15.15s %5d\n",
|
||||
atomic_read(&server->usage),
|
||||
ipaddr,
|
||||
server->fs_state
|
||||
);
|
||||
|
||||
return 0;
|
||||
} /* end afs_proc_cell_servers_show() */
|
502
fs/afs/server.c
Normal file
502
fs/afs/server.c
Normal file
@@ -0,0 +1,502 @@
|
||||
/* server.c: AFS server record management
|
||||
*
|
||||
* Copyright (C) 2002 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/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <rxrpc/peer.h>
|
||||
#include <rxrpc/connection.h>
|
||||
#include "volume.h"
|
||||
#include "cell.h"
|
||||
#include "server.h"
|
||||
#include "transport.h"
|
||||
#include "vlclient.h"
|
||||
#include "kafstimod.h"
|
||||
#include "internal.h"
|
||||
|
||||
DEFINE_SPINLOCK(afs_server_peer_lock);
|
||||
|
||||
#define FS_SERVICE_ID 1 /* AFS Volume Location Service ID */
|
||||
#define VL_SERVICE_ID 52 /* AFS Volume Location Service ID */
|
||||
|
||||
static void __afs_server_timeout(struct afs_timer *timer)
|
||||
{
|
||||
struct afs_server *server =
|
||||
list_entry(timer, struct afs_server, timeout);
|
||||
|
||||
_debug("SERVER TIMEOUT [%p{u=%d}]",
|
||||
server, atomic_read(&server->usage));
|
||||
|
||||
afs_server_do_timeout(server);
|
||||
}
|
||||
|
||||
static const struct afs_timer_ops afs_server_timer_ops = {
|
||||
.timed_out = __afs_server_timeout,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* lookup a server record in a cell
|
||||
* - TODO: search the cell's server list
|
||||
*/
|
||||
int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr,
|
||||
struct afs_server **_server)
|
||||
{
|
||||
struct afs_server *server, *active, *zombie;
|
||||
int loop;
|
||||
|
||||
_enter("%p,%08x,", cell, ntohl(addr->s_addr));
|
||||
|
||||
/* allocate and initialise a server record */
|
||||
server = kmalloc(sizeof(struct afs_server), GFP_KERNEL);
|
||||
if (!server) {
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(server, 0, sizeof(struct afs_server));
|
||||
atomic_set(&server->usage, 1);
|
||||
|
||||
INIT_LIST_HEAD(&server->link);
|
||||
init_rwsem(&server->sem);
|
||||
INIT_LIST_HEAD(&server->fs_callq);
|
||||
spin_lock_init(&server->fs_lock);
|
||||
INIT_LIST_HEAD(&server->cb_promises);
|
||||
spin_lock_init(&server->cb_lock);
|
||||
|
||||
for (loop = 0; loop < AFS_SERVER_CONN_LIST_SIZE; loop++)
|
||||
server->fs_conn_cnt[loop] = 4;
|
||||
|
||||
memcpy(&server->addr, addr, sizeof(struct in_addr));
|
||||
server->addr.s_addr = addr->s_addr;
|
||||
|
||||
afs_timer_init(&server->timeout, &afs_server_timer_ops);
|
||||
|
||||
/* add to the cell */
|
||||
write_lock(&cell->sv_lock);
|
||||
|
||||
/* check the active list */
|
||||
list_for_each_entry(active, &cell->sv_list, link) {
|
||||
if (active->addr.s_addr == addr->s_addr)
|
||||
goto use_active_server;
|
||||
}
|
||||
|
||||
/* check the inactive list */
|
||||
spin_lock(&cell->sv_gylock);
|
||||
list_for_each_entry(zombie, &cell->sv_graveyard, link) {
|
||||
if (zombie->addr.s_addr == addr->s_addr)
|
||||
goto resurrect_server;
|
||||
}
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
|
||||
afs_get_cell(cell);
|
||||
server->cell = cell;
|
||||
list_add_tail(&server->link, &cell->sv_list);
|
||||
|
||||
write_unlock(&cell->sv_lock);
|
||||
|
||||
*_server = server;
|
||||
_leave(" = 0 (%p)", server);
|
||||
return 0;
|
||||
|
||||
/* found a matching active server */
|
||||
use_active_server:
|
||||
_debug("active server");
|
||||
afs_get_server(active);
|
||||
write_unlock(&cell->sv_lock);
|
||||
|
||||
kfree(server);
|
||||
|
||||
*_server = active;
|
||||
_leave(" = 0 (%p)", active);
|
||||
return 0;
|
||||
|
||||
/* found a matching server in the graveyard, so resurrect it and
|
||||
* dispose of the new record */
|
||||
resurrect_server:
|
||||
_debug("resurrecting server");
|
||||
|
||||
list_del(&zombie->link);
|
||||
list_add_tail(&zombie->link, &cell->sv_list);
|
||||
afs_get_server(zombie);
|
||||
afs_kafstimod_del_timer(&zombie->timeout);
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
write_unlock(&cell->sv_lock);
|
||||
|
||||
kfree(server);
|
||||
|
||||
*_server = zombie;
|
||||
_leave(" = 0 (%p)", zombie);
|
||||
return 0;
|
||||
|
||||
} /* end afs_server_lookup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* destroy a server record
|
||||
* - removes from the cell list
|
||||
*/
|
||||
void afs_put_server(struct afs_server *server)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
|
||||
if (!server)
|
||||
return;
|
||||
|
||||
_enter("%p", server);
|
||||
|
||||
cell = server->cell;
|
||||
|
||||
/* sanity check */
|
||||
BUG_ON(atomic_read(&server->usage) <= 0);
|
||||
|
||||
/* to prevent a race, the decrement and the dequeue must be effectively
|
||||
* atomic */
|
||||
write_lock(&cell->sv_lock);
|
||||
|
||||
if (likely(!atomic_dec_and_test(&server->usage))) {
|
||||
write_unlock(&cell->sv_lock);
|
||||
_leave("");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&cell->sv_gylock);
|
||||
list_del(&server->link);
|
||||
list_add_tail(&server->link, &cell->sv_graveyard);
|
||||
|
||||
/* time out in 10 secs */
|
||||
afs_kafstimod_add_timer(&server->timeout, 10 * HZ);
|
||||
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
write_unlock(&cell->sv_lock);
|
||||
|
||||
_leave(" [killed]");
|
||||
} /* end afs_put_server() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* timeout server record
|
||||
* - removes from the cell's graveyard if the usage count is zero
|
||||
*/
|
||||
void afs_server_do_timeout(struct afs_server *server)
|
||||
{
|
||||
struct rxrpc_peer *peer;
|
||||
struct afs_cell *cell;
|
||||
int loop;
|
||||
|
||||
_enter("%p", server);
|
||||
|
||||
cell = server->cell;
|
||||
|
||||
BUG_ON(atomic_read(&server->usage) < 0);
|
||||
|
||||
/* remove from graveyard if still dead */
|
||||
spin_lock(&cell->vl_gylock);
|
||||
if (atomic_read(&server->usage) == 0)
|
||||
list_del_init(&server->link);
|
||||
else
|
||||
server = NULL;
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
if (!server) {
|
||||
_leave("");
|
||||
return; /* resurrected */
|
||||
}
|
||||
|
||||
/* we can now destroy it properly */
|
||||
afs_put_cell(cell);
|
||||
|
||||
/* uncross-point the structs under a global lock */
|
||||
spin_lock(&afs_server_peer_lock);
|
||||
peer = server->peer;
|
||||
if (peer) {
|
||||
server->peer = NULL;
|
||||
peer->user = NULL;
|
||||
}
|
||||
spin_unlock(&afs_server_peer_lock);
|
||||
|
||||
/* finish cleaning up the server */
|
||||
for (loop = AFS_SERVER_CONN_LIST_SIZE - 1; loop >= 0; loop--)
|
||||
if (server->fs_conn[loop])
|
||||
rxrpc_put_connection(server->fs_conn[loop]);
|
||||
|
||||
if (server->vlserver)
|
||||
rxrpc_put_connection(server->vlserver);
|
||||
|
||||
kfree(server);
|
||||
|
||||
_leave(" [destroyed]");
|
||||
} /* end afs_server_do_timeout() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* get a callslot on a connection to the fileserver on the specified server
|
||||
*/
|
||||
int afs_server_request_callslot(struct afs_server *server,
|
||||
struct afs_server_callslot *callslot)
|
||||
{
|
||||
struct afs_server_callslot *pcallslot;
|
||||
struct rxrpc_connection *conn;
|
||||
int nconn, ret;
|
||||
|
||||
_enter("%p,",server);
|
||||
|
||||
INIT_LIST_HEAD(&callslot->link);
|
||||
callslot->task = current;
|
||||
callslot->conn = NULL;
|
||||
callslot->nconn = -1;
|
||||
callslot->ready = 0;
|
||||
|
||||
ret = 0;
|
||||
conn = NULL;
|
||||
|
||||
/* get hold of a callslot first */
|
||||
spin_lock(&server->fs_lock);
|
||||
|
||||
/* resurrect the server if it's death timeout has expired */
|
||||
if (server->fs_state) {
|
||||
if (time_before(jiffies, server->fs_dead_jif)) {
|
||||
ret = server->fs_state;
|
||||
spin_unlock(&server->fs_lock);
|
||||
_leave(" = %d [still dead]", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
server->fs_state = 0;
|
||||
}
|
||||
|
||||
/* try and find a connection that has spare callslots */
|
||||
for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) {
|
||||
if (server->fs_conn_cnt[nconn] > 0) {
|
||||
server->fs_conn_cnt[nconn]--;
|
||||
spin_unlock(&server->fs_lock);
|
||||
callslot->nconn = nconn;
|
||||
goto obtained_slot;
|
||||
}
|
||||
}
|
||||
|
||||
/* none were available - wait interruptibly for one to become
|
||||
* available */
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
list_add_tail(&callslot->link, &server->fs_callq);
|
||||
spin_unlock(&server->fs_lock);
|
||||
|
||||
while (!callslot->ready && !signal_pending(current)) {
|
||||
schedule();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
/* even if we were interrupted we may still be queued */
|
||||
if (!callslot->ready) {
|
||||
spin_lock(&server->fs_lock);
|
||||
list_del_init(&callslot->link);
|
||||
spin_unlock(&server->fs_lock);
|
||||
}
|
||||
|
||||
nconn = callslot->nconn;
|
||||
|
||||
/* if interrupted, we must release any slot we also got before
|
||||
* returning an error */
|
||||
if (signal_pending(current)) {
|
||||
ret = -EINTR;
|
||||
goto error_release;
|
||||
}
|
||||
|
||||
/* if we were woken up with an error, then pass that error back to the
|
||||
* called */
|
||||
if (nconn < 0) {
|
||||
_leave(" = %d", callslot->errno);
|
||||
return callslot->errno;
|
||||
}
|
||||
|
||||
/* were we given a connection directly? */
|
||||
if (callslot->conn) {
|
||||
/* yes - use it */
|
||||
_leave(" = 0 (nc=%d)", nconn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* got a callslot, but no connection */
|
||||
obtained_slot:
|
||||
|
||||
/* need to get hold of the RxRPC connection */
|
||||
down_write(&server->sem);
|
||||
|
||||
/* quick check to see if there's an outstanding error */
|
||||
ret = server->fs_state;
|
||||
if (ret)
|
||||
goto error_release_upw;
|
||||
|
||||
if (server->fs_conn[nconn]) {
|
||||
/* reuse an existing connection */
|
||||
rxrpc_get_connection(server->fs_conn[nconn]);
|
||||
callslot->conn = server->fs_conn[nconn];
|
||||
}
|
||||
else {
|
||||
/* create a new connection */
|
||||
ret = rxrpc_create_connection(afs_transport,
|
||||
htons(7000),
|
||||
server->addr.s_addr,
|
||||
FS_SERVICE_ID,
|
||||
NULL,
|
||||
&server->fs_conn[nconn]);
|
||||
|
||||
if (ret < 0)
|
||||
goto error_release_upw;
|
||||
|
||||
callslot->conn = server->fs_conn[0];
|
||||
rxrpc_get_connection(callslot->conn);
|
||||
}
|
||||
|
||||
up_write(&server->sem);
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
/* handle an error occurring */
|
||||
error_release_upw:
|
||||
up_write(&server->sem);
|
||||
|
||||
error_release:
|
||||
/* either release the callslot or pass it along to another deserving
|
||||
* task */
|
||||
spin_lock(&server->fs_lock);
|
||||
|
||||
if (nconn < 0) {
|
||||
/* no callslot allocated */
|
||||
}
|
||||
else if (list_empty(&server->fs_callq)) {
|
||||
/* no one waiting */
|
||||
server->fs_conn_cnt[nconn]++;
|
||||
spin_unlock(&server->fs_lock);
|
||||
}
|
||||
else {
|
||||
/* someone's waiting - dequeue them and wake them up */
|
||||
pcallslot = list_entry(server->fs_callq.next,
|
||||
struct afs_server_callslot, link);
|
||||
list_del_init(&pcallslot->link);
|
||||
|
||||
pcallslot->errno = server->fs_state;
|
||||
if (!pcallslot->errno) {
|
||||
/* pass them out callslot details */
|
||||
callslot->conn = xchg(&pcallslot->conn,
|
||||
callslot->conn);
|
||||
pcallslot->nconn = nconn;
|
||||
callslot->nconn = nconn = -1;
|
||||
}
|
||||
pcallslot->ready = 1;
|
||||
wake_up_process(pcallslot->task);
|
||||
spin_unlock(&server->fs_lock);
|
||||
}
|
||||
|
||||
rxrpc_put_connection(callslot->conn);
|
||||
callslot->conn = NULL;
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_server_request_callslot() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* release a callslot back to the server
|
||||
* - transfers the RxRPC connection to the next pending callslot if possible
|
||||
*/
|
||||
void afs_server_release_callslot(struct afs_server *server,
|
||||
struct afs_server_callslot *callslot)
|
||||
{
|
||||
struct afs_server_callslot *pcallslot;
|
||||
|
||||
_enter("{ad=%08x,cnt=%u},{%d}",
|
||||
ntohl(server->addr.s_addr),
|
||||
server->fs_conn_cnt[callslot->nconn],
|
||||
callslot->nconn);
|
||||
|
||||
BUG_ON(callslot->nconn < 0);
|
||||
|
||||
spin_lock(&server->fs_lock);
|
||||
|
||||
if (list_empty(&server->fs_callq)) {
|
||||
/* no one waiting */
|
||||
server->fs_conn_cnt[callslot->nconn]++;
|
||||
spin_unlock(&server->fs_lock);
|
||||
}
|
||||
else {
|
||||
/* someone's waiting - dequeue them and wake them up */
|
||||
pcallslot = list_entry(server->fs_callq.next,
|
||||
struct afs_server_callslot, link);
|
||||
list_del_init(&pcallslot->link);
|
||||
|
||||
pcallslot->errno = server->fs_state;
|
||||
if (!pcallslot->errno) {
|
||||
/* pass them out callslot details */
|
||||
callslot->conn = xchg(&pcallslot->conn, callslot->conn);
|
||||
pcallslot->nconn = callslot->nconn;
|
||||
callslot->nconn = -1;
|
||||
}
|
||||
|
||||
pcallslot->ready = 1;
|
||||
wake_up_process(pcallslot->task);
|
||||
spin_unlock(&server->fs_lock);
|
||||
}
|
||||
|
||||
rxrpc_put_connection(callslot->conn);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_server_release_callslot() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* get a handle to a connection to the vlserver (volume location) on the
|
||||
* specified server
|
||||
*/
|
||||
int afs_server_get_vlconn(struct afs_server *server,
|
||||
struct rxrpc_connection **_conn)
|
||||
{
|
||||
struct rxrpc_connection *conn;
|
||||
int ret;
|
||||
|
||||
_enter("%p,", server);
|
||||
|
||||
ret = 0;
|
||||
conn = NULL;
|
||||
down_read(&server->sem);
|
||||
|
||||
if (server->vlserver) {
|
||||
/* reuse an existing connection */
|
||||
rxrpc_get_connection(server->vlserver);
|
||||
conn = server->vlserver;
|
||||
up_read(&server->sem);
|
||||
}
|
||||
else {
|
||||
/* create a new connection */
|
||||
up_read(&server->sem);
|
||||
down_write(&server->sem);
|
||||
if (!server->vlserver) {
|
||||
ret = rxrpc_create_connection(afs_transport,
|
||||
htons(7003),
|
||||
server->addr.s_addr,
|
||||
VL_SERVICE_ID,
|
||||
NULL,
|
||||
&server->vlserver);
|
||||
}
|
||||
if (ret == 0) {
|
||||
rxrpc_get_connection(server->vlserver);
|
||||
conn = server->vlserver;
|
||||
}
|
||||
up_write(&server->sem);
|
||||
}
|
||||
|
||||
*_conn = conn;
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_server_get_vlconn() */
|
102
fs/afs/server.h
Normal file
102
fs/afs/server.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* server.h: AFS server record
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_SERVER_H
|
||||
#define _LINUX_AFS_SERVER_H
|
||||
|
||||
#include "types.h"
|
||||
#include "kafstimod.h"
|
||||
#include <rxrpc/peer.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
extern spinlock_t afs_server_peer_lock;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS server record
|
||||
*/
|
||||
struct afs_server
|
||||
{
|
||||
atomic_t usage;
|
||||
struct afs_cell *cell; /* cell in which server resides */
|
||||
struct list_head link; /* link in cell's server list */
|
||||
struct rw_semaphore sem; /* access lock */
|
||||
struct afs_timer timeout; /* graveyard timeout */
|
||||
struct in_addr addr; /* server address */
|
||||
struct rxrpc_peer *peer; /* peer record for this server */
|
||||
struct rxrpc_connection *vlserver; /* connection to the volume location service */
|
||||
|
||||
/* file service access */
|
||||
#define AFS_SERVER_CONN_LIST_SIZE 2
|
||||
struct rxrpc_connection *fs_conn[AFS_SERVER_CONN_LIST_SIZE]; /* FS connections */
|
||||
unsigned fs_conn_cnt[AFS_SERVER_CONN_LIST_SIZE]; /* per conn call count */
|
||||
struct list_head fs_callq; /* queue of processes waiting to make a call */
|
||||
spinlock_t fs_lock; /* access lock */
|
||||
int fs_state; /* 0 or reason FS currently marked dead (-errno) */
|
||||
unsigned fs_rtt; /* FS round trip time */
|
||||
unsigned long fs_act_jif; /* time at which last activity occurred */
|
||||
unsigned long fs_dead_jif; /* time at which no longer to be considered dead */
|
||||
|
||||
/* callback promise management */
|
||||
struct list_head cb_promises; /* as yet unbroken promises from this server */
|
||||
spinlock_t cb_lock; /* access lock */
|
||||
};
|
||||
|
||||
extern int afs_server_lookup(struct afs_cell *cell,
|
||||
const struct in_addr *addr,
|
||||
struct afs_server **_server);
|
||||
|
||||
#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0)
|
||||
|
||||
extern void afs_put_server(struct afs_server *server);
|
||||
extern void afs_server_do_timeout(struct afs_server *server);
|
||||
|
||||
extern int afs_server_find_by_peer(const struct rxrpc_peer *peer,
|
||||
struct afs_server **_server);
|
||||
|
||||
extern int afs_server_get_vlconn(struct afs_server *server,
|
||||
struct rxrpc_connection **_conn);
|
||||
|
||||
static inline
|
||||
struct afs_server *afs_server_get_from_peer(struct rxrpc_peer *peer)
|
||||
{
|
||||
struct afs_server *server;
|
||||
|
||||
spin_lock(&afs_server_peer_lock);
|
||||
server = peer->user;
|
||||
if (server)
|
||||
afs_get_server(server);
|
||||
spin_unlock(&afs_server_peer_lock);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS server callslot grant record
|
||||
*/
|
||||
struct afs_server_callslot
|
||||
{
|
||||
struct list_head link; /* link in server's list */
|
||||
struct task_struct *task; /* process waiting to make call */
|
||||
struct rxrpc_connection *conn; /* connection to use (or NULL on error) */
|
||||
short nconn; /* connection slot number (-1 on error) */
|
||||
char ready; /* T when ready */
|
||||
int errno; /* error number if nconn==-1 */
|
||||
};
|
||||
|
||||
extern int afs_server_request_callslot(struct afs_server *server,
|
||||
struct afs_server_callslot *callslot);
|
||||
|
||||
extern void afs_server_release_callslot(struct afs_server *server,
|
||||
struct afs_server_callslot *callslot);
|
||||
|
||||
#endif /* _LINUX_AFS_SERVER_H */
|
441
fs/afs/super.c
Normal file
441
fs/afs/super.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Copyright (c) 2002 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This software may be freely redistributed under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Authors: David Howells <dhowells@redhat.com>
|
||||
* David Woodhouse <dwmw2@cambridge.redhat.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "vnode.h"
|
||||
#include "volume.h"
|
||||
#include "cell.h"
|
||||
#include "cmservice.h"
|
||||
#include "fsclient.h"
|
||||
#include "super.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */
|
||||
|
||||
struct afs_mount_params {
|
||||
int rwpath;
|
||||
struct afs_cell *default_cell;
|
||||
struct afs_volume *volume;
|
||||
};
|
||||
|
||||
static void afs_i_init_once(void *foo, kmem_cache_t *cachep,
|
||||
unsigned long flags);
|
||||
|
||||
static struct super_block *afs_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data);
|
||||
|
||||
static struct inode *afs_alloc_inode(struct super_block *sb);
|
||||
|
||||
static void afs_put_super(struct super_block *sb);
|
||||
|
||||
static void afs_destroy_inode(struct inode *inode);
|
||||
|
||||
static struct file_system_type afs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "afs",
|
||||
.get_sb = afs_get_sb,
|
||||
.kill_sb = kill_anon_super,
|
||||
.fs_flags = FS_BINARY_MOUNTDATA,
|
||||
};
|
||||
|
||||
static struct super_operations afs_super_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.alloc_inode = afs_alloc_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.destroy_inode = afs_destroy_inode,
|
||||
.clear_inode = afs_clear_inode,
|
||||
.put_super = afs_put_super,
|
||||
};
|
||||
|
||||
static kmem_cache_t *afs_inode_cachep;
|
||||
static atomic_t afs_count_active_inodes;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* initialise the filesystem
|
||||
*/
|
||||
int __init afs_fs_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
_enter("");
|
||||
|
||||
afs_timer_init(&afs_mntpt_expiry_timer, &afs_mntpt_expiry_timer_ops);
|
||||
|
||||
/* create ourselves an inode cache */
|
||||
atomic_set(&afs_count_active_inodes, 0);
|
||||
|
||||
ret = -ENOMEM;
|
||||
afs_inode_cachep = kmem_cache_create("afs_inode_cache",
|
||||
sizeof(struct afs_vnode),
|
||||
0,
|
||||
SLAB_HWCACHE_ALIGN,
|
||||
afs_i_init_once,
|
||||
NULL);
|
||||
if (!afs_inode_cachep) {
|
||||
printk(KERN_NOTICE "kAFS: Failed to allocate inode cache\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* now export our filesystem to lesser mortals */
|
||||
ret = register_filesystem(&afs_fs_type);
|
||||
if (ret < 0) {
|
||||
kmem_cache_destroy(afs_inode_cachep);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
} /* end afs_fs_init() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* clean up the filesystem
|
||||
*/
|
||||
void __exit afs_fs_exit(void)
|
||||
{
|
||||
unregister_filesystem(&afs_fs_type);
|
||||
|
||||
if (atomic_read(&afs_count_active_inodes) != 0) {
|
||||
printk("kAFS: %d active inode objects still present\n",
|
||||
atomic_read(&afs_count_active_inodes));
|
||||
BUG();
|
||||
}
|
||||
|
||||
kmem_cache_destroy(afs_inode_cachep);
|
||||
|
||||
} /* end afs_fs_exit() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* check that an argument has a value
|
||||
*/
|
||||
static int want_arg(char **_value, const char *option)
|
||||
{
|
||||
if (!_value || !*_value || !**_value) {
|
||||
printk(KERN_NOTICE "kAFS: %s: argument missing\n", option);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
} /* end want_arg() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* check that there's no subsequent value
|
||||
*/
|
||||
static int want_no_value(char *const *_value, const char *option)
|
||||
{
|
||||
if (*_value && **_value) {
|
||||
printk(KERN_NOTICE "kAFS: %s: Invalid argument: %s\n",
|
||||
option, *_value);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
} /* end want_no_value() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* parse the mount options
|
||||
* - this function has been shamelessly adapted from the ext3 fs which
|
||||
* shamelessly adapted it from the msdos fs
|
||||
*/
|
||||
static int afs_super_parse_options(struct afs_mount_params *params,
|
||||
char *options,
|
||||
const char **devname)
|
||||
{
|
||||
char *key, *value;
|
||||
int ret;
|
||||
|
||||
_enter("%s", options);
|
||||
|
||||
options[PAGE_SIZE - 1] = 0;
|
||||
|
||||
ret = 0;
|
||||
while ((key = strsep(&options, ",")) != 0)
|
||||
{
|
||||
value = strchr(key, '=');
|
||||
if (value)
|
||||
*value++ = 0;
|
||||
|
||||
printk("kAFS: KEY: %s, VAL:%s\n", key, value ?: "-");
|
||||
|
||||
if (strcmp(key, "rwpath") == 0) {
|
||||
if (!want_no_value(&value, "rwpath"))
|
||||
return -EINVAL;
|
||||
params->rwpath = 1;
|
||||
continue;
|
||||
}
|
||||
else if (strcmp(key, "vol") == 0) {
|
||||
if (!want_arg(&value, "vol"))
|
||||
return -EINVAL;
|
||||
*devname = value;
|
||||
continue;
|
||||
}
|
||||
else if (strcmp(key, "cell") == 0) {
|
||||
if (!want_arg(&value, "cell"))
|
||||
return -EINVAL;
|
||||
afs_put_cell(params->default_cell);
|
||||
ret = afs_cell_lookup(value,
|
||||
strlen(value),
|
||||
¶ms->default_cell);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
|
||||
printk("kAFS: Unknown mount option: '%s'\n", key);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_super_parse_options() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* check a superblock to see if it's the one we're looking for
|
||||
*/
|
||||
static int afs_test_super(struct super_block *sb, void *data)
|
||||
{
|
||||
struct afs_mount_params *params = data;
|
||||
struct afs_super_info *as = sb->s_fs_info;
|
||||
|
||||
return as->volume == params->volume;
|
||||
} /* end afs_test_super() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* fill in the superblock
|
||||
*/
|
||||
static int afs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct afs_mount_params *params = data;
|
||||
struct afs_super_info *as = NULL;
|
||||
struct afs_fid fid;
|
||||
struct dentry *root = NULL;
|
||||
struct inode *inode = NULL;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
/* allocate a superblock info record */
|
||||
as = kmalloc(sizeof(struct afs_super_info), GFP_KERNEL);
|
||||
if (!as) {
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(as, 0, sizeof(struct afs_super_info));
|
||||
|
||||
afs_get_volume(params->volume);
|
||||
as->volume = params->volume;
|
||||
|
||||
/* fill in the superblock */
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = AFS_FS_MAGIC;
|
||||
sb->s_op = &afs_super_ops;
|
||||
sb->s_fs_info = as;
|
||||
|
||||
/* allocate the root inode and dentry */
|
||||
fid.vid = as->volume->vid;
|
||||
fid.vnode = 1;
|
||||
fid.unique = 1;
|
||||
ret = afs_iget(sb, &fid, &inode);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = -ENOMEM;
|
||||
root = d_alloc_root(inode);
|
||||
if (!root)
|
||||
goto error;
|
||||
|
||||
sb->s_root = root;
|
||||
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
iput(inode);
|
||||
afs_put_volume(as->volume);
|
||||
kfree(as);
|
||||
|
||||
sb->s_fs_info = NULL;
|
||||
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_fill_super() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* get an AFS superblock
|
||||
* - TODO: don't use get_sb_nodev(), but rather call sget() directly
|
||||
*/
|
||||
static struct super_block *afs_get_sb(struct file_system_type *fs_type,
|
||||
int flags,
|
||||
const char *dev_name,
|
||||
void *options)
|
||||
{
|
||||
struct afs_mount_params params;
|
||||
struct super_block *sb;
|
||||
int ret;
|
||||
|
||||
_enter(",,%s,%p", dev_name, options);
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
/* start the cache manager */
|
||||
ret = afscm_start();
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* parse the options */
|
||||
if (options) {
|
||||
ret = afs_super_parse_options(¶ms, options, &dev_name);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
if (!dev_name) {
|
||||
printk("kAFS: no volume name specified\n");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the device name */
|
||||
ret = afs_volume_lookup(dev_name,
|
||||
params.default_cell,
|
||||
params.rwpath,
|
||||
¶ms.volume);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* allocate a deviceless superblock */
|
||||
sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms);
|
||||
if (IS_ERR(sb))
|
||||
goto error;
|
||||
|
||||
sb->s_flags = flags;
|
||||
|
||||
ret = afs_fill_super(sb, ¶ms, flags & MS_VERBOSE ? 1 : 0);
|
||||
if (ret < 0) {
|
||||
up_write(&sb->s_umount);
|
||||
deactivate_super(sb);
|
||||
goto error;
|
||||
}
|
||||
sb->s_flags |= MS_ACTIVE;
|
||||
|
||||
afs_put_volume(params.volume);
|
||||
afs_put_cell(params.default_cell);
|
||||
_leave(" = %p", sb);
|
||||
return sb;
|
||||
|
||||
error:
|
||||
afs_put_volume(params.volume);
|
||||
afs_put_cell(params.default_cell);
|
||||
afscm_stop();
|
||||
_leave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
} /* end afs_get_sb() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* finish the unmounting process on the superblock
|
||||
*/
|
||||
static void afs_put_super(struct super_block *sb)
|
||||
{
|
||||
struct afs_super_info *as = sb->s_fs_info;
|
||||
|
||||
_enter("");
|
||||
|
||||
afs_put_volume(as->volume);
|
||||
afscm_stop();
|
||||
|
||||
_leave("");
|
||||
} /* end afs_put_super() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* initialise an inode cache slab element prior to any use
|
||||
*/
|
||||
static void afs_i_init_once(void *_vnode, kmem_cache_t *cachep,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct afs_vnode *vnode = (struct afs_vnode *) _vnode;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
|
||||
SLAB_CTOR_CONSTRUCTOR) {
|
||||
memset(vnode, 0, sizeof(*vnode));
|
||||
inode_init_once(&vnode->vfs_inode);
|
||||
init_waitqueue_head(&vnode->update_waitq);
|
||||
spin_lock_init(&vnode->lock);
|
||||
INIT_LIST_HEAD(&vnode->cb_link);
|
||||
INIT_LIST_HEAD(&vnode->cb_hash_link);
|
||||
afs_timer_init(&vnode->cb_timeout,
|
||||
&afs_vnode_cb_timed_out_ops);
|
||||
}
|
||||
|
||||
} /* end afs_i_init_once() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* allocate an AFS inode struct from our slab cache
|
||||
*/
|
||||
static struct inode *afs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
|
||||
vnode = (struct afs_vnode *)
|
||||
kmem_cache_alloc(afs_inode_cachep, SLAB_KERNEL);
|
||||
if (!vnode)
|
||||
return NULL;
|
||||
|
||||
atomic_inc(&afs_count_active_inodes);
|
||||
|
||||
memset(&vnode->fid, 0, sizeof(vnode->fid));
|
||||
memset(&vnode->status, 0, sizeof(vnode->status));
|
||||
|
||||
vnode->volume = NULL;
|
||||
vnode->update_cnt = 0;
|
||||
vnode->flags = 0;
|
||||
|
||||
return &vnode->vfs_inode;
|
||||
} /* end afs_alloc_inode() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* destroy an AFS inode struct
|
||||
*/
|
||||
static void afs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
_enter("{%lu}", inode->i_ino);
|
||||
|
||||
kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode));
|
||||
|
||||
atomic_dec(&afs_count_active_inodes);
|
||||
|
||||
} /* end afs_destroy_inode() */
|
43
fs/afs/super.h
Normal file
43
fs/afs/super.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* super.h: AFS filesystem internal private data
|
||||
*
|
||||
* Copyright (c) 2002 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This software may be freely redistributed under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
|
||||
* David Howells <dhowells@redhat.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_SUPER_H
|
||||
#define _LINUX_AFS_SUPER_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include "server.h"
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS superblock private data
|
||||
* - there's one superblock per volume
|
||||
*/
|
||||
struct afs_super_info
|
||||
{
|
||||
struct afs_volume *volume; /* volume record */
|
||||
char rwparent; /* T if parent is R/W AFS volume */
|
||||
};
|
||||
|
||||
static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
|
||||
{
|
||||
return sb->s_fs_info;
|
||||
}
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _LINUX_AFS_SUPER_H */
|
21
fs/afs/transport.h
Normal file
21
fs/afs/transport.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* transport.h: AFS transport management
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_TRANSPORT_H
|
||||
#define _LINUX_AFS_TRANSPORT_H
|
||||
|
||||
#include "types.h"
|
||||
#include <rxrpc/transport.h>
|
||||
|
||||
/* the cache manager transport endpoint */
|
||||
extern struct rxrpc_transport *afs_transport;
|
||||
|
||||
#endif /* _LINUX_AFS_TRANSPORT_H */
|
125
fs/afs/types.h
Normal file
125
fs/afs/types.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/* types.h: AFS types
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_TYPES_H
|
||||
#define _LINUX_AFS_TYPES_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <rxrpc/types.h>
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
typedef unsigned afs_volid_t;
|
||||
typedef unsigned afs_vnodeid_t;
|
||||
typedef unsigned long long afs_dataversion_t;
|
||||
|
||||
typedef enum {
|
||||
AFSVL_RWVOL, /* read/write volume */
|
||||
AFSVL_ROVOL, /* read-only volume */
|
||||
AFSVL_BACKVOL, /* backup volume */
|
||||
} __attribute__((packed)) afs_voltype_t;
|
||||
|
||||
typedef enum {
|
||||
AFS_FTYPE_INVALID = 0,
|
||||
AFS_FTYPE_FILE = 1,
|
||||
AFS_FTYPE_DIR = 2,
|
||||
AFS_FTYPE_SYMLINK = 3,
|
||||
} afs_file_type_t;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct afs_cell;
|
||||
struct afs_vnode;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS file identifier
|
||||
*/
|
||||
struct afs_fid
|
||||
{
|
||||
afs_volid_t vid; /* volume ID */
|
||||
afs_vnodeid_t vnode; /* file index within volume */
|
||||
unsigned unique; /* unique ID number (file index version) */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS callback notification
|
||||
*/
|
||||
typedef enum {
|
||||
AFSCM_CB_UNTYPED = 0, /* no type set on CB break */
|
||||
AFSCM_CB_EXCLUSIVE = 1, /* CB exclusive to CM [not implemented] */
|
||||
AFSCM_CB_SHARED = 2, /* CB shared by other CM's */
|
||||
AFSCM_CB_DROPPED = 3, /* CB promise cancelled by file server */
|
||||
} afs_callback_type_t;
|
||||
|
||||
struct afs_callback
|
||||
{
|
||||
struct afs_server *server; /* server that made the promise */
|
||||
struct afs_fid fid; /* file identifier */
|
||||
unsigned version; /* callback version */
|
||||
unsigned expiry; /* time at which expires */
|
||||
afs_callback_type_t type; /* type of callback */
|
||||
};
|
||||
|
||||
#define AFSCBMAX 50
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS volume information
|
||||
*/
|
||||
struct afs_volume_info
|
||||
{
|
||||
afs_volid_t vid; /* volume ID */
|
||||
afs_voltype_t type; /* type of this volume */
|
||||
afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */
|
||||
|
||||
/* list of fileservers serving this volume */
|
||||
size_t nservers; /* number of entries used in servers[] */
|
||||
struct {
|
||||
struct in_addr addr; /* fileserver address */
|
||||
} servers[8];
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS file status information
|
||||
*/
|
||||
struct afs_file_status
|
||||
{
|
||||
unsigned if_version; /* interface version */
|
||||
#define AFS_FSTATUS_VERSION 1
|
||||
|
||||
afs_file_type_t type; /* file type */
|
||||
unsigned nlink; /* link count */
|
||||
size_t size; /* file size */
|
||||
afs_dataversion_t version; /* current data version */
|
||||
unsigned author; /* author ID */
|
||||
unsigned owner; /* owner ID */
|
||||
unsigned caller_access; /* access rights for authenticated caller */
|
||||
unsigned anon_access; /* access rights for unauthenticated caller */
|
||||
umode_t mode; /* UNIX mode */
|
||||
struct afs_fid parent; /* parent file ID */
|
||||
time_t mtime_client; /* last time client changed data */
|
||||
time_t mtime_server; /* last time server changed data */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS volume synchronisation information
|
||||
*/
|
||||
struct afs_volsync
|
||||
{
|
||||
time_t creation; /* volume creation time */
|
||||
};
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _LINUX_AFS_TYPES_H */
|
695
fs/afs/vlclient.c
Normal file
695
fs/afs/vlclient.c
Normal file
@@ -0,0 +1,695 @@
|
||||
/* vlclient.c: AFS Volume Location Service client
|
||||
*
|
||||
* Copyright (C) 2002 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/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <rxrpc/rxrpc.h>
|
||||
#include <rxrpc/transport.h>
|
||||
#include <rxrpc/connection.h>
|
||||
#include <rxrpc/call.h>
|
||||
#include "server.h"
|
||||
#include "volume.h"
|
||||
#include "vlclient.h"
|
||||
#include "kafsasyncd.h"
|
||||
#include "kafstimod.h"
|
||||
#include "errors.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define VLGETENTRYBYID 503 /* AFS Get Cache Entry By ID operation ID */
|
||||
#define VLGETENTRYBYNAME 504 /* AFS Get Cache Entry By Name operation ID */
|
||||
#define VLPROBE 514 /* AFS Probe Volume Location Service operation ID */
|
||||
|
||||
static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call);
|
||||
static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call);
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* map afs VL abort codes to/from Linux error codes
|
||||
* - called with call->lock held
|
||||
*/
|
||||
static void afs_rxvl_aemap(struct rxrpc_call *call)
|
||||
{
|
||||
int err;
|
||||
|
||||
_enter("{%u,%u,%d}",
|
||||
call->app_err_state, call->app_abort_code, call->app_errno);
|
||||
|
||||
switch (call->app_err_state) {
|
||||
case RXRPC_ESTATE_LOCAL_ABORT:
|
||||
call->app_abort_code = -call->app_errno;
|
||||
return;
|
||||
|
||||
case RXRPC_ESTATE_PEER_ABORT:
|
||||
switch (call->app_abort_code) {
|
||||
case AFSVL_IDEXIST: err = -EEXIST; break;
|
||||
case AFSVL_IO: err = -EREMOTEIO; break;
|
||||
case AFSVL_NAMEEXIST: err = -EEXIST; break;
|
||||
case AFSVL_CREATEFAIL: err = -EREMOTEIO; break;
|
||||
case AFSVL_NOENT: err = -ENOMEDIUM; break;
|
||||
case AFSVL_EMPTY: err = -ENOMEDIUM; break;
|
||||
case AFSVL_ENTDELETED: err = -ENOMEDIUM; break;
|
||||
case AFSVL_BADNAME: err = -EINVAL; break;
|
||||
case AFSVL_BADINDEX: err = -EINVAL; break;
|
||||
case AFSVL_BADVOLTYPE: err = -EINVAL; break;
|
||||
case AFSVL_BADSERVER: err = -EINVAL; break;
|
||||
case AFSVL_BADPARTITION: err = -EINVAL; break;
|
||||
case AFSVL_REPSFULL: err = -EFBIG; break;
|
||||
case AFSVL_NOREPSERVER: err = -ENOENT; break;
|
||||
case AFSVL_DUPREPSERVER: err = -EEXIST; break;
|
||||
case AFSVL_RWNOTFOUND: err = -ENOENT; break;
|
||||
case AFSVL_BADREFCOUNT: err = -EINVAL; break;
|
||||
case AFSVL_SIZEEXCEEDED: err = -EINVAL; break;
|
||||
case AFSVL_BADENTRY: err = -EINVAL; break;
|
||||
case AFSVL_BADVOLIDBUMP: err = -EINVAL; break;
|
||||
case AFSVL_IDALREADYHASHED: err = -EINVAL; break;
|
||||
case AFSVL_ENTRYLOCKED: err = -EBUSY; break;
|
||||
case AFSVL_BADVOLOPER: err = -EBADRQC; break;
|
||||
case AFSVL_BADRELLOCKTYPE: err = -EINVAL; break;
|
||||
case AFSVL_RERELEASE: err = -EREMOTEIO; break;
|
||||
case AFSVL_BADSERVERFLAG: err = -EINVAL; break;
|
||||
case AFSVL_PERM: err = -EACCES; break;
|
||||
case AFSVL_NOMEM: err = -EREMOTEIO; break;
|
||||
default:
|
||||
err = afs_abort_to_error(call->app_abort_code);
|
||||
break;
|
||||
}
|
||||
call->app_errno = err;
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} /* end afs_rxvl_aemap() */
|
||||
|
||||
#if 0
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* probe a volume location server to see if it is still alive -- unused
|
||||
*/
|
||||
static int afs_rxvl_probe(struct afs_server *server, int alloc_flags)
|
||||
{
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[1];
|
||||
size_t sent;
|
||||
int ret;
|
||||
__be32 param[1];
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
/* get hold of the vlserver connection */
|
||||
ret = afs_server_get_vlconn(server, &conn);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = VLPROBE;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
param[0] = htonl(VLPROBE);
|
||||
piov[0].iov_len = sizeof(param);
|
||||
piov[0].iov_base = param;
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET,
|
||||
alloc_flags, 0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY ||
|
||||
signal_pending(current))
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
ret = -EINTR;
|
||||
if (signal_pending(current))
|
||||
goto abort;
|
||||
|
||||
switch (call->app_call_state) {
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
|
||||
case RXRPC_CSTATE_CLNT_GOT_REPLY:
|
||||
ret = 0;
|
||||
goto out_unwait;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
rxrpc_put_connection(conn);
|
||||
out:
|
||||
return ret;
|
||||
|
||||
} /* end afs_rxvl_probe() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* look up a volume location database entry by name
|
||||
*/
|
||||
int afs_rxvl_get_entry_by_name(struct afs_server *server,
|
||||
const char *volname,
|
||||
unsigned volnamesz,
|
||||
struct afs_cache_vlocation *entry)
|
||||
{
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[3];
|
||||
unsigned tmp;
|
||||
size_t sent;
|
||||
int ret, loop;
|
||||
__be32 *bp, param[2], zero;
|
||||
|
||||
_enter(",%*.*s,%u,", volnamesz, volnamesz, volname, volnamesz);
|
||||
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
/* get hold of the vlserver connection */
|
||||
ret = afs_server_get_vlconn(server, &conn);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = VLGETENTRYBYNAME;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
piov[1].iov_len = volnamesz;
|
||||
piov[1].iov_base = (char *) volname;
|
||||
|
||||
zero = 0;
|
||||
piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3;
|
||||
piov[2].iov_base = &zero;
|
||||
|
||||
param[0] = htonl(VLGETENTRYBYNAME);
|
||||
param[1] = htonl(piov[1].iov_len);
|
||||
|
||||
piov[0].iov_len = sizeof(param);
|
||||
piov[0].iov_base = param;
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
bp = rxrpc_call_alloc_scratch(call, 384);
|
||||
|
||||
ret = rxrpc_call_read_data(call, bp, 384,
|
||||
RXRPC_CALL_READ_BLOCK |
|
||||
RXRPC_CALL_READ_ALL);
|
||||
if (ret < 0) {
|
||||
if (ret == -ECONNABORTED) {
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
}
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* unmarshall the reply */
|
||||
for (loop = 0; loop < 64; loop++)
|
||||
entry->name[loop] = ntohl(*bp++);
|
||||
bp++; /* final NUL */
|
||||
|
||||
bp++; /* type */
|
||||
entry->nservers = ntohl(*bp++);
|
||||
|
||||
for (loop = 0; loop < 8; loop++)
|
||||
entry->servers[loop].s_addr = *bp++;
|
||||
|
||||
bp += 8; /* partition IDs */
|
||||
|
||||
for (loop = 0; loop < 8; loop++) {
|
||||
tmp = ntohl(*bp++);
|
||||
if (tmp & AFS_VLSF_RWVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
|
||||
if (tmp & AFS_VLSF_ROVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
|
||||
if (tmp & AFS_VLSF_BACKVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
|
||||
}
|
||||
|
||||
entry->vid[0] = ntohl(*bp++);
|
||||
entry->vid[1] = ntohl(*bp++);
|
||||
entry->vid[2] = ntohl(*bp++);
|
||||
|
||||
bp++; /* clone ID */
|
||||
|
||||
tmp = ntohl(*bp++); /* flags */
|
||||
if (tmp & AFS_VLF_RWEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_RW;
|
||||
if (tmp & AFS_VLF_ROEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_RO;
|
||||
if (tmp & AFS_VLF_BACKEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_BAK;
|
||||
|
||||
ret = -ENOMEDIUM;
|
||||
if (!entry->vidmask)
|
||||
goto abort;
|
||||
|
||||
/* success */
|
||||
entry->rtime = get_seconds();
|
||||
ret = 0;
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
rxrpc_put_connection(conn);
|
||||
out:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
goto out_unwait;
|
||||
} /* end afs_rxvl_get_entry_by_name() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* look up a volume location database entry by ID
|
||||
*/
|
||||
int afs_rxvl_get_entry_by_id(struct afs_server *server,
|
||||
afs_volid_t volid,
|
||||
afs_voltype_t voltype,
|
||||
struct afs_cache_vlocation *entry)
|
||||
{
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[1];
|
||||
unsigned tmp;
|
||||
size_t sent;
|
||||
int ret, loop;
|
||||
__be32 *bp, param[3];
|
||||
|
||||
_enter(",%x,%d,", volid, voltype);
|
||||
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
/* get hold of the vlserver connection */
|
||||
ret = afs_server_get_vlconn(server, &conn);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
goto out_put_conn;
|
||||
}
|
||||
call->app_opcode = VLGETENTRYBYID;
|
||||
|
||||
/* we want to get event notifications from the call */
|
||||
add_wait_queue(&call->waitq, &myself);
|
||||
|
||||
/* marshall the parameters */
|
||||
param[0] = htonl(VLGETENTRYBYID);
|
||||
param[1] = htonl(volid);
|
||||
param[2] = htonl(voltype);
|
||||
|
||||
piov[0].iov_len = sizeof(param);
|
||||
piov[0].iov_base = param;
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
bp = rxrpc_call_alloc_scratch(call, 384);
|
||||
|
||||
ret = rxrpc_call_read_data(call, bp, 384,
|
||||
RXRPC_CALL_READ_BLOCK |
|
||||
RXRPC_CALL_READ_ALL);
|
||||
if (ret < 0) {
|
||||
if (ret == -ECONNABORTED) {
|
||||
ret = call->app_errno;
|
||||
goto out_unwait;
|
||||
}
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* unmarshall the reply */
|
||||
for (loop = 0; loop < 64; loop++)
|
||||
entry->name[loop] = ntohl(*bp++);
|
||||
bp++; /* final NUL */
|
||||
|
||||
bp++; /* type */
|
||||
entry->nservers = ntohl(*bp++);
|
||||
|
||||
for (loop = 0; loop < 8; loop++)
|
||||
entry->servers[loop].s_addr = *bp++;
|
||||
|
||||
bp += 8; /* partition IDs */
|
||||
|
||||
for (loop = 0; loop < 8; loop++) {
|
||||
tmp = ntohl(*bp++);
|
||||
if (tmp & AFS_VLSF_RWVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
|
||||
if (tmp & AFS_VLSF_ROVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
|
||||
if (tmp & AFS_VLSF_BACKVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
|
||||
}
|
||||
|
||||
entry->vid[0] = ntohl(*bp++);
|
||||
entry->vid[1] = ntohl(*bp++);
|
||||
entry->vid[2] = ntohl(*bp++);
|
||||
|
||||
bp++; /* clone ID */
|
||||
|
||||
tmp = ntohl(*bp++); /* flags */
|
||||
if (tmp & AFS_VLF_RWEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_RW;
|
||||
if (tmp & AFS_VLF_ROEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_RO;
|
||||
if (tmp & AFS_VLF_BACKEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_BAK;
|
||||
|
||||
ret = -ENOMEDIUM;
|
||||
if (!entry->vidmask)
|
||||
goto abort;
|
||||
|
||||
#if 0 /* TODO: remove */
|
||||
entry->nservers = 3;
|
||||
entry->servers[0].s_addr = htonl(0xac101249);
|
||||
entry->servers[1].s_addr = htonl(0xac101243);
|
||||
entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/);
|
||||
|
||||
entry->srvtmask[0] = AFS_VOL_VTM_RO;
|
||||
entry->srvtmask[1] = AFS_VOL_VTM_RO;
|
||||
entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW;
|
||||
#endif
|
||||
|
||||
/* success */
|
||||
entry->rtime = get_seconds();
|
||||
ret = 0;
|
||||
|
||||
out_unwait:
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&call->waitq, &myself);
|
||||
rxrpc_put_call(call);
|
||||
out_put_conn:
|
||||
rxrpc_put_connection(conn);
|
||||
out:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
abort:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
rxrpc_call_abort(call, ret);
|
||||
schedule();
|
||||
goto out_unwait;
|
||||
} /* end afs_rxvl_get_entry_by_id() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* look up a volume location database entry by ID asynchronously
|
||||
*/
|
||||
int afs_rxvl_get_entry_by_id_async(struct afs_async_op *op,
|
||||
afs_volid_t volid,
|
||||
afs_voltype_t voltype)
|
||||
{
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_call *call;
|
||||
struct kvec piov[1];
|
||||
size_t sent;
|
||||
int ret;
|
||||
__be32 param[3];
|
||||
|
||||
_enter(",%x,%d,", volid, voltype);
|
||||
|
||||
/* get hold of the vlserver connection */
|
||||
ret = afs_server_get_vlconn(op->server, &conn);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* create a call through that connection */
|
||||
ret = rxrpc_create_call(conn,
|
||||
afs_rxvl_get_entry_by_id_attn,
|
||||
afs_rxvl_get_entry_by_id_error,
|
||||
afs_rxvl_aemap,
|
||||
&op->call);
|
||||
rxrpc_put_connection(conn);
|
||||
|
||||
if (ret < 0) {
|
||||
printk("kAFS: Unable to create call: %d\n", ret);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
op->call->app_opcode = VLGETENTRYBYID;
|
||||
op->call->app_user = op;
|
||||
|
||||
call = op->call;
|
||||
rxrpc_get_call(call);
|
||||
|
||||
/* send event notifications from the call to kafsasyncd */
|
||||
afs_kafsasyncd_begin_op(op);
|
||||
|
||||
/* marshall the parameters */
|
||||
param[0] = htonl(VLGETENTRYBYID);
|
||||
param[1] = htonl(volid);
|
||||
param[2] = htonl(voltype);
|
||||
|
||||
piov[0].iov_len = sizeof(param);
|
||||
piov[0].iov_base = param;
|
||||
|
||||
/* allocate result read buffer in scratch space */
|
||||
call->app_scr_ptr = rxrpc_call_alloc_scratch(op->call, 384);
|
||||
|
||||
/* send the parameters to the server */
|
||||
ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
|
||||
0, &sent);
|
||||
if (ret < 0) {
|
||||
rxrpc_call_abort(call, ret); /* handle from kafsasyncd */
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* wait for the reply to completely arrive */
|
||||
ret = rxrpc_call_read_data(call, call->app_scr_ptr, 384, 0);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
case -EAGAIN:
|
||||
case -ECONNABORTED:
|
||||
ret = 0;
|
||||
break; /* all handled by kafsasyncd */
|
||||
|
||||
default:
|
||||
rxrpc_call_abort(call, ret); /* make kafsasyncd handle it */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
rxrpc_put_call(call);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_rxvl_get_entry_by_id_async() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* attend to the asynchronous get VLDB entry by ID
|
||||
*/
|
||||
int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *op,
|
||||
struct afs_cache_vlocation *entry)
|
||||
{
|
||||
__be32 *bp;
|
||||
__u32 tmp;
|
||||
int loop, ret;
|
||||
|
||||
_enter("{op=%p cst=%u}", op, op->call->app_call_state);
|
||||
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
if (op->call->app_call_state == RXRPC_CSTATE_COMPLETE) {
|
||||
/* operation finished */
|
||||
afs_kafsasyncd_terminate_op(op);
|
||||
|
||||
bp = op->call->app_scr_ptr;
|
||||
|
||||
/* unmarshall the reply */
|
||||
for (loop = 0; loop < 64; loop++)
|
||||
entry->name[loop] = ntohl(*bp++);
|
||||
bp++; /* final NUL */
|
||||
|
||||
bp++; /* type */
|
||||
entry->nservers = ntohl(*bp++);
|
||||
|
||||
for (loop = 0; loop < 8; loop++)
|
||||
entry->servers[loop].s_addr = *bp++;
|
||||
|
||||
bp += 8; /* partition IDs */
|
||||
|
||||
for (loop = 0; loop < 8; loop++) {
|
||||
tmp = ntohl(*bp++);
|
||||
if (tmp & AFS_VLSF_RWVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
|
||||
if (tmp & AFS_VLSF_ROVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
|
||||
if (tmp & AFS_VLSF_BACKVOL)
|
||||
entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
|
||||
}
|
||||
|
||||
entry->vid[0] = ntohl(*bp++);
|
||||
entry->vid[1] = ntohl(*bp++);
|
||||
entry->vid[2] = ntohl(*bp++);
|
||||
|
||||
bp++; /* clone ID */
|
||||
|
||||
tmp = ntohl(*bp++); /* flags */
|
||||
if (tmp & AFS_VLF_RWEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_RW;
|
||||
if (tmp & AFS_VLF_ROEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_RO;
|
||||
if (tmp & AFS_VLF_BACKEXISTS)
|
||||
entry->vidmask |= AFS_VOL_VTM_BAK;
|
||||
|
||||
ret = -ENOMEDIUM;
|
||||
if (!entry->vidmask) {
|
||||
rxrpc_call_abort(op->call, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
#if 0 /* TODO: remove */
|
||||
entry->nservers = 3;
|
||||
entry->servers[0].s_addr = htonl(0xac101249);
|
||||
entry->servers[1].s_addr = htonl(0xac101243);
|
||||
entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/);
|
||||
|
||||
entry->srvtmask[0] = AFS_VOL_VTM_RO;
|
||||
entry->srvtmask[1] = AFS_VOL_VTM_RO;
|
||||
entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW;
|
||||
#endif
|
||||
|
||||
/* success */
|
||||
entry->rtime = get_seconds();
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (op->call->app_call_state == RXRPC_CSTATE_ERROR) {
|
||||
/* operation error */
|
||||
ret = op->call->app_errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
_leave(" = -EAGAIN");
|
||||
return -EAGAIN;
|
||||
|
||||
done:
|
||||
rxrpc_put_call(op->call);
|
||||
op->call = NULL;
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_rxvl_get_entry_by_id_async2() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle attention events on an async get-entry-by-ID op
|
||||
* - called from krxiod
|
||||
*/
|
||||
static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call)
|
||||
{
|
||||
struct afs_async_op *op = call->app_user;
|
||||
|
||||
_enter("{op=%p cst=%u}", op, call->app_call_state);
|
||||
|
||||
switch (call->app_call_state) {
|
||||
case RXRPC_CSTATE_COMPLETE:
|
||||
afs_kafsasyncd_attend_op(op);
|
||||
break;
|
||||
case RXRPC_CSTATE_CLNT_RCV_REPLY:
|
||||
if (call->app_async_read)
|
||||
break;
|
||||
case RXRPC_CSTATE_CLNT_GOT_REPLY:
|
||||
if (call->app_read_count == 0)
|
||||
break;
|
||||
printk("kAFS: Reply bigger than expected"
|
||||
" {cst=%u asyn=%d mark=%Zu rdy=%Zu pr=%u%s}",
|
||||
call->app_call_state,
|
||||
call->app_async_read,
|
||||
call->app_mark,
|
||||
call->app_ready_qty,
|
||||
call->pkt_rcv_count,
|
||||
call->app_last_rcv ? " last" : "");
|
||||
|
||||
rxrpc_call_abort(call, -EBADMSG);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
_leave("");
|
||||
|
||||
} /* end afs_rxvl_get_entry_by_id_attn() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle error events on an async get-entry-by-ID op
|
||||
* - called from krxiod
|
||||
*/
|
||||
static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call)
|
||||
{
|
||||
struct afs_async_op *op = call->app_user;
|
||||
|
||||
_enter("{op=%p cst=%u}", op, call->app_call_state);
|
||||
|
||||
afs_kafsasyncd_attend_op(op);
|
||||
|
||||
_leave("");
|
||||
|
||||
} /* end afs_rxvl_get_entry_by_id_error() */
|
93
fs/afs/vlclient.h
Normal file
93
fs/afs/vlclient.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* vlclient.h: Volume Location Service client interface
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_VLCLIENT_H
|
||||
#define _LINUX_AFS_VLCLIENT_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
enum AFSVL_Errors {
|
||||
AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */
|
||||
AFSVL_IO = 363521, /* I/O related error */
|
||||
AFSVL_NAMEEXIST = 363522, /* Volume name entry exists in vl database */
|
||||
AFSVL_CREATEFAIL = 363523, /* Internal creation failure */
|
||||
AFSVL_NOENT = 363524, /* No such entry */
|
||||
AFSVL_EMPTY = 363525, /* Vl database is empty */
|
||||
AFSVL_ENTDELETED = 363526, /* Entry is deleted (soft delete) */
|
||||
AFSVL_BADNAME = 363527, /* Volume name is illegal */
|
||||
AFSVL_BADINDEX = 363528, /* Index is out of range */
|
||||
AFSVL_BADVOLTYPE = 363529, /* Bad volume type */
|
||||
AFSVL_BADSERVER = 363530, /* Illegal server number (out of range) */
|
||||
AFSVL_BADPARTITION = 363531, /* Bad partition number */
|
||||
AFSVL_REPSFULL = 363532, /* Run out of space for Replication sites */
|
||||
AFSVL_NOREPSERVER = 363533, /* No such Replication server site exists */
|
||||
AFSVL_DUPREPSERVER = 363534, /* Replication site already exists */
|
||||
AFSVL_RWNOTFOUND = 363535, /* Parent R/W entry not found */
|
||||
AFSVL_BADREFCOUNT = 363536, /* Illegal Reference Count number */
|
||||
AFSVL_SIZEEXCEEDED = 363537, /* Vl size for attributes exceeded */
|
||||
AFSVL_BADENTRY = 363538, /* Bad incoming vl entry */
|
||||
AFSVL_BADVOLIDBUMP = 363539, /* Illegal max volid increment */
|
||||
AFSVL_IDALREADYHASHED = 363540, /* RO/BACK id already hashed */
|
||||
AFSVL_ENTRYLOCKED = 363541, /* Vl entry is already locked */
|
||||
AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */
|
||||
AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */
|
||||
AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */
|
||||
AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server <20>ag */
|
||||
AFSVL_PERM = 363546, /* No permission access */
|
||||
AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */
|
||||
};
|
||||
|
||||
/* maps to "struct vldbentry" in vvl-spec.pdf */
|
||||
struct afs_vldbentry {
|
||||
char name[65]; /* name of volume (including NUL char) */
|
||||
afs_voltype_t type; /* volume type */
|
||||
unsigned num_servers; /* num servers that hold instances of this vol */
|
||||
unsigned clone_id; /* cloning ID */
|
||||
|
||||
unsigned flags;
|
||||
#define AFS_VLF_RWEXISTS 0x1000 /* R/W volume exists */
|
||||
#define AFS_VLF_ROEXISTS 0x2000 /* R/O volume exists */
|
||||
#define AFS_VLF_BACKEXISTS 0x4000 /* backup volume exists */
|
||||
|
||||
afs_volid_t volume_ids[3]; /* volume IDs */
|
||||
|
||||
struct {
|
||||
struct in_addr addr; /* server address */
|
||||
unsigned partition; /* partition ID on this server */
|
||||
unsigned flags; /* server specific flags */
|
||||
#define AFS_VLSF_NEWREPSITE 0x0001 /* unused */
|
||||
#define AFS_VLSF_ROVOL 0x0002 /* this server holds a R/O instance of the volume */
|
||||
#define AFS_VLSF_RWVOL 0x0004 /* this server holds a R/W instance of the volume */
|
||||
#define AFS_VLSF_BACKVOL 0x0008 /* this server holds a backup instance of the volume */
|
||||
} servers[8];
|
||||
|
||||
};
|
||||
|
||||
/* look up a volume location database entry by name */
|
||||
extern int afs_rxvl_get_entry_by_name(struct afs_server *server,
|
||||
const char *volname,
|
||||
unsigned volnamesz,
|
||||
struct afs_cache_vlocation *entry);
|
||||
|
||||
/* look up a volume location database entry by ID */
|
||||
extern int afs_rxvl_get_entry_by_id(struct afs_server *server,
|
||||
afs_volid_t volid,
|
||||
afs_voltype_t voltype,
|
||||
struct afs_cache_vlocation *entry);
|
||||
|
||||
extern int afs_rxvl_get_entry_by_id_async(struct afs_async_op *op,
|
||||
afs_volid_t volid,
|
||||
afs_voltype_t voltype);
|
||||
|
||||
extern int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *op,
|
||||
struct afs_cache_vlocation *entry);
|
||||
|
||||
#endif /* _LINUX_AFS_VLCLIENT_H */
|
954
fs/afs/vlocation.c
Normal file
954
fs/afs/vlocation.c
Normal file
@@ -0,0 +1,954 @@
|
||||
/* vlocation.c: volume location management
|
||||
*
|
||||
* Copyright (C) 2002 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/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "volume.h"
|
||||
#include "cell.h"
|
||||
#include "cmservice.h"
|
||||
#include "fsclient.h"
|
||||
#include "vlclient.h"
|
||||
#include "kafstimod.h"
|
||||
#include <rxrpc/connection.h>
|
||||
#include "internal.h"
|
||||
|
||||
#define AFS_VLDB_TIMEOUT HZ*1000
|
||||
|
||||
static void afs_vlocation_update_timer(struct afs_timer *timer);
|
||||
static void afs_vlocation_update_attend(struct afs_async_op *op);
|
||||
static void afs_vlocation_update_discard(struct afs_async_op *op);
|
||||
static void __afs_put_vlocation(struct afs_vlocation *vlocation);
|
||||
|
||||
static void __afs_vlocation_timeout(struct afs_timer *timer)
|
||||
{
|
||||
struct afs_vlocation *vlocation =
|
||||
list_entry(timer, struct afs_vlocation, timeout);
|
||||
|
||||
_debug("VL TIMEOUT [%s{u=%d}]",
|
||||
vlocation->vldb.name, atomic_read(&vlocation->usage));
|
||||
|
||||
afs_vlocation_do_timeout(vlocation);
|
||||
}
|
||||
|
||||
static const struct afs_timer_ops afs_vlocation_timer_ops = {
|
||||
.timed_out = __afs_vlocation_timeout,
|
||||
};
|
||||
|
||||
static const struct afs_timer_ops afs_vlocation_update_timer_ops = {
|
||||
.timed_out = afs_vlocation_update_timer,
|
||||
};
|
||||
|
||||
static const struct afs_async_op_ops afs_vlocation_update_op_ops = {
|
||||
.attend = afs_vlocation_update_attend,
|
||||
.discard = afs_vlocation_update_discard,
|
||||
};
|
||||
|
||||
static LIST_HEAD(afs_vlocation_update_pendq); /* queue of VLs awaiting update */
|
||||
static struct afs_vlocation *afs_vlocation_update; /* VL currently being updated */
|
||||
static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_vlocation_cache_match(void *target,
|
||||
const void *entry);
|
||||
static void afs_vlocation_cache_update(void *source, void *entry);
|
||||
|
||||
struct cachefs_index_def afs_vlocation_cache_index_def = {
|
||||
.name = "vldb",
|
||||
.data_size = sizeof(struct afs_cache_vlocation),
|
||||
.keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
|
||||
.match = afs_vlocation_cache_match,
|
||||
.update = afs_vlocation_cache_update,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* iterate through the VL servers in a cell until one of them admits knowing
|
||||
* about the volume in question
|
||||
* - caller must have cell->vl_sem write-locked
|
||||
*/
|
||||
static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation,
|
||||
const char *name,
|
||||
unsigned namesz,
|
||||
struct afs_cache_vlocation *vldb)
|
||||
{
|
||||
struct afs_server *server = NULL;
|
||||
struct afs_cell *cell = vlocation->cell;
|
||||
int count, ret;
|
||||
|
||||
_enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz);
|
||||
|
||||
ret = -ENOMEDIUM;
|
||||
for (count = cell->vl_naddrs; count > 0; count--) {
|
||||
_debug("CellServ[%hu]: %08x",
|
||||
cell->vl_curr_svix,
|
||||
cell->vl_addrs[cell->vl_curr_svix].s_addr);
|
||||
|
||||
/* try and create a server */
|
||||
ret = afs_server_lookup(cell,
|
||||
&cell->vl_addrs[cell->vl_curr_svix],
|
||||
&server);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOMEM:
|
||||
case -ENONET:
|
||||
goto out;
|
||||
default:
|
||||
goto rotate;
|
||||
}
|
||||
|
||||
/* attempt to access the VL server */
|
||||
ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
afs_put_server(server);
|
||||
goto out;
|
||||
case -ENOMEM:
|
||||
case -ENONET:
|
||||
case -ENETUNREACH:
|
||||
case -EHOSTUNREACH:
|
||||
case -ECONNREFUSED:
|
||||
down_write(&server->sem);
|
||||
if (server->vlserver) {
|
||||
rxrpc_put_connection(server->vlserver);
|
||||
server->vlserver = NULL;
|
||||
}
|
||||
up_write(&server->sem);
|
||||
afs_put_server(server);
|
||||
if (ret == -ENOMEM || ret == -ENONET)
|
||||
goto out;
|
||||
goto rotate;
|
||||
case -ENOMEDIUM:
|
||||
afs_put_server(server);
|
||||
goto out;
|
||||
default:
|
||||
afs_put_server(server);
|
||||
ret = -ENOMEDIUM;
|
||||
goto rotate;
|
||||
}
|
||||
|
||||
/* rotate the server records upon lookup failure */
|
||||
rotate:
|
||||
cell->vl_curr_svix++;
|
||||
cell->vl_curr_svix %= cell->vl_naddrs;
|
||||
}
|
||||
|
||||
out:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_vlocation_access_vl_by_name() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* iterate through the VL servers in a cell until one of them admits knowing
|
||||
* about the volume in question
|
||||
* - caller must have cell->vl_sem write-locked
|
||||
*/
|
||||
static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation,
|
||||
afs_volid_t volid,
|
||||
afs_voltype_t voltype,
|
||||
struct afs_cache_vlocation *vldb)
|
||||
{
|
||||
struct afs_server *server = NULL;
|
||||
struct afs_cell *cell = vlocation->cell;
|
||||
int count, ret;
|
||||
|
||||
_enter("%s,%x,%d,", cell->name, volid, voltype);
|
||||
|
||||
ret = -ENOMEDIUM;
|
||||
for (count = cell->vl_naddrs; count > 0; count--) {
|
||||
_debug("CellServ[%hu]: %08x",
|
||||
cell->vl_curr_svix,
|
||||
cell->vl_addrs[cell->vl_curr_svix].s_addr);
|
||||
|
||||
/* try and create a server */
|
||||
ret = afs_server_lookup(cell,
|
||||
&cell->vl_addrs[cell->vl_curr_svix],
|
||||
&server);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOMEM:
|
||||
case -ENONET:
|
||||
goto out;
|
||||
default:
|
||||
goto rotate;
|
||||
}
|
||||
|
||||
/* attempt to access the VL server */
|
||||
ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
afs_put_server(server);
|
||||
goto out;
|
||||
case -ENOMEM:
|
||||
case -ENONET:
|
||||
case -ENETUNREACH:
|
||||
case -EHOSTUNREACH:
|
||||
case -ECONNREFUSED:
|
||||
down_write(&server->sem);
|
||||
if (server->vlserver) {
|
||||
rxrpc_put_connection(server->vlserver);
|
||||
server->vlserver = NULL;
|
||||
}
|
||||
up_write(&server->sem);
|
||||
afs_put_server(server);
|
||||
if (ret == -ENOMEM || ret == -ENONET)
|
||||
goto out;
|
||||
goto rotate;
|
||||
case -ENOMEDIUM:
|
||||
afs_put_server(server);
|
||||
goto out;
|
||||
default:
|
||||
afs_put_server(server);
|
||||
ret = -ENOMEDIUM;
|
||||
goto rotate;
|
||||
}
|
||||
|
||||
/* rotate the server records upon lookup failure */
|
||||
rotate:
|
||||
cell->vl_curr_svix++;
|
||||
cell->vl_curr_svix %= cell->vl_naddrs;
|
||||
}
|
||||
|
||||
out:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_vlocation_access_vl_by_id() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* lookup volume location
|
||||
* - caller must have cell->vol_sem write-locked
|
||||
* - iterate through the VL servers in a cell until one of them admits knowing
|
||||
* about the volume in question
|
||||
* - lookup in the local cache if not able to find on the VL server
|
||||
* - insert/update in the local cache if did get a VL response
|
||||
*/
|
||||
int afs_vlocation_lookup(struct afs_cell *cell,
|
||||
const char *name,
|
||||
unsigned namesz,
|
||||
struct afs_vlocation **_vlocation)
|
||||
{
|
||||
struct afs_cache_vlocation vldb;
|
||||
struct afs_vlocation *vlocation;
|
||||
afs_voltype_t voltype;
|
||||
afs_volid_t vid;
|
||||
int active = 0, ret;
|
||||
|
||||
_enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz);
|
||||
|
||||
if (namesz > sizeof(vlocation->vldb.name)) {
|
||||
_leave(" = -ENAMETOOLONG");
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
|
||||
/* search the cell's active list first */
|
||||
list_for_each_entry(vlocation, &cell->vl_list, link) {
|
||||
if (namesz < sizeof(vlocation->vldb.name) &&
|
||||
vlocation->vldb.name[namesz] != '\0')
|
||||
continue;
|
||||
|
||||
if (memcmp(vlocation->vldb.name, name, namesz) == 0)
|
||||
goto found_in_memory;
|
||||
}
|
||||
|
||||
/* search the cell's graveyard list second */
|
||||
spin_lock(&cell->vl_gylock);
|
||||
list_for_each_entry(vlocation, &cell->vl_graveyard, link) {
|
||||
if (namesz < sizeof(vlocation->vldb.name) &&
|
||||
vlocation->vldb.name[namesz] != '\0')
|
||||
continue;
|
||||
|
||||
if (memcmp(vlocation->vldb.name, name, namesz) == 0)
|
||||
goto found_in_graveyard;
|
||||
}
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
/* not in the cell's in-memory lists - create a new record */
|
||||
vlocation = kmalloc(sizeof(struct afs_vlocation), GFP_KERNEL);
|
||||
if (!vlocation)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(vlocation, 0, sizeof(struct afs_vlocation));
|
||||
atomic_set(&vlocation->usage, 1);
|
||||
INIT_LIST_HEAD(&vlocation->link);
|
||||
rwlock_init(&vlocation->lock);
|
||||
memcpy(vlocation->vldb.name, name, namesz);
|
||||
|
||||
afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops);
|
||||
afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops);
|
||||
afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops);
|
||||
|
||||
afs_get_cell(cell);
|
||||
vlocation->cell = cell;
|
||||
|
||||
list_add_tail(&vlocation->link, &cell->vl_list);
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
/* we want to store it in the cache, plus it might already be
|
||||
* encached */
|
||||
cachefs_acquire_cookie(cell->cache,
|
||||
&afs_volume_cache_index_def,
|
||||
vlocation,
|
||||
&vlocation->cache);
|
||||
|
||||
if (vlocation->valid)
|
||||
goto found_in_cache;
|
||||
#endif
|
||||
|
||||
/* try to look up an unknown volume in the cell VL databases by name */
|
||||
ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb);
|
||||
if (ret < 0) {
|
||||
printk("kAFS: failed to locate '%*.*s' in cell '%s'\n",
|
||||
namesz, namesz, name, cell->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
goto found_on_vlserver;
|
||||
|
||||
found_in_graveyard:
|
||||
/* found in the graveyard - resurrect */
|
||||
_debug("found in graveyard");
|
||||
atomic_inc(&vlocation->usage);
|
||||
list_del(&vlocation->link);
|
||||
list_add_tail(&vlocation->link, &cell->vl_list);
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
afs_kafstimod_del_timer(&vlocation->timeout);
|
||||
goto active;
|
||||
|
||||
found_in_memory:
|
||||
/* found in memory - check to see if it's active */
|
||||
_debug("found in memory");
|
||||
atomic_inc(&vlocation->usage);
|
||||
|
||||
active:
|
||||
active = 1;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
found_in_cache:
|
||||
#endif
|
||||
/* try to look up a cached volume in the cell VL databases by ID */
|
||||
_debug("found in cache");
|
||||
|
||||
_debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
|
||||
vlocation->vldb.name,
|
||||
vlocation->vldb.vidmask,
|
||||
ntohl(vlocation->vldb.servers[0].s_addr),
|
||||
vlocation->vldb.srvtmask[0],
|
||||
ntohl(vlocation->vldb.servers[1].s_addr),
|
||||
vlocation->vldb.srvtmask[1],
|
||||
ntohl(vlocation->vldb.servers[2].s_addr),
|
||||
vlocation->vldb.srvtmask[2]
|
||||
);
|
||||
|
||||
_debug("Vids: %08x %08x %08x",
|
||||
vlocation->vldb.vid[0],
|
||||
vlocation->vldb.vid[1],
|
||||
vlocation->vldb.vid[2]);
|
||||
|
||||
if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
|
||||
vid = vlocation->vldb.vid[0];
|
||||
voltype = AFSVL_RWVOL;
|
||||
}
|
||||
else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
|
||||
vid = vlocation->vldb.vid[1];
|
||||
voltype = AFSVL_ROVOL;
|
||||
}
|
||||
else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
|
||||
vid = vlocation->vldb.vid[2];
|
||||
voltype = AFSVL_BACKVOL;
|
||||
}
|
||||
else {
|
||||
BUG();
|
||||
vid = 0;
|
||||
voltype = 0;
|
||||
}
|
||||
|
||||
ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb);
|
||||
switch (ret) {
|
||||
/* net error */
|
||||
default:
|
||||
printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n",
|
||||
namesz, namesz, name, vid, cell->name, ret);
|
||||
goto error;
|
||||
|
||||
/* pulled from local cache into memory */
|
||||
case 0:
|
||||
goto found_on_vlserver;
|
||||
|
||||
/* uh oh... looks like the volume got deleted */
|
||||
case -ENOMEDIUM:
|
||||
printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n",
|
||||
namesz, namesz, name, vid, cell->name);
|
||||
|
||||
/* TODO: make existing record unavailable */
|
||||
goto error;
|
||||
}
|
||||
|
||||
found_on_vlserver:
|
||||
_debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }",
|
||||
namesz, namesz, name,
|
||||
vldb.vidmask,
|
||||
ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
|
||||
ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
|
||||
ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
|
||||
);
|
||||
|
||||
_debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]);
|
||||
|
||||
if ((namesz < sizeof(vlocation->vldb.name) &&
|
||||
vlocation->vldb.name[namesz] != '\0') ||
|
||||
memcmp(vldb.name, name, namesz) != 0)
|
||||
printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n",
|
||||
namesz, namesz, name, vldb.name);
|
||||
|
||||
memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
|
||||
|
||||
afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ);
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
/* update volume entry in local cache */
|
||||
cachefs_update_cookie(vlocation->cache);
|
||||
#endif
|
||||
|
||||
*_vlocation = vlocation;
|
||||
_leave(" = 0 (%p)",vlocation);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (vlocation) {
|
||||
if (active) {
|
||||
__afs_put_vlocation(vlocation);
|
||||
}
|
||||
else {
|
||||
list_del(&vlocation->link);
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_relinquish_cookie(vlocation->cache, 0);
|
||||
#endif
|
||||
afs_put_cell(vlocation->cell);
|
||||
kfree(vlocation);
|
||||
}
|
||||
}
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_vlocation_lookup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* finish using a volume location record
|
||||
* - caller must have cell->vol_sem write-locked
|
||||
*/
|
||||
static void __afs_put_vlocation(struct afs_vlocation *vlocation)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
|
||||
if (!vlocation)
|
||||
return;
|
||||
|
||||
_enter("%s", vlocation->vldb.name);
|
||||
|
||||
cell = vlocation->cell;
|
||||
|
||||
/* sanity check */
|
||||
BUG_ON(atomic_read(&vlocation->usage) <= 0);
|
||||
|
||||
spin_lock(&cell->vl_gylock);
|
||||
if (likely(!atomic_dec_and_test(&vlocation->usage))) {
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
_leave("");
|
||||
return;
|
||||
}
|
||||
|
||||
/* move to graveyard queue */
|
||||
list_del(&vlocation->link);
|
||||
list_add_tail(&vlocation->link,&cell->vl_graveyard);
|
||||
|
||||
/* remove from pending timeout queue (refcounted if actually being
|
||||
* updated) */
|
||||
list_del_init(&vlocation->upd_op.link);
|
||||
|
||||
/* time out in 10 secs */
|
||||
afs_kafstimod_del_timer(&vlocation->upd_timer);
|
||||
afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ);
|
||||
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
_leave(" [killed]");
|
||||
} /* end __afs_put_vlocation() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* finish using a volume location record
|
||||
*/
|
||||
void afs_put_vlocation(struct afs_vlocation *vlocation)
|
||||
{
|
||||
if (vlocation) {
|
||||
struct afs_cell *cell = vlocation->cell;
|
||||
|
||||
down_write(&cell->vl_sem);
|
||||
__afs_put_vlocation(vlocation);
|
||||
up_write(&cell->vl_sem);
|
||||
}
|
||||
} /* end afs_put_vlocation() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* timeout vlocation record
|
||||
* - removes from the cell's graveyard if the usage count is zero
|
||||
*/
|
||||
void afs_vlocation_do_timeout(struct afs_vlocation *vlocation)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
|
||||
_enter("%s", vlocation->vldb.name);
|
||||
|
||||
cell = vlocation->cell;
|
||||
|
||||
BUG_ON(atomic_read(&vlocation->usage) < 0);
|
||||
|
||||
/* remove from graveyard if still dead */
|
||||
spin_lock(&cell->vl_gylock);
|
||||
if (atomic_read(&vlocation->usage) == 0)
|
||||
list_del_init(&vlocation->link);
|
||||
else
|
||||
vlocation = NULL;
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
if (!vlocation) {
|
||||
_leave("");
|
||||
return; /* resurrected */
|
||||
}
|
||||
|
||||
/* we can now destroy it properly */
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_relinquish_cookie(vlocation->cache, 0);
|
||||
#endif
|
||||
afs_put_cell(cell);
|
||||
|
||||
kfree(vlocation);
|
||||
|
||||
_leave(" [destroyed]");
|
||||
} /* end afs_vlocation_do_timeout() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* send an update operation to the currently selected server
|
||||
*/
|
||||
static int afs_vlocation_update_begin(struct afs_vlocation *vlocation)
|
||||
{
|
||||
afs_voltype_t voltype;
|
||||
afs_volid_t vid;
|
||||
int ret;
|
||||
|
||||
_enter("%s{ufs=%u ucs=%u}",
|
||||
vlocation->vldb.name,
|
||||
vlocation->upd_first_svix,
|
||||
vlocation->upd_curr_svix);
|
||||
|
||||
/* try to look up a cached volume in the cell VL databases by ID */
|
||||
if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
|
||||
vid = vlocation->vldb.vid[0];
|
||||
voltype = AFSVL_RWVOL;
|
||||
}
|
||||
else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
|
||||
vid = vlocation->vldb.vid[1];
|
||||
voltype = AFSVL_ROVOL;
|
||||
}
|
||||
else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
|
||||
vid = vlocation->vldb.vid[2];
|
||||
voltype = AFSVL_BACKVOL;
|
||||
}
|
||||
else {
|
||||
BUG();
|
||||
vid = 0;
|
||||
voltype = 0;
|
||||
}
|
||||
|
||||
/* contact the chosen server */
|
||||
ret = afs_server_lookup(
|
||||
vlocation->cell,
|
||||
&vlocation->cell->vl_addrs[vlocation->upd_curr_svix],
|
||||
&vlocation->upd_op.server);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOMEM:
|
||||
case -ENONET:
|
||||
default:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* initiate the update operation */
|
||||
ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_vlocation_update_begin() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* abandon updating a VL record
|
||||
* - does not restart the update timer
|
||||
*/
|
||||
static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation,
|
||||
afs_vlocation_upd_t state,
|
||||
int ret)
|
||||
{
|
||||
_enter("%s,%u", vlocation->vldb.name, state);
|
||||
|
||||
if (ret < 0)
|
||||
printk("kAFS: Abandoning VL update '%s': %d\n",
|
||||
vlocation->vldb.name, ret);
|
||||
|
||||
/* discard the server record */
|
||||
afs_put_server(vlocation->upd_op.server);
|
||||
vlocation->upd_op.server = NULL;
|
||||
|
||||
spin_lock(&afs_vlocation_update_lock);
|
||||
afs_vlocation_update = NULL;
|
||||
vlocation->upd_state = state;
|
||||
|
||||
/* TODO: start updating next VL record on pending list */
|
||||
|
||||
spin_unlock(&afs_vlocation_update_lock);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_vlocation_update_abandon() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle periodic update timeouts and busy retry timeouts
|
||||
* - called from kafstimod
|
||||
*/
|
||||
static void afs_vlocation_update_timer(struct afs_timer *timer)
|
||||
{
|
||||
struct afs_vlocation *vlocation =
|
||||
list_entry(timer, struct afs_vlocation, upd_timer);
|
||||
int ret;
|
||||
|
||||
_enter("%s", vlocation->vldb.name);
|
||||
|
||||
/* only update if not in the graveyard (defend against putting too) */
|
||||
spin_lock(&vlocation->cell->vl_gylock);
|
||||
|
||||
if (!atomic_read(&vlocation->usage))
|
||||
goto out_unlock1;
|
||||
|
||||
spin_lock(&afs_vlocation_update_lock);
|
||||
|
||||
/* if we were woken up due to EBUSY sleep then restart immediately if
|
||||
* possible or else jump to front of pending queue */
|
||||
if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) {
|
||||
if (afs_vlocation_update) {
|
||||
list_add(&vlocation->upd_op.link,
|
||||
&afs_vlocation_update_pendq);
|
||||
}
|
||||
else {
|
||||
afs_get_vlocation(vlocation);
|
||||
afs_vlocation_update = vlocation;
|
||||
vlocation->upd_state = AFS_VLUPD_INPROGRESS;
|
||||
}
|
||||
goto out_unlock2;
|
||||
}
|
||||
|
||||
/* put on pending queue if there's already another update in progress */
|
||||
if (afs_vlocation_update) {
|
||||
vlocation->upd_state = AFS_VLUPD_PENDING;
|
||||
list_add_tail(&vlocation->upd_op.link,
|
||||
&afs_vlocation_update_pendq);
|
||||
goto out_unlock2;
|
||||
}
|
||||
|
||||
/* hold a ref on it while actually updating */
|
||||
afs_get_vlocation(vlocation);
|
||||
afs_vlocation_update = vlocation;
|
||||
vlocation->upd_state = AFS_VLUPD_INPROGRESS;
|
||||
|
||||
spin_unlock(&afs_vlocation_update_lock);
|
||||
spin_unlock(&vlocation->cell->vl_gylock);
|
||||
|
||||
/* okay... we can start the update */
|
||||
_debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name);
|
||||
vlocation->upd_first_svix = vlocation->cell->vl_curr_svix;
|
||||
vlocation->upd_curr_svix = vlocation->upd_first_svix;
|
||||
vlocation->upd_rej_cnt = 0;
|
||||
vlocation->upd_busy_cnt = 0;
|
||||
|
||||
ret = afs_vlocation_update_begin(vlocation);
|
||||
if (ret < 0) {
|
||||
afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
|
||||
afs_kafstimod_add_timer(&vlocation->upd_timer,
|
||||
AFS_VLDB_TIMEOUT);
|
||||
afs_put_vlocation(vlocation);
|
||||
}
|
||||
|
||||
_leave("");
|
||||
return;
|
||||
|
||||
out_unlock2:
|
||||
spin_unlock(&afs_vlocation_update_lock);
|
||||
out_unlock1:
|
||||
spin_unlock(&vlocation->cell->vl_gylock);
|
||||
_leave("");
|
||||
return;
|
||||
|
||||
} /* end afs_vlocation_update_timer() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* attend to an update operation upon which an event happened
|
||||
* - called in kafsasyncd context
|
||||
*/
|
||||
static void afs_vlocation_update_attend(struct afs_async_op *op)
|
||||
{
|
||||
struct afs_cache_vlocation vldb;
|
||||
struct afs_vlocation *vlocation =
|
||||
list_entry(op, struct afs_vlocation, upd_op);
|
||||
unsigned tmp;
|
||||
int ret;
|
||||
|
||||
_enter("%s", vlocation->vldb.name);
|
||||
|
||||
ret = afs_rxvl_get_entry_by_id_async2(op, &vldb);
|
||||
switch (ret) {
|
||||
case -EAGAIN:
|
||||
_leave(" [unfinished]");
|
||||
return;
|
||||
|
||||
case 0:
|
||||
_debug("END VL UPDATE: %d\n", ret);
|
||||
vlocation->valid = 1;
|
||||
|
||||
_debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }",
|
||||
vldb.vidmask,
|
||||
ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
|
||||
ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
|
||||
ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
|
||||
);
|
||||
|
||||
_debug("Vids: %08x %08x %08x",
|
||||
vldb.vid[0], vldb.vid[1], vldb.vid[2]);
|
||||
|
||||
afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
|
||||
|
||||
down_write(&vlocation->cell->vl_sem);
|
||||
|
||||
/* actually update the cache */
|
||||
if (strncmp(vldb.name, vlocation->vldb.name,
|
||||
sizeof(vlocation->vldb.name)) != 0)
|
||||
printk("kAFS: name of volume '%s'"
|
||||
" changed to '%s' on server\n",
|
||||
vlocation->vldb.name, vldb.name);
|
||||
|
||||
memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
|
||||
|
||||
#if 0
|
||||
/* TODO update volume entry in local cache */
|
||||
#endif
|
||||
|
||||
up_write(&vlocation->cell->vl_sem);
|
||||
|
||||
if (ret < 0)
|
||||
printk("kAFS: failed to update local cache: %d\n", ret);
|
||||
|
||||
afs_kafstimod_add_timer(&vlocation->upd_timer,
|
||||
AFS_VLDB_TIMEOUT);
|
||||
afs_put_vlocation(vlocation);
|
||||
_leave(" [found]");
|
||||
return;
|
||||
|
||||
case -ENOMEDIUM:
|
||||
vlocation->upd_rej_cnt++;
|
||||
goto try_next;
|
||||
|
||||
/* the server is locked - retry in a very short while */
|
||||
case -EBUSY:
|
||||
vlocation->upd_busy_cnt++;
|
||||
if (vlocation->upd_busy_cnt > 3)
|
||||
goto try_next; /* too many retries */
|
||||
|
||||
afs_vlocation_update_abandon(vlocation,
|
||||
AFS_VLUPD_BUSYSLEEP, 0);
|
||||
afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2);
|
||||
afs_put_vlocation(vlocation);
|
||||
_leave(" [busy]");
|
||||
return;
|
||||
|
||||
case -ENETUNREACH:
|
||||
case -EHOSTUNREACH:
|
||||
case -ECONNREFUSED:
|
||||
case -EREMOTEIO:
|
||||
/* record bad vlserver info in the cell too
|
||||
* - TODO: use down_write_trylock() if available
|
||||
*/
|
||||
if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix)
|
||||
vlocation->cell->vl_curr_svix =
|
||||
vlocation->cell->vl_curr_svix %
|
||||
vlocation->cell->vl_naddrs;
|
||||
|
||||
case -EBADRQC:
|
||||
case -EINVAL:
|
||||
case -EACCES:
|
||||
case -EBADMSG:
|
||||
goto try_next;
|
||||
|
||||
default:
|
||||
goto abandon;
|
||||
}
|
||||
|
||||
/* try contacting the next server */
|
||||
try_next:
|
||||
vlocation->upd_busy_cnt = 0;
|
||||
|
||||
/* discard the server record */
|
||||
afs_put_server(vlocation->upd_op.server);
|
||||
vlocation->upd_op.server = NULL;
|
||||
|
||||
tmp = vlocation->cell->vl_naddrs;
|
||||
if (tmp == 0)
|
||||
goto abandon;
|
||||
|
||||
vlocation->upd_curr_svix++;
|
||||
if (vlocation->upd_curr_svix >= tmp)
|
||||
vlocation->upd_curr_svix = 0;
|
||||
if (vlocation->upd_first_svix >= tmp)
|
||||
vlocation->upd_first_svix = tmp - 1;
|
||||
|
||||
/* move to the next server */
|
||||
if (vlocation->upd_curr_svix != vlocation->upd_first_svix) {
|
||||
afs_vlocation_update_begin(vlocation);
|
||||
_leave(" [next]");
|
||||
return;
|
||||
}
|
||||
|
||||
/* run out of servers to try - was the volume rejected? */
|
||||
if (vlocation->upd_rej_cnt > 0) {
|
||||
printk("kAFS: Active volume no longer valid '%s'\n",
|
||||
vlocation->vldb.name);
|
||||
vlocation->valid = 0;
|
||||
afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
|
||||
afs_kafstimod_add_timer(&vlocation->upd_timer,
|
||||
AFS_VLDB_TIMEOUT);
|
||||
afs_put_vlocation(vlocation);
|
||||
_leave(" [invalidated]");
|
||||
return;
|
||||
}
|
||||
|
||||
/* abandon the update */
|
||||
abandon:
|
||||
afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
|
||||
afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10);
|
||||
afs_put_vlocation(vlocation);
|
||||
_leave(" [abandoned]");
|
||||
|
||||
} /* end afs_vlocation_update_attend() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* deal with an update operation being discarded
|
||||
* - called in kafsasyncd context when it's dying due to rmmod
|
||||
* - the call has already been aborted and put()'d
|
||||
*/
|
||||
static void afs_vlocation_update_discard(struct afs_async_op *op)
|
||||
{
|
||||
struct afs_vlocation *vlocation =
|
||||
list_entry(op, struct afs_vlocation, upd_op);
|
||||
|
||||
_enter("%s", vlocation->vldb.name);
|
||||
|
||||
afs_put_server(op->server);
|
||||
op->server = NULL;
|
||||
|
||||
afs_put_vlocation(vlocation);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_vlocation_update_discard() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* match a VLDB record stored in the cache
|
||||
* - may also load target from entry
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_vlocation_cache_match(void *target,
|
||||
const void *entry)
|
||||
{
|
||||
const struct afs_cache_vlocation *vldb = entry;
|
||||
struct afs_vlocation *vlocation = target;
|
||||
|
||||
_enter("{%s},{%s}", vlocation->vldb.name, vldb->name);
|
||||
|
||||
if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0
|
||||
) {
|
||||
if (!vlocation->valid ||
|
||||
vlocation->vldb.rtime == vldb->rtime
|
||||
) {
|
||||
vlocation->vldb = *vldb;
|
||||
vlocation->valid = 1;
|
||||
_leave(" = SUCCESS [c->m]");
|
||||
return CACHEFS_MATCH_SUCCESS;
|
||||
}
|
||||
/* need to update cache if cached info differs */
|
||||
else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) {
|
||||
/* delete if VIDs for this name differ */
|
||||
if (memcmp(&vlocation->vldb.vid,
|
||||
&vldb->vid,
|
||||
sizeof(vldb->vid)) != 0) {
|
||||
_leave(" = DELETE");
|
||||
return CACHEFS_MATCH_SUCCESS_DELETE;
|
||||
}
|
||||
|
||||
_leave(" = UPDATE");
|
||||
return CACHEFS_MATCH_SUCCESS_UPDATE;
|
||||
}
|
||||
else {
|
||||
_leave(" = SUCCESS");
|
||||
return CACHEFS_MATCH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
_leave(" = FAILED");
|
||||
return CACHEFS_MATCH_FAILED;
|
||||
} /* end afs_vlocation_cache_match() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* update a VLDB record stored in the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static void afs_vlocation_cache_update(void *source, void *entry)
|
||||
{
|
||||
struct afs_cache_vlocation *vldb = entry;
|
||||
struct afs_vlocation *vlocation = source;
|
||||
|
||||
_enter("");
|
||||
|
||||
*vldb = vlocation->vldb;
|
||||
|
||||
} /* end afs_vlocation_cache_update() */
|
||||
#endif
|
395
fs/afs/vnode.c
Normal file
395
fs/afs/vnode.c
Normal file
@@ -0,0 +1,395 @@
|
||||
/* vnode.c: AFS vnode management
|
||||
*
|
||||
* Copyright (C) 2002 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/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "volume.h"
|
||||
#include "cell.h"
|
||||
#include "cmservice.h"
|
||||
#include "fsclient.h"
|
||||
#include "vlclient.h"
|
||||
#include "vnode.h"
|
||||
#include "internal.h"
|
||||
|
||||
static void afs_vnode_cb_timed_out(struct afs_timer *timer);
|
||||
|
||||
struct afs_timer_ops afs_vnode_cb_timed_out_ops = {
|
||||
.timed_out = afs_vnode_cb_timed_out,
|
||||
};
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_vnode_cache_match(void *target,
|
||||
const void *entry);
|
||||
static void afs_vnode_cache_update(void *source, void *entry);
|
||||
|
||||
struct cachefs_index_def afs_vnode_cache_index_def = {
|
||||
.name = "vnode",
|
||||
.data_size = sizeof(struct afs_cache_vnode),
|
||||
.keys[0] = { CACHEFS_INDEX_KEYS_BIN, 4 },
|
||||
.match = afs_vnode_cache_match,
|
||||
.update = afs_vnode_cache_update,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handle a callback timing out
|
||||
* TODO: retain a ref to vnode struct for an outstanding callback timeout
|
||||
*/
|
||||
static void afs_vnode_cb_timed_out(struct afs_timer *timer)
|
||||
{
|
||||
struct afs_server *oldserver;
|
||||
struct afs_vnode *vnode;
|
||||
|
||||
vnode = list_entry(timer, struct afs_vnode, cb_timeout);
|
||||
|
||||
_enter("%p", vnode);
|
||||
|
||||
/* set the changed flag in the vnode and release the server */
|
||||
spin_lock(&vnode->lock);
|
||||
|
||||
oldserver = xchg(&vnode->cb_server, NULL);
|
||||
if (oldserver) {
|
||||
vnode->flags |= AFS_VNODE_CHANGED;
|
||||
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
list_del_init(&vnode->cb_hash_link);
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
|
||||
spin_lock(&oldserver->cb_lock);
|
||||
list_del_init(&vnode->cb_link);
|
||||
spin_unlock(&oldserver->cb_lock);
|
||||
}
|
||||
|
||||
spin_unlock(&vnode->lock);
|
||||
|
||||
afs_put_server(oldserver);
|
||||
|
||||
_leave("");
|
||||
} /* end afs_vnode_cb_timed_out() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* finish off updating the recorded status of a file
|
||||
* - starts callback expiry timer
|
||||
* - adds to server's callback list
|
||||
*/
|
||||
static void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
|
||||
struct afs_server *server,
|
||||
int ret)
|
||||
{
|
||||
struct afs_server *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() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* 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 afs_server *server;
|
||||
int ret;
|
||||
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
_enter("%s,{%u,%u,%u}",
|
||||
vnode->volume->vlocation->vldb.name,
|
||||
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
|
||||
|
||||
if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) {
|
||||
_leave(" [unchanged]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vnode->flags & AFS_VNODE_DELETED) {
|
||||
_leave(" [deleted]");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
spin_lock(&vnode->lock);
|
||||
|
||||
if (!(vnode->flags & AFS_VNODE_CHANGED)) {
|
||||
spin_unlock(&vnode->lock);
|
||||
_leave(" [unchanged]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vnode->update_cnt > 0) {
|
||||
/* someone else started a fetch */
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&vnode->update_waitq, &myself);
|
||||
|
||||
/* wait for the status to be updated */
|
||||
for (;;) {
|
||||
if (!(vnode->flags & AFS_VNODE_CHANGED))
|
||||
break;
|
||||
if (vnode->flags & AFS_VNODE_DELETED)
|
||||
break;
|
||||
|
||||
/* 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 vnode->flags & AFS_VNODE_DELETED ? -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 */
|
||||
ret = afs_volume_pick_fileserver(vnode->volume, &server);
|
||||
if (ret<0)
|
||||
return ret;
|
||||
|
||||
_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
|
||||
|
||||
ret = afs_rxfs_fetch_file_status(server, vnode, NULL);
|
||||
|
||||
} while (!afs_volume_release_fileserver(vnode->volume, server, ret));
|
||||
|
||||
/* adjust the flags */
|
||||
afs_vnode_finalise_status_update(vnode, server, ret);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_vnode_fetch_status() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* fetch file data from the volume
|
||||
* - TODO implement caching and server failover
|
||||
*/
|
||||
int afs_vnode_fetch_data(struct afs_vnode *vnode,
|
||||
struct afs_rxfs_fetch_descriptor *desc)
|
||||
{
|
||||
struct afs_server *server;
|
||||
int ret;
|
||||
|
||||
_enter("%s,{%u,%u,%u}",
|
||||
vnode->volume->vlocation->vldb.name,
|
||||
vnode->fid.vid,
|
||||
vnode->fid.vnode,
|
||||
vnode->fid.unique);
|
||||
|
||||
/* 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 */
|
||||
ret = afs_volume_pick_fileserver(vnode->volume, &server);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
|
||||
|
||||
ret = afs_rxfs_fetch_file_data(server, vnode, desc, NULL);
|
||||
|
||||
} while (!afs_volume_release_fileserver(vnode->volume, server, ret));
|
||||
|
||||
/* adjust the flags */
|
||||
afs_vnode_finalise_status_update(vnode, server, ret);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
} /* end afs_vnode_fetch_data() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* break any outstanding callback on a vnode
|
||||
* - only relevent to server that issued it
|
||||
*/
|
||||
int afs_vnode_give_up_callback(struct afs_vnode *vnode)
|
||||
{
|
||||
struct afs_server *server;
|
||||
int ret;
|
||||
|
||||
_enter("%s,{%u,%u,%u}",
|
||||
vnode->volume->vlocation->vldb.name,
|
||||
vnode->fid.vid,
|
||||
vnode->fid.vnode,
|
||||
vnode->fid.unique);
|
||||
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
list_del_init(&vnode->cb_hash_link);
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
|
||||
/* set the changed flag in the vnode and release the server */
|
||||
spin_lock(&vnode->lock);
|
||||
|
||||
afs_kafstimod_del_timer(&vnode->cb_timeout);
|
||||
|
||||
server = xchg(&vnode->cb_server, NULL);
|
||||
if (server) {
|
||||
vnode->flags |= AFS_VNODE_CHANGED;
|
||||
|
||||
spin_lock(&server->cb_lock);
|
||||
list_del_init(&vnode->cb_link);
|
||||
spin_unlock(&server->cb_lock);
|
||||
}
|
||||
|
||||
spin_unlock(&vnode->lock);
|
||||
|
||||
ret = 0;
|
||||
if (server) {
|
||||
ret = afs_rxfs_give_up_callback(server, vnode);
|
||||
afs_put_server(server);
|
||||
}
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
} /* end afs_vnode_give_up_callback() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* match a vnode record stored in the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_vnode_cache_match(void *target,
|
||||
const void *entry)
|
||||
{
|
||||
const struct afs_cache_vnode *cvnode = entry;
|
||||
struct afs_vnode *vnode = target;
|
||||
|
||||
_enter("{%x,%x,%Lx},{%x,%x,%Lx}",
|
||||
vnode->fid.vnode,
|
||||
vnode->fid.unique,
|
||||
vnode->status.version,
|
||||
cvnode->vnode_id,
|
||||
cvnode->vnode_unique,
|
||||
cvnode->data_version);
|
||||
|
||||
if (vnode->fid.vnode != cvnode->vnode_id) {
|
||||
_leave(" = FAILED");
|
||||
return CACHEFS_MATCH_FAILED;
|
||||
}
|
||||
|
||||
if (vnode->fid.unique != cvnode->vnode_unique ||
|
||||
vnode->status.version != cvnode->data_version) {
|
||||
_leave(" = DELETE");
|
||||
return CACHEFS_MATCH_SUCCESS_DELETE;
|
||||
}
|
||||
|
||||
_leave(" = SUCCESS");
|
||||
return CACHEFS_MATCH_SUCCESS;
|
||||
} /* end afs_vnode_cache_match() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* update a vnode record stored in the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static void afs_vnode_cache_update(void *source, void *entry)
|
||||
{
|
||||
struct afs_cache_vnode *cvnode = entry;
|
||||
struct afs_vnode *vnode = source;
|
||||
|
||||
_enter("");
|
||||
|
||||
cvnode->vnode_id = vnode->fid.vnode;
|
||||
cvnode->vnode_unique = vnode->fid.unique;
|
||||
cvnode->data_version = vnode->status.version;
|
||||
|
||||
} /* end afs_vnode_cache_update() */
|
||||
#endif
|
94
fs/afs/vnode.h
Normal file
94
fs/afs/vnode.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* vnode.h: AFS vnode record
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_VNODE_H
|
||||
#define _LINUX_AFS_VNODE_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include "server.h"
|
||||
#include "kafstimod.h"
|
||||
#include "cache.h"
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct afs_rxfs_fetch_descriptor;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* vnode catalogue entry
|
||||
*/
|
||||
struct afs_cache_vnode
|
||||
{
|
||||
afs_vnodeid_t vnode_id; /* vnode ID */
|
||||
unsigned vnode_unique; /* vnode ID uniquifier */
|
||||
afs_dataversion_t data_version; /* data version */
|
||||
};
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
extern struct cachefs_index_def afs_vnode_cache_index_def;
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS inode private data
|
||||
*/
|
||||
struct afs_vnode
|
||||
{
|
||||
struct inode vfs_inode; /* the VFS's inode record */
|
||||
|
||||
struct afs_volume *volume; /* volume on which vnode resides */
|
||||
struct afs_fid fid; /* the file identifier for this inode */
|
||||
struct afs_file_status status; /* AFS status info for this file */
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct cachefs_cookie *cache; /* caching cookie */
|
||||
#endif
|
||||
|
||||
wait_queue_head_t update_waitq; /* status fetch waitqueue */
|
||||
unsigned update_cnt; /* number of outstanding ops that will update the
|
||||
* status */
|
||||
spinlock_t lock; /* waitqueue/flags lock */
|
||||
unsigned flags;
|
||||
#define AFS_VNODE_CHANGED 0x00000001 /* set if vnode reported changed by callback */
|
||||
#define AFS_VNODE_DELETED 0x00000002 /* set if vnode deleted on server */
|
||||
#define AFS_VNODE_MOUNTPOINT 0x00000004 /* set if vnode is a mountpoint symlink */
|
||||
|
||||
/* outstanding callback notification on this file */
|
||||
struct afs_server *cb_server; /* server that made the current promise */
|
||||
struct list_head cb_link; /* link in server's promises list */
|
||||
struct list_head cb_hash_link; /* link in master callback hash */
|
||||
struct afs_timer cb_timeout; /* timeout on promise */
|
||||
unsigned cb_version; /* callback version */
|
||||
unsigned cb_expiry; /* callback expiry time */
|
||||
afs_callback_type_t cb_type; /* type of callback */
|
||||
};
|
||||
|
||||
static inline struct afs_vnode *AFS_FS_I(struct inode *inode)
|
||||
{
|
||||
return container_of(inode,struct afs_vnode,vfs_inode);
|
||||
}
|
||||
|
||||
static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
|
||||
{
|
||||
return &vnode->vfs_inode;
|
||||
}
|
||||
|
||||
extern int afs_vnode_fetch_status(struct afs_vnode *vnode);
|
||||
|
||||
extern int afs_vnode_fetch_data(struct afs_vnode *vnode,
|
||||
struct afs_rxfs_fetch_descriptor *desc);
|
||||
|
||||
extern int afs_vnode_give_up_callback(struct afs_vnode *vnode);
|
||||
|
||||
extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _LINUX_AFS_VNODE_H */
|
520
fs/afs/volume.c
Normal file
520
fs/afs/volume.c
Normal file
@@ -0,0 +1,520 @@
|
||||
/* volume.c: AFS volume management
|
||||
*
|
||||
* Copyright (C) 2002 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/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "volume.h"
|
||||
#include "vnode.h"
|
||||
#include "cell.h"
|
||||
#include "cache.h"
|
||||
#include "cmservice.h"
|
||||
#include "fsclient.h"
|
||||
#include "vlclient.h"
|
||||
#include "internal.h"
|
||||
|
||||
#ifdef __KDEBUG
|
||||
static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
|
||||
#endif
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_volume_cache_match(void *target,
|
||||
const void *entry);
|
||||
static void afs_volume_cache_update(void *source, void *entry);
|
||||
|
||||
struct cachefs_index_def afs_volume_cache_index_def = {
|
||||
.name = "volume",
|
||||
.data_size = sizeof(struct afs_cache_vhash),
|
||||
.keys[0] = { CACHEFS_INDEX_KEYS_BIN, 1 },
|
||||
.keys[1] = { CACHEFS_INDEX_KEYS_BIN, 1 },
|
||||
.match = afs_volume_cache_match,
|
||||
.update = afs_volume_cache_update,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* lookup a volume by name
|
||||
* - this can be one of the following:
|
||||
* "%[cell:]volume[.]" R/W volume
|
||||
* "#[cell:]volume[.]" R/O or R/W volume (rwparent=0),
|
||||
* or R/W (rwparent=1) volume
|
||||
* "%[cell:]volume.readonly" R/O volume
|
||||
* "#[cell:]volume.readonly" R/O volume
|
||||
* "%[cell:]volume.backup" Backup volume
|
||||
* "#[cell:]volume.backup" Backup volume
|
||||
*
|
||||
* The cell name is optional, and defaults to the current cell.
|
||||
*
|
||||
* See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
|
||||
* Guide
|
||||
* - Rule 1: Explicit type suffix forces access of that type or nothing
|
||||
* (no suffix, then use Rule 2 & 3)
|
||||
* - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
|
||||
* if not available
|
||||
* - Rule 3: If parent volume is R/W, then only mount R/W volume unless
|
||||
* explicitly told otherwise
|
||||
*/
|
||||
int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
|
||||
struct afs_volume **_volume)
|
||||
{
|
||||
struct afs_vlocation *vlocation = NULL;
|
||||
struct afs_volume *volume = NULL;
|
||||
afs_voltype_t type;
|
||||
const char *cellname, *volname, *suffix;
|
||||
char srvtmask;
|
||||
int force, ret, loop, cellnamesz, volnamesz;
|
||||
|
||||
_enter("%s,,%d,", name, rwpath);
|
||||
|
||||
if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
|
||||
printk("kAFS: unparsable volume name\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* determine the type of volume we're looking for */
|
||||
force = 0;
|
||||
type = AFSVL_ROVOL;
|
||||
|
||||
if (rwpath || name[0] == '%') {
|
||||
type = AFSVL_RWVOL;
|
||||
force = 1;
|
||||
}
|
||||
|
||||
suffix = strrchr(name, '.');
|
||||
if (suffix) {
|
||||
if (strcmp(suffix, ".readonly") == 0) {
|
||||
type = AFSVL_ROVOL;
|
||||
force = 1;
|
||||
}
|
||||
else if (strcmp(suffix, ".backup") == 0) {
|
||||
type = AFSVL_BACKVOL;
|
||||
force = 1;
|
||||
}
|
||||
else if (suffix[1] == 0) {
|
||||
}
|
||||
else {
|
||||
suffix = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* split the cell and volume names */
|
||||
name++;
|
||||
volname = strchr(name, ':');
|
||||
if (volname) {
|
||||
cellname = name;
|
||||
cellnamesz = volname - name;
|
||||
volname++;
|
||||
}
|
||||
else {
|
||||
volname = name;
|
||||
cellname = NULL;
|
||||
cellnamesz = 0;
|
||||
}
|
||||
|
||||
volnamesz = suffix ? suffix - volname : strlen(volname);
|
||||
|
||||
_debug("CELL:%*.*s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s",
|
||||
cellnamesz, cellnamesz, cellname ?: "", cell,
|
||||
volnamesz, volnamesz, volname, suffix ?: "-",
|
||||
type,
|
||||
force ? " FORCE" : "");
|
||||
|
||||
/* lookup the cell record */
|
||||
if (cellname || !cell) {
|
||||
ret = afs_cell_lookup(cellname, cellnamesz, &cell);
|
||||
if (ret<0) {
|
||||
printk("kAFS: unable to lookup cell '%s'\n",
|
||||
cellname ?: "");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
afs_get_cell(cell);
|
||||
}
|
||||
|
||||
/* lookup the volume location record */
|
||||
ret = afs_vlocation_lookup(cell, volname, volnamesz, &vlocation);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* make the final decision on the type we want */
|
||||
ret = -ENOMEDIUM;
|
||||
if (force && !(vlocation->vldb.vidmask & (1 << type)))
|
||||
goto error;
|
||||
|
||||
srvtmask = 0;
|
||||
for (loop = 0; loop < vlocation->vldb.nservers; loop++)
|
||||
srvtmask |= vlocation->vldb.srvtmask[loop];
|
||||
|
||||
if (force) {
|
||||
if (!(srvtmask & (1 << type)))
|
||||
goto error;
|
||||
}
|
||||
else if (srvtmask & AFS_VOL_VTM_RO) {
|
||||
type = AFSVL_ROVOL;
|
||||
}
|
||||
else if (srvtmask & AFS_VOL_VTM_RW) {
|
||||
type = AFSVL_RWVOL;
|
||||
}
|
||||
else {
|
||||
goto error;
|
||||
}
|
||||
|
||||
down_write(&cell->vl_sem);
|
||||
|
||||
/* is the volume already active? */
|
||||
if (vlocation->vols[type]) {
|
||||
/* yes - re-use it */
|
||||
volume = vlocation->vols[type];
|
||||
afs_get_volume(volume);
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* create a new volume record */
|
||||
_debug("creating new volume record");
|
||||
|
||||
ret = -ENOMEM;
|
||||
volume = kmalloc(sizeof(struct afs_volume), GFP_KERNEL);
|
||||
if (!volume)
|
||||
goto error_up;
|
||||
|
||||
memset(volume, 0, sizeof(struct afs_volume));
|
||||
atomic_set(&volume->usage, 1);
|
||||
volume->type = type;
|
||||
volume->type_force = force;
|
||||
volume->cell = cell;
|
||||
volume->vid = vlocation->vldb.vid[type];
|
||||
|
||||
init_rwsem(&volume->server_sem);
|
||||
|
||||
/* look up all the applicable server records */
|
||||
for (loop = 0; loop < 8; loop++) {
|
||||
if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
|
||||
ret = afs_server_lookup(
|
||||
volume->cell,
|
||||
&vlocation->vldb.servers[loop],
|
||||
&volume->servers[volume->nservers]);
|
||||
if (ret < 0)
|
||||
goto error_discard;
|
||||
|
||||
volume->nservers++;
|
||||
}
|
||||
}
|
||||
|
||||
/* attach the cache and volume location */
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_acquire_cookie(vlocation->cache,
|
||||
&afs_vnode_cache_index_def,
|
||||
volume,
|
||||
&volume->cache);
|
||||
#endif
|
||||
|
||||
afs_get_vlocation(vlocation);
|
||||
volume->vlocation = vlocation;
|
||||
|
||||
vlocation->vols[type] = volume;
|
||||
|
||||
success:
|
||||
_debug("kAFS selected %s volume %08x",
|
||||
afs_voltypes[volume->type], volume->vid);
|
||||
*_volume = volume;
|
||||
ret = 0;
|
||||
|
||||
/* clean up */
|
||||
error_up:
|
||||
up_write(&cell->vl_sem);
|
||||
error:
|
||||
afs_put_vlocation(vlocation);
|
||||
afs_put_cell(cell);
|
||||
|
||||
_leave(" = %d (%p)", ret, volume);
|
||||
return ret;
|
||||
|
||||
error_discard:
|
||||
up_write(&cell->vl_sem);
|
||||
|
||||
for (loop = volume->nservers - 1; loop >= 0; loop--)
|
||||
afs_put_server(volume->servers[loop]);
|
||||
|
||||
kfree(volume);
|
||||
goto error;
|
||||
} /* end afs_volume_lookup() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* destroy a volume record
|
||||
*/
|
||||
void afs_put_volume(struct afs_volume *volume)
|
||||
{
|
||||
struct afs_vlocation *vlocation;
|
||||
int loop;
|
||||
|
||||
if (!volume)
|
||||
return;
|
||||
|
||||
_enter("%p", volume);
|
||||
|
||||
vlocation = volume->vlocation;
|
||||
|
||||
/* sanity check */
|
||||
BUG_ON(atomic_read(&volume->usage) <= 0);
|
||||
|
||||
/* to prevent a race, the decrement and the dequeue must be effectively
|
||||
* atomic */
|
||||
down_write(&vlocation->cell->vl_sem);
|
||||
|
||||
if (likely(!atomic_dec_and_test(&volume->usage))) {
|
||||
up_write(&vlocation->cell->vl_sem);
|
||||
_leave("");
|
||||
return;
|
||||
}
|
||||
|
||||
vlocation->vols[volume->type] = NULL;
|
||||
|
||||
up_write(&vlocation->cell->vl_sem);
|
||||
|
||||
/* finish cleaning up the volume */
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_relinquish_cookie(volume->cache, 0);
|
||||
#endif
|
||||
afs_put_vlocation(vlocation);
|
||||
|
||||
for (loop = volume->nservers - 1; loop >= 0; loop--)
|
||||
afs_put_server(volume->servers[loop]);
|
||||
|
||||
kfree(volume);
|
||||
|
||||
_leave(" [destroyed]");
|
||||
} /* end afs_put_volume() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* pick a server to use to try accessing this volume
|
||||
* - returns with an elevated usage count on the server chosen
|
||||
*/
|
||||
int afs_volume_pick_fileserver(struct afs_volume *volume,
|
||||
struct afs_server **_server)
|
||||
{
|
||||
struct afs_server *server;
|
||||
int ret, state, loop;
|
||||
|
||||
_enter("%s", volume->vlocation->vldb.name);
|
||||
|
||||
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 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;
|
||||
|
||||
switch (state) {
|
||||
/* found an apparently healthy server */
|
||||
case 0:
|
||||
afs_get_server(server);
|
||||
up_read(&volume->server_sem);
|
||||
*_server = server;
|
||||
_leave(" = 0 (picked %08x)",
|
||||
ntohl(server->addr.s_addr));
|
||||
return 0;
|
||||
|
||||
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 ret;
|
||||
} /* end afs_volume_pick_fileserver() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* release a server after use
|
||||
* - releases the ref on the server struct that was acquired by picking
|
||||
* - records result of using a particular server to access a volume
|
||||
* - return 0 to try again, 1 if okay or to issue error
|
||||
*/
|
||||
int afs_volume_release_fileserver(struct afs_volume *volume,
|
||||
struct afs_server *server,
|
||||
int result)
|
||||
{
|
||||
unsigned loop;
|
||||
|
||||
_enter("%s,%08x,%d",
|
||||
volume->vlocation->vldb.name, ntohl(server->addr.s_addr),
|
||||
result);
|
||||
|
||||
switch (result) {
|
||||
/* success */
|
||||
case 0:
|
||||
server->fs_act_jif = jiffies;
|
||||
break;
|
||||
|
||||
/* the fileserver denied all knowledge of the volume */
|
||||
case -ENOMEDIUM:
|
||||
server->fs_act_jif = jiffies;
|
||||
down_write(&volume->server_sem);
|
||||
|
||||
/* first, find where the server is in the active list (if it
|
||||
* is) */
|
||||
for (loop = 0; loop < volume->nservers; loop++)
|
||||
if (volume->servers[loop] == server)
|
||||
goto present;
|
||||
|
||||
/* no longer there - may have been discarded by another op */
|
||||
goto try_next_server_upw;
|
||||
|
||||
present:
|
||||
volume->nservers--;
|
||||
memmove(&volume->servers[loop],
|
||||
&volume->servers[loop + 1],
|
||||
sizeof(volume->servers[loop]) *
|
||||
(volume->nservers - loop));
|
||||
volume->servers[volume->nservers] = NULL;
|
||||
afs_put_server(server);
|
||||
volume->rjservers++;
|
||||
|
||||
if (volume->nservers > 0)
|
||||
/* another server might acknowledge its existence */
|
||||
goto try_next_server_upw;
|
||||
|
||||
/* handle the case where all the fileservers have rejected the
|
||||
* volume
|
||||
* - TODO: try asking the fileservers for volume information
|
||||
* - TODO: contact the VL server again to see if the volume is
|
||||
* no longer registered
|
||||
*/
|
||||
up_write(&volume->server_sem);
|
||||
afs_put_server(server);
|
||||
_leave(" [completely rejected]");
|
||||
return 1;
|
||||
|
||||
/* problem reaching the server */
|
||||
case -ENETUNREACH:
|
||||
case -EHOSTUNREACH:
|
||||
case -ECONNREFUSED:
|
||||
case -ETIMEDOUT:
|
||||
case -EREMOTEIO:
|
||||
/* mark the server as dead
|
||||
* TODO: vary dead timeout depending on error
|
||||
*/
|
||||
spin_lock(&server->fs_lock);
|
||||
if (!server->fs_state) {
|
||||
server->fs_dead_jif = jiffies + HZ * 10;
|
||||
server->fs_state = result;
|
||||
printk("kAFS: SERVER DEAD state=%d\n", result);
|
||||
}
|
||||
spin_unlock(&server->fs_lock);
|
||||
goto try_next_server;
|
||||
|
||||
/* miscellaneous error */
|
||||
default:
|
||||
server->fs_act_jif = jiffies;
|
||||
case -ENOMEM:
|
||||
case -ENONET:
|
||||
break;
|
||||
}
|
||||
|
||||
/* tell the caller to accept the result */
|
||||
afs_put_server(server);
|
||||
_leave("");
|
||||
return 1;
|
||||
|
||||
/* tell the caller to loop around and try the next server */
|
||||
try_next_server_upw:
|
||||
up_write(&volume->server_sem);
|
||||
try_next_server:
|
||||
afs_put_server(server);
|
||||
_leave(" [try next server]");
|
||||
return 0;
|
||||
|
||||
} /* end afs_volume_release_fileserver() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* match a volume hash record stored in the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_volume_cache_match(void *target,
|
||||
const void *entry)
|
||||
{
|
||||
const struct afs_cache_vhash *vhash = entry;
|
||||
struct afs_volume *volume = target;
|
||||
|
||||
_enter("{%u},{%u}", volume->type, vhash->vtype);
|
||||
|
||||
if (volume->type == vhash->vtype) {
|
||||
_leave(" = SUCCESS");
|
||||
return CACHEFS_MATCH_SUCCESS;
|
||||
}
|
||||
|
||||
_leave(" = FAILED");
|
||||
return CACHEFS_MATCH_FAILED;
|
||||
} /* end afs_volume_cache_match() */
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* update a volume hash record stored in the cache
|
||||
*/
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static void afs_volume_cache_update(void *source, void *entry)
|
||||
{
|
||||
struct afs_cache_vhash *vhash = entry;
|
||||
struct afs_volume *volume = source;
|
||||
|
||||
_enter("");
|
||||
|
||||
vhash->vtype = volume->type;
|
||||
|
||||
} /* end afs_volume_cache_update() */
|
||||
#endif
|
142
fs/afs/volume.h
Normal file
142
fs/afs/volume.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/* volume.h: AFS volume management
|
||||
*
|
||||
* Copyright (C) 2002 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AFS_VOLUME_H
|
||||
#define _LINUX_AFS_VOLUME_H
|
||||
|
||||
#include "types.h"
|
||||
#include "fsclient.h"
|
||||
#include "kafstimod.h"
|
||||
#include "kafsasyncd.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define __packed __attribute__((packed))
|
||||
|
||||
typedef enum {
|
||||
AFS_VLUPD_SLEEP, /* sleeping waiting for update timer to fire */
|
||||
AFS_VLUPD_PENDING, /* on pending queue */
|
||||
AFS_VLUPD_INPROGRESS, /* op in progress */
|
||||
AFS_VLUPD_BUSYSLEEP, /* sleeping because server returned EBUSY */
|
||||
|
||||
} __attribute__((packed)) afs_vlocation_upd_t;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* entry in the cached volume location catalogue
|
||||
*/
|
||||
struct afs_cache_vlocation
|
||||
{
|
||||
uint8_t name[64]; /* volume name (lowercase, padded with NULs) */
|
||||
uint8_t nservers; /* number of entries used in servers[] */
|
||||
uint8_t vidmask; /* voltype mask for vid[] */
|
||||
uint8_t srvtmask[8]; /* voltype masks for servers[] */
|
||||
#define AFS_VOL_VTM_RW 0x01 /* R/W version of the volume is available (on this server) */
|
||||
#define AFS_VOL_VTM_RO 0x02 /* R/O version of the volume is available (on this server) */
|
||||
#define AFS_VOL_VTM_BAK 0x04 /* backup version of the volume is available (on this server) */
|
||||
|
||||
afs_volid_t vid[3]; /* volume IDs for R/W, R/O and Bak volumes */
|
||||
struct in_addr servers[8]; /* fileserver addresses */
|
||||
time_t rtime; /* last retrieval time */
|
||||
};
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
extern struct cachefs_index_def afs_vlocation_cache_index_def;
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* volume -> vnode hash table entry
|
||||
*/
|
||||
struct afs_cache_vhash
|
||||
{
|
||||
afs_voltype_t vtype; /* which volume variation */
|
||||
uint8_t hash_bucket; /* which hash bucket this represents */
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
extern struct cachefs_index_def afs_volume_cache_index_def;
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS volume location record
|
||||
*/
|
||||
struct afs_vlocation
|
||||
{
|
||||
atomic_t usage;
|
||||
struct list_head link; /* link in cell volume location list */
|
||||
struct afs_timer timeout; /* decaching timer */
|
||||
struct afs_cell *cell; /* cell to which volume belongs */
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct cachefs_cookie *cache; /* caching cookie */
|
||||
#endif
|
||||
struct afs_cache_vlocation vldb; /* volume information DB record */
|
||||
struct afs_volume *vols[3]; /* volume access record pointer (index by type) */
|
||||
rwlock_t lock; /* access lock */
|
||||
unsigned long read_jif; /* time at which last read from vlserver */
|
||||
struct afs_timer upd_timer; /* update timer */
|
||||
struct afs_async_op upd_op; /* update operation */
|
||||
afs_vlocation_upd_t upd_state; /* update state */
|
||||
unsigned short upd_first_svix; /* first server index during update */
|
||||
unsigned short upd_curr_svix; /* current server index during update */
|
||||
unsigned short upd_rej_cnt; /* ENOMEDIUM count during update */
|
||||
unsigned short upd_busy_cnt; /* EBUSY count during update */
|
||||
unsigned short valid; /* T if valid */
|
||||
};
|
||||
|
||||
extern int afs_vlocation_lookup(struct afs_cell *cell,
|
||||
const char *name,
|
||||
unsigned namesz,
|
||||
struct afs_vlocation **_vlocation);
|
||||
|
||||
#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0)
|
||||
|
||||
extern void afs_put_vlocation(struct afs_vlocation *vlocation);
|
||||
extern void afs_vlocation_do_timeout(struct afs_vlocation *vlocation);
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* AFS volume access record
|
||||
*/
|
||||
struct afs_volume
|
||||
{
|
||||
atomic_t usage;
|
||||
struct afs_cell *cell; /* cell to which belongs (unrefd ptr) */
|
||||
struct afs_vlocation *vlocation; /* volume location */
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct cachefs_cookie *cache; /* caching cookie */
|
||||
#endif
|
||||
afs_volid_t vid; /* volume ID */
|
||||
afs_voltype_t __packed type; /* type of volume */
|
||||
char type_force; /* force volume type (suppress R/O -> R/W) */
|
||||
unsigned short nservers; /* number of server slots filled */
|
||||
unsigned short rjservers; /* number of servers discarded due to -ENOMEDIUM */
|
||||
struct afs_server *servers[8]; /* servers on which volume resides (ordered) */
|
||||
struct rw_semaphore server_sem; /* lock for accessing current server */
|
||||
};
|
||||
|
||||
extern int afs_volume_lookup(const char *name,
|
||||
struct afs_cell *cell,
|
||||
int rwpath,
|
||||
struct afs_volume **_volume);
|
||||
|
||||
#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
|
||||
|
||||
extern void afs_put_volume(struct afs_volume *volume);
|
||||
|
||||
extern int afs_volume_pick_fileserver(struct afs_volume *volume,
|
||||
struct afs_server **_server);
|
||||
|
||||
extern int afs_volume_release_fileserver(struct afs_volume *volume,
|
||||
struct afs_server *server,
|
||||
int result);
|
||||
|
||||
#endif /* _LINUX_AFS_VOLUME_H */
|
Reference in New Issue
Block a user