[AF_RXRPC]: Make the in-kernel AFS filesystem use AF_RXRPC.

Make the in-kernel AFS filesystem use AF_RXRPC instead of the old RxRPC code.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David Howells 2007-04-26 15:55:03 -07:00 committed by David S. Miller
parent 651350d10f
commit 08e0e7c82e
39 changed files with 4164 additions and 5393 deletions

View file

@ -2019,7 +2019,7 @@ config CODA_FS_OLD_API
config AFS_FS config AFS_FS
tristate "Andrew File System support (AFS) (EXPERIMENTAL)" tristate "Andrew File System support (AFS) (EXPERIMENTAL)"
depends on INET && EXPERIMENTAL depends on INET && EXPERIMENTAL
select RXRPC select AF_RXRPC
help help
If you say Y here, you will get an experimental Andrew File System If you say Y here, you will get an experimental Andrew File System
driver. It currently only supports unsecured read-only AFS access. driver. It currently only supports unsecured read-only AFS access.
@ -2028,6 +2028,17 @@ config AFS_FS
If unsure, say N. If unsure, say N.
config AFS_DEBUG
bool "AFS dynamic debugging"
depends on AFS_FS
help
Say Y here to make runtime controllable debugging messages appear.
See <file:Documentation/filesystems/afs.txt> for more information.
If unsure, say N.
config RXRPC config RXRPC
tristate tristate

View file

@ -10,12 +10,11 @@ kafs-objs := \
file.o \ file.o \
fsclient.o \ fsclient.o \
inode.o \ inode.o \
kafsasyncd.o \
kafstimod.o \
main.o \ main.o \
misc.o \ misc.o \
mntpt.o \ mntpt.o \
proc.o \ proc.o \
rxrpc.o \
server.o \ server.o \
super.o \ super.o \
vlclient.o \ vlclient.o \

View file

@ -1,6 +1,6 @@
/* AFS types /* AFS common types
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -9,10 +9,10 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#ifndef AFS_TYPES_H #ifndef AFS_H
#define AFS_TYPES_H #define AFS_H
#include <rxrpc/types.h> #include <linux/in.h>
typedef unsigned afs_volid_t; typedef unsigned afs_volid_t;
typedef unsigned afs_vnodeid_t; typedef unsigned afs_vnodeid_t;
@ -31,9 +31,6 @@ typedef enum {
AFS_FTYPE_SYMLINK = 3, AFS_FTYPE_SYMLINK = 3,
} afs_file_type_t; } afs_file_type_t;
struct afs_cell;
struct afs_vnode;
/* /*
* AFS file identifier * AFS file identifier
*/ */
@ -54,14 +51,13 @@ typedef enum {
} afs_callback_type_t; } afs_callback_type_t;
struct afs_callback { struct afs_callback {
struct afs_server *server; /* server that made the promise */
struct afs_fid fid; /* file identifier */ struct afs_fid fid; /* file identifier */
unsigned version; /* callback version */ unsigned version; /* callback version */
unsigned expiry; /* time at which expires */ unsigned expiry; /* time at which expires */
afs_callback_type_t type; /* type of callback */ afs_callback_type_t type; /* type of callback */
}; };
#define AFSCBMAX 50 #define AFSCBMAX 50 /* maximum callbacks transferred per bulk op */
/* /*
* AFS volume information * AFS volume information
@ -70,7 +66,7 @@ struct afs_volume_info {
afs_volid_t vid; /* volume ID */ afs_volid_t vid; /* volume ID */
afs_voltype_t type; /* type of this volume */ afs_voltype_t type; /* type of this volume */
afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */ afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */
/* list of fileservers serving this volume */ /* list of fileservers serving this volume */
size_t nservers; /* number of entries used in servers[] */ size_t nservers; /* number of entries used in servers[] */
struct { struct {
@ -88,7 +84,7 @@ struct afs_file_status {
afs_file_type_t type; /* file type */ afs_file_type_t type; /* file type */
unsigned nlink; /* link count */ unsigned nlink; /* link count */
size_t size; /* file size */ size_t size; /* file size */
afs_dataversion_t version; /* current data version */ afs_dataversion_t data_version; /* current data version */
unsigned author; /* author ID */ unsigned author; /* author ID */
unsigned owner; /* owner ID */ unsigned owner; /* owner ID */
unsigned caller_access; /* access rights for authenticated caller */ unsigned caller_access; /* access rights for authenticated caller */
@ -106,4 +102,4 @@ struct afs_volsync {
time_t creation; /* volume creation time */ time_t creation; /* volume creation time */
}; };
#endif /* AFS_TYPES_H */ #endif /* AFS_H */

28
fs/afs/afs_cm.h Normal file
View file

@ -0,0 +1,28 @@
/* AFS Cache Manager definitions
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef AFS_CM_H
#define AFS_CM_H
#define AFS_CM_PORT 7001 /* AFS file server port */
#define CM_SERVICE 1 /* AFS File Service ID */
enum AFS_CM_Operations {
CBCallBack = 204, /* break callback promises */
CBInitCallBackState = 205, /* initialise callback state */
CBProbe = 206, /* probe client */
CBGetLock = 207, /* get contents of CM lock table */
CBGetCE = 208, /* get cache file description */
CBGetXStatsVersion = 209, /* get version of extended statistics */
CBGetXStats = 210, /* get contents of extended statistics data */
};
#endif /* AFS_FS_H */

View file

@ -1,6 +1,6 @@
/* AFS abort/error codes /* AFS File Service definitions
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -9,15 +9,22 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#ifndef AFS_ERRORS_H #ifndef AFS_FS_H
#define AFS_ERRORS_H #define AFS_FS_H
#include "types.h" #define AFS_FS_PORT 7000 /* AFS file server port */
#define FS_SERVICE 1 /* AFS File Service ID */
/* enum AFS_FS_Operations {
* file server abort codes FSFETCHSTATUS = 132, /* AFS Fetch file status */
*/ FSFETCHDATA = 130, /* AFS Fetch file data */
typedef enum { FSGIVEUPCALLBACKS = 147, /* AFS Discard callback promises */
FSGETVOLUMEINFO = 148, /* AFS Get root volume information */
FSGETROOTVOLUME = 151, /* AFS Get root volume name */
FSLOOKUP = 161 /* AFS lookup file in directory */
};
enum AFS_FS_Errors {
VSALVAGE = 101, /* volume needs salvaging */ VSALVAGE = 101, /* volume needs salvaging */
VNOVNODE = 102, /* no such file/dir (vnode) */ VNOVNODE = 102, /* no such file/dir (vnode) */
VNOVOL = 103, /* no such volume or volume unavailable */ VNOVOL = 103, /* no such volume or volume unavailable */
@ -29,8 +36,6 @@ typedef enum {
VOVERQUOTA = 109, /* volume's maximum quota exceeded */ VOVERQUOTA = 109, /* volume's maximum quota exceeded */
VBUSY = 110, /* volume is temporarily unavailable */ VBUSY = 110, /* volume is temporarily unavailable */
VMOVED = 111, /* volume moved to new server - ask this FS where */ VMOVED = 111, /* volume moved to new server - ask this FS where */
} afs_rxfs_abort_t; };
extern int afs_abort_to_error(int); #endif /* AFS_FS_H */
#endif /* AFS_ERRORS_H */

View file

@ -1,6 +1,6 @@
/* Volume Location Service client interface /* AFS Volume Location Service client interface
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -9,10 +9,19 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#ifndef AFS_VLCLIENT_H #ifndef AFS_VL_H
#define AFS_VLCLIENT_H #define AFS_VL_H
#include "types.h" #include "afs.h"
#define AFS_VL_PORT 7003 /* volume location service port */
#define VL_SERVICE 52 /* RxRPC service ID for the Volume Location service */
enum AFSVL_Operations {
VLGETENTRYBYID = 503, /* AFS Get Cache Entry By ID operation ID */
VLGETENTRYBYNAME = 504, /* AFS Get Cache Entry By Name operation ID */
VLPROBE = 514, /* AFS Probe Volume Location Service operation ID */
};
enum AFSVL_Errors { enum AFSVL_Errors {
AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */ AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */
@ -40,14 +49,16 @@ enum AFSVL_Errors {
AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */ AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */
AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */ AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */
AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */ AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */
AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server °ag */ AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server °ag */
AFSVL_PERM = 363546, /* No permission access */ AFSVL_PERM = 363546, /* No permission access */
AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */ AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */
}; };
/* maps to "struct vldbentry" in vvl-spec.pdf */ /*
* maps to "struct vldbentry" in vvl-spec.pdf
*/
struct afs_vldbentry { struct afs_vldbentry {
char name[65]; /* name of volume (including NUL char) */ char name[65]; /* name of volume (with NUL char) */
afs_voltype_t type; /* volume type */ afs_voltype_t type; /* volume type */
unsigned num_servers; /* num servers that hold instances of this vol */ unsigned num_servers; /* num servers that hold instances of this vol */
unsigned clone_id; /* cloning ID */ unsigned clone_id; /* cloning ID */
@ -70,16 +81,4 @@ struct afs_vldbentry {
} servers[8]; } servers[8];
}; };
extern int afs_rxvl_get_entry_by_name(struct afs_server *, const char *, #endif /* AFS_VL_H */
unsigned, struct afs_cache_vlocation *);
extern int afs_rxvl_get_entry_by_id(struct afs_server *, afs_volid_t,
afs_voltype_t,
struct afs_cache_vlocation *);
extern int afs_rxvl_get_entry_by_id_async(struct afs_async_op *,
afs_volid_t, afs_voltype_t);
extern int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *,
struct afs_cache_vlocation *);
#endif /* AFS_VLCLIENT_H */

256
fs/afs/cache.c Normal file
View file

@ -0,0 +1,256 @@
/* AFS caching stuff
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#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
/*
* 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;
}
#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)));
}
#endif
#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
/*
* 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;
} 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;
}
#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;
}
#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
/*
* 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;
}
#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;
}
#endif
#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
/*
* 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;
}
#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;
}
#endif

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002 Red Hat, Inc. All rights reserved. * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
* *
* This software may be freely redistributed under the terms of the * This software may be freely redistributed under the terms of the
* GNU General Public License. * GNU General Public License.
@ -16,83 +16,182 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include "server.h" #include <linux/circ_buf.h>
#include "vnode.h"
#include "internal.h" #include "internal.h"
#include "cmservice.h"
unsigned afs_vnode_update_timeout = 10;
#define afs_breakring_space(server) \
CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail, \
ARRAY_SIZE((server)->cb_break))
//static void afs_callback_updater(struct work_struct *);
static struct workqueue_struct *afs_callback_update_worker;
/* /*
* allow the fileserver to request callback state (re-)initialisation * allow the fileserver to request callback state (re-)initialisation
*/ */
int SRXAFSCM_InitCallBackState(struct afs_server *server) void afs_init_callback_state(struct afs_server *server)
{ {
struct list_head callbacks; struct afs_vnode *vnode;
_enter("%p", server); _enter("{%p}", server);
INIT_LIST_HEAD(&callbacks);
/* transfer the callback list from the server to a temp holding area */
spin_lock(&server->cb_lock); spin_lock(&server->cb_lock);
list_add(&callbacks, &server->cb_promises); /* kill all the promises on record from this server */
list_del_init(&server->cb_promises); while (!RB_EMPTY_ROOT(&server->cb_promises)) {
vnode = rb_entry(server->cb_promises.rb_node,
/* munch our way through the list, grabbing the inode, dropping all the struct afs_vnode, cb_promise);
* locks and regetting them in the right order printk("\nUNPROMISE on %p\n", vnode);
*/ rb_erase(&vnode->cb_promise, &server->cb_promises);
while (!list_empty(&callbacks)) { vnode->cb_promised = false;
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); spin_unlock(&server->cb_lock);
_leave("");
}
_leave(" = 0"); /*
return 0; * handle the data invalidation side of a callback being broken
*/
void afs_broken_callback_work(struct work_struct *work)
{
struct afs_vnode *vnode =
container_of(work, struct afs_vnode, cb_broken_work);
_enter("");
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
return;
/* we're only interested in dealing with a broken callback on *this*
* vnode and only if no-one else has dealt with it yet */
if (!mutex_trylock(&vnode->cb_broken_lock))
return; /* someone else is dealing with it */
if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
if (afs_vnode_fetch_status(vnode) < 0)
goto out;
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
goto out;
/* if the vnode's data version number changed then its contents
* are different */
if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
_debug("zap data");
invalidate_remote_inode(&vnode->vfs_inode);
}
}
out:
mutex_unlock(&vnode->cb_broken_lock);
/* avoid the potential race whereby the mutex_trylock() in this
* function happens again between the clear_bit() and the
* mutex_unlock() */
if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
_debug("requeue");
queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
}
_leave("");
}
/*
* actually break a callback
*/
static void afs_break_callback(struct afs_server *server,
struct afs_vnode *vnode)
{
_enter("");
set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
if (vnode->cb_promised) {
spin_lock(&vnode->lock);
_debug("break callback");
spin_lock(&server->cb_lock);
if (vnode->cb_promised) {
rb_erase(&vnode->cb_promise, &server->cb_promises);
vnode->cb_promised = false;
}
spin_unlock(&server->cb_lock);
queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
spin_unlock(&vnode->lock);
}
}
/*
* allow the fileserver to explicitly break one callback
* - happens when
* - the backing file is changed
* - a lock is released
*/
static void afs_break_one_callback(struct afs_server *server,
struct afs_fid *fid)
{
struct afs_vnode *vnode;
struct rb_node *p;
_debug("find");
spin_lock(&server->fs_lock);
p = server->fs_vnodes.rb_node;
while (p) {
vnode = rb_entry(p, struct afs_vnode, server_rb);
if (fid->vid < vnode->fid.vid)
p = p->rb_left;
else if (fid->vid > vnode->fid.vid)
p = p->rb_right;
else if (fid->vnode < vnode->fid.vnode)
p = p->rb_left;
else if (fid->vnode > vnode->fid.vnode)
p = p->rb_right;
else if (fid->unique < vnode->fid.unique)
p = p->rb_left;
else if (fid->unique > vnode->fid.unique)
p = p->rb_right;
else
goto found;
}
/* not found so we just ignore it (it may have moved to another
* server) */
not_available:
_debug("not avail");
spin_unlock(&server->fs_lock);
_leave("");
return;
found:
_debug("found");
ASSERTCMP(server, ==, vnode->server);
if (!igrab(AFS_VNODE_TO_I(vnode)))
goto not_available;
spin_unlock(&server->fs_lock);
afs_break_callback(server, vnode);
iput(&vnode->vfs_inode);
_leave("");
} }
/* /*
* allow the fileserver to break callback promises * allow the fileserver to break callback promises
*/ */
int SRXAFSCM_CallBack(struct afs_server *server, size_t count, void afs_break_callbacks(struct afs_server *server, size_t count,
struct afs_callback callbacks[]) struct afs_callback callbacks[])
{ {
_enter("%p,%u,", server, count); _enter("%p,%zu,", server, count);
ASSERT(server != NULL);
ASSERTCMP(count, <=, AFSCBMAX);
for (; count > 0; callbacks++, 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 }", _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
callbacks->fid.vid, callbacks->fid.vid,
callbacks->fid.vnode, callbacks->fid.vnode,
@ -101,66 +200,244 @@ int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
callbacks->expiry, callbacks->expiry,
callbacks->type callbacks->type
); );
afs_break_one_callback(server, &callbacks->fid);
/* 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"); _leave("");
return 0; return;
} }
/* /*
* allow the fileserver to see if the cache manager is still alive * record the callback for breaking
* - the caller must hold server->cb_lock
*/ */
int SRXAFSCM_Probe(struct afs_server *server) static void afs_do_give_up_callback(struct afs_server *server,
struct afs_vnode *vnode)
{ {
_debug("SRXAFSCM_Probe(%p)\n", server); struct afs_callback *cb;
return 0;
_enter("%p,%p", server, vnode);
cb = &server->cb_break[server->cb_break_head];
cb->fid = vnode->fid;
cb->version = vnode->cb_version;
cb->expiry = vnode->cb_expiry;
cb->type = vnode->cb_type;
smp_wmb();
server->cb_break_head =
(server->cb_break_head + 1) &
(ARRAY_SIZE(server->cb_break) - 1);
/* defer the breaking of callbacks to try and collect as many as
* possible to ship in one operation */
switch (atomic_inc_return(&server->cb_break_n)) {
case 1 ... AFSCBMAX - 1:
queue_delayed_work(afs_callback_update_worker,
&server->cb_break_work, HZ * 2);
break;
case AFSCBMAX:
afs_flush_callback_breaks(server);
break;
default:
break;
}
ASSERT(server->cb_promises.rb_node != NULL);
rb_erase(&vnode->cb_promise, &server->cb_promises);
vnode->cb_promised = false;
_leave("");
}
/*
* give up the callback registered for a vnode on the file server when the
* inode is being cleared
*/
void afs_give_up_callback(struct afs_vnode *vnode)
{
struct afs_server *server = vnode->server;
DECLARE_WAITQUEUE(myself, current);
_enter("%d", vnode->cb_promised);
_debug("GIVE UP INODE %p", &vnode->vfs_inode);
if (!vnode->cb_promised) {
_leave(" [not promised]");
return;
}
ASSERT(server != NULL);
spin_lock(&server->cb_lock);
if (vnode->cb_promised && afs_breakring_space(server) == 0) {
add_wait_queue(&server->cb_break_waitq, &myself);
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!vnode->cb_promised ||
afs_breakring_space(server) != 0)
break;
spin_unlock(&server->cb_lock);
schedule();
spin_lock(&server->cb_lock);
}
remove_wait_queue(&server->cb_break_waitq, &myself);
__set_current_state(TASK_RUNNING);
}
/* of course, it's always possible for the server to break this vnode's
* callback first... */
if (vnode->cb_promised)
afs_do_give_up_callback(server, vnode);
spin_unlock(&server->cb_lock);
_leave("");
}
/*
* dispatch a deferred give up callbacks operation
*/
void afs_dispatch_give_up_callbacks(struct work_struct *work)
{
struct afs_server *server =
container_of(work, struct afs_server, cb_break_work.work);
_enter("");
/* tell the fileserver to discard the callback promises it has
* - in the event of ENOMEM or some other error, we just forget that we
* had callbacks entirely, and the server will call us later to break
* them
*/
afs_fs_give_up_callbacks(server, &afs_async_call);
}
/*
* flush the outstanding callback breaks on a server
*/
void afs_flush_callback_breaks(struct afs_server *server)
{
cancel_delayed_work(&server->cb_break_work);
queue_delayed_work(afs_callback_update_worker,
&server->cb_break_work, 0);
}
#if 0
/*
* update a bunch of callbacks
*/
static void afs_callback_updater(struct work_struct *work)
{
struct afs_server *server;
struct afs_vnode *vnode, *xvnode;
time_t now;
long timeout;
int ret;
server = container_of(work, struct afs_server, updater);
_enter("");
now = get_seconds();
/* find the first vnode to update */
spin_lock(&server->cb_lock);
for (;;) {
if (RB_EMPTY_ROOT(&server->cb_promises)) {
spin_unlock(&server->cb_lock);
_leave(" [nothing]");
return;
}
vnode = rb_entry(rb_first(&server->cb_promises),
struct afs_vnode, cb_promise);
if (atomic_read(&vnode->usage) > 0)
break;
rb_erase(&vnode->cb_promise, &server->cb_promises);
vnode->cb_promised = false;
}
timeout = vnode->update_at - now;
if (timeout > 0) {
queue_delayed_work(afs_vnode_update_worker,
&afs_vnode_update, timeout * HZ);
spin_unlock(&server->cb_lock);
_leave(" [nothing]");
return;
}
list_del_init(&vnode->update);
atomic_inc(&vnode->usage);
spin_unlock(&server->cb_lock);
/* we can now perform the update */
_debug("update %s", vnode->vldb.name);
vnode->state = AFS_VL_UPDATING;
vnode->upd_rej_cnt = 0;
vnode->upd_busy_cnt = 0;
ret = afs_vnode_update_record(vl, &vldb);
switch (ret) {
case 0:
afs_vnode_apply_update(vl, &vldb);
vnode->state = AFS_VL_UPDATING;
break;
case -ENOMEDIUM:
vnode->state = AFS_VL_VOLUME_DELETED;
break;
default:
vnode->state = AFS_VL_UNCERTAIN;
break;
}
/* and then reschedule */
_debug("reschedule");
vnode->update_at = get_seconds() + afs_vnode_update_timeout;
spin_lock(&server->cb_lock);
if (!list_empty(&server->cb_promises)) {
/* next update in 10 minutes, but wait at least 1 second more
* than the newest record already queued so that we don't spam
* the VL server suddenly with lots of requests
*/
xvnode = list_entry(server->cb_promises.prev,
struct afs_vnode, update);
if (vnode->update_at <= xvnode->update_at)
vnode->update_at = xvnode->update_at + 1;
xvnode = list_entry(server->cb_promises.next,
struct afs_vnode, update);
timeout = xvnode->update_at - now;
if (timeout < 0)
timeout = 0;
} else {
timeout = afs_vnode_update_timeout;
}
list_add_tail(&vnode->update, &server->cb_promises);
_debug("timeout %ld", timeout);
queue_delayed_work(afs_vnode_update_worker,
&afs_vnode_update, timeout * HZ);
spin_unlock(&server->cb_lock);
afs_put_vnode(vl);
}
#endif
/*
* initialise the callback update process
*/
int __init afs_callback_update_init(void)
{
afs_callback_update_worker =
create_singlethread_workqueue("kafs_callbackd");
return afs_callback_update_worker ? 0 : -ENOMEM;
}
/*
* shut down the callback update process
*/
void __exit afs_callback_update_kill(void)
{
destroy_workqueue(afs_callback_update_worker);
} }

View file

@ -11,15 +11,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.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" #include "internal.h"
DECLARE_RWSEM(afs_proc_cells_sem); DECLARE_RWSEM(afs_proc_cells_sem);
@ -28,34 +19,21 @@ LIST_HEAD(afs_proc_cells);
static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells); static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells);
static DEFINE_RWLOCK(afs_cells_lock); static DEFINE_RWLOCK(afs_cells_lock);
static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */ static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
static DECLARE_WAIT_QUEUE_HEAD(afs_cells_freeable_wq);
static struct afs_cell *afs_cell_root; 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 * create a cell record
* - "name" is the name of the cell * - "name" is the name of the cell
* - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format * - "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 *afs_cell_create(const char *name, char *vllist)
{ {
struct afs_cell *cell; struct afs_cell *cell;
char *next; char *next;
int ret; int ret;
_enter("%s", name); _enter("%s,%s", name, vllist);
BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */ BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
@ -63,27 +41,24 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL); cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
if (!cell) { if (!cell) {
_leave(" = -ENOMEM"); _leave(" = -ENOMEM");
return -ENOMEM; return ERR_PTR(-ENOMEM);
} }
down_write(&afs_cells_sem); down_write(&afs_cells_sem);
memset(cell, 0, sizeof(struct afs_cell)); memset(cell, 0, sizeof(struct afs_cell));
atomic_set(&cell->usage, 0); atomic_set(&cell->usage, 1);
INIT_LIST_HEAD(&cell->link); INIT_LIST_HEAD(&cell->link);
rwlock_init(&cell->sv_lock); rwlock_init(&cell->servers_lock);
INIT_LIST_HEAD(&cell->sv_list); INIT_LIST_HEAD(&cell->servers);
INIT_LIST_HEAD(&cell->sv_graveyard);
spin_lock_init(&cell->sv_gylock);
init_rwsem(&cell->vl_sem); init_rwsem(&cell->vl_sem);
INIT_LIST_HEAD(&cell->vl_list); INIT_LIST_HEAD(&cell->vl_list);
INIT_LIST_HEAD(&cell->vl_graveyard); spin_lock_init(&cell->vl_lock);
spin_lock_init(&cell->vl_gylock);
strcpy(cell->name,name); strcpy(cell->name, name);
/* fill in the VL server list from the rest of the string */ /* fill in the VL server list from the rest of the string */
ret = -EINVAL; ret = -EINVAL;
@ -106,9 +81,9 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS) if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS)
break; break;
} while(vllist = next, vllist); } while ((vllist = next));
/* add a proc dir for this cell */ /* add a proc directory for this cell */
ret = afs_proc_cell_setup(cell); ret = afs_proc_cell_setup(cell);
if (ret < 0) if (ret < 0)
goto error; goto error;
@ -129,30 +104,29 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
down_write(&afs_proc_cells_sem); down_write(&afs_proc_cells_sem);
list_add_tail(&cell->proc_link, &afs_proc_cells); list_add_tail(&cell->proc_link, &afs_proc_cells);
up_write(&afs_proc_cells_sem); up_write(&afs_proc_cells_sem);
*_cell = cell;
up_write(&afs_cells_sem); up_write(&afs_cells_sem);
_leave(" = 0 (%p)", cell); _leave(" = %p", cell);
return 0; return cell;
badaddr: badaddr:
printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist); printk(KERN_ERR "kAFS: bad VL server IP address\n");
error: error:
up_write(&afs_cells_sem); up_write(&afs_cells_sem);
kfree(cell); kfree(cell);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ERR_PTR(ret);
} }
/* /*
* initialise the cell database from module parameters * set the root cell information
* - can be called with a module parameter string
* - can be called from a write to /proc/fs/afs/rootcell
*/ */
int afs_cell_init(char *rootcell) int afs_cell_init(char *rootcell)
{ {
struct afs_cell *old_root, *new_root; struct afs_cell *old_root, *new_root;
char *cp; char *cp;
int ret;
_enter(""); _enter("");
@ -160,79 +134,60 @@ int afs_cell_init(char *rootcell)
/* module is loaded with no parameters, or built statically. /* module is loaded with no parameters, or built statically.
* - in the future we might initialize cell DB here. * - in the future we might initialize cell DB here.
*/ */
_leave(" = 0 (but no root)"); _leave(" = 0 [no root]");
return 0; return 0;
} }
cp = strchr(rootcell, ':'); cp = strchr(rootcell, ':');
if (!cp) { if (!cp) {
printk(KERN_ERR "kAFS: no VL server IP addresses specified\n"); printk(KERN_ERR "kAFS: no VL server IP addresses specified\n");
_leave(" = %d (no colon)", -EINVAL); _leave(" = -EINVAL");
return -EINVAL; return -EINVAL;
} }
/* allocate a cell record for the root cell */ /* allocate a cell record for the root cell */
*cp++ = 0; *cp++ = 0;
ret = afs_cell_create(rootcell, cp, &new_root); new_root = afs_cell_create(rootcell, cp);
if (ret < 0) { if (IS_ERR(new_root)) {
_leave(" = %d", ret); _leave(" = %ld", PTR_ERR(new_root));
return ret; return PTR_ERR(new_root);
} }
/* as afs_put_cell() takes locks by itself, we have to do /* install the new cell */
* a little gymnastics to be race-free.
*/
afs_get_cell(new_root);
write_lock(&afs_cells_lock); write_lock(&afs_cells_lock);
while (afs_cell_root) { old_root = 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; afs_cell_root = new_root;
write_unlock(&afs_cells_lock); write_unlock(&afs_cells_lock);
afs_put_cell(old_root);
_leave(" = %d", ret); _leave(" = 0");
return ret; return 0;
} }
/* /*
* lookup a cell record * lookup a cell record
*/ */
int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell) struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz)
{ {
struct afs_cell *cell; struct afs_cell *cell;
int ret;
_enter("\"%*.*s\",", namesz, namesz, name ? name : ""); _enter("\"%*.*s\",", namesz, namesz, name ? name : "");
*_cell = NULL; down_read(&afs_cells_sem);
read_lock(&afs_cells_lock);
if (name) { if (name) {
/* if the cell was named, look for it in the cell record list */ /* 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) { list_for_each_entry(cell, &afs_cells, link) {
if (strncmp(cell->name, name, namesz) == 0) { if (strncmp(cell->name, name, namesz) == 0) {
afs_get_cell(cell); afs_get_cell(cell);
goto found; goto found;
} }
} }
cell = NULL; cell = ERR_PTR(-ENOENT);
found: found:
;
read_unlock(&afs_cells_lock);
if (cell)
ret = 0;
} else { } else {
read_lock(&afs_cells_lock);
cell = afs_cell_root; cell = afs_cell_root;
if (!cell) { if (!cell) {
/* this should not happen unless user tries to mount /* this should not happen unless user tries to mount
@ -241,37 +196,32 @@ int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell)
* ENOENT might be "more appropriate" but they happen * ENOENT might be "more appropriate" but they happen
* for other reasons. * for other reasons.
*/ */
ret = -EDESTADDRREQ; cell = ERR_PTR(-EDESTADDRREQ);
} else { } else {
afs_get_cell(cell); afs_get_cell(cell);
ret = 0;
} }
read_unlock(&afs_cells_lock);
} }
*_cell = cell; read_unlock(&afs_cells_lock);
_leave(" = %d (%p)", ret, cell); up_read(&afs_cells_sem);
return ret; _leave(" = %p", cell);
return cell;
} }
/* /*
* try and get a cell record * try and get a cell record
*/ */
struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell) struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell)
{ {
struct afs_cell *cell;
write_lock(&afs_cells_lock); write_lock(&afs_cells_lock);
cell = *_cell;
if (cell && !list_empty(&cell->link)) if (cell && !list_empty(&cell->link))
afs_get_cell(cell); afs_get_cell(cell);
else else
cell = NULL; cell = NULL;
write_unlock(&afs_cells_lock); write_unlock(&afs_cells_lock);
return cell; return cell;
} }
@ -285,8 +235,7 @@ void afs_put_cell(struct afs_cell *cell)
_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
/* sanity check */ ASSERTCMP(atomic_read(&cell->usage), >, 0);
BUG_ON(atomic_read(&cell->usage) <= 0);
/* to prevent a race, the decrement and the dequeue must be effectively /* to prevent a race, the decrement and the dequeue must be effectively
* atomic */ * atomic */
@ -298,35 +247,49 @@ void afs_put_cell(struct afs_cell *cell)
return; return;
} }
ASSERT(list_empty(&cell->servers));
ASSERT(list_empty(&cell->vl_list));
write_unlock(&afs_cells_lock); write_unlock(&afs_cells_lock);
BUG_ON(!list_empty(&cell->sv_list)); wake_up(&afs_cells_freeable_wq);
BUG_ON(!list_empty(&cell->sv_graveyard));
BUG_ON(!list_empty(&cell->vl_list));
BUG_ON(!list_empty(&cell->vl_graveyard));
_leave(" [unused]"); _leave(" [unused]");
} }
/* /*
* destroy a cell record * destroy a cell record
* - must be called with the afs_cells_sem write-locked
* - cell->link should have been broken by the caller
*/ */
static void afs_cell_destroy(struct afs_cell *cell) static void afs_cell_destroy(struct afs_cell *cell)
{ {
_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
/* to prevent a race, the decrement and the dequeue must be effectively ASSERTCMP(atomic_read(&cell->usage), >=, 0);
* atomic */ ASSERT(list_empty(&cell->link));
write_lock(&afs_cells_lock);
/* sanity check */ /* wait for everyone to stop using the cell */
BUG_ON(atomic_read(&cell->usage) != 0); if (atomic_read(&cell->usage) > 0) {
DECLARE_WAITQUEUE(myself, current);
list_del_init(&cell->link); _debug("wait for cell %s", cell->name);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&afs_cells_freeable_wq, &myself);
write_unlock(&afs_cells_lock); while (atomic_read(&cell->usage) > 0) {
schedule();
set_current_state(TASK_UNINTERRUPTIBLE);
}
down_write(&afs_cells_sem); remove_wait_queue(&afs_cells_freeable_wq, &myself);
set_current_state(TASK_RUNNING);
}
_debug("cell dead");
ASSERTCMP(atomic_read(&cell->usage), ==, 0);
ASSERT(list_empty(&cell->servers));
ASSERT(list_empty(&cell->vl_list));
afs_proc_cell_remove(cell); afs_proc_cell_remove(cell);
@ -338,101 +301,25 @@ static void afs_cell_destroy(struct afs_cell *cell)
cachefs_relinquish_cookie(cell->cache, 0); cachefs_relinquish_cookie(cell->cache, 0);
#endif #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); kfree(cell);
_leave(" [destroyed]"); _leave(" [destroyed]");
} }
/*
* 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_move_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;
}
/* /*
* purge in-memory cell database on module unload or afs_init() failure * purge in-memory cell database on module unload or afs_init() failure
* - the timeout daemon is stopped before calling this * - the timeout daemon is stopped before calling this
*/ */
void afs_cell_purge(void) void afs_cell_purge(void)
{ {
struct afs_vlocation *vlocation;
struct afs_cell *cell; struct afs_cell *cell;
_enter(""); _enter("");
afs_put_cell(afs_cell_root); afs_put_cell(afs_cell_root);
down_write(&afs_cells_sem);
while (!list_empty(&afs_cells)) { while (!list_empty(&afs_cells)) {
cell = NULL; cell = NULL;
@ -451,102 +338,11 @@ void afs_cell_purge(void)
_debug("PURGING CELL %s (%d)", _debug("PURGING CELL %s (%d)",
cell->name, atomic_read(&cell->usage)); 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 */ /* now the cell should be left with no references */
afs_cell_destroy(cell); afs_cell_destroy(cell);
} }
} }
up_write(&afs_cells_sem);
_leave(""); _leave("");
} }
/*
* 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;
}
#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)));
}
#endif

View file

@ -1,70 +0,0 @@
/* 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 AFS_CELL_H
#define 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 *);
extern int afs_cell_create(const char *, char *, struct afs_cell **);
extern int afs_cell_lookup(const char *, unsigned, struct afs_cell **);
#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
extern struct afs_cell *afs_get_cell_maybe(struct afs_cell **);
extern void afs_put_cell(struct afs_cell *);
extern void afs_cell_purge(void);
#endif /* AFS_CELL_H */

View file

@ -12,623 +12,316 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/completion.h> #include <linux/ip.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" #include "internal.h"
#include "afs_cm.h"
static unsigned afscm_usage; /* AFS cache manager usage count */ struct workqueue_struct *afs_cm_workqueue;
static struct rw_semaphore afscm_sem; /* AFS cache manager start/stop semaphore */
static int afscm_new_call(struct rxrpc_call *call); static int afs_deliver_cb_init_call_back_state(struct afs_call *,
static void afscm_attention(struct rxrpc_call *call); struct sk_buff *, bool);
static void afscm_error(struct rxrpc_call *call); static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool);
static void afscm_aemap(struct rxrpc_call *call); static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool);
static void afs_cm_destructor(struct afs_call *);
static void _SRXAFSCM_CallBack(struct rxrpc_call *call); /*
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call); * CB.CallBack operation type
static void _SRXAFSCM_Probe(struct rxrpc_call *call); */
static const struct afs_call_type afs_SRXCBCallBack = {
typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call); .deliver = afs_deliver_cb_callback,
.abort_to_error = afs_abort_to_error,
static const struct rxrpc_operation AFSCM_ops[] = { .destructor = afs_cm_destructor,
{
.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", * CB.InitCallBackState operation type
.owner = THIS_MODULE, */
.link = LIST_HEAD_INIT(AFSCM_service.link), static const struct afs_call_type afs_SRXCBInitCallBackState = {
.new_call = afscm_new_call, .deliver = afs_deliver_cb_init_call_back_state,
.service_id = 1, .abort_to_error = afs_abort_to_error,
.attn_func = afscm_attention, .destructor = afs_cm_destructor,
.error_func = afscm_error,
.aemap_func = afscm_aemap,
.ops_begin = &AFSCM_ops[0],
.ops_end = &AFSCM_ops[ARRAY_SIZE(AFSCM_ops)],
}; };
static DECLARE_COMPLETION(kafscmd_alive); /*
static DECLARE_COMPLETION(kafscmd_dead); * CB.Probe operation type
static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq); */
static LIST_HEAD(kafscmd_attention_list); static const struct afs_call_type afs_SRXCBProbe = {
static LIST_HEAD(afscm_calls); .deliver = afs_deliver_cb_probe,
static DEFINE_SPINLOCK(afscm_calls_lock); .abort_to_error = afs_abort_to_error,
static DEFINE_SPINLOCK(kafscmd_attention_lock); .destructor = afs_cm_destructor,
static int kafscmd_die; };
/* /*
* AFS Cache Manager kernel thread * route an incoming cache manager call
* - return T if supported, F if not
*/ */
static int kafscmd(void *arg) bool afs_cm_incoming_call(struct afs_call *call)
{ {
DECLARE_WAITQUEUE(myself, current); u32 operation_id = ntohl(call->operation_ID);
struct rxrpc_call *call; _enter("{CB.OP %u}", operation_id);
_SRXAFSCM_xxxx_t func;
int die;
printk(KERN_INFO "kAFS: Started kafscmd %d\n", current->pid); switch (operation_id) {
case CBCallBack:
daemonize("kafscmd"); call->type = &afs_SRXCBCallBack;
return true;
complete(&kafscmd_alive); case CBInitCallBackState:
call->type = &afs_SRXCBInitCallBackState;
/* loop around looking for things to attend to */ return true;
do { case CBProbe:
if (list_empty(&kafscmd_attention_list)) { call->type = &afs_SRXCBProbe;
set_current_state(TASK_INTERRUPTIBLE); return true;
add_wait_queue(&kafscmd_sleepq, &myself); default:
return false;
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);
} }
/* /*
* handle a call coming in to the cache manager * clean up a cache manager call
* - 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) static void afs_cm_destructor(struct afs_call *call)
{ {
_enter("%p{cid=%u u=%d}", _enter("");
call, ntohl(call->call_id), atomic_read(&call->usage));
rxrpc_get_call(call); afs_put_server(call->server);
call->server = NULL;
/* add to my current call list */ kfree(call->buffer);
spin_lock(&afscm_calls_lock); call->buffer = NULL;
list_add(&call->app_link,&afscm_calls);
spin_unlock(&afscm_calls_lock);
_leave(" = 0");
return 0;
} }
/* /*
* queue on the kafscmd queue for attention * allow the fileserver to see if the cache manager is still alive
*/ */
static void afscm_attention(struct rxrpc_call *call) static void SRXAFSCB_CallBack(struct work_struct *work)
{ {
_enter("%p{cid=%u u=%d}", struct afs_call *call = container_of(work, struct afs_call, work);
call, ntohl(call->call_id), atomic_read(&call->usage));
spin_lock(&kafscmd_attention_lock); _enter("");
if (list_empty(&call->app_attn_link)) { /* be sure to send the reply *before* attempting to spam the AFS server
list_add_tail(&call->app_attn_link, &kafscmd_attention_list); * with FSFetchStatus requests on the vnodes with broken callbacks lest
rxrpc_get_call(call); * the AFS server get into a vicious cycle of trying to break further
} * callbacks because it hadn't received completion of the CBCallBack op
* yet */
spin_unlock(&kafscmd_attention_lock); afs_send_empty_reply(call);
wake_up(&kafscmd_sleepq);
_leave(" {u=%d}", atomic_read(&call->usage));
}
/*
* 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);
afs_break_callbacks(call->server, call->count, call->request);
_leave(""); _leave("");
} }
/* /*
* map afs abort codes to/from Linux error codes * deliver request data to a CB.CallBack call
* - called with call->lock held
*/ */
static void afscm_aemap(struct rxrpc_call *call) static int afs_deliver_cb_callback(struct afs_call *call, struct sk_buff *skb,
bool last)
{ {
switch (call->app_err_state) { struct afs_callback *cb;
case RXRPC_ESTATE_LOCAL_ABORT: struct afs_server *server;
call->app_abort_code = -call->app_errno; struct in_addr addr;
break; __be32 *bp;
case RXRPC_ESTATE_PEER_ABORT: u32 tmp;
call->app_errno = -ECONNABORTED; int ret, loop;
break;
default: _enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
switch (call->unmarshall) {
case 0:
call->offset = 0;
call->unmarshall++;
/* extract the FID array and its count in two steps */
case 1:
_debug("extract FID count");
ret = afs_extract_data(call, skb, last, &call->tmp, 4);
switch (ret) {
case 0: break;
case -EAGAIN: return 0;
default: return ret;
}
call->count = ntohl(call->tmp);
_debug("FID count: %u", call->count);
if (call->count > AFSCBMAX)
return -EBADMSG;
call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL);
if (!call->buffer)
return -ENOMEM;
call->offset = 0;
call->unmarshall++;
case 2:
_debug("extract FID array");
ret = afs_extract_data(call, skb, last, call->buffer,
call->count * 3 * 4);
switch (ret) {
case 0: break;
case -EAGAIN: return 0;
default: return ret;
}
_debug("unmarshall FID array");
call->request = kcalloc(call->count,
sizeof(struct afs_callback),
GFP_KERNEL);
if (!call->request)
return -ENOMEM;
cb = call->request;
bp = call->buffer;
for (loop = call->count; loop > 0; loop--, cb++) {
cb->fid.vid = ntohl(*bp++);
cb->fid.vnode = ntohl(*bp++);
cb->fid.unique = ntohl(*bp++);
cb->type = AFSCM_CB_UNTYPED;
}
call->offset = 0;
call->unmarshall++;
/* extract the callback array and its count in two steps */
case 3:
_debug("extract CB count");
ret = afs_extract_data(call, skb, last, &call->tmp, 4);
switch (ret) {
case 0: break;
case -EAGAIN: return 0;
default: return ret;
}
tmp = ntohl(call->tmp);
_debug("CB count: %u", tmp);
if (tmp != call->count && tmp != 0)
return -EBADMSG;
call->offset = 0;
call->unmarshall++;
if (tmp == 0)
goto empty_cb_array;
case 4:
_debug("extract CB array");
ret = afs_extract_data(call, skb, last, call->request,
call->count * 3 * 4);
switch (ret) {
case 0: break;
case -EAGAIN: return 0;
default: return ret;
}
_debug("unmarshall CB array");
cb = call->request;
bp = call->buffer;
for (loop = call->count; loop > 0; loop--, cb++) {
cb->version = ntohl(*bp++);
cb->expiry = ntohl(*bp++);
cb->type = ntohl(*bp++);
}
empty_cb_array:
call->offset = 0;
call->unmarshall++;
case 5:
_debug("trailer");
if (skb->len != 0)
return -EBADMSG;
break; break;
} }
}
/* if (!last)
* start the cache manager service if not already started return 0;
*/
int afscm_start(void)
{
int ret;
down_write(&afscm_sem); call->state = AFS_CALL_REPLYING;
if (!afscm_usage) {
ret = kernel_thread(kafscmd, NULL, 0);
if (ret < 0)
goto out;
wait_for_completion(&kafscmd_alive); /* we'll need the file server record as that tells us which set of
* vnodes to operate upon */
ret = rxrpc_add_service(afs_transport, &AFSCM_service); memcpy(&addr, &ip_hdr(skb)->saddr, 4);
if (ret < 0) server = afs_find_server(&addr);
goto kill; if (!server)
return -ENOTCONN;
afs_kafstimod_add_timer(&afs_mntpt_expiry_timer, call->server = server;
afs_mntpt_expiry_timeout * HZ);
}
afscm_usage++;
up_write(&afscm_sem);
INIT_WORK(&call->work, SRXAFSCB_CallBack);
schedule_work(&call->work);
return 0; return 0;
kill:
kafscmd_die = 1;
wake_up(&kafscmd_sleepq);
wait_for_completion(&kafscmd_dead);
out:
up_write(&afscm_sem);
return ret;
} }
/* /*
* stop the cache manager service * allow the fileserver to request callback state (re-)initialisation
*/ */
void afscm_stop(void) static void SRXAFSCB_InitCallBackState(struct work_struct *work)
{ {
struct rxrpc_call *call; struct afs_call *call = container_of(work, struct afs_call, work);
down_write(&afscm_sem); _enter("{%p}", call->server);
BUG_ON(afscm_usage == 0); afs_init_callback_state(call->server);
afscm_usage--; afs_send_empty_reply(call);
_leave("");
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);
} }
/* /*
* handle the fileserver breaking a set of callbacks * deliver request data to a CB.InitCallBackState call
*/ */
static void _SRXAFSCM_CallBack(struct rxrpc_call *call) static int afs_deliver_cb_init_call_back_state(struct afs_call *call,
struct sk_buff *skb,
bool last)
{ {
struct afs_server *server; struct afs_server *server;
size_t count, qty, tmp; struct in_addr addr;
int ret = 0, removed;
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); _enter(",{%u},%d", skb->len, last);
server = afs_server_get_from_peer(call->conn->peer); if (skb->len > 0)
return -EBADMSG;
if (!last)
return 0;
switch (call->app_call_state) { /* no unmarshalling required */
/* we've received the last packet call->state = AFS_CALL_REPLYING;
* - 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;
{ /* we'll need the file server record as that tells us which set of
struct afs_callback *cb, *pcb; * vnodes to operate upon */
int loop; memcpy(&addr, &ip_hdr(skb)->saddr, 4);
__be32 *fp, *bp; server = afs_find_server(&addr);
if (!server)
return -ENOTCONN;
call->server = server;
fp = rxrpc_call_alloc_scratch(call, qty); INIT_WORK(&call->work, SRXAFSCB_InitCallBackState);
schedule_work(&call->work);
/* drag the entire argument block out to the scratch return 0;
* 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);
} }
/* /*
* handle the fileserver asking us to initialise our callback state * allow the fileserver to see if the cache manager is still alive
*/ */
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call) static void SRXAFSCB_Probe(struct work_struct *work)
{ {
struct afs_server *server; struct afs_call *call = container_of(work, struct afs_call, work);
size_t count;
int ret = 0, removed;
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); _enter("");
afs_send_empty_reply(call);
server = afs_server_get_from_peer(call->conn->peer); _leave("");
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);
} }
/* /*
* handle a probe from a fileserver * deliver request data to a CB.Probe call
*/ */
static void _SRXAFSCM_Probe(struct rxrpc_call *call) static int afs_deliver_cb_probe(struct afs_call *call, struct sk_buff *skb,
bool last)
{ {
struct afs_server *server; _enter(",{%u},%d", skb->len, last);
size_t count;
int ret = 0, removed;
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); if (skb->len > 0)
return -EBADMSG;
if (!last)
return 0;
server = afs_server_get_from_peer(call->conn->peer); /* no unmarshalling required */
call->state = AFS_CALL_REPLYING;
switch (call->app_call_state) { INIT_WORK(&call->work, SRXAFSCB_Probe);
/* we've received the last packet - drain all the data from the schedule_work(&call->work);
* call */ return 0;
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);
} }

View file

@ -1,28 +0,0 @@
/* 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 AFS_CMSERVICE_H
#define 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 *);
extern int SRXAFSCM_CallBack(struct afs_server *, size_t,
struct afs_callback[]);
extern int SRXAFSCM_Probe(struct afs_server *);
#endif /* AFS_CMSERVICE_H */

View file

@ -15,11 +15,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pagemap.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" #include "internal.h"
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
@ -127,9 +122,10 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page)
if (qty == 0) if (qty == 0)
goto error; goto error;
if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) { if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) {
printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n", printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",
__FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages)); __FUNCTION__, dir->i_ino, qty,
ntohs(dbuf->blocks[0].pagehdr.npages));
goto error; goto error;
} }
#endif #endif
@ -194,6 +190,7 @@ static struct page *afs_dir_get_page(struct inode *dir, unsigned long index)
fail: fail:
afs_dir_put_page(page); afs_dir_put_page(page);
_leave(" = -EIO");
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
@ -207,7 +204,7 @@ static int afs_dir_open(struct inode *inode, struct file *file)
BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
BUILD_BUG_ON(sizeof(union afs_dirent) != 32); BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
return -ENOENT; return -ENOENT;
_leave(" = 0"); _leave(" = 0");
@ -242,7 +239,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
/* skip entries marked unused in the bitmap */ /* skip entries marked unused in the bitmap */
if (!(block->pagehdr.bitmap[offset / 8] & if (!(block->pagehdr.bitmap[offset / 8] &
(1 << (offset % 8)))) { (1 << (offset % 8)))) {
_debug("ENT[%Zu.%u]: unused\n", _debug("ENT[%Zu.%u]: unused",
blkoff / sizeof(union afs_dir_block), offset); blkoff / sizeof(union afs_dir_block), offset);
if (offset >= curr) if (offset >= curr)
*fpos = blkoff + *fpos = blkoff +
@ -256,7 +253,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
sizeof(*block) - sizeof(*block) -
offset * sizeof(union afs_dirent)); offset * sizeof(union afs_dirent));
_debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n", _debug("ENT[%Zu.%u]: %s %Zu \"%s\"",
blkoff / sizeof(union afs_dir_block), offset, blkoff / sizeof(union afs_dir_block), offset,
(offset < curr ? "skip" : "fill"), (offset < curr ? "skip" : "fill"),
nlen, dire->u.name); nlen, dire->u.name);
@ -266,7 +263,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
if (next >= AFS_DIRENT_PER_BLOCK) { if (next >= AFS_DIRENT_PER_BLOCK) {
_debug("ENT[%Zu.%u]:" _debug("ENT[%Zu.%u]:"
" %u travelled beyond end dir block" " %u travelled beyond end dir block"
" (len %u/%Zu)\n", " (len %u/%Zu)",
blkoff / sizeof(union afs_dir_block), blkoff / sizeof(union afs_dir_block),
offset, next, tmp, nlen); offset, next, tmp, nlen);
return -EIO; return -EIO;
@ -274,13 +271,13 @@ static int afs_dir_iterate_block(unsigned *fpos,
if (!(block->pagehdr.bitmap[next / 8] & if (!(block->pagehdr.bitmap[next / 8] &
(1 << (next % 8)))) { (1 << (next % 8)))) {
_debug("ENT[%Zu.%u]:" _debug("ENT[%Zu.%u]:"
" %u unmarked extension (len %u/%Zu)\n", " %u unmarked extension (len %u/%Zu)",
blkoff / sizeof(union afs_dir_block), blkoff / sizeof(union afs_dir_block),
offset, next, tmp, nlen); offset, next, tmp, nlen);
return -EIO; return -EIO;
} }
_debug("ENT[%Zu.%u]: ext %u/%Zu\n", _debug("ENT[%Zu.%u]: ext %u/%Zu",
blkoff / sizeof(union afs_dir_block), blkoff / sizeof(union afs_dir_block),
next, tmp, nlen); next, tmp, nlen);
next++; next++;
@ -311,12 +308,12 @@ static int afs_dir_iterate_block(unsigned *fpos,
} }
/* /*
* read an AFS directory * iterate through the data blob that lists the contents of an AFS directory
*/ */
static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie, static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
filldir_t filldir) filldir_t filldir)
{ {
union afs_dir_block *dblock; union afs_dir_block *dblock;
struct afs_dir_page *dbuf; struct afs_dir_page *dbuf;
struct page *page; struct page *page;
unsigned blkoff, limit; unsigned blkoff, limit;
@ -324,7 +321,7 @@ static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
_enter("{%lu},%u,,", dir->i_ino, *fpos); _enter("{%lu},%u,,", dir->i_ino, *fpos);
if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
_leave(" = -ESTALE"); _leave(" = -ESTALE");
return -ESTALE; return -ESTALE;
} }
@ -381,10 +378,12 @@ static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
unsigned fpos; unsigned fpos;
int ret; int ret;
_enter("{%Ld,{%lu}}", file->f_pos, file->f_path.dentry->d_inode->i_ino); _enter("{%Ld,{%lu}}",
file->f_pos, file->f_path.dentry->d_inode->i_ino);
fpos = file->f_pos; fpos = file->f_pos;
ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, cookie, filldir); ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos,
cookie, filldir);
file->f_pos = fpos; file->f_pos = fpos;
_leave(" = %d", ret); _leave(" = %d", ret);
@ -401,9 +400,13 @@ static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
{ {
struct afs_dir_lookup_cookie *cookie = _cookie; struct afs_dir_lookup_cookie *cookie = _cookie;
_enter("{%s,%Zu},%s,%u,,%lu,%u", _enter("{%s,%Zu},%s,%u,,%llu,%u",
cookie->name, cookie->nlen, name, nlen, ino, dtype); cookie->name, cookie->nlen, name, nlen, ino, dtype);
/* insanity checks first */
BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) { if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) {
_leave(" = 0 [no]"); _leave(" = 0 [no]");
return 0; return 0;
@ -418,34 +421,17 @@ static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
} }
/* /*
* look up an entry in a directory * do a lookup in a directory
*/ */
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd) struct afs_fid *fid)
{ {
struct afs_dir_lookup_cookie cookie; struct afs_dir_lookup_cookie cookie;
struct afs_super_info *as; struct afs_super_info *as;
struct afs_vnode *vnode;
struct inode *inode;
unsigned fpos; unsigned fpos;
int ret; int ret;
_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); _enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name);
/* insanity checks first */
BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
BUILD_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; as = dir->i_sb->s_fs_info;
@ -458,54 +444,130 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
fpos = 0; fpos = 0;
ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir); ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
if (ret < 0) { if (ret < 0) {
_leave(" = %d", ret); _leave(" = %d [iter]", ret);
return ERR_PTR(ret); return ret;
} }
ret = -ENOENT; ret = -ENOENT;
if (!cookie.found) { if (!cookie.found) {
_leave(" = %d", ret); _leave(" = -ENOENT [not found]");
return -ENOENT;
}
*fid = cookie.fid;
_leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique);
return 0;
}
/*
* look up an entry in a directory
*/
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct afs_vnode *vnode;
struct afs_fid fid;
struct inode *inode;
int ret;
_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);
if (dentry->d_name.len > 255) {
_leave(" = -ENAMETOOLONG");
return ERR_PTR(-ENAMETOOLONG);
}
vnode = AFS_FS_I(dir);
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
_leave(" = -ESTALE");
return ERR_PTR(-ESTALE);
}
ret = afs_do_lookup(dir, dentry, &fid);
if (ret < 0) {
_leave(" = %d [do]", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/* instantiate the dentry */ /* instantiate the dentry */
ret = afs_iget(dir->i_sb, &cookie.fid, &inode); inode = afs_iget(dir->i_sb, &fid);
if (ret < 0) { if (IS_ERR(inode)) {
_leave(" = %d", ret); _leave(" = %ld", PTR_ERR(inode));
return ERR_PTR(ret); return ERR_PTR(PTR_ERR(inode));
} }
dentry->d_op = &afs_fs_dentry_operations; dentry->d_op = &afs_fs_dentry_operations;
dentry->d_fsdata = (void *) (unsigned long) vnode->status.version;
d_add(dentry, inode); d_add(dentry, inode);
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }", _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }",
cookie.fid.vnode, fid.vnode,
cookie.fid.unique, fid.unique,
dentry->d_inode->i_ino, dentry->d_inode->i_ino,
dentry->d_inode->i_version); dentry->d_inode->i_version);
return NULL; return NULL;
} }
/*
* propagate changed and modified flags on a directory to all the children of
* that directory as they may indicate that the ACL on the dir has changed,
* potentially rendering the child inaccessible or that a file has been deleted
* or renamed
*/
static void afs_propagate_dir_changes(struct dentry *dir)
{
struct dentry *child;
bool c, m;
c = test_bit(AFS_VNODE_CHANGED, &AFS_FS_I(dir->d_inode)->flags);
m = test_bit(AFS_VNODE_MODIFIED, &AFS_FS_I(dir->d_inode)->flags);
_enter("{%d,%d}", c, m);
spin_lock(&dir->d_lock);
list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) {
if (child->d_inode) {
struct afs_vnode *vnode;
_debug("tag %s", child->d_name.name);
vnode = AFS_FS_I(child->d_inode);
if (c)
set_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags);
if (m)
set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
}
}
spin_unlock(&dir->d_lock);
}
/* /*
* check that a dentry lookup hit has found a valid entry * 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 * - NOTE! the hit can be a negative hit too, so we can't assume we have an
* inode * inode
* (derived from nfs_lookup_revalidate) * - there are several things we need to check
* - parent dir data changes (rm, rmdir, rename, mkdir, create, link,
* symlink)
* - parent dir metadata changed (security changes)
* - dentry data changed (write, truncate)
* - dentry metadata changed (security changes)
*/ */
static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
{ {
struct afs_dir_lookup_cookie cookie; struct afs_vnode *vnode;
struct afs_fid fid;
struct dentry *parent; struct dentry *parent;
struct inode *inode, *dir; struct inode *inode, *dir;
unsigned fpos;
int ret; int ret;
_enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name); vnode = AFS_FS_I(dentry->d_inode);
_enter("{sb=%p n=%s fl=%lx},",
dentry->d_sb, dentry->d_name.name, vnode->flags);
/* lock down the parent dentry so we can peer at it */ /* lock down the parent dentry so we can peer at it */
parent = dget_parent(dentry->d_parent); parent = dget_parent(dentry);
dir = parent->d_inode; dir = parent->d_inode;
inode = dentry->d_inode; inode = dentry->d_inode;
@ -517,81 +579,92 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
/* handle a bad inode */ /* handle a bad inode */
if (is_bad_inode(inode)) { if (is_bad_inode(inode)) {
printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
dentry->d_parent->d_name.name, dentry->d_name.name); parent->d_name.name, dentry->d_name.name);
goto out_bad; goto out_bad;
} }
/* force a full look up if the parent directory changed since last the /* check that this dirent still exists if the directory's contents were
* server was consulted * modified */
* - otherwise this inode must still exist, even if the inode details if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
* 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); _debug("%s: parent dir deleted", dentry->d_name.name);
goto out_bad; goto out_bad;
} }
if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) { if (test_and_clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags)) {
_debug("%s: file already deleted", dentry->d_name.name); /* rm/rmdir/rename may have occurred */
goto out_bad; _debug("dir modified");
}
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 */ /* search the directory for this vnode */
cookie.name = dentry->d_name.name; ret = afs_do_lookup(dir, dentry, &fid);
cookie.nlen = dentry->d_name.len; if (ret == -ENOENT) {
cookie.fid.vid = AFS_FS_I(inode)->volume->vid; _debug("%s: dirent not found", dentry->d_name.name);
cookie.found = 0; goto not_found;
}
fpos = 0;
ret = afs_dir_iterate(dir, &fpos, &cookie,
afs_dir_lookup_filldir);
if (ret < 0) { if (ret < 0) {
_debug("failed to iterate dir %s: %d", _debug("failed to iterate dir %s: %d",
parent->d_name.name, ret); parent->d_name.name, ret);
goto out_bad; 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 /* if the vnode ID has changed, then the dirent points to a
* different file */ * different file */
if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) { if (fid.vnode != vnode->fid.vnode) {
_debug("%s: dirent changed", dentry->d_name.name); _debug("%s: dirent changed [%u != %u]",
dentry->d_name.name, fid.vnode,
vnode->fid.vnode);
goto not_found; goto not_found;
} }
/* if the vnode ID uniqifier has changed, then the file has /* if the vnode ID uniqifier has changed, then the file has
* been deleted */ * been deleted */
if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) { if (fid.unique != vnode->fid.unique) {
_debug("%s: file deleted (uq %u -> %u I:%lu)", _debug("%s: file deleted (uq %u -> %u I:%lu)",
dentry->d_name.name, dentry->d_name.name, fid.unique,
cookie.fid.unique, vnode->fid.unique, inode->i_version);
AFS_FS_I(inode)->fid.unique, spin_lock(&vnode->lock);
inode->i_version); set_bit(AFS_VNODE_DELETED, &vnode->flags);
spin_lock(&AFS_FS_I(inode)->lock); spin_unlock(&vnode->lock);
AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED;
spin_unlock(&AFS_FS_I(inode)->lock);
invalidate_remote_inode(inode); invalidate_remote_inode(inode);
goto out_bad; goto out_bad;
} }
dentry->d_fsdata =
(void *) (unsigned long) AFS_FS_I(dir)->status.version;
} }
/* if the directory's metadata were changed then the security may be
* different and we may no longer have access */
mutex_lock(&vnode->cb_broken_lock);
if (test_and_clear_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags) ||
test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
_debug("%s: changed", dentry->d_name.name);
set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
if (afs_vnode_fetch_status(vnode) < 0) {
mutex_unlock(&vnode->cb_broken_lock);
goto out_bad;
}
}
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
_debug("%s: file already deleted", dentry->d_name.name);
mutex_unlock(&vnode->cb_broken_lock);
goto out_bad;
}
/* if the vnode's data version number changed then its contents are
* different */
if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
_debug("zap data");
invalidate_remote_inode(inode);
}
if (S_ISDIR(inode->i_mode) &&
(test_bit(AFS_VNODE_CHANGED, &vnode->flags) ||
test_bit(AFS_VNODE_MODIFIED, &vnode->flags)))
afs_propagate_dir_changes(dentry);
clear_bit(AFS_VNODE_CHANGED, &vnode->flags);
clear_bit(AFS_VNODE_MODIFIED, &vnode->flags);
mutex_unlock(&vnode->cb_broken_lock);
out_valid: out_valid:
dput(parent); dput(parent);
_leave(" = 1 [valid]"); _leave(" = 1 [valid]");
@ -610,12 +683,10 @@ out_bad:
goto out_valid; goto out_valid;
} }
shrink_dcache_parent(dentry);
_debug("dropping dentry %s/%s", _debug("dropping dentry %s/%s",
dentry->d_parent->d_name.name, dentry->d_name.name); parent->d_name.name, dentry->d_name.name);
shrink_dcache_parent(dentry);
d_drop(dentry); d_drop(dentry);
dput(parent); dput(parent);
_leave(" = 0 [bad]"); _leave(" = 0 [bad]");
@ -635,10 +706,9 @@ static int afs_d_delete(struct dentry *dentry)
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
goto zap; goto zap;
if (dentry->d_inode) { if (dentry->d_inode &&
if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED) test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags))
goto zap; goto zap;
}
_leave(" = 0 [keep]"); _leave(" = 0 [keep]");
return 0; return 0;

View file

@ -1,6 +1,6 @@
/* file.c: AFS filesystem file handling /* AFS filesystem file handling
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -15,9 +15,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include "volume.h"
#include "vnode.h"
#include <rxrpc/call.h>
#include "internal.h" #include "internal.h"
#if 0 #if 0
@ -80,12 +77,10 @@ static void afs_file_readpage_write_complete(void *cookie_data,
*/ */
static int afs_file_readpage(struct file *file, struct page *page) 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 afs_vnode *vnode;
struct inode *inode; struct inode *inode;
size_t len;
off_t offset;
int ret; int ret;
inode = page->mapping->host; inode = page->mapping->host;
@ -97,14 +92,10 @@ static int afs_file_readpage(struct file *file, struct page *page)
BUG_ON(!PageLocked(page)); BUG_ON(!PageLocked(page));
ret = -ESTALE; ret = -ESTALE;
if (vnode->flags & AFS_VNODE_DELETED) if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
goto error; goto error;
#ifdef AFS_CACHING_SUPPORT #ifdef AFS_CACHING_SUPPORT
ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
if (ret < 0)
goto error;
/* is it cached? */ /* is it cached? */
ret = cachefs_read_or_alloc_page(vnode->cache, ret = cachefs_read_or_alloc_page(vnode->cache,
page, page,
@ -128,26 +119,19 @@ static int afs_file_readpage(struct file *file, struct page *page)
case -ENOBUFS: case -ENOBUFS:
case -ENODATA: case -ENODATA:
default: default:
desc.fid = vnode->fid; offset = page->index << PAGE_CACHE_SHIFT;
desc.offset = page->index << PAGE_CACHE_SHIFT; len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
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 /* read the contents of the file from the server into the
* page */ * page */
ret = afs_vnode_fetch_data(vnode, &desc); ret = afs_vnode_fetch_data(vnode, offset, len, page);
kunmap(page);
if (ret < 0) { if (ret < 0) {
if (ret==-ENOENT) { if (ret == -ENOENT) {
_debug("got NOENT from server" _debug("got NOENT from server"
" - marking file deleted and stale"); " - marking file deleted and stale");
vnode->flags |= AFS_VNODE_DELETED; set_bit(AFS_VNODE_DELETED, &vnode->flags);
ret = -ESTALE; ret = -ESTALE;
} }
#ifdef AFS_CACHING_SUPPORT #ifdef AFS_CACHING_SUPPORT
cachefs_uncache_page(vnode->cache, page); cachefs_uncache_page(vnode->cache, page);
#endif #endif
@ -174,10 +158,9 @@ static int afs_file_readpage(struct file *file, struct page *page)
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
error: error:
SetPageError(page); SetPageError(page);
unlock_page(page); unlock_page(page);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,54 +0,0 @@
/* 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 AFS_FSCLIENT_H
#define AFS_FSCLIENT_H
#include "server.h"
extern int afs_rxfs_get_volume_info(struct afs_server *,
const char *,
struct afs_volume_info *);
extern int afs_rxfs_fetch_file_status(struct afs_server *,
struct afs_vnode *,
struct afs_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 *,
struct afs_vnode *,
struct afs_rxfs_fetch_descriptor *,
struct afs_volsync *);
extern int afs_rxfs_give_up_callback(struct afs_server *,
struct afs_vnode *);
/* this doesn't appear to work in OpenAFS server */
extern int afs_rxfs_lookup(struct afs_server *,
struct afs_vnode *,
const char *,
struct afs_vnode *,
struct afs_volsync *);
/* this is apparently mis-implemented in OpenAFS server */
extern int afs_rxfs_get_root_volume(struct afs_server *,
char *,
size_t *);
#endif /* AFS_FSCLIENT_H */

View file

@ -19,9 +19,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include "volume.h"
#include "vnode.h"
#include "super.h"
#include "internal.h" #include "internal.h"
struct afs_iget_data { struct afs_iget_data {
@ -40,7 +37,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
vnode->status.type, vnode->status.type,
vnode->status.nlink, vnode->status.nlink,
vnode->status.size, vnode->status.size,
vnode->status.version, vnode->status.data_version,
vnode->status.mode); vnode->status.mode);
switch (vnode->status.type) { switch (vnode->status.type) {
@ -78,7 +75,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
if (vnode->status.type == AFS_FTYPE_SYMLINK) { if (vnode->status.type == AFS_FTYPE_SYMLINK) {
afs_mntpt_check_symlink(vnode); afs_mntpt_check_symlink(vnode);
if (vnode->flags & AFS_VNODE_MOUNTPOINT) { if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) {
inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_mode = S_IFDIR | vnode->status.mode;
inode->i_op = &afs_mntpt_inode_operations; inode->i_op = &afs_mntpt_inode_operations;
inode->i_fop = &afs_mntpt_file_operations; inode->i_fop = &afs_mntpt_file_operations;
@ -88,25 +85,6 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
return 0; return 0;
} }
/*
* 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;
}
/* /*
* iget5() comparator * iget5() comparator
*/ */
@ -137,8 +115,7 @@ static int afs_iget5_set(struct inode *inode, void *opaque)
/* /*
* inode retrieval * inode retrieval
*/ */
inline int afs_iget(struct super_block *sb, struct afs_fid *fid, inline struct inode *afs_iget(struct super_block *sb, struct afs_fid *fid)
struct inode **_inode)
{ {
struct afs_iget_data data = { .fid = *fid }; struct afs_iget_data data = { .fid = *fid };
struct afs_super_info *as; struct afs_super_info *as;
@ -155,20 +132,18 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
&data); &data);
if (!inode) { if (!inode) {
_leave(" = -ENOMEM"); _leave(" = -ENOMEM");
return -ENOMEM; return ERR_PTR(-ENOMEM);
} }
_debug("GOT INODE %p { vl=%x vn=%x, u=%x }",
inode, fid->vid, fid->vnode, fid->unique);
vnode = AFS_FS_I(inode); vnode = AFS_FS_I(inode);
/* deal with an existing inode */ /* deal with an existing inode */
if (!(inode->i_state & I_NEW)) { if (!(inode->i_state & I_NEW)) {
ret = afs_vnode_fetch_status(vnode); _leave(" = %p", inode);
if (ret == 0) return inode;
*_inode = inode;
else
iput(inode);
_leave(" = %d", ret);
return ret;
} }
#ifdef AFS_CACHING_SUPPORT #ifdef AFS_CACHING_SUPPORT
@ -181,21 +156,19 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
#endif #endif
/* okay... it's a new inode */ /* okay... it's a new inode */
inode->i_flags |= S_NOATIME; set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
vnode->flags |= AFS_VNODE_CHANGED; ret = afs_vnode_fetch_status(vnode);
ret = afs_inode_fetch_status(inode); if (ret < 0)
if (ret<0) goto bad_inode;
ret = afs_inode_map_status(vnode);
if (ret < 0)
goto bad_inode; goto bad_inode;
/* success */ /* success */
inode->i_flags |= S_NOATIME;
unlock_new_inode(inode); unlock_new_inode(inode);
_leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type);
*_inode = inode; return inode;
_leave(" = 0 [CB { v=%u x=%lu t=%u }]",
vnode->cb_version,
vnode->cb_timeout.timo_jif,
vnode->cb_type);
return 0;
/* failure */ /* failure */
bad_inode: bad_inode:
@ -204,7 +177,7 @@ bad_inode:
iput(inode); iput(inode);
_leave(" = %d [bad]", ret); _leave(" = %d [bad]", ret);
return ret; return ERR_PTR(ret);
} }
/* /*
@ -213,36 +186,13 @@ bad_inode:
int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat) struct kstat *stat)
{ {
struct afs_vnode *vnode;
struct inode *inode; struct inode *inode;
int ret;
inode = dentry->d_inode; inode = dentry->d_inode;
_enter("{ ino=%lu v=%lu }", inode->i_ino, inode->i_version); _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); generic_fillattr(inode, stat);
_leave(" = 0 CB { v=%u x=%u t=%u }",
vnode->cb_version,
vnode->cb_expiry,
vnode->cb_type);
return 0; return 0;
} }
@ -260,12 +210,23 @@ void afs_clear_inode(struct inode *inode)
vnode->fid.vnode, vnode->fid.vnode,
vnode->cb_version, vnode->cb_version,
vnode->cb_expiry, vnode->cb_expiry,
vnode->cb_type vnode->cb_type);
);
BUG_ON(inode->i_ino != vnode->fid.vnode); _debug("CLEAR INODE %p", inode);
afs_vnode_give_up_callback(vnode); ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);
afs_give_up_callback(vnode);
if (vnode->server) {
spin_lock(&vnode->server->fs_lock);
rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes);
spin_unlock(&vnode->server->fs_lock);
afs_put_server(vnode->server);
vnode->server = NULL;
}
ASSERT(!vnode->cb_promised);
#ifdef AFS_CACHING_SUPPORT #ifdef AFS_CACHING_SUPPORT
cachefs_relinquish_cookie(vnode->cache, 0); cachefs_relinquish_cookie(vnode->cache, 0);

View file

@ -1,6 +1,6 @@
/* internal AFS stuff /* internal AFS stuff
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -9,47 +9,320 @@
* 2 of the License, or (at your option) any later 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/compiler.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/skbuff.h>
#include <linux/rxrpc.h>
#include "afs.h"
#include "afs_vl.h"
#define AFS_CELL_MAX_ADDRS 15
struct afs_call;
typedef enum {
AFS_VL_NEW, /* new, uninitialised record */
AFS_VL_CREATING, /* creating record */
AFS_VL_VALID, /* record is pending */
AFS_VL_NO_VOLUME, /* no such volume available */
AFS_VL_UPDATING, /* update in progress */
AFS_VL_VOLUME_DELETED, /* volume was deleted */
AFS_VL_UNCERTAIN, /* uncertain state (update failed) */
} __attribute__((packed)) afs_vlocation_state_t;
/* /*
* debug tracing * definition of how to wait for the completion of an operation
*/ */
#define kenter(FMT, a...) printk("==> %s("FMT")\n",__FUNCTION__ , ## a) struct afs_wait_mode {
#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ## a) /* RxRPC received message notification */
#define kdebug(FMT, a...) printk(FMT"\n" , ## a) void (*rx_wakeup)(struct afs_call *call);
#define kproto(FMT, a...) printk("### "FMT"\n" , ## a)
#define knet(FMT, a...) printk(FMT"\n" , ## a)
#ifdef __KDEBUG /* synchronous call waiter and call dispatched notification */
#define _enter(FMT, a...) kenter(FMT , ## a) int (*wait)(struct afs_call *call);
#define _leave(FMT, a...) kleave(FMT , ## a)
#define _debug(FMT, a...) kdebug(FMT , ## a) /* asynchronous call completion */
#define _proto(FMT, a...) kproto(FMT , ## a) void (*async_complete)(void *reply, int error);
#define _net(FMT, a...) knet(FMT , ## a) };
#else
#define _enter(FMT, a...) do { } while(0) extern const struct afs_wait_mode afs_sync_call;
#define _leave(FMT, a...) do { } while(0) extern const struct afs_wait_mode afs_async_call;
#define _debug(FMT, a...) do { } while(0)
#define _proto(FMT, a...) do { } while(0) /*
#define _net(FMT, a...) do { } while(0) * a record of an in-progress RxRPC call
*/
struct afs_call {
const struct afs_call_type *type; /* type of call */
const struct afs_wait_mode *wait_mode; /* completion wait mode */
wait_queue_head_t waitq; /* processes awaiting completion */
struct work_struct async_work; /* asynchronous work processor */
struct work_struct work; /* actual work processor */
struct sk_buff_head rx_queue; /* received packets */
struct rxrpc_call *rxcall; /* RxRPC call handle */
struct key *key; /* security for this call */
struct afs_server *server; /* server affected by incoming CM call */
void *request; /* request data (first part) */
void *request2; /* request data (second part) */
void *buffer; /* reply receive buffer */
void *reply; /* reply buffer (first part) */
void *reply2; /* reply buffer (second part) */
void *reply3; /* reply buffer (third part) */
enum { /* call state */
AFS_CALL_REQUESTING, /* request is being sent for outgoing call */
AFS_CALL_AWAIT_REPLY, /* awaiting reply to outgoing call */
AFS_CALL_AWAIT_OP_ID, /* awaiting op ID on incoming call */
AFS_CALL_AWAIT_REQUEST, /* awaiting request data on incoming call */
AFS_CALL_REPLYING, /* replying to incoming call */
AFS_CALL_AWAIT_ACK, /* awaiting final ACK of incoming call */
AFS_CALL_COMPLETE, /* successfully completed */
AFS_CALL_BUSY, /* server was busy */
AFS_CALL_ABORTED, /* call was aborted */
AFS_CALL_ERROR, /* call failed due to error */
} state;
int error; /* error code */
unsigned request_size; /* size of request data */
unsigned reply_max; /* maximum size of reply */
unsigned reply_size; /* current size of reply */
unsigned short offset; /* offset into received data store */
unsigned char unmarshall; /* unmarshalling phase */
bool incoming; /* T if incoming call */
u16 service_id; /* RxRPC service ID to call */
__be16 port; /* target UDP port */
__be32 operation_ID; /* operation ID for an incoming call */
u32 count; /* count for use in unmarshalling */
__be32 tmp; /* place to extract temporary data */
};
struct afs_call_type {
/* deliver request or reply data to an call
* - returning an error will cause the call to be aborted
*/
int (*deliver)(struct afs_call *call, struct sk_buff *skb,
bool last);
/* map an abort code to an error number */
int (*abort_to_error)(u32 abort_code);
/* clean up a call */
void (*destructor)(struct afs_call *call);
};
/*
* 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;
}
extern struct file_system_type afs_fs_type;
/*
* 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 #endif
static inline void afs_discard_my_signals(void) /* server record management */
{ rwlock_t servers_lock; /* active server list lock */
while (signal_pending(current)) { struct list_head servers; /* active server list */
siginfo_t sinfo;
spin_lock_irq(&current->sighand->siglock); /* volume location record management */
dequeue_signal(current,&current->blocked, &sinfo); struct rw_semaphore vl_sem; /* volume management serialisation semaphore */
spin_unlock_irq(&current->sighand->siglock); struct list_head vl_list; /* cell's active VL record list */
} spinlock_t vl_lock; /* vl_list 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 */
};
/*
* entry in the cached volume location catalogue
*/
struct afs_cache_vlocation {
uint8_t name[64 + 1]; /* 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 */
};
/*
* 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));
/*
* AFS volume location record
*/
struct afs_vlocation {
atomic_t usage;
time_t time_of_death; /* time at which put reduced usage to 0 */
struct list_head link; /* link in cell volume location list */
struct list_head grave; /* link in master graveyard list */
struct list_head update; /* link in master update list */
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) */
wait_queue_head_t waitq; /* status change waitqueue */
time_t update_at; /* time at which record should be updated */
rwlock_t lock; /* access lock */
afs_vlocation_state_t state; /* volume location state */
unsigned short upd_rej_cnt; /* ENOMEDIUM count during update */
unsigned short upd_busy_cnt; /* EBUSY count during update */
bool valid; /* T if valid */
};
/*
* AFS fileserver record
*/
struct afs_server {
atomic_t usage;
time_t time_of_death; /* time at which put reduced usage to 0 */
struct in_addr addr; /* server address */
struct afs_cell *cell; /* cell in which server resides */
struct list_head link; /* link in cell's server list */
struct list_head grave; /* link in master graveyard list */
struct rb_node master_rb; /* link in master by-addr tree */
struct rw_semaphore sem; /* access lock */
/* file service access */
struct rb_root fs_vnodes; /* vnodes backed by this server (ordered by FID) */
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 */
spinlock_t fs_lock; /* access lock */
int fs_state; /* 0 or reason FS currently marked dead (-errno) */
/* callback promise management */
struct rb_root cb_promises; /* vnode expiration list (ordered earliest first) */
struct delayed_work cb_updater; /* callback updater */
struct delayed_work cb_break_work; /* collected break dispatcher */
wait_queue_head_t cb_break_waitq; /* space available in cb_break waitqueue */
spinlock_t cb_lock; /* access lock */
struct afs_callback cb_break[64]; /* ring of callbacks awaiting breaking */
atomic_t cb_break_n; /* number of pending breaks */
u8 cb_break_head; /* head of callback breaking ring */
u8 cb_break_tail; /* tail of callback breaking ring */
};
/*
* 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 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 */
};
/*
* 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 */
};
/*
* 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_server *server; /* server currently supplying this file */
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 long flags;
#define AFS_VNODE_CB_BROKEN 0 /* set if vnode's callback was broken */
#define AFS_VNODE_CHANGED 1 /* set if vnode's metadata changed */
#define AFS_VNODE_MODIFIED 2 /* set if vnode's data modified */
#define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */
#define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */
#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */
#define AFS_VNODE_DIR_CHANGED 6 /* set if vnode's parent dir metadata changed */
#define AFS_VNODE_DIR_MODIFIED 7 /* set if vnode's parent dir data modified */
/* outstanding callback notification on this file */
struct rb_node server_rb; /* link in server->fs_vnodes */
struct rb_node cb_promise; /* link in server->cb_promises */
struct work_struct cb_broken_work; /* work to be done on callback break */
struct mutex cb_broken_lock; /* lock against multiple attempts to fix break */
// struct list_head cb_hash_link; /* link in master callback hash */
time_t cb_expires; /* time at which callback expires */
time_t cb_expires_at; /* time used to order cb_promise */
unsigned cb_version; /* callback version */
unsigned cb_expiry; /* callback expiry time */
afs_callback_type_t cb_type; /* type of callback */
bool cb_promised; /* true if promise still holds */
};
/*****************************************************************************/
/*
* callback.c
*/
extern void afs_init_callback_state(struct afs_server *);
extern void afs_broken_callback_work(struct work_struct *);
extern void afs_break_callbacks(struct afs_server *, size_t,
struct afs_callback[]);
extern void afs_give_up_callback(struct afs_vnode *);
extern void afs_dispatch_give_up_callbacks(struct work_struct *);
extern void afs_flush_callback_breaks(struct afs_server *);
extern int __init afs_callback_update_init(void);
extern void __exit afs_callback_update_kill(void);
/* /*
* cell.c * cell.c
@ -60,6 +333,19 @@ extern struct list_head afs_proc_cells;
extern struct cachefs_index_def afs_cache_cell_index_def; extern struct cachefs_index_def afs_cache_cell_index_def;
#endif #endif
#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
extern int afs_cell_init(char *);
extern struct afs_cell *afs_cell_create(const char *, char *);
extern struct afs_cell *afs_cell_lookup(const char *, unsigned);
extern struct afs_cell *afs_grab_cell(struct afs_cell *);
extern void afs_put_cell(struct afs_cell *);
extern void afs_cell_purge(void);
/*
* cmservice.c
*/
extern bool afs_cm_incoming_call(struct afs_call *);
/* /*
* dir.c * dir.c
*/ */
@ -76,10 +362,23 @@ extern const struct inode_operations afs_file_inode_operations;
extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **); extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **);
#endif #endif
/*
* fsclient.c
*/
extern int afs_fs_fetch_file_status(struct afs_server *,
struct afs_vnode *,
struct afs_volsync *,
const struct afs_wait_mode *);
extern int afs_fs_give_up_callbacks(struct afs_server *,
const struct afs_wait_mode *);
extern int afs_fs_fetch_data(struct afs_server *, struct afs_vnode *, off_t,
size_t, struct page *, struct afs_volsync *,
const struct afs_wait_mode *);
/* /*
* inode.c * inode.c
*/ */
extern int afs_iget(struct super_block *, struct afs_fid *, struct inode **); extern struct inode *afs_iget(struct super_block *, struct afs_fid *);
extern int afs_inode_getattr(struct vfsmount *, struct dentry *, extern int afs_inode_getattr(struct vfsmount *, struct dentry *,
struct kstat *); struct kstat *);
extern void afs_clear_inode(struct inode *); extern void afs_clear_inode(struct inode *);
@ -91,16 +390,21 @@ extern void afs_clear_inode(struct inode *);
extern struct cachefs_netfs afs_cache_netfs; extern struct cachefs_netfs afs_cache_netfs;
#endif #endif
/*
* misc.c
*/
extern int afs_abort_to_error(u32);
/* /*
* mntpt.c * mntpt.c
*/ */
extern const struct inode_operations afs_mntpt_inode_operations; extern const struct inode_operations afs_mntpt_inode_operations;
extern const struct file_operations afs_mntpt_file_operations; extern const 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 unsigned long afs_mntpt_expiry_timeout;
extern int afs_mntpt_check_symlink(struct afs_vnode *); extern int afs_mntpt_check_symlink(struct afs_vnode *);
extern void afs_mntpt_kill_timer(void);
extern void afs_umount_begin(struct vfsmount *, int);
/* /*
* super.c * super.c
@ -108,16 +412,6 @@ extern int afs_mntpt_check_symlink(struct afs_vnode *);
extern int afs_fs_init(void); extern int afs_fs_init(void);
extern void afs_fs_exit(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 - 1)]
/* /*
* proc.c * proc.c
*/ */
@ -126,4 +420,217 @@ extern void afs_proc_cleanup(void);
extern int afs_proc_cell_setup(struct afs_cell *); extern int afs_proc_cell_setup(struct afs_cell *);
extern void afs_proc_cell_remove(struct afs_cell *); extern void afs_proc_cell_remove(struct afs_cell *);
#endif /* AFS_INTERNAL_H */ /*
* rxrpc.c
*/
extern int afs_open_socket(void);
extern void afs_close_socket(void);
extern int afs_make_call(struct in_addr *, struct afs_call *, gfp_t,
const struct afs_wait_mode *);
extern struct afs_call *afs_alloc_flat_call(const struct afs_call_type *,
size_t, size_t);
extern void afs_flat_call_destructor(struct afs_call *);
extern void afs_transfer_reply(struct afs_call *, struct sk_buff *);
extern void afs_send_empty_reply(struct afs_call *);
extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *,
size_t);
/*
* server.c
*/
extern spinlock_t afs_server_peer_lock;
#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0)
extern struct afs_server *afs_lookup_server(struct afs_cell *,
const struct in_addr *);
extern struct afs_server *afs_find_server(const struct in_addr *);
extern void afs_put_server(struct afs_server *);
extern void __exit afs_purge_servers(void);
/*
* vlclient.c
*/
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_index_def afs_vlocation_cache_index_def;
#endif
extern int afs_vl_get_entry_by_name(struct in_addr *, const char *,
struct afs_cache_vlocation *,
const struct afs_wait_mode *);
extern int afs_vl_get_entry_by_id(struct in_addr *, afs_volid_t, afs_voltype_t,
struct afs_cache_vlocation *,
const struct afs_wait_mode *);
/*
* vlocation.c
*/
#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0)
extern int __init afs_vlocation_update_init(void);
extern struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *,
const char *, size_t);
extern void afs_put_vlocation(struct afs_vlocation *);
extern void __exit afs_vlocation_purge(void);
/*
* vnode.c
*/
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_index_def afs_vnode_cache_index_def;
#endif
extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
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 *);
extern int afs_vnode_fetch_data(struct afs_vnode *vnode, off_t, size_t,
struct page *);
/*
* volume.c
*/
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_index_def afs_volume_cache_index_def;
#endif
#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
extern void afs_put_volume(struct afs_volume *);
extern struct afs_volume *afs_volume_lookup(const char *, struct afs_cell *,
int);
extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *);
extern int afs_volume_release_fileserver(struct afs_vnode *,
struct afs_server *, int);
/*****************************************************************************/
/*
* debug tracing
*/
extern unsigned afs_debug;
#define dbgprintk(FMT,...) \
printk("[%x%-6.6s] "FMT"\n", smp_processor_id(), current->comm ,##__VA_ARGS__)
/* make sure we maintain the format strings, even when debugging is disabled */
static inline __attribute__((format(printf,1,2)))
void _dbprintk(const char *fmt, ...)
{
}
#define kenter(FMT,...) dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
#define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
#define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__)
#if defined(__KDEBUG)
#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__)
#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__)
#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__)
#elif defined(CONFIG_AFS_DEBUG)
#define AFS_DEBUG_KENTER 0x01
#define AFS_DEBUG_KLEAVE 0x02
#define AFS_DEBUG_KDEBUG 0x04
#define _enter(FMT,...) \
do { \
if (unlikely(afs_debug & AFS_DEBUG_KENTER)) \
kenter(FMT,##__VA_ARGS__); \
} while (0)
#define _leave(FMT,...) \
do { \
if (unlikely(afs_debug & AFS_DEBUG_KLEAVE)) \
kleave(FMT,##__VA_ARGS__); \
} while (0)
#define _debug(FMT,...) \
do { \
if (unlikely(afs_debug & AFS_DEBUG_KDEBUG)) \
kdebug(FMT,##__VA_ARGS__); \
} while (0)
#else
#define _enter(FMT,...) _dbprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
#define _leave(FMT,...) _dbprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
#define _debug(FMT,...) _dbprintk(" "FMT ,##__VA_ARGS__)
#endif
/*
* debug assertion checking
*/
#if 1 // defined(__KDEBUGALL)
#define ASSERT(X) \
do { \
if (unlikely(!(X))) { \
printk(KERN_ERR "\n"); \
printk(KERN_ERR "AFS: Assertion failed\n"); \
BUG(); \
} \
} while(0)
#define ASSERTCMP(X, OP, Y) \
do { \
if (unlikely(!((X) OP (Y)))) { \
printk(KERN_ERR "\n"); \
printk(KERN_ERR "AFS: Assertion failed\n"); \
printk(KERN_ERR "%lu " #OP " %lu is false\n", \
(unsigned long)(X), (unsigned long)(Y)); \
printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \
(unsigned long)(X), (unsigned long)(Y)); \
BUG(); \
} \
} while(0)
#define ASSERTIF(C, X) \
do { \
if (unlikely((C) && !(X))) { \
printk(KERN_ERR "\n"); \
printk(KERN_ERR "AFS: Assertion failed\n"); \
BUG(); \
} \
} while(0)
#define ASSERTIFCMP(C, X, OP, Y) \
do { \
if (unlikely((C) && !((X) OP (Y)))) { \
printk(KERN_ERR "\n"); \
printk(KERN_ERR "AFS: Assertion failed\n"); \
printk(KERN_ERR "%lu " #OP " %lu is false\n", \
(unsigned long)(X), (unsigned long)(Y)); \
printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \
(unsigned long)(X), (unsigned long)(Y)); \
BUG(); \
} \
} while(0)
#else
#define ASSERT(X) \
do { \
} while(0)
#define ASSERTCMP(X, OP, Y) \
do { \
} while(0)
#define ASSERTIF(C, X) \
do { \
} while(0)
#define ASSERTIFCMP(C, X, OP, Y) \
do { \
} while(0)
#endif /* __KDEBUGALL */

View file

@ -1,247 +0,0 @@
/* 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 <linux/freezer.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;
}
/*
* 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);
}
/*
* 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();
/* 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_move_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);
}
/*
* 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_move_tail(&op->link, &kafsasyncd_async_busyq);
spin_unlock(&kafsasyncd_async_lock);
_leave("");
}
/*
* 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_move_tail(&op->link, &kafsasyncd_async_attnq);
spin_unlock(&kafsasyncd_async_lock);
wake_up(&kafsasyncd_sleepq);
_leave("");
}
/*
* 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("");
}

View file

@ -1,50 +0,0 @@
/* 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 AFS_KAFSASYNCD_H
#define AFS_KAFSASYNCD_H
#include "types.h"
struct afs_async_op;
struct afs_async_op_ops {
void (*attend)(struct afs_async_op *);
void (*discard)(struct afs_async_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 *);
extern void afs_kafsasyncd_attend_op(struct afs_async_op *);
extern void afs_kafsasyncd_terminate_op(struct afs_async_op *);
#endif /* AFS_KAFSASYNCD_H */

View file

@ -1,194 +0,0 @@
/* 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 <linux/freezer.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;
}
/*
* 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);
}
/*
* 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();
/* 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;
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;
}
/*
* (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("");
}
/*
* 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;
}

View file

@ -1,45 +0,0 @@
/* 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 AFS_KAFSTIMOD_H
#define 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 *);
};
/*
* 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 *, unsigned long);
extern int afs_kafstimod_del_timer(struct afs_timer *);
#endif /* AFS_KAFSTIMOD_H */

View file

@ -13,43 +13,21 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/completion.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" #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_DESCRIPTION("AFS Client File System");
MODULE_AUTHOR("Red Hat, Inc."); MODULE_AUTHOR("Red Hat, Inc.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
unsigned afs_debug;
module_param_named(debug, afs_debug, uint, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(afs_debug, "AFS debugging mask");
static char *rootcell; static char *rootcell;
module_param(rootcell, charp, 0); module_param(rootcell, charp, 0);
MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); 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 #ifdef AFS_CACHING_SUPPORT
static struct cachefs_netfs_operations afs_cache_ops = { static struct cachefs_netfs_operations afs_cache_ops = {
.get_page_cookie = afs_cache_get_page_cookie, .get_page_cookie = afs_cache_get_page_cookie,
@ -67,15 +45,10 @@ struct cachefs_netfs afs_cache_netfs = {
*/ */
static int __init afs_init(void) static int __init afs_init(void)
{ {
int loop, ret; int ret;
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n"); 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 */ /* register the /proc stuff */
ret = afs_proc_init(); ret = afs_proc_init();
if (ret < 0) if (ret < 0)
@ -94,22 +67,18 @@ static int __init afs_init(void)
if (ret < 0) if (ret < 0)
goto error_cell_init; goto error_cell_init;
/* start the timeout daemon */ /* initialise the VL update process */
ret = afs_kafstimod_start(); ret = afs_vlocation_update_init();
if (ret < 0) if (ret < 0)
goto error_kafstimod; goto error_vl_update_init;
/* start the async operation daemon */ /* initialise the callback update process */
ret = afs_kafsasyncd_start(); ret = afs_callback_update_init();
if (ret < 0)
goto error_kafsasyncd;
/* create the RxRPC transport */ /* create the RxRPC transport */
ret = rxrpc_create_transport(7001, &afs_transport); ret = afs_open_socket();
if (ret < 0) if (ret < 0)
goto error_transport; goto error_open_socket;
afs_transport->peer_ops = &afs_peer_ops;
/* register the filesystems */ /* register the filesystems */
ret = afs_fs_init(); ret = afs_fs_init();
@ -119,17 +88,16 @@ static int __init afs_init(void)
return ret; return ret;
error_fs: error_fs:
rxrpc_put_transport(afs_transport); afs_close_socket();
error_transport: error_open_socket:
afs_kafsasyncd_stop(); error_vl_update_init:
error_kafsasyncd:
afs_kafstimod_stop();
error_kafstimod:
error_cell_init: error_cell_init:
#ifdef AFS_CACHING_SUPPORT #ifdef AFS_CACHING_SUPPORT
cachefs_unregister_netfs(&afs_cache_netfs); cachefs_unregister_netfs(&afs_cache_netfs);
error_cache: error_cache:
#endif #endif
afs_callback_update_kill();
afs_vlocation_purge();
afs_cell_purge(); afs_cell_purge();
afs_proc_cleanup(); afs_proc_cleanup();
printk(KERN_ERR "kAFS: failed to register: %d\n", ret); printk(KERN_ERR "kAFS: failed to register: %d\n", ret);
@ -149,9 +117,11 @@ static void __exit afs_exit(void)
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n"); printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
afs_fs_exit(); afs_fs_exit();
rxrpc_put_transport(afs_transport); afs_close_socket();
afs_kafstimod_stop(); afs_purge_servers();
afs_kafsasyncd_stop(); afs_callback_update_kill();
afs_vlocation_purge();
flush_scheduled_work();
afs_cell_purge(); afs_cell_purge();
#ifdef AFS_CACHING_SUPPORT #ifdef AFS_CACHING_SUPPORT
cachefs_unregister_netfs(&afs_cache_netfs); cachefs_unregister_netfs(&afs_cache_netfs);
@ -160,64 +130,3 @@ static void __exit afs_exit(void)
} }
module_exit(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;
}
/*
* 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("");
}

View file

@ -1,6 +1,6 @@
/* miscellaneous bits /* miscellaneous bits
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -12,18 +12,19 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
#include "errors.h"
#include "internal.h" #include "internal.h"
#include "afs_fs.h"
/* /*
* convert an AFS abort code to a Linux error number * convert an AFS abort code to a Linux error number
*/ */
int afs_abort_to_error(int abortcode) int afs_abort_to_error(u32 abort_code)
{ {
switch (abortcode) { switch (abort_code) {
case 13: return -EACCES;
case VSALVAGE: return -EIO; case VSALVAGE: return -EIO;
case VNOVNODE: return -ENOENT; case VNOVNODE: return -ENOENT;
case VNOVOL: return -ENXIO; case VNOVOL: return -ENOMEDIUM;
case VVOLEXISTS: return -EEXIST; case VVOLEXISTS: return -EEXIST;
case VNOSERVICE: return -EIO; case VNOSERVICE: return -EIO;
case VOFFLINE: return -ENOENT; case VOFFLINE: return -ENOENT;

View file

@ -18,10 +18,6 @@
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mnt_namespace.h> #include <linux/mnt_namespace.h>
#include "super.h"
#include "cell.h"
#include "volume.h"
#include "vnode.h"
#include "internal.h" #include "internal.h"
@ -30,6 +26,7 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
struct nameidata *nd); struct nameidata *nd);
static int afs_mntpt_open(struct inode *inode, struct file *file); static int afs_mntpt_open(struct inode *inode, struct file *file);
static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd); static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
static void afs_mntpt_expiry_timed_out(struct work_struct *work);
const struct file_operations afs_mntpt_file_operations = { const struct file_operations afs_mntpt_file_operations = {
.open = afs_mntpt_open, .open = afs_mntpt_open,
@ -43,16 +40,9 @@ const struct inode_operations afs_mntpt_inode_operations = {
}; };
static LIST_HEAD(afs_vfsmounts); static LIST_HEAD(afs_vfsmounts);
static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
static void afs_mntpt_expiry_timed_out(struct afs_timer *timer); unsigned long afs_mntpt_expiry_timeout = 10 * 60;
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 * check a symbolic link to see whether it actually encodes a mountpoint
@ -84,7 +74,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode)
/* examine the symlink's contents */ /* examine the symlink's contents */
size = vnode->status.size; size = vnode->status.size;
_debug("symlink to %*.*s", size, (int) size, buf); _debug("symlink to %*.*s", (int) size, (int) size, buf);
if (size > 2 && if (size > 2 &&
(buf[0] == '%' || buf[0] == '#') && (buf[0] == '%' || buf[0] == '#') &&
@ -92,7 +82,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode)
) { ) {
_debug("symlink is a mountpoint"); _debug("symlink is a mountpoint");
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
vnode->flags |= AFS_VNODE_MOUNTPOINT; set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
} }
@ -113,7 +103,7 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
struct dentry *dentry, struct dentry *dentry,
struct nameidata *nd) struct nameidata *nd)
{ {
kenter("%p,%p{%p{%s},%s}", _enter("%p,%p{%p{%s},%s}",
dir, dir,
dentry, dentry,
dentry->d_parent, dentry->d_parent,
@ -129,7 +119,7 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
*/ */
static int afs_mntpt_open(struct inode *inode, struct file *file) static int afs_mntpt_open(struct inode *inode, struct file *file)
{ {
kenter("%p,%p{%p{%s},%s}", _enter("%p,%p{%p{%s},%s}",
inode, file, inode, file,
file->f_path.dentry->d_parent, file->f_path.dentry->d_parent,
file->f_path.dentry->d_parent ? file->f_path.dentry->d_parent ?
@ -152,7 +142,7 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
char *buf, *devname = NULL, *options = NULL; char *buf, *devname = NULL, *options = NULL;
int ret; int ret;
kenter("{%s}", mntpt->d_name.name); _enter("{%s}", mntpt->d_name.name);
BUG_ON(!mntpt->d_inode); BUG_ON(!mntpt->d_inode);
@ -196,13 +186,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
strcat(options, ",rwpath"); strcat(options, ",rwpath");
/* try and do the mount */ /* try and do the mount */
kdebug("--- attempting mount %s -o %s ---", devname, options); _debug("--- attempting mount %s -o %s ---", devname, options);
mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options); mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options);
kdebug("--- mount result %p ---", mnt); _debug("--- mount result %p ---", mnt);
free_page((unsigned long) devname); free_page((unsigned long) devname);
free_page((unsigned long) options); free_page((unsigned long) options);
kleave(" = %p", mnt); _leave(" = %p", mnt);
return mnt; return mnt;
error: error:
@ -212,7 +202,7 @@ error:
free_page((unsigned long) devname); free_page((unsigned long) devname);
if (options) if (options)
free_page((unsigned long) options); free_page((unsigned long) options);
kleave(" = %d", ret); _leave(" = %d", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -222,51 +212,81 @@ error:
static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
{ {
struct vfsmount *newmnt; struct vfsmount *newmnt;
struct dentry *old_dentry;
int err; int err;
kenter("%p{%s},{%s:%p{%s}}", _enter("%p{%s},{%s:%p{%s}}",
dentry, dentry,
dentry->d_name.name, dentry->d_name.name,
nd->mnt->mnt_devname, nd->mnt->mnt_devname,
dentry, dentry,
nd->dentry->d_name.name); nd->dentry->d_name.name);
newmnt = afs_mntpt_do_automount(dentry); dput(nd->dentry);
nd->dentry = dget(dentry);
newmnt = afs_mntpt_do_automount(nd->dentry);
if (IS_ERR(newmnt)) { if (IS_ERR(newmnt)) {
path_release(nd); path_release(nd);
return (void *)newmnt; return (void *)newmnt;
} }
old_dentry = nd->dentry; mntget(newmnt);
nd->dentry = dentry; err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts);
err = do_add_mount(newmnt, nd, 0, &afs_vfsmounts); switch (err) {
nd->dentry = old_dentry; case 0:
path_release(nd);
path_release(nd);
if (!err) {
mntget(newmnt);
nd->mnt = newmnt; nd->mnt = newmnt;
dget(newmnt->mnt_root); nd->dentry = dget(newmnt->mnt_root);
nd->dentry = newmnt->mnt_root; schedule_delayed_work(&afs_mntpt_expiry_timer,
afs_mntpt_expiry_timeout * HZ);
break;
case -EBUSY:
/* someone else made a mount here whilst we were busy */
while (d_mountpoint(nd->dentry) &&
follow_down(&nd->mnt, &nd->dentry))
;
err = 0;
default:
mntput(newmnt);
break;
} }
kleave(" = %d", err); _leave(" = %d", err);
return ERR_PTR(err); return ERR_PTR(err);
} }
/* /*
* handle mountpoint expiry timer going off * handle mountpoint expiry timer going off
*/ */
static void afs_mntpt_expiry_timed_out(struct afs_timer *timer) static void afs_mntpt_expiry_timed_out(struct work_struct *work)
{ {
kenter(""); _enter("");
mark_mounts_for_expiry(&afs_vfsmounts); if (!list_empty(&afs_vfsmounts)) {
mark_mounts_for_expiry(&afs_vfsmounts);
schedule_delayed_work(&afs_mntpt_expiry_timer,
afs_mntpt_expiry_timeout * HZ);
}
afs_kafstimod_add_timer(&afs_mntpt_expiry_timer, _leave("");
afs_mntpt_expiry_timeout * HZ); }
kleave(""); /*
* kill the AFS mountpoint timer if it's still running
*/
void afs_mntpt_kill_timer(void)
{
_enter("");
ASSERT(list_empty(&afs_vfsmounts));
cancel_delayed_work(&afs_mntpt_expiry_timer);
flush_scheduled_work();
}
/*
* begin unmount by attempting to remove all automounted mountpoints we added
*/
void afs_umount_begin(struct vfsmount *vfsmnt, int flags)
{
shrink_submounts(vfsmnt, &afs_vfsmounts);
} }

View file

@ -1,23 +0,0 @@
/* 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 AFS_MOUNT_H
#define 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 /* AFS_MOUNT_H */

View file

@ -13,8 +13,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include "cell.h"
#include "volume.h"
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "internal.h" #include "internal.h"
@ -315,10 +313,14 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
if (strcmp(kbuf, "add") == 0) { if (strcmp(kbuf, "add") == 0) {
struct afs_cell *cell; struct afs_cell *cell;
ret = afs_cell_create(name, args, &cell);
if (ret < 0)
goto done;
cell = afs_cell_create(name, args);
if (IS_ERR(cell)) {
ret = PTR_ERR(cell);
goto done;
}
afs_put_cell(cell);
printk("kAFS: Added new cell '%s'\n", name); printk("kAFS: Added new cell '%s'\n", name);
} else { } else {
goto inval; goto inval;
@ -472,7 +474,7 @@ static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
struct seq_file *m; struct seq_file *m;
int ret; int ret;
cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data); cell = PDE(inode)->data;
if (!cell) if (!cell)
return -ENOENT; return -ENOENT;
@ -491,13 +493,7 @@ 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 int afs_proc_cell_volumes_release(struct inode *inode, struct file *file)
{ {
struct afs_cell *cell = PDE(inode)->data; return seq_release(inode, file);
int ret;
ret = seq_release(inode, file);
afs_put_cell(cell);
return ret;
} }
/* /*
@ -557,6 +553,16 @@ static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
up_read(&cell->vl_sem); up_read(&cell->vl_sem);
} }
const char afs_vlocation_states[][4] = {
[AFS_VL_NEW] = "New",
[AFS_VL_CREATING] = "Crt",
[AFS_VL_VALID] = "Val",
[AFS_VL_NO_VOLUME] = "NoV",
[AFS_VL_UPDATING] = "Upd",
[AFS_VL_VOLUME_DELETED] = "Del",
[AFS_VL_UNCERTAIN] = "Unc",
};
/* /*
* display a header line followed by a load of volume lines * display a header line followed by a load of volume lines
*/ */
@ -567,13 +573,14 @@ static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
/* display header on line 1 */ /* display header on line 1 */
if (v == (void *) 1) { if (v == (void *) 1) {
seq_puts(m, "USE VLID[0] VLID[1] VLID[2] NAME\n"); seq_puts(m, "USE STT VLID[0] VLID[1] VLID[2] NAME\n");
return 0; return 0;
} }
/* display one cell per line on subsequent lines */ /* display one cell per line on subsequent lines */
seq_printf(m, "%3d %08x %08x %08x %s\n", seq_printf(m, "%3d %s %08x %08x %08x %s\n",
atomic_read(&vlocation->usage), atomic_read(&vlocation->usage),
afs_vlocation_states[vlocation->state],
vlocation->vldb.vid[0], vlocation->vldb.vid[0],
vlocation->vldb.vid[1], vlocation->vldb.vid[1],
vlocation->vldb.vid[2], vlocation->vldb.vid[2],
@ -592,11 +599,11 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
struct seq_file *m; struct seq_file *m;
int ret; int ret;
cell = afs_get_cell_maybe((struct afs_cell**)&PDE(inode)->data); cell = PDE(inode)->data;
if (!cell) if (!cell)
return -ENOENT; return -ENOENT;
ret = seq_open(file,&afs_proc_cell_vlservers_ops); ret = seq_open(file, &afs_proc_cell_vlservers_ops);
if (ret<0) if (ret<0)
return ret; return ret;
@ -612,13 +619,7 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
static int afs_proc_cell_vlservers_release(struct inode *inode, static int afs_proc_cell_vlservers_release(struct inode *inode,
struct file *file) struct file *file)
{ {
struct afs_cell *cell = PDE(inode)->data; return seq_release(inode, file);
int ret;
ret = seq_release(inode,file);
afs_put_cell(cell);
return ret;
} }
/* /*
@ -703,7 +704,7 @@ static int afs_proc_cell_servers_open(struct inode *inode, struct file *file)
struct seq_file *m; struct seq_file *m;
int ret; int ret;
cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data); cell = PDE(inode)->data;
if (!cell) if (!cell)
return -ENOENT; return -ENOENT;
@ -722,13 +723,7 @@ static int afs_proc_cell_servers_open(struct inode *inode, struct file *file)
static int afs_proc_cell_servers_release(struct inode *inode, static int afs_proc_cell_servers_release(struct inode *inode,
struct file *file) struct file *file)
{ {
struct afs_cell *cell = PDE(inode)->data; return seq_release(inode, file);
int ret;
ret = seq_release(inode, file);
afs_put_cell(cell);
return ret;
} }
/* /*
@ -736,7 +731,7 @@ static int afs_proc_cell_servers_release(struct inode *inode,
* first item * first item
*/ */
static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
__acquires(m->private->sv_lock) __acquires(m->private->servers_lock)
{ {
struct list_head *_p; struct list_head *_p;
struct afs_cell *cell = m->private; struct afs_cell *cell = m->private;
@ -745,7 +740,7 @@ static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
_enter("cell=%p pos=%Ld", cell, *_pos); _enter("cell=%p pos=%Ld", cell, *_pos);
/* lock the list against modification */ /* lock the list against modification */
read_lock(&cell->sv_lock); read_lock(&cell->servers_lock);
/* allow for the header line */ /* allow for the header line */
if (!pos) if (!pos)
@ -753,11 +748,11 @@ static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
pos--; pos--;
/* find the n'th element in the list */ /* find the n'th element in the list */
list_for_each(_p, &cell->sv_list) list_for_each(_p, &cell->servers)
if (!pos--) if (!pos--)
break; break;
return _p != &cell->sv_list ? _p : NULL; return _p != &cell->servers ? _p : NULL;
} }
/* /*
@ -774,20 +769,20 @@ static void *afs_proc_cell_servers_next(struct seq_file *p, void *v,
(*_pos)++; (*_pos)++;
_p = v; _p = v;
_p = v == (void *) 1 ? cell->sv_list.next : _p->next; _p = v == (void *) 1 ? cell->servers.next : _p->next;
return _p != &cell->sv_list ? _p : NULL; return _p != &cell->servers ? _p : NULL;
} }
/* /*
* clean up after reading from the cells list * clean up after reading from the cells list
*/ */
static void afs_proc_cell_servers_stop(struct seq_file *p, void *v) static void afs_proc_cell_servers_stop(struct seq_file *p, void *v)
__releases(p->private->sv_lock) __releases(p->private->servers_lock)
{ {
struct afs_cell *cell = p->private; struct afs_cell *cell = p->private;
read_unlock(&cell->sv_lock); read_unlock(&cell->servers_lock);
} }
/* /*

666
fs/afs/rxrpc.c Normal file
View file

@ -0,0 +1,666 @@
/* Maintain an RxRPC server socket to do AFS communications through
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include <rxrpc/packet.h>
#include "internal.h"
#include "afs_cm.h"
static struct socket *afs_socket; /* my RxRPC socket */
static struct workqueue_struct *afs_async_calls;
static void afs_wake_up_call_waiter(struct afs_call *);
static int afs_wait_for_call_to_complete(struct afs_call *);
static void afs_wake_up_async_call(struct afs_call *);
static int afs_dont_wait_for_call_to_complete(struct afs_call *);
static void afs_process_async_call(struct work_struct *);
static void afs_rx_interceptor(struct sock *, unsigned long, struct sk_buff *);
static int afs_deliver_cm_op_id(struct afs_call *, struct sk_buff *, bool);
/* synchronous call management */
const struct afs_wait_mode afs_sync_call = {
.rx_wakeup = afs_wake_up_call_waiter,
.wait = afs_wait_for_call_to_complete,
};
/* asynchronous call management */
const struct afs_wait_mode afs_async_call = {
.rx_wakeup = afs_wake_up_async_call,
.wait = afs_dont_wait_for_call_to_complete,
};
/* asynchronous incoming call management */
static const struct afs_wait_mode afs_async_incoming_call = {
.rx_wakeup = afs_wake_up_async_call,
};
/* asynchronous incoming call initial processing */
static const struct afs_call_type afs_RXCMxxxx = {
.deliver = afs_deliver_cm_op_id,
.abort_to_error = afs_abort_to_error,
};
static void afs_collect_incoming_call(struct work_struct *);
static struct sk_buff_head afs_incoming_calls;
static DECLARE_WORK(afs_collect_incoming_call_work, afs_collect_incoming_call);
/*
* open an RxRPC socket and bind it to be a server for callback notifications
* - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT
*/
int afs_open_socket(void)
{
struct sockaddr_rxrpc srx;
struct socket *socket;
int ret;
_enter("");
skb_queue_head_init(&afs_incoming_calls);
afs_async_calls = create_singlethread_workqueue("kafsd");
if (!afs_async_calls) {
_leave(" = -ENOMEM [wq]");
return -ENOMEM;
}
ret = sock_create_kern(AF_RXRPC, SOCK_DGRAM, PF_INET, &socket);
if (ret < 0) {
destroy_workqueue(afs_async_calls);
_leave(" = %d [socket]", ret);
return ret;
}
socket->sk->sk_allocation = GFP_NOFS;
/* bind the callback manager's address to make this a server socket */
srx.srx_family = AF_RXRPC;
srx.srx_service = CM_SERVICE;
srx.transport_type = SOCK_DGRAM;
srx.transport_len = sizeof(srx.transport.sin);
srx.transport.sin.sin_family = AF_INET;
srx.transport.sin.sin_port = htons(AFS_CM_PORT);
memset(&srx.transport.sin.sin_addr, 0,
sizeof(srx.transport.sin.sin_addr));
ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
if (ret < 0) {
sock_release(socket);
_leave(" = %d [bind]", ret);
return ret;
}
rxrpc_kernel_intercept_rx_messages(socket, afs_rx_interceptor);
afs_socket = socket;
_leave(" = 0");
return 0;
}
/*
* close the RxRPC socket AFS was using
*/
void afs_close_socket(void)
{
_enter("");
sock_release(afs_socket);
_debug("dework");
destroy_workqueue(afs_async_calls);
_leave("");
}
/*
* allocate a call with flat request and reply buffers
*/
struct afs_call *afs_alloc_flat_call(const struct afs_call_type *type,
size_t request_size, size_t reply_size)
{
struct afs_call *call;
call = kzalloc(sizeof(*call), GFP_NOFS);
if (!call)
goto nomem_call;
if (request_size) {
call->request = kmalloc(request_size, GFP_NOFS);
if (!call->request)
goto nomem_request;
}
if (reply_size) {
call->buffer = kmalloc(reply_size, GFP_NOFS);
if (!call->buffer)
goto nomem_buffer;
}
call->type = type;
call->request_size = request_size;
call->reply_max = reply_size;
init_waitqueue_head(&call->waitq);
skb_queue_head_init(&call->rx_queue);
return call;
nomem_buffer:
kfree(call->request);
nomem_request:
kfree(call);
nomem_call:
return NULL;
}
/*
* clean up a call with flat buffer
*/
void afs_flat_call_destructor(struct afs_call *call)
{
_enter("");
kfree(call->request);
call->request = NULL;
kfree(call->buffer);
call->buffer = NULL;
}
/*
* initiate a call
*/
int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
const struct afs_wait_mode *wait_mode)
{
struct sockaddr_rxrpc srx;
struct rxrpc_call *rxcall;
struct msghdr msg;
struct kvec iov[1];
int ret;
_enter("%x,{%d},", addr->s_addr, ntohs(call->port));
call->wait_mode = wait_mode;
INIT_WORK(&call->async_work, afs_process_async_call);
memset(&srx, 0, sizeof(srx));
srx.srx_family = AF_RXRPC;
srx.srx_service = call->service_id;
srx.transport_type = SOCK_DGRAM;
srx.transport_len = sizeof(srx.transport.sin);
srx.transport.sin.sin_family = AF_INET;
srx.transport.sin.sin_port = call->port;
memcpy(&srx.transport.sin.sin_addr, addr, 4);
/* create a call */
rxcall = rxrpc_kernel_begin_call(afs_socket, &srx, call->key,
(unsigned long) call, gfp);
if (IS_ERR(rxcall)) {
ret = PTR_ERR(rxcall);
goto error_kill_call;
}
call->rxcall = rxcall;
/* send the request */
iov[0].iov_base = call->request;
iov[0].iov_len = call->request_size;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = (struct iovec *) iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
/* have to change the state *before* sending the last packet as RxRPC
* might give us the reply before it returns from sending the
* request */
call->state = AFS_CALL_AWAIT_REPLY;
ret = rxrpc_kernel_send_data(rxcall, &msg, call->request_size);
if (ret < 0)
goto error_do_abort;
/* at this point, an async call may no longer exist as it may have
* already completed */
return wait_mode->wait(call);
error_do_abort:
rxrpc_kernel_abort_call(rxcall, RX_USER_ABORT);
rxrpc_kernel_end_call(rxcall);
error_kill_call:
call->type->destructor(call);
ASSERT(skb_queue_empty(&call->rx_queue));
kfree(call);
_leave(" = %d", ret);
return ret;
}
/*
* handles intercepted messages that were arriving in the socket's Rx queue
* - called with the socket receive queue lock held to ensure message ordering
* - called with softirqs disabled
*/
static void afs_rx_interceptor(struct sock *sk, unsigned long user_call_ID,
struct sk_buff *skb)
{
struct afs_call *call = (struct afs_call *) user_call_ID;
_enter("%p,,%u", call, skb->mark);
ASSERTCMP(sk, ==, afs_socket->sk);
if (!call) {
/* its an incoming call for our callback service */
__skb_queue_tail(&afs_incoming_calls, skb);
schedule_work(&afs_collect_incoming_call_work);
} else {
/* route the messages directly to the appropriate call */
__skb_queue_tail(&call->rx_queue, skb);
call->wait_mode->rx_wakeup(call);
}
_leave("");
}
/*
* deliver messages to a call
*/
static void afs_deliver_to_call(struct afs_call *call)
{
struct sk_buff *skb;
bool last;
u32 abort_code;
int ret;
_enter("");
while ((call->state == AFS_CALL_AWAIT_REPLY ||
call->state == AFS_CALL_AWAIT_OP_ID ||
call->state == AFS_CALL_AWAIT_REQUEST ||
call->state == AFS_CALL_AWAIT_ACK) &&
(skb = skb_dequeue(&call->rx_queue))) {
switch (skb->mark) {
case RXRPC_SKB_MARK_DATA:
_debug("Rcv DATA");
last = rxrpc_kernel_is_data_last(skb);
ret = call->type->deliver(call, skb, last);
switch (ret) {
case 0:
if (last &&
call->state == AFS_CALL_AWAIT_REPLY)
call->state = AFS_CALL_COMPLETE;
break;
case -ENOTCONN:
abort_code = RX_CALL_DEAD;
goto do_abort;
case -ENOTSUPP:
abort_code = RX_INVALID_OPERATION;
goto do_abort;
default:
abort_code = RXGEN_CC_UNMARSHAL;
if (call->state != AFS_CALL_AWAIT_REPLY)
abort_code = RXGEN_SS_UNMARSHAL;
do_abort:
rxrpc_kernel_abort_call(call->rxcall,
abort_code);
call->error = ret;
call->state = AFS_CALL_ERROR;
break;
}
rxrpc_kernel_data_delivered(skb);
skb = NULL;
break;
case RXRPC_SKB_MARK_FINAL_ACK:
_debug("Rcv ACK");
call->state = AFS_CALL_COMPLETE;
break;
case RXRPC_SKB_MARK_BUSY:
_debug("Rcv BUSY");
call->error = -EBUSY;
call->state = AFS_CALL_BUSY;
break;
case RXRPC_SKB_MARK_REMOTE_ABORT:
abort_code = rxrpc_kernel_get_abort_code(skb);
call->error = call->type->abort_to_error(abort_code);
call->state = AFS_CALL_ABORTED;
_debug("Rcv ABORT %u -> %d", abort_code, call->error);
break;
case RXRPC_SKB_MARK_NET_ERROR:
call->error = -rxrpc_kernel_get_error_number(skb);
call->state = AFS_CALL_ERROR;
_debug("Rcv NET ERROR %d", call->error);
break;
case RXRPC_SKB_MARK_LOCAL_ERROR:
call->error = -rxrpc_kernel_get_error_number(skb);
call->state = AFS_CALL_ERROR;
_debug("Rcv LOCAL ERROR %d", call->error);
break;
default:
BUG();
break;
}
rxrpc_kernel_free_skb(skb);
}
/* make sure the queue is empty if the call is done with (we might have
* aborted the call early because of an unmarshalling error) */
if (call->state >= AFS_CALL_COMPLETE) {
while ((skb = skb_dequeue(&call->rx_queue)))
rxrpc_kernel_free_skb(skb);
if (call->incoming) {
rxrpc_kernel_end_call(call->rxcall);
call->type->destructor(call);
ASSERT(skb_queue_empty(&call->rx_queue));
kfree(call);
}
}
_leave("");
}
/*
* wait synchronously for a call to complete
*/
static int afs_wait_for_call_to_complete(struct afs_call *call)
{
struct sk_buff *skb;
int ret;
DECLARE_WAITQUEUE(myself, current);
_enter("");
add_wait_queue(&call->waitq, &myself);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
/* deliver any messages that are in the queue */
if (!skb_queue_empty(&call->rx_queue)) {
__set_current_state(TASK_RUNNING);
afs_deliver_to_call(call);
continue;
}
ret = call->error;
if (call->state >= AFS_CALL_COMPLETE)
break;
ret = -EINTR;
if (signal_pending(current))
break;
schedule();
}
remove_wait_queue(&call->waitq, &myself);
__set_current_state(TASK_RUNNING);
/* kill the call */
if (call->state < AFS_CALL_COMPLETE) {
_debug("call incomplete");
rxrpc_kernel_abort_call(call->rxcall, RX_CALL_DEAD);
while ((skb = skb_dequeue(&call->rx_queue)))
rxrpc_kernel_free_skb(skb);
}
_debug("call complete");
rxrpc_kernel_end_call(call->rxcall);
call->type->destructor(call);
ASSERT(skb_queue_empty(&call->rx_queue));
kfree(call);
_leave(" = %d", ret);
return ret;
}
/*
* wake up a waiting call
*/
static void afs_wake_up_call_waiter(struct afs_call *call)
{
wake_up(&call->waitq);
}
/*
* wake up an asynchronous call
*/
static void afs_wake_up_async_call(struct afs_call *call)
{
_enter("");
queue_work(afs_async_calls, &call->async_work);
}
/*
* put a call into asynchronous mode
* - mustn't touch the call descriptor as the call my have completed by the
* time we get here
*/
static int afs_dont_wait_for_call_to_complete(struct afs_call *call)
{
_enter("");
return -EINPROGRESS;
}
/*
* delete an asynchronous call
*/
static void afs_delete_async_call(struct work_struct *work)
{
struct afs_call *call =
container_of(work, struct afs_call, async_work);
_enter("");
ASSERT(skb_queue_empty(&call->rx_queue));
ASSERT(!work_pending(&call->async_work));
kfree(call);
_leave("");
}
/*
* perform processing on an asynchronous call
* - on a multiple-thread workqueue this work item may try to run on several
* CPUs at the same time
*/
static void afs_process_async_call(struct work_struct *work)
{
struct afs_call *call =
container_of(work, struct afs_call, async_work);
_enter("");
if (!skb_queue_empty(&call->rx_queue))
afs_deliver_to_call(call);
if (call->state >= AFS_CALL_COMPLETE && call->wait_mode) {
if (call->wait_mode->async_complete)
call->wait_mode->async_complete(call->reply,
call->error);
call->reply = NULL;
/* kill the call */
rxrpc_kernel_end_call(call->rxcall);
if (call->type->destructor)
call->type->destructor(call);
/* we can't just delete the call because the work item may be
* queued */
PREPARE_WORK(&call->async_work, afs_delete_async_call);
queue_work(afs_async_calls, &call->async_work);
}
_leave("");
}
/*
* empty a socket buffer into a flat reply buffer
*/
void afs_transfer_reply(struct afs_call *call, struct sk_buff *skb)
{
size_t len = skb->len;
if (skb_copy_bits(skb, 0, call->buffer + call->reply_size, len) < 0)
BUG();
call->reply_size += len;
}
/*
* accept the backlog of incoming calls
*/
static void afs_collect_incoming_call(struct work_struct *work)
{
struct rxrpc_call *rxcall;
struct afs_call *call = NULL;
struct sk_buff *skb;
while ((skb = skb_dequeue(&afs_incoming_calls))) {
_debug("new call");
/* don't need the notification */
rxrpc_kernel_free_skb(skb);
if (!call) {
call = kzalloc(sizeof(struct afs_call), GFP_KERNEL);
if (!call) {
rxrpc_kernel_reject_call(afs_socket);
return;
}
INIT_WORK(&call->async_work, afs_process_async_call);
call->wait_mode = &afs_async_incoming_call;
call->type = &afs_RXCMxxxx;
init_waitqueue_head(&call->waitq);
skb_queue_head_init(&call->rx_queue);
call->state = AFS_CALL_AWAIT_OP_ID;
}
rxcall = rxrpc_kernel_accept_call(afs_socket,
(unsigned long) call);
if (!IS_ERR(rxcall)) {
call->rxcall = rxcall;
call = NULL;
}
}
kfree(call);
}
/*
* grab the operation ID from an incoming cache manager call
*/
static int afs_deliver_cm_op_id(struct afs_call *call, struct sk_buff *skb,
bool last)
{
size_t len = skb->len;
void *oibuf = (void *) &call->operation_ID;
_enter("{%u},{%zu},%d", call->offset, len, last);
ASSERTCMP(call->offset, <, 4);
/* the operation ID forms the first four bytes of the request data */
len = min_t(size_t, len, 4 - call->offset);
if (skb_copy_bits(skb, 0, oibuf + call->offset, len) < 0)
BUG();
if (!pskb_pull(skb, len))
BUG();
call->offset += len;
if (call->offset < 4) {
if (last) {
_leave(" = -EBADMSG [op ID short]");
return -EBADMSG;
}
_leave(" = 0 [incomplete]");
return 0;
}
call->state = AFS_CALL_AWAIT_REQUEST;
/* ask the cache manager to route the call (it'll change the call type
* if successful) */
if (!afs_cm_incoming_call(call))
return -ENOTSUPP;
/* pass responsibility for the remainer of this message off to the
* cache manager op */
return call->type->deliver(call, skb, last);
}
/*
* send an empty reply
*/
void afs_send_empty_reply(struct afs_call *call)
{
struct msghdr msg;
struct iovec iov[1];
_enter("");
iov[0].iov_base = NULL;
iov[0].iov_len = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
call->state = AFS_CALL_AWAIT_ACK;
switch (rxrpc_kernel_send_data(call->rxcall, &msg, 0)) {
case 0:
_leave(" [replied]");
return;
case -ENOMEM:
_debug("oom");
rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT);
default:
rxrpc_kernel_end_call(call->rxcall);
call->rxcall = NULL;
call->type->destructor(call);
ASSERT(skb_queue_empty(&call->rx_queue));
kfree(call);
_leave(" [error]");
return;
}
}
/*
* extract a piece of data from the received data socket buffers
*/
int afs_extract_data(struct afs_call *call, struct sk_buff *skb,
bool last, void *buf, size_t count)
{
size_t len = skb->len;
_enter("{%u},{%zu},%d,,%zu", call->offset, len, last, count);
ASSERTCMP(call->offset, <, count);
len = min_t(size_t, len, count - call->offset);
if (skb_copy_bits(skb, 0, buf + call->offset, len) < 0 ||
!pskb_pull(skb, len))
BUG();
call->offset += len;
if (call->offset < count) {
if (last) {
_leave(" = -EBADMSG [%d < %lu]", call->offset, count);
return -EBADMSG;
}
_leave(" = -EAGAIN");
return -EAGAIN;
}
return 0;
}

View file

@ -1,6 +1,6 @@
/* AFS server record management /* AFS server record management
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -11,127 +11,205 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.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" #include "internal.h"
DEFINE_SPINLOCK(afs_server_peer_lock); unsigned afs_server_timeout = 10; /* server timeout in seconds */
#define FS_SERVICE_ID 1 /* AFS Volume Location Service ID */ static void afs_reap_server(struct work_struct *);
#define VL_SERVICE_ID 52 /* AFS Volume Location Service ID */
static void __afs_server_timeout(struct afs_timer *timer) /* tree of all the servers, indexed by IP address */
{ static struct rb_root afs_servers = RB_ROOT;
struct afs_server *server = static DEFINE_RWLOCK(afs_servers_lock);
list_entry(timer, struct afs_server, timeout);
_debug("SERVER TIMEOUT [%p{u=%d}]", /* LRU list of all the servers not currently in use */
server, atomic_read(&server->usage)); static LIST_HEAD(afs_server_graveyard);
static DEFINE_SPINLOCK(afs_server_graveyard_lock);
afs_server_do_timeout(server); static DECLARE_DELAYED_WORK(afs_server_reaper, afs_reap_server);
}
static const struct afs_timer_ops afs_server_timer_ops = {
.timed_out = __afs_server_timeout,
};
/* /*
* lookup a server record in a cell * install a server record in the master tree
* - TODO: search the cell's server list
*/ */
int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr, static int afs_install_server(struct afs_server *server)
struct afs_server **_server)
{ {
struct afs_server *server, *active, *zombie; struct afs_server *xserver;
int loop; struct rb_node **pp, *p;
int ret;
_enter("%p,%08x,", cell, ntohl(addr->s_addr)); _enter("%p", server);
write_lock(&afs_servers_lock);
ret = -EEXIST;
pp = &afs_servers.rb_node;
p = NULL;
while (*pp) {
p = *pp;
_debug("- consider %p", p);
xserver = rb_entry(p, struct afs_server, master_rb);
if (server->addr.s_addr < xserver->addr.s_addr)
pp = &(*pp)->rb_left;
else if (server->addr.s_addr > xserver->addr.s_addr)
pp = &(*pp)->rb_right;
else
goto error;
}
rb_link_node(&server->master_rb, p, pp);
rb_insert_color(&server->master_rb, &afs_servers);
ret = 0;
error:
write_unlock(&afs_servers_lock);
return ret;
}
/*
* allocate a new server record
*/
static struct afs_server *afs_alloc_server(struct afs_cell *cell,
const struct in_addr *addr)
{
struct afs_server *server;
_enter("");
/* allocate and initialise a server record */
server = kzalloc(sizeof(struct afs_server), GFP_KERNEL); server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
if (!server) { if (server) {
atomic_set(&server->usage, 1);
server->cell = cell;
INIT_LIST_HEAD(&server->link);
INIT_LIST_HEAD(&server->grave);
init_rwsem(&server->sem);
spin_lock_init(&server->fs_lock);
server->fs_vnodes = RB_ROOT;
server->cb_promises = RB_ROOT;
spin_lock_init(&server->cb_lock);
init_waitqueue_head(&server->cb_break_waitq);
INIT_DELAYED_WORK(&server->cb_break_work,
afs_dispatch_give_up_callbacks);
memcpy(&server->addr, addr, sizeof(struct in_addr));
server->addr.s_addr = addr->s_addr;
}
_leave(" = %p{%d}", server, atomic_read(&server->usage));
return server;
}
/*
* get an FS-server record for a cell
*/
struct afs_server *afs_lookup_server(struct afs_cell *cell,
const struct in_addr *addr)
{
struct afs_server *server, *candidate;
_enter("%p,"NIPQUAD_FMT, cell, NIPQUAD(addr->s_addr));
/* quick scan of the list to see if we already have the server */
read_lock(&cell->servers_lock);
list_for_each_entry(server, &cell->servers, link) {
if (server->addr.s_addr == addr->s_addr)
goto found_server_quickly;
}
read_unlock(&cell->servers_lock);
candidate = afs_alloc_server(cell, addr);
if (!candidate) {
_leave(" = -ENOMEM"); _leave(" = -ENOMEM");
return -ENOMEM; return ERR_PTR(-ENOMEM);
} }
atomic_set(&server->usage, 1); write_lock(&cell->servers_lock);
INIT_LIST_HEAD(&server->link); /* check the cell's server list again */
init_rwsem(&server->sem); list_for_each_entry(server, &cell->servers, link) {
INIT_LIST_HEAD(&server->fs_callq); if (server->addr.s_addr == addr->s_addr)
spin_lock_init(&server->fs_lock); goto found_server;
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 */ _debug("new");
spin_lock(&cell->sv_gylock); server = candidate;
list_for_each_entry(zombie, &cell->sv_graveyard, link) { if (afs_install_server(server) < 0)
if (zombie->addr.s_addr == addr->s_addr) goto server_in_two_cells;
goto resurrect_server;
}
spin_unlock(&cell->sv_gylock);
afs_get_cell(cell); afs_get_cell(cell);
server->cell = cell; list_add_tail(&server->link, &cell->servers);
list_add_tail(&server->link, &cell->sv_list);
write_unlock(&cell->sv_lock); write_unlock(&cell->servers_lock);
_leave(" = %p{%d}", server, atomic_read(&server->usage));
return server;
*_server = server; /* found a matching server quickly */
_leave(" = 0 (%p)", server); found_server_quickly:
return 0; _debug("found quickly");
afs_get_server(server);
read_unlock(&cell->servers_lock);
no_longer_unused:
if (!list_empty(&server->grave)) {
spin_lock(&afs_server_graveyard_lock);
list_del_init(&server->grave);
spin_unlock(&afs_server_graveyard_lock);
}
_leave(" = %p{%d}", server, atomic_read(&server->usage));
return server;
/* found a matching active server */ /* found a matching server on the second pass */
use_active_server: found_server:
_debug("active server"); _debug("found");
afs_get_server(active); afs_get_server(server);
write_unlock(&cell->sv_lock); write_unlock(&cell->servers_lock);
kfree(candidate);
goto no_longer_unused;
kfree(server); /* found a server that seems to be in two cells */
server_in_two_cells:
write_unlock(&cell->servers_lock);
kfree(candidate);
printk(KERN_NOTICE "kAFS:"
" Server "NIPQUAD_FMT" appears to be in two cells\n",
NIPQUAD(*addr));
_leave(" = -EEXIST");
return ERR_PTR(-EEXIST);
}
*_server = active; /*
_leave(" = 0 (%p)", active); * look up a server by its IP address
return 0; */
struct afs_server *afs_find_server(const struct in_addr *_addr)
{
struct afs_server *server = NULL;
struct rb_node *p;
struct in_addr addr = *_addr;
/* found a matching server in the graveyard, so resurrect it and _enter(NIPQUAD_FMT, NIPQUAD(addr.s_addr));
* dispose of the new record */
resurrect_server:
_debug("resurrecting server");
list_move_tail(&zombie->link, &cell->sv_list); read_lock(&afs_servers_lock);
afs_get_server(zombie);
afs_kafstimod_del_timer(&zombie->timeout);
spin_unlock(&cell->sv_gylock);
write_unlock(&cell->sv_lock);
kfree(server); p = afs_servers.rb_node;
while (p) {
server = rb_entry(p, struct afs_server, master_rb);
*_server = zombie; _debug("- consider %p", p);
_leave(" = 0 (%p)", zombie);
return 0; if (addr.s_addr < server->addr.s_addr) {
p = p->rb_left;
} else if (addr.s_addr > server->addr.s_addr) {
p = p->rb_right;
} else {
afs_get_server(server);
goto found;
}
}
server = NULL;
found:
read_unlock(&afs_servers_lock);
ASSERTIFCMP(server, server->addr.s_addr, ==, addr.s_addr);
_leave(" = %p", server);
return server;
} }
/* /*
@ -140,347 +218,105 @@ resurrect_server:
*/ */
void afs_put_server(struct afs_server *server) void afs_put_server(struct afs_server *server)
{ {
struct afs_cell *cell;
if (!server) if (!server)
return; return;
_enter("%p", server); _enter("%p{%d}", server, atomic_read(&server->usage));
cell = server->cell; ASSERTCMP(atomic_read(&server->usage), >, 0);
/* 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))) { if (likely(!atomic_dec_and_test(&server->usage))) {
write_unlock(&cell->sv_lock);
_leave(""); _leave("");
return; return;
} }
spin_lock(&cell->sv_gylock); afs_flush_callback_breaks(server);
list_move_tail(&server->link, &cell->sv_graveyard);
/* time out in 10 secs */ spin_lock(&afs_server_graveyard_lock);
afs_kafstimod_add_timer(&server->timeout, 10 * HZ); if (atomic_read(&server->usage) == 0) {
list_move_tail(&server->grave, &afs_server_graveyard);
spin_unlock(&cell->sv_gylock); server->time_of_death = get_seconds();
write_unlock(&cell->sv_lock); schedule_delayed_work(&afs_server_reaper,
afs_server_timeout * HZ);
_leave(" [killed]"); }
spin_unlock(&afs_server_graveyard_lock);
_leave(" [dead]");
} }
/* /*
* timeout server record * destroy a dead server
* - removes from the cell's graveyard if the usage count is zero
*/ */
void afs_server_do_timeout(struct afs_server *server) static void afs_destroy_server(struct afs_server *server)
{ {
struct rxrpc_peer *peer;
struct afs_cell *cell;
int loop;
_enter("%p", server); _enter("%p", server);
cell = server->cell; ASSERTCMP(server->fs_vnodes.rb_node, ==, NULL);
ASSERTCMP(server->cb_promises.rb_node, ==, NULL);
BUG_ON(atomic_read(&server->usage) < 0); ASSERTCMP(server->cb_break_head, ==, server->cb_break_tail);
ASSERTCMP(atomic_read(&server->cb_break_n), ==, 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);
afs_put_cell(server->cell);
kfree(server); kfree(server);
_leave(" [destroyed]");
} }
/* /*
* get a callslot on a connection to the fileserver on the specified server * reap dead server records
*/ */
int afs_server_request_callslot(struct afs_server *server, static void afs_reap_server(struct work_struct *work)
struct afs_server_callslot *callslot)
{ {
struct afs_server_callslot *pcallslot; LIST_HEAD(corpses);
struct rxrpc_connection *conn; struct afs_server *server;
int nconn, ret; unsigned long delay, expiry;
time_t now;
_enter("%p,",server); now = get_seconds();
spin_lock(&afs_server_graveyard_lock);
INIT_LIST_HEAD(&callslot->link); while (!list_empty(&afs_server_graveyard)) {
callslot->task = current; server = list_entry(afs_server_graveyard.next,
callslot->conn = NULL; struct afs_server, grave);
callslot->nconn = -1;
callslot->ready = 0;
ret = 0; /* the queue is ordered most dead first */
conn = NULL; expiry = server->time_of_death + afs_server_timeout;
if (expiry > now) {
/* get hold of a callslot first */ delay = (expiry - now) * HZ;
spin_lock(&server->fs_lock); if (!schedule_delayed_work(&afs_server_reaper, delay)) {
cancel_delayed_work(&afs_server_reaper);
/* resurrect the server if it's death timeout has expired */ schedule_delayed_work(&afs_server_reaper,
if (server->fs_state) { delay);
if (time_before(jiffies, server->fs_dead_jif)) { }
ret = server->fs_state; break;
spin_unlock(&server->fs_lock);
_leave(" = %d [still dead]", ret);
return ret;
} }
server->fs_state = 0; write_lock(&server->cell->servers_lock);
} write_lock(&afs_servers_lock);
if (atomic_read(&server->usage) > 0) {
/* try and find a connection that has spare callslots */ list_del_init(&server->grave);
for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) { } else {
if (server->fs_conn_cnt[nconn] > 0) { list_move_tail(&server->grave, &corpses);
server->fs_conn_cnt[nconn]--; list_del_init(&server->link);
spin_unlock(&server->fs_lock); rb_erase(&server->master_rb, &afs_servers);
callslot->nconn = nconn;
goto obtained_slot;
} }
write_unlock(&afs_servers_lock);
write_unlock(&server->cell->servers_lock);
} }
/* none were available - wait interruptibly for one to become spin_unlock(&afs_server_graveyard_lock);
* 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)) { /* now reap the corpses we've extracted */
schedule(); while (!list_empty(&corpses)) {
set_current_state(TASK_INTERRUPTIBLE); server = list_entry(corpses.next, struct afs_server, grave);
list_del(&server->grave);
afs_destroy_server(server);
} }
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;
} }
/* /*
* release a callslot back to the server * discard all the server records for rmmod
* - transfers the RxRPC connection to the next pending callslot if possible
*/ */
void afs_server_release_callslot(struct afs_server *server, void __exit afs_purge_servers(void)
struct afs_server_callslot *callslot)
{ {
struct afs_server_callslot *pcallslot; afs_server_timeout = 0;
cancel_delayed_work(&afs_server_reaper);
_enter("{ad=%08x,cnt=%u},{%d}", schedule_delayed_work(&afs_server_reaper, 0);
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("");
}
/*
* 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;
} }

View file

@ -1,97 +0,0 @@
/* 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 AFS_SERVER_H
#define 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 *, const struct in_addr *,
struct afs_server **);
#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0)
extern void afs_put_server(struct afs_server *);
extern void afs_server_do_timeout(struct afs_server *);
extern int afs_server_find_by_peer(const struct rxrpc_peer *,
struct afs_server **);
extern int afs_server_get_vlconn(struct afs_server *,
struct rxrpc_connection **);
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 *,
struct afs_server_callslot *);
extern void afs_server_release_callslot(struct afs_server *,
struct afs_server_callslot *);
#endif /* AFS_SERVER_H */

View file

@ -1,6 +1,6 @@
/* AFS superblock handling /* AFS superblock handling
* *
* Copyright (c) 2002 Red Hat, Inc. All rights reserved. * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
* *
* This software may be freely redistributed under the terms of the * This software may be freely redistributed under the terms of the
* GNU General Public License. * GNU General Public License.
@ -20,12 +20,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pagemap.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" #include "internal.h"
#define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */
@ -63,6 +57,7 @@ static const struct super_operations afs_super_ops = {
.drop_inode = generic_delete_inode, .drop_inode = generic_delete_inode,
.destroy_inode = afs_destroy_inode, .destroy_inode = afs_destroy_inode,
.clear_inode = afs_clear_inode, .clear_inode = afs_clear_inode,
.umount_begin = afs_umount_begin,
.put_super = afs_put_super, .put_super = afs_put_super,
}; };
@ -78,8 +73,6 @@ int __init afs_fs_init(void)
_enter(""); _enter("");
afs_timer_init(&afs_mntpt_expiry_timer, &afs_mntpt_expiry_timer_ops);
/* create ourselves an inode cache */ /* create ourselves an inode cache */
atomic_set(&afs_count_active_inodes, 0); atomic_set(&afs_count_active_inodes, 0);
@ -99,11 +92,11 @@ int __init afs_fs_init(void)
ret = register_filesystem(&afs_fs_type); ret = register_filesystem(&afs_fs_type);
if (ret < 0) { if (ret < 0) {
kmem_cache_destroy(afs_inode_cachep); kmem_cache_destroy(afs_inode_cachep);
kleave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
kleave(" = 0"); _leave(" = 0");
return 0; return 0;
} }
@ -112,6 +105,9 @@ int __init afs_fs_init(void)
*/ */
void __exit afs_fs_exit(void) void __exit afs_fs_exit(void)
{ {
_enter("");
afs_mntpt_kill_timer();
unregister_filesystem(&afs_fs_type); unregister_filesystem(&afs_fs_type);
if (atomic_read(&afs_count_active_inodes) != 0) { if (atomic_read(&afs_count_active_inodes) != 0) {
@ -121,6 +117,7 @@ void __exit afs_fs_exit(void)
} }
kmem_cache_destroy(afs_inode_cachep); kmem_cache_destroy(afs_inode_cachep);
_leave("");
} }
/* /*
@ -154,9 +151,9 @@ static int want_no_value(char *const *_value, const char *option)
* shamelessly adapted it from the msdos fs * shamelessly adapted it from the msdos fs
*/ */
static int afs_super_parse_options(struct afs_mount_params *params, static int afs_super_parse_options(struct afs_mount_params *params,
char *options, char *options, const char **devname)
const char **devname)
{ {
struct afs_cell *cell;
char *key, *value; char *key, *value;
int ret; int ret;
@ -165,43 +162,37 @@ static int afs_super_parse_options(struct afs_mount_params *params,
options[PAGE_SIZE - 1] = 0; options[PAGE_SIZE - 1] = 0;
ret = 0; ret = 0;
while ((key = strsep(&options, ",")) != 0) while ((key = strsep(&options, ","))) {
{
value = strchr(key, '='); value = strchr(key, '=');
if (value) if (value)
*value++ = 0; *value++ = 0;
printk("kAFS: KEY: %s, VAL:%s\n", key, value ?: "-"); _debug("kAFS: KEY: %s, VAL:%s", key, value ?: "-");
if (strcmp(key, "rwpath") == 0) { if (strcmp(key, "rwpath") == 0) {
if (!want_no_value(&value, "rwpath")) if (!want_no_value(&value, "rwpath"))
return -EINVAL; return -EINVAL;
params->rwpath = 1; params->rwpath = 1;
continue;
} else if (strcmp(key, "vol") == 0) { } else if (strcmp(key, "vol") == 0) {
if (!want_arg(&value, "vol")) if (!want_arg(&value, "vol"))
return -EINVAL; return -EINVAL;
*devname = value; *devname = value;
continue;
} else if (strcmp(key, "cell") == 0) { } else if (strcmp(key, "cell") == 0) {
if (!want_arg(&value, "cell")) if (!want_arg(&value, "cell"))
return -EINVAL; return -EINVAL;
cell = afs_cell_lookup(value, strlen(value));
if (IS_ERR(cell))
return PTR_ERR(cell);
afs_put_cell(params->default_cell); afs_put_cell(params->default_cell);
ret = afs_cell_lookup(value, params->default_cell = cell;
strlen(value), } else {
&params->default_cell); printk("kAFS: Unknown mount option: '%s'\n", key);
if (ret < 0) ret = -EINVAL;
return -EINVAL; goto error;
continue;
} }
printk("kAFS: Unknown mount option: '%s'\n", key);
ret = -EINVAL;
goto error;
} }
ret = 0; ret = 0;
error: error:
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
@ -230,7 +221,7 @@ static int afs_fill_super(struct super_block *sb, void *data, int silent)
struct inode *inode = NULL; struct inode *inode = NULL;
int ret; int ret;
kenter(""); _enter("");
/* allocate a superblock info record */ /* allocate a superblock info record */
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
@ -253,9 +244,9 @@ static int afs_fill_super(struct super_block *sb, void *data, int silent)
fid.vid = as->volume->vid; fid.vid = as->volume->vid;
fid.vnode = 1; fid.vnode = 1;
fid.unique = 1; fid.unique = 1;
ret = afs_iget(sb, &fid, &inode); inode = afs_iget(sb, &fid);
if (ret < 0) if (IS_ERR(inode))
goto error; goto error_inode;
ret = -ENOMEM; ret = -ENOMEM;
root = d_alloc_root(inode); root = d_alloc_root(inode);
@ -264,9 +255,12 @@ static int afs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_root = root; sb->s_root = root;
kleave(" = 0"); _leave(" = 0");
return 0; return 0;
error_inode:
ret = PTR_ERR(inode);
inode = NULL;
error: error:
iput(inode); iput(inode);
afs_put_volume(as->volume); afs_put_volume(as->volume);
@ -274,7 +268,7 @@ error:
sb->s_fs_info = NULL; sb->s_fs_info = NULL;
kleave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
@ -290,19 +284,13 @@ static int afs_get_sb(struct file_system_type *fs_type,
{ {
struct afs_mount_params params; struct afs_mount_params params;
struct super_block *sb; struct super_block *sb;
struct afs_volume *vol;
int ret; int ret;
_enter(",,%s,%p", dev_name, options); _enter(",,%s,%p", dev_name, options);
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
/* start the cache manager */
ret = afscm_start();
if (ret < 0) {
_leave(" = %d", ret);
return ret;
}
/* parse the options */ /* parse the options */
if (options) { if (options) {
ret = afs_super_parse_options(&params, options, &dev_name); ret = afs_super_parse_options(&params, options, &dev_name);
@ -316,17 +304,20 @@ static int afs_get_sb(struct file_system_type *fs_type,
} }
/* parse the device name */ /* parse the device name */
ret = afs_volume_lookup(dev_name, vol = afs_volume_lookup(dev_name, params.default_cell, params.rwpath);
params.default_cell, if (IS_ERR(vol)) {
params.rwpath, ret = PTR_ERR(vol);
&params.volume);
if (ret < 0)
goto error; goto error;
}
params.volume = vol;
/* allocate a deviceless superblock */ /* allocate a deviceless superblock */
sb = sget(fs_type, afs_test_super, set_anon_super, &params); sb = sget(fs_type, afs_test_super, set_anon_super, &params);
if (IS_ERR(sb)) if (IS_ERR(sb)) {
ret = PTR_ERR(sb);
goto error; goto error;
}
sb->s_flags = flags; sb->s_flags = flags;
@ -341,13 +332,12 @@ static int afs_get_sb(struct file_system_type *fs_type,
afs_put_volume(params.volume); afs_put_volume(params.volume);
afs_put_cell(params.default_cell); afs_put_cell(params.default_cell);
_leave(" = 0 [%p]", 0, sb); _leave(" = 0 [%p]", sb);
return 0; return 0;
error: error:
afs_put_volume(params.volume); afs_put_volume(params.volume);
afs_put_cell(params.default_cell); afs_put_cell(params.default_cell);
afscm_stop();
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
@ -362,7 +352,6 @@ static void afs_put_super(struct super_block *sb)
_enter(""); _enter("");
afs_put_volume(as->volume); afs_put_volume(as->volume);
afscm_stop();
_leave(""); _leave("");
} }
@ -381,10 +370,8 @@ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep,
inode_init_once(&vnode->vfs_inode); inode_init_once(&vnode->vfs_inode);
init_waitqueue_head(&vnode->update_waitq); init_waitqueue_head(&vnode->update_waitq);
spin_lock_init(&vnode->lock); spin_lock_init(&vnode->lock);
INIT_LIST_HEAD(&vnode->cb_link); INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work);
INIT_LIST_HEAD(&vnode->cb_hash_link); mutex_init(&vnode->cb_broken_lock);
afs_timer_init(&vnode->cb_timeout,
&afs_vnode_cb_timed_out_ops);
} }
} }
@ -407,6 +394,7 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
vnode->volume = NULL; vnode->volume = NULL;
vnode->update_cnt = 0; vnode->update_cnt = 0;
vnode->flags = 0; vnode->flags = 0;
vnode->cb_promised = false;
return &vnode->vfs_inode; return &vnode->vfs_inode;
} }
@ -416,8 +404,14 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
*/ */
static void afs_destroy_inode(struct inode *inode) static void afs_destroy_inode(struct inode *inode)
{ {
struct afs_vnode *vnode = AFS_FS_I(inode);
_enter("{%lu}", inode->i_ino); _enter("{%lu}", inode->i_ino);
kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode)); _debug("DESTROY INODE %p", inode);
ASSERTCMP(vnode->server, ==, NULL);
kmem_cache_free(afs_inode_cachep, vnode);
atomic_dec(&afs_count_active_inodes); atomic_dec(&afs_count_active_inodes);
} }

View file

@ -1,39 +0,0 @@
/* 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 AFS_SUPER_H
#define AFS_SUPER_H
#include <linux/fs.h>
#include "server.h"
/*
* 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;
}
extern struct file_system_type afs_fs_type;
#endif /* AFS_SUPER_H */

View file

@ -1,21 +0,0 @@
/* 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 AFS_TRANSPORT_H
#define AFS_TRANSPORT_H
#include "types.h"
#include <rxrpc/transport.h>
/* the cache manager transport endpoint */
extern struct rxrpc_transport *afs_transport;
#endif /* AFS_TRANSPORT_H */

View file

@ -11,673 +11,202 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.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" #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 * map volume locator abort codes to error codes
* - called with call->lock held
*/ */
static void afs_rxvl_aemap(struct rxrpc_call *call) static int afs_vl_abort_to_error(u32 abort_code)
{ {
int err; _enter("%u", abort_code);
_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;
switch (abort_code) {
case AFSVL_IDEXIST: return -EEXIST;
case AFSVL_IO: return -EREMOTEIO;
case AFSVL_NAMEEXIST: return -EEXIST;
case AFSVL_CREATEFAIL: return -EREMOTEIO;
case AFSVL_NOENT: return -ENOMEDIUM;
case AFSVL_EMPTY: return -ENOMEDIUM;
case AFSVL_ENTDELETED: return -ENOMEDIUM;
case AFSVL_BADNAME: return -EINVAL;
case AFSVL_BADINDEX: return -EINVAL;
case AFSVL_BADVOLTYPE: return -EINVAL;
case AFSVL_BADSERVER: return -EINVAL;
case AFSVL_BADPARTITION: return -EINVAL;
case AFSVL_REPSFULL: return -EFBIG;
case AFSVL_NOREPSERVER: return -ENOENT;
case AFSVL_DUPREPSERVER: return -EEXIST;
case AFSVL_RWNOTFOUND: return -ENOENT;
case AFSVL_BADREFCOUNT: return -EINVAL;
case AFSVL_SIZEEXCEEDED: return -EINVAL;
case AFSVL_BADENTRY: return -EINVAL;
case AFSVL_BADVOLIDBUMP: return -EINVAL;
case AFSVL_IDALREADYHASHED: return -EINVAL;
case AFSVL_ENTRYLOCKED: return -EBUSY;
case AFSVL_BADVOLOPER: return -EBADRQC;
case AFSVL_BADRELLOCKTYPE: return -EINVAL;
case AFSVL_RERELEASE: return -EREMOTEIO;
case AFSVL_BADSERVERFLAG: return -EINVAL;
case AFSVL_PERM: return -EACCES;
case AFSVL_NOMEM: return -EREMOTEIO;
default: default:
return; return afs_abort_to_error(abort_code);
} }
} }
#if 0
/* /*
* probe a volume location server to see if it is still alive -- unused * deliver reply data to a VL.GetEntryByXXX call
*/ */
static int afs_rxvl_probe(struct afs_server *server, int alloc_flags) static int afs_deliver_vl_get_entry_by_xxx(struct afs_call *call,
{ struct sk_buff *skb, bool last)
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;
}
#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;
}
/*
* 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;
}
/*
* 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;
}
/*
* 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)
{ {
struct afs_cache_vlocation *entry;
__be32 *bp; __be32 *bp;
__u32 tmp; u32 tmp;
int loop, ret; int loop;
_enter("{op=%p cst=%u}", op, op->call->app_call_state); _enter(",,%u", last);
memset(entry, 0, sizeof(*entry)); afs_transfer_reply(call, skb);
if (!last)
return 0;
if (op->call->app_call_state == RXRPC_CSTATE_COMPLETE) { if (call->reply_size != call->reply_max)
/* operation finished */ return -EBADMSG;
afs_kafsasyncd_terminate_op(op);
bp = op->call->app_scr_ptr; /* unmarshall the reply once we've received all of it */
entry = call->reply;
bp = call->buffer;
/* unmarshall the reply */ for (loop = 0; loop < 64; loop++)
for (loop = 0; loop < 64; loop++) entry->name[loop] = ntohl(*bp++);
entry->name[loop] = ntohl(*bp++); entry->name[loop] = 0;
bp++; /* final NUL */ bp++; /* final NUL */
bp++; /* type */ bp++; /* type */
entry->nservers = ntohl(*bp++); entry->nservers = ntohl(*bp++);
for (loop = 0; loop < 8; loop++) for (loop = 0; loop < 8; loop++)
entry->servers[loop].s_addr = *bp++; entry->servers[loop].s_addr = *bp++;
bp += 8; /* partition IDs */ bp += 8; /* partition IDs */
for (loop = 0; loop < 8; loop++) { for (loop = 0; loop < 8; loop++) {
tmp = ntohl(*bp++); tmp = ntohl(*bp++);
if (tmp & AFS_VLSF_RWVOL) entry->srvtmask[loop] = 0;
entry->srvtmask[loop] |= AFS_VOL_VTM_RW; if (tmp & AFS_VLSF_RWVOL)
if (tmp & AFS_VLSF_ROVOL) entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
entry->srvtmask[loop] |= AFS_VOL_VTM_RO; if (tmp & AFS_VLSF_ROVOL)
if (tmp & AFS_VLSF_BACKVOL) entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
entry->srvtmask[loop] |= AFS_VOL_VTM_BAK; 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) { entry->vid[0] = ntohl(*bp++);
/* operation error */ entry->vid[1] = ntohl(*bp++);
ret = op->call->app_errno; entry->vid[2] = ntohl(*bp++);
goto done;
}
_leave(" = -EAGAIN"); bp++; /* clone ID */
return -EAGAIN;
done: tmp = ntohl(*bp++); /* flags */
rxrpc_put_call(op->call); entry->vidmask = 0;
op->call = NULL; if (tmp & AFS_VLF_RWEXISTS)
_leave(" = %d", ret); entry->vidmask |= AFS_VOL_VTM_RW;
return ret; if (tmp & AFS_VLF_ROEXISTS)
entry->vidmask |= AFS_VOL_VTM_RO;
if (tmp & AFS_VLF_BACKEXISTS)
entry->vidmask |= AFS_VOL_VTM_BAK;
if (!entry->vidmask)
return -EBADMSG;
_leave(" = 0 [done]");
return 0;
} }
/* /*
* handle attention events on an async get-entry-by-ID op * VL.GetEntryByName operation type
* - called from krxiod
*/ */
static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call) static const struct afs_call_type afs_RXVLGetEntryByName = {
.deliver = afs_deliver_vl_get_entry_by_xxx,
.abort_to_error = afs_vl_abort_to_error,
.destructor = afs_flat_call_destructor,
};
/*
* VL.GetEntryById operation type
*/
static const struct afs_call_type afs_RXVLGetEntryById = {
.deliver = afs_deliver_vl_get_entry_by_xxx,
.abort_to_error = afs_vl_abort_to_error,
.destructor = afs_flat_call_destructor,
};
/*
* dispatch a get volume entry by name operation
*/
int afs_vl_get_entry_by_name(struct in_addr *addr,
const char *volname,
struct afs_cache_vlocation *entry,
const struct afs_wait_mode *wait_mode)
{ {
struct afs_async_op *op = call->app_user; struct afs_call *call;
size_t volnamesz, reqsz, padsz;
__be32 *bp;
_enter("{op=%p cst=%u}", op, call->app_call_state); _enter("");
switch (call->app_call_state) { volnamesz = strlen(volname);
case RXRPC_CSTATE_COMPLETE: padsz = (4 - (volnamesz & 3)) & 3;
afs_kafsasyncd_attend_op(op); reqsz = 8 + volnamesz + padsz;
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); call = afs_alloc_flat_call(&afs_RXVLGetEntryByName, reqsz, 384);
break; if (!call)
default: return -ENOMEM;
BUG();
}
_leave(""); call->reply = entry;
call->service_id = VL_SERVICE;
call->port = htons(AFS_VL_PORT);
/* marshall the parameters */
bp = call->request;
*bp++ = htonl(VLGETENTRYBYNAME);
*bp++ = htonl(volnamesz);
memcpy(bp, volname, volnamesz);
if (padsz > 0)
memset((void *) bp + volnamesz, 0, padsz);
/* initiate the call */
return afs_make_call(addr, call, GFP_KERNEL, wait_mode);
} }
/* /*
* handle error events on an async get-entry-by-ID op * dispatch a get volume entry by ID operation
* - called from krxiod
*/ */
static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call) int afs_vl_get_entry_by_id(struct in_addr *addr,
afs_volid_t volid,
afs_voltype_t voltype,
struct afs_cache_vlocation *entry,
const struct afs_wait_mode *wait_mode)
{ {
struct afs_async_op *op = call->app_user; struct afs_call *call;
__be32 *bp;
_enter("{op=%p cst=%u}", op, call->app_call_state); _enter("");
afs_kafsasyncd_attend_op(op); call = afs_alloc_flat_call(&afs_RXVLGetEntryById, 12, 384);
if (!call)
return -ENOMEM;
_leave(""); call->reply = entry;
call->service_id = VL_SERVICE;
call->port = htons(AFS_VL_PORT);
/* marshall the parameters */
bp = call->request;
*bp++ = htonl(VLGETENTRYBYID);
*bp++ = htonl(volid);
*bp = htonl(voltype);
/* initiate the call */
return afs_make_call(addr, call, GFP_KERNEL, wait_mode);
} }

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* AFS vnode management /* AFS vnode management
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -14,71 +14,182 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs.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" #include "internal.h"
static void afs_vnode_cb_timed_out(struct afs_timer *timer); #if 0
static noinline bool dump_tree_aux(struct rb_node *node, struct rb_node *parent,
int depth, char lr)
{
struct afs_vnode *vnode;
bool bad = false;
struct afs_timer_ops afs_vnode_cb_timed_out_ops = { if (!node)
.timed_out = afs_vnode_cb_timed_out, return false;
};
#ifdef AFS_CACHING_SUPPORT if (node->rb_left)
static cachefs_match_val_t afs_vnode_cache_match(void *target, bad = dump_tree_aux(node->rb_left, node, depth + 2, '/');
const void *entry);
static void afs_vnode_cache_update(void *source, void *entry);
struct cachefs_index_def afs_vnode_cache_index_def = { vnode = rb_entry(node, struct afs_vnode, cb_promise);
.name = "vnode", kdebug("%c %*.*s%c%p {%d}",
.data_size = sizeof(struct afs_cache_vnode), rb_is_red(node) ? 'R' : 'B',
.keys[0] = { CACHEFS_INDEX_KEYS_BIN, 4 }, depth, depth, "", lr,
.match = afs_vnode_cache_match, vnode, vnode->cb_expires_at);
.update = afs_vnode_cache_update, if (rb_parent(node) != parent) {
}; printk("BAD: %p != %p\n", rb_parent(node), parent);
bad = true;
}
if (node->rb_right)
bad |= dump_tree_aux(node->rb_right, node, depth + 2, '\\');
return bad;
}
static noinline void dump_tree(const char *name, struct afs_server *server)
{
kenter("%s", name);
if (dump_tree_aux(server->cb_promises.rb_node, NULL, 0, '-'))
BUG();
}
#endif #endif
/* /*
* handle a callback timing out * insert a vnode into the backing server's vnode tree
* TODO: retain a ref to vnode struct for an outstanding callback timeout
*/ */
static void afs_vnode_cb_timed_out(struct afs_timer *timer) static void afs_install_vnode(struct afs_vnode *vnode,
struct afs_server *server)
{ {
struct afs_server *oldserver; struct afs_server *old_server = vnode->server;
struct afs_vnode *vnode; struct afs_vnode *xvnode;
struct rb_node *parent, **p;
vnode = list_entry(timer, struct afs_vnode, cb_timeout); _enter("%p,%p", vnode, server);
_enter("%p", vnode); if (old_server) {
spin_lock(&old_server->fs_lock);
/* set the changed flag in the vnode and release the server */ rb_erase(&vnode->server_rb, &old_server->fs_vnodes);
spin_lock(&vnode->lock); spin_unlock(&old_server->fs_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_get_server(server);
vnode->server = server;
afs_put_server(old_server);
afs_put_server(oldserver); /* insert into the server's vnode tree in FID order */
spin_lock(&server->fs_lock);
parent = NULL;
p = &server->fs_vnodes.rb_node;
while (*p) {
parent = *p;
xvnode = rb_entry(parent, struct afs_vnode, server_rb);
if (vnode->fid.vid < xvnode->fid.vid)
p = &(*p)->rb_left;
else if (vnode->fid.vid > xvnode->fid.vid)
p = &(*p)->rb_right;
else if (vnode->fid.vnode < xvnode->fid.vnode)
p = &(*p)->rb_left;
else if (vnode->fid.vnode > xvnode->fid.vnode)
p = &(*p)->rb_right;
else if (vnode->fid.unique < xvnode->fid.unique)
p = &(*p)->rb_left;
else if (vnode->fid.unique > xvnode->fid.unique)
p = &(*p)->rb_right;
else
BUG(); /* can't happen unless afs_iget() malfunctions */
}
rb_link_node(&vnode->server_rb, parent, p);
rb_insert_color(&vnode->server_rb, &server->fs_vnodes);
spin_unlock(&server->fs_lock);
_leave(""); _leave("");
} }
/*
* insert a vnode into the promising server's update/expiration tree
* - caller must hold vnode->lock
*/
static void afs_vnode_note_promise(struct afs_vnode *vnode,
struct afs_server *server)
{
struct afs_server *old_server;
struct afs_vnode *xvnode;
struct rb_node *parent, **p;
_enter("%p,%p", vnode, server);
ASSERT(server != NULL);
old_server = vnode->server;
if (vnode->cb_promised) {
if (server == old_server &&
vnode->cb_expires == vnode->cb_expires_at) {
_leave(" [no change]");
return;
}
spin_lock(&old_server->cb_lock);
if (vnode->cb_promised) {
_debug("delete");
rb_erase(&vnode->cb_promise, &old_server->cb_promises);
vnode->cb_promised = false;
}
spin_unlock(&old_server->cb_lock);
}
if (vnode->server != server)
afs_install_vnode(vnode, server);
vnode->cb_expires_at = vnode->cb_expires;
_debug("PROMISE on %p {%lu}",
vnode, (unsigned long) vnode->cb_expires_at);
/* abuse an RB-tree to hold the expiration order (we may have multiple
* items with the same expiration time) */
spin_lock(&server->cb_lock);
parent = NULL;
p = &server->cb_promises.rb_node;
while (*p) {
parent = *p;
xvnode = rb_entry(parent, struct afs_vnode, cb_promise);
if (vnode->cb_expires_at < xvnode->cb_expires_at)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&vnode->cb_promise, parent, p);
rb_insert_color(&vnode->cb_promise, &server->cb_promises);
vnode->cb_promised = true;
spin_unlock(&server->cb_lock);
_leave("");
}
/*
* handle remote file deletion by discarding the callback promise
*/
static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
{
struct afs_server *server;
set_bit(AFS_VNODE_DELETED, &vnode->flags);
server = vnode->server;
if (vnode->cb_promised) {
spin_lock(&server->cb_lock);
if (vnode->cb_promised) {
rb_erase(&vnode->cb_promise, &server->cb_promises);
vnode->cb_promised = false;
}
spin_unlock(&server->cb_lock);
}
afs_put_server(server);
}
/* /*
* finish off updating the recorded status of a file * finish off updating the recorded status of a file
* - starts callback expiry timer * - starts callback expiry timer
@ -94,43 +205,19 @@ static void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
vnode->flags &= ~AFS_VNODE_CHANGED; clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
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_move_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);
switch (ret) {
case 0:
afs_vnode_note_promise(vnode, server);
break;
case -ENOENT:
/* the file was deleted on the server */
_debug("got NOENT from server - marking file deleted"); _debug("got NOENT from server - marking file deleted");
vnode->flags |= AFS_VNODE_DELETED; afs_vnode_deleted_remotely(vnode);
break;
default:
break;
} }
vnode->update_cnt--; vnode->update_cnt--;
@ -162,19 +249,21 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
vnode->volume->vlocation->vldb.name, vnode->volume->vlocation->vldb.name,
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) { if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
vnode->cb_promised) {
_leave(" [unchanged]"); _leave(" [unchanged]");
return 0; return 0;
} }
if (vnode->flags & AFS_VNODE_DELETED) { if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
_leave(" [deleted]"); _leave(" [deleted]");
return -ENOENT; return -ENOENT;
} }
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
if (!(vnode->flags & AFS_VNODE_CHANGED)) { if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
vnode->cb_promised) {
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
_leave(" [unchanged]"); _leave(" [unchanged]");
return 0; return 0;
@ -183,17 +272,18 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
if (vnode->update_cnt > 0) { if (vnode->update_cnt > 0) {
/* someone else started a fetch */ /* someone else started a fetch */
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
ASSERT(myself.func != NULL);
add_wait_queue(&vnode->update_waitq, &myself); add_wait_queue(&vnode->update_waitq, &myself);
/* wait for the status to be updated */ /* wait for the status to be updated */
for (;;) { for (;;) {
if (!(vnode->flags & AFS_VNODE_CHANGED)) if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
break; break;
if (vnode->flags & AFS_VNODE_DELETED) if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
break; break;
/* it got updated and invalidated all before we saw /* check to see if it got updated and invalidated all
* it */ * before we saw it */
if (vnode->update_cnt == 0) { if (vnode->update_cnt == 0) {
remove_wait_queue(&vnode->update_waitq, remove_wait_queue(&vnode->update_waitq,
&myself); &myself);
@ -213,7 +303,8 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0; return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
-ENOENT : 0;
} }
get_anyway: get_anyway:
@ -226,15 +317,17 @@ get_anyway:
* vnode */ * vnode */
do { do {
/* pick a server to query */ /* pick a server to query */
ret = afs_volume_pick_fileserver(vnode->volume, &server); server = afs_volume_pick_fileserver(vnode);
if (ret<0) if (IS_ERR(server))
return ret; return PTR_ERR(server);
_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); _debug("USING SERVER: %p{%08x}",
server, ntohl(server->addr.s_addr));
ret = afs_rxfs_fetch_file_status(server, vnode, NULL); ret = afs_fs_fetch_file_status(server, vnode, NULL,
&afs_sync_call);
} while (!afs_volume_release_fileserver(vnode->volume, server, ret)); } while (!afs_volume_release_fileserver(vnode, server, ret));
/* adjust the flags */ /* adjust the flags */
afs_vnode_finalise_status_update(vnode, server, ret); afs_vnode_finalise_status_update(vnode, server, ret);
@ -247,8 +340,8 @@ get_anyway:
* fetch file data from the volume * fetch file data from the volume
* - TODO implement caching and server failover * - TODO implement caching and server failover
*/ */
int afs_vnode_fetch_data(struct afs_vnode *vnode, int afs_vnode_fetch_data(struct afs_vnode *vnode, off_t offset, size_t length,
struct afs_rxfs_fetch_descriptor *desc) struct page *page)
{ {
struct afs_server *server; struct afs_server *server;
int ret; int ret;
@ -268,15 +361,16 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode,
* vnode */ * vnode */
do { do {
/* pick a server to query */ /* pick a server to query */
ret = afs_volume_pick_fileserver(vnode->volume, &server); server = afs_volume_pick_fileserver(vnode);
if (ret < 0) if (IS_ERR(server))
return ret; return PTR_ERR(server);
_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
ret = afs_rxfs_fetch_file_data(server, vnode, desc, NULL); ret = afs_fs_fetch_data(server, vnode, offset, length, page,
NULL, &afs_sync_call);
} while (!afs_volume_release_fileserver(vnode->volume, server, ret)); } while (!afs_volume_release_fileserver(vnode, server, ret));
/* adjust the flags */ /* adjust the flags */
afs_vnode_finalise_status_update(vnode, server, ret); afs_vnode_finalise_status_update(vnode, server, ret);
@ -284,99 +378,3 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode,
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
/*
* 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;
}
/*
* 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;
}
#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;
}
#endif

View file

@ -1,84 +0,0 @@
/* 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 AFS_VNODE_H
#define AFS_VNODE_H
#include <linux/fs.h>
#include "server.h"
#include "kafstimod.h"
#include "cache.h"
struct afs_rxfs_fetch_descriptor;
extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
/*
* 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 *);
extern int afs_vnode_fetch_data(struct afs_vnode *,
struct afs_rxfs_fetch_descriptor *);
extern int afs_vnode_give_up_callback(struct afs_vnode *);
#endif /* AFS_VNODE_H */

View file

@ -1,6 +1,6 @@
/* AFS volume management /* AFS volume management
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -15,33 +15,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pagemap.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" #include "internal.h"
#ifdef __KDEBUG
static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" }; 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 * lookup a volume by name
@ -65,11 +41,12 @@ struct cachefs_index_def afs_volume_cache_index_def = {
* - Rule 3: If parent volume is R/W, then only mount R/W volume unless * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
* explicitly told otherwise * explicitly told otherwise
*/ */
int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath, struct afs_volume *afs_volume_lookup(const char *name, struct afs_cell *cell,
struct afs_volume **_volume) int rwpath)
{ {
struct afs_vlocation *vlocation = NULL; struct afs_vlocation *vlocation = NULL;
struct afs_volume *volume = NULL; struct afs_volume *volume = NULL;
struct afs_server *server = NULL;
afs_voltype_t type; afs_voltype_t type;
const char *cellname, *volname, *suffix; const char *cellname, *volname, *suffix;
char srvtmask; char srvtmask;
@ -79,7 +56,7 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) { if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
printk("kAFS: unparsable volume name\n"); printk("kAFS: unparsable volume name\n");
return -EINVAL; return ERR_PTR(-EINVAL);
} }
/* determine the type of volume we're looking for */ /* determine the type of volume we're looking for */
@ -128,8 +105,9 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
/* lookup the cell record */ /* lookup the cell record */
if (cellname || !cell) { if (cellname || !cell) {
ret = afs_cell_lookup(cellname, cellnamesz, &cell); cell = afs_cell_lookup(cellname, cellnamesz);
if (ret<0) { if (IS_ERR(cell)) {
ret = PTR_ERR(cell);
printk("kAFS: unable to lookup cell '%s'\n", printk("kAFS: unable to lookup cell '%s'\n",
cellname ?: ""); cellname ?: "");
goto error; goto error;
@ -139,9 +117,12 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
} }
/* lookup the volume location record */ /* lookup the volume location record */
ret = afs_vlocation_lookup(cell, volname, volnamesz, &vlocation); vlocation = afs_vlocation_lookup(cell, volname, volnamesz);
if (ret < 0) if (IS_ERR(vlocation)) {
ret = PTR_ERR(vlocation);
vlocation = NULL;
goto error; goto error;
}
/* make the final decision on the type we want */ /* make the final decision on the type we want */
ret = -ENOMEDIUM; ret = -ENOMEDIUM;
@ -192,13 +173,14 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
/* look up all the applicable server records */ /* look up all the applicable server records */
for (loop = 0; loop < 8; loop++) { for (loop = 0; loop < 8; loop++) {
if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) { if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
ret = afs_server_lookup( server = afs_lookup_server(
volume->cell, volume->cell, &vlocation->vldb.servers[loop]);
&vlocation->vldb.servers[loop], if (IS_ERR(server)) {
&volume->servers[volume->nservers]); ret = PTR_ERR(server);
if (ret < 0)
goto error_discard; goto error_discard;
}
volume->servers[volume->nservers] = server;
volume->nservers++; volume->nservers++;
} }
} }
@ -219,8 +201,11 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
success: success:
_debug("kAFS selected %s volume %08x", _debug("kAFS selected %s volume %08x",
afs_voltypes[volume->type], volume->vid); afs_voltypes[volume->type], volume->vid);
*_volume = volume; up_write(&cell->vl_sem);
ret = 0; afs_put_vlocation(vlocation);
afs_put_cell(cell);
_leave(" = %p", volume);
return volume;
/* clean up */ /* clean up */
error_up: error_up:
@ -228,9 +213,8 @@ error_up:
error: error:
afs_put_vlocation(vlocation); afs_put_vlocation(vlocation);
afs_put_cell(cell); afs_put_cell(cell);
_leave(" = %d", ret);
_leave(" = %d (%p)", ret, volume); return ERR_PTR(ret);
return ret;
error_discard: error_discard:
up_write(&cell->vl_sem); up_write(&cell->vl_sem);
@ -255,10 +239,9 @@ void afs_put_volume(struct afs_volume *volume)
_enter("%p", volume); _enter("%p", volume);
vlocation = volume->vlocation; ASSERTCMP(atomic_read(&volume->usage), >, 0);
/* sanity check */ vlocation = volume->vlocation;
BUG_ON(atomic_read(&volume->usage) <= 0);
/* to prevent a race, the decrement and the dequeue must be effectively /* to prevent a race, the decrement and the dequeue must be effectively
* atomic */ * atomic */
@ -292,14 +275,21 @@ void afs_put_volume(struct afs_volume *volume)
* pick a server to use to try accessing this volume * pick a server to use to try accessing this volume
* - returns with an elevated usage count on the server chosen * - returns with an elevated usage count on the server chosen
*/ */
int afs_volume_pick_fileserver(struct afs_volume *volume, struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
struct afs_server **_server)
{ {
struct afs_volume *volume = vnode->volume;
struct afs_server *server; struct afs_server *server;
int ret, state, loop; int ret, state, loop;
_enter("%s", volume->vlocation->vldb.name); _enter("%s", volume->vlocation->vldb.name);
/* stick with the server we're already using if we can */
if (vnode->server && vnode->server->fs_state == 0) {
afs_get_server(vnode->server);
_leave(" = %p [current]", vnode->server);
return vnode->server;
}
down_read(&volume->server_sem); down_read(&volume->server_sem);
/* handle the no-server case */ /* handle the no-server case */
@ -307,7 +297,7 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
ret = volume->rjservers ? -ENOMEDIUM : -ESTALE; ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
up_read(&volume->server_sem); up_read(&volume->server_sem);
_leave(" = %d [no servers]", ret); _leave(" = %d [no servers]", ret);
return ret; return ERR_PTR(ret);
} }
/* basically, just search the list for the first live server and use /* basically, just search the list for the first live server and use
@ -317,15 +307,16 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
server = volume->servers[loop]; server = volume->servers[loop];
state = server->fs_state; state = server->fs_state;
_debug("consider %d [%d]", loop, state);
switch (state) { switch (state) {
/* found an apparently healthy server */ /* found an apparently healthy server */
case 0: case 0:
afs_get_server(server); afs_get_server(server);
up_read(&volume->server_sem); up_read(&volume->server_sem);
*_server = server; _leave(" = %p (picked %08x)",
_leave(" = 0 (picked %08x)", server, ntohl(server->addr.s_addr));
ntohl(server->addr.s_addr)); return server;
return 0;
case -ENETUNREACH: case -ENETUNREACH:
if (ret == 0) if (ret == 0)
@ -361,7 +352,7 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
*/ */
up_read(&volume->server_sem); up_read(&volume->server_sem);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ERR_PTR(ret);
} }
/* /*
@ -370,10 +361,11 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
* - records result of using a particular server to access a volume * - records result of using a particular server to access a volume
* - return 0 to try again, 1 if okay or to issue error * - return 0 to try again, 1 if okay or to issue error
*/ */
int afs_volume_release_fileserver(struct afs_volume *volume, int afs_volume_release_fileserver(struct afs_vnode *vnode,
struct afs_server *server, struct afs_server *server,
int result) int result)
{ {
struct afs_volume *volume = vnode->volume;
unsigned loop; unsigned loop;
_enter("%s,%08x,%d", _enter("%s,%08x,%d",
@ -384,6 +376,7 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
/* success */ /* success */
case 0: case 0:
server->fs_act_jif = jiffies; server->fs_act_jif = jiffies;
server->fs_state = 0;
break; break;
/* the fileserver denied all knowledge of the volume */ /* the fileserver denied all knowledge of the volume */
@ -391,7 +384,7 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
server->fs_act_jif = jiffies; server->fs_act_jif = jiffies;
down_write(&volume->server_sem); down_write(&volume->server_sem);
/* first, find where the server is in the active list (if it /* firstly, find where the server is in the active list (if it
* is) */ * is) */
for (loop = 0; loop < volume->nservers; loop++) for (loop = 0; loop < volume->nservers; loop++)
if (volume->servers[loop] == server) if (volume->servers[loop] == server)
@ -429,6 +422,7 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
case -ENETUNREACH: case -ENETUNREACH:
case -EHOSTUNREACH: case -EHOSTUNREACH:
case -ECONNREFUSED: case -ECONNREFUSED:
case -ETIME:
case -ETIMEDOUT: case -ETIMEDOUT:
case -EREMOTEIO: case -EREMOTEIO:
/* mark the server as dead /* mark the server as dead
@ -464,40 +458,3 @@ try_next_server:
_leave(" [try next server]"); _leave(" [try next server]");
return 0; return 0;
} }
/*
* 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;
}
#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;
}
#endif

View file

@ -1,126 +0,0 @@
/* 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 AFS_VOLUME_H
#define AFS_VOLUME_H
#include "types.h"
#include "fsclient.h"
#include "kafstimod.h"
#include "kafsasyncd.h"
#include "cache.h"
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 *, const char *, unsigned,
struct afs_vlocation **);
#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0)
extern void afs_put_vlocation(struct afs_vlocation *);
extern void afs_vlocation_do_timeout(struct afs_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 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 *, struct afs_cell *, int,
struct afs_volume **);
#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
extern void afs_put_volume(struct afs_volume *);
extern int afs_volume_pick_fileserver(struct afs_volume *,
struct afs_server **);
extern int afs_volume_release_fileserver(struct afs_volume *,
struct afs_server *, int);
#endif /* AFS_VOLUME_H */