mirror of
https://github.com/a52xq-dev/kernel.git
synced 2026-10-19 06:08:12 +07:00
[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:
parent
651350d10f
commit
08e0e7c82e
39 changed files with 4011 additions and 5240 deletions
13
fs/Kconfig
13
fs/Kconfig
|
|
@ -2019,7 +2019,7 @@ config CODA_FS_OLD_API
|
|||
config AFS_FS
|
||||
tristate "Andrew File System support (AFS) (EXPERIMENTAL)"
|
||||
depends on INET && EXPERIMENTAL
|
||||
select RXRPC
|
||||
select AF_RXRPC
|
||||
help
|
||||
If you say Y here, you will get an experimental Andrew File System
|
||||
driver. It currently only supports unsecured read-only AFS access.
|
||||
|
|
@ -2028,6 +2028,17 @@ config AFS_FS
|
|||
|
||||
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
|
||||
tristate
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@ kafs-objs := \
|
|||
file.o \
|
||||
fsclient.o \
|
||||
inode.o \
|
||||
kafsasyncd.o \
|
||||
kafstimod.o \
|
||||
main.o \
|
||||
misc.o \
|
||||
mntpt.o \
|
||||
proc.o \
|
||||
rxrpc.o \
|
||||
server.o \
|
||||
super.o \
|
||||
vlclient.o \
|
||||
|
|
|
|||
105
fs/afs/afs.h
Normal file
105
fs/afs/afs.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* AFS common types
|
||||
*
|
||||
* Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef AFS_H
|
||||
#define AFS_H
|
||||
|
||||
#include <linux/in.h>
|
||||
|
||||
typedef unsigned afs_volid_t;
|
||||
typedef unsigned afs_vnodeid_t;
|
||||
typedef unsigned long long afs_dataversion_t;
|
||||
|
||||
typedef enum {
|
||||
AFSVL_RWVOL, /* read/write volume */
|
||||
AFSVL_ROVOL, /* read-only volume */
|
||||
AFSVL_BACKVOL, /* backup volume */
|
||||
} __attribute__((packed)) afs_voltype_t;
|
||||
|
||||
typedef enum {
|
||||
AFS_FTYPE_INVALID = 0,
|
||||
AFS_FTYPE_FILE = 1,
|
||||
AFS_FTYPE_DIR = 2,
|
||||
AFS_FTYPE_SYMLINK = 3,
|
||||
} afs_file_type_t;
|
||||
|
||||
/*
|
||||
* AFS file identifier
|
||||
*/
|
||||
struct afs_fid {
|
||||
afs_volid_t vid; /* volume ID */
|
||||
afs_vnodeid_t vnode; /* file index within volume */
|
||||
unsigned unique; /* unique ID number (file index version) */
|
||||
};
|
||||
|
||||
/*
|
||||
* AFS callback notification
|
||||
*/
|
||||
typedef enum {
|
||||
AFSCM_CB_UNTYPED = 0, /* no type set on CB break */
|
||||
AFSCM_CB_EXCLUSIVE = 1, /* CB exclusive to CM [not implemented] */
|
||||
AFSCM_CB_SHARED = 2, /* CB shared by other CM's */
|
||||
AFSCM_CB_DROPPED = 3, /* CB promise cancelled by file server */
|
||||
} afs_callback_type_t;
|
||||
|
||||
struct afs_callback {
|
||||
struct afs_fid fid; /* file identifier */
|
||||
unsigned version; /* callback version */
|
||||
unsigned expiry; /* time at which expires */
|
||||
afs_callback_type_t type; /* type of callback */
|
||||
};
|
||||
|
||||
#define AFSCBMAX 50 /* maximum callbacks transferred per bulk op */
|
||||
|
||||
/*
|
||||
* AFS volume information
|
||||
*/
|
||||
struct afs_volume_info {
|
||||
afs_volid_t vid; /* volume ID */
|
||||
afs_voltype_t type; /* type of this volume */
|
||||
afs_volid_t type_vids[5]; /* volume ID's for possible types for this vol */
|
||||
|
||||
/* list of fileservers serving this volume */
|
||||
size_t nservers; /* number of entries used in servers[] */
|
||||
struct {
|
||||
struct in_addr addr; /* fileserver address */
|
||||
} servers[8];
|
||||
};
|
||||
|
||||
/*
|
||||
* AFS file status information
|
||||
*/
|
||||
struct afs_file_status {
|
||||
unsigned if_version; /* interface version */
|
||||
#define AFS_FSTATUS_VERSION 1
|
||||
|
||||
afs_file_type_t type; /* file type */
|
||||
unsigned nlink; /* link count */
|
||||
size_t size; /* file size */
|
||||
afs_dataversion_t data_version; /* current data version */
|
||||
unsigned author; /* author ID */
|
||||
unsigned owner; /* owner ID */
|
||||
unsigned caller_access; /* access rights for authenticated caller */
|
||||
unsigned anon_access; /* access rights for unauthenticated caller */
|
||||
umode_t mode; /* UNIX mode */
|
||||
struct afs_fid parent; /* parent file ID */
|
||||
time_t mtime_client; /* last time client changed data */
|
||||
time_t mtime_server; /* last time server changed data */
|
||||
};
|
||||
|
||||
/*
|
||||
* AFS volume synchronisation information
|
||||
*/
|
||||
struct afs_volsync {
|
||||
time_t creation; /* volume creation time */
|
||||
};
|
||||
|
||||
#endif /* AFS_H */
|
||||
28
fs/afs/afs_cm.h
Normal file
28
fs/afs/afs_cm.h
Normal 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 */
|
||||
41
fs/afs/afs_fs.h
Normal file
41
fs/afs/afs_fs.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* AFS File Service 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_FS_H
|
||||
#define AFS_FS_H
|
||||
|
||||
#define AFS_FS_PORT 7000 /* AFS file server port */
|
||||
#define FS_SERVICE 1 /* AFS File Service ID */
|
||||
|
||||
enum AFS_FS_Operations {
|
||||
FSFETCHSTATUS = 132, /* AFS Fetch file status */
|
||||
FSFETCHDATA = 130, /* AFS Fetch file data */
|
||||
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 */
|
||||
VNOVNODE = 102, /* no such file/dir (vnode) */
|
||||
VNOVOL = 103, /* no such volume or volume unavailable */
|
||||
VVOLEXISTS = 104, /* volume name already exists */
|
||||
VNOSERVICE = 105, /* volume not currently in service */
|
||||
VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */
|
||||
VONLINE = 107, /* volume is already online */
|
||||
VDISKFULL = 108, /* disk partition is full */
|
||||
VOVERQUOTA = 109, /* volume's maximum quota exceeded */
|
||||
VBUSY = 110, /* volume is temporarily unavailable */
|
||||
VMOVED = 111, /* volume moved to new server - ask this FS where */
|
||||
};
|
||||
|
||||
#endif /* AFS_FS_H */
|
||||
84
fs/afs/afs_vl.h
Normal file
84
fs/afs/afs_vl.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* AFS Volume Location Service client interface
|
||||
*
|
||||
* Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef AFS_VL_H
|
||||
#define AFS_VL_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 {
|
||||
AFSVL_IDEXIST = 363520, /* Volume Id entry exists in vl database */
|
||||
AFSVL_IO = 363521, /* I/O related error */
|
||||
AFSVL_NAMEEXIST = 363522, /* Volume name entry exists in vl database */
|
||||
AFSVL_CREATEFAIL = 363523, /* Internal creation failure */
|
||||
AFSVL_NOENT = 363524, /* No such entry */
|
||||
AFSVL_EMPTY = 363525, /* Vl database is empty */
|
||||
AFSVL_ENTDELETED = 363526, /* Entry is deleted (soft delete) */
|
||||
AFSVL_BADNAME = 363527, /* Volume name is illegal */
|
||||
AFSVL_BADINDEX = 363528, /* Index is out of range */
|
||||
AFSVL_BADVOLTYPE = 363529, /* Bad volume type */
|
||||
AFSVL_BADSERVER = 363530, /* Illegal server number (out of range) */
|
||||
AFSVL_BADPARTITION = 363531, /* Bad partition number */
|
||||
AFSVL_REPSFULL = 363532, /* Run out of space for Replication sites */
|
||||
AFSVL_NOREPSERVER = 363533, /* No such Replication server site exists */
|
||||
AFSVL_DUPREPSERVER = 363534, /* Replication site already exists */
|
||||
AFSVL_RWNOTFOUND = 363535, /* Parent R/W entry not found */
|
||||
AFSVL_BADREFCOUNT = 363536, /* Illegal Reference Count number */
|
||||
AFSVL_SIZEEXCEEDED = 363537, /* Vl size for attributes exceeded */
|
||||
AFSVL_BADENTRY = 363538, /* Bad incoming vl entry */
|
||||
AFSVL_BADVOLIDBUMP = 363539, /* Illegal max volid increment */
|
||||
AFSVL_IDALREADYHASHED = 363540, /* RO/BACK id already hashed */
|
||||
AFSVL_ENTRYLOCKED = 363541, /* Vl entry is already locked */
|
||||
AFSVL_BADVOLOPER = 363542, /* Bad volume operation code */
|
||||
AFSVL_BADRELLOCKTYPE = 363543, /* Bad release lock type */
|
||||
AFSVL_RERELEASE = 363544, /* Status report: last release was aborted */
|
||||
AFSVL_BADSERVERFLAG = 363545, /* Invalid replication site server °ag */
|
||||
AFSVL_PERM = 363546, /* No permission access */
|
||||
AFSVL_NOMEM = 363547, /* malloc/realloc failed to alloc enough memory */
|
||||
};
|
||||
|
||||
/*
|
||||
* maps to "struct vldbentry" in vvl-spec.pdf
|
||||
*/
|
||||
struct afs_vldbentry {
|
||||
char name[65]; /* name of volume (with NUL char) */
|
||||
afs_voltype_t type; /* volume type */
|
||||
unsigned num_servers; /* num servers that hold instances of this vol */
|
||||
unsigned clone_id; /* cloning ID */
|
||||
|
||||
unsigned flags;
|
||||
#define AFS_VLF_RWEXISTS 0x1000 /* R/W volume exists */
|
||||
#define AFS_VLF_ROEXISTS 0x2000 /* R/O volume exists */
|
||||
#define AFS_VLF_BACKEXISTS 0x4000 /* backup volume exists */
|
||||
|
||||
afs_volid_t volume_ids[3]; /* volume IDs */
|
||||
|
||||
struct {
|
||||
struct in_addr addr; /* server address */
|
||||
unsigned partition; /* partition ID on this server */
|
||||
unsigned flags; /* server specific flags */
|
||||
#define AFS_VLSF_NEWREPSITE 0x0001 /* unused */
|
||||
#define AFS_VLSF_ROVOL 0x0002 /* this server holds a R/O instance of the volume */
|
||||
#define AFS_VLSF_RWVOL 0x0004 /* this server holds a R/W instance of the volume */
|
||||
#define AFS_VLSF_BACKVOL 0x0008 /* this server holds a backup instance of the volume */
|
||||
} servers[8];
|
||||
};
|
||||
|
||||
#endif /* AFS_VL_H */
|
||||
256
fs/afs/cache.c
Normal file
256
fs/afs/cache.c
Normal 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
|
||||
|
|
@ -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
|
||||
* GNU General Public License.
|
||||
|
|
@ -16,83 +16,182 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include "server.h"
|
||||
#include "vnode.h"
|
||||
#include <linux/circ_buf.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
|
||||
*/
|
||||
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);
|
||||
|
||||
list_add(&callbacks, &server->cb_promises);
|
||||
list_del_init(&server->cb_promises);
|
||||
|
||||
/* munch our way through the list, grabbing the inode, dropping all the
|
||||
* locks and regetting them in the right order
|
||||
*/
|
||||
while (!list_empty(&callbacks)) {
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
|
||||
vnode = list_entry(callbacks.next, struct afs_vnode, cb_link);
|
||||
list_del_init(&vnode->cb_link);
|
||||
|
||||
/* try and grab the inode - may fail */
|
||||
inode = igrab(AFS_VNODE_TO_I(vnode));
|
||||
if (inode) {
|
||||
int release = 0;
|
||||
|
||||
spin_unlock(&server->cb_lock);
|
||||
spin_lock(&vnode->lock);
|
||||
|
||||
if (vnode->cb_server == server) {
|
||||
vnode->cb_server = NULL;
|
||||
afs_kafstimod_del_timer(&vnode->cb_timeout);
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
list_del_init(&vnode->cb_hash_link);
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
release = 1;
|
||||
}
|
||||
|
||||
spin_unlock(&vnode->lock);
|
||||
|
||||
iput(inode);
|
||||
afs_put_server(server);
|
||||
|
||||
spin_lock(&server->cb_lock);
|
||||
}
|
||||
/* kill all the promises on record from this server */
|
||||
while (!RB_EMPTY_ROOT(&server->cb_promises)) {
|
||||
vnode = rb_entry(server->cb_promises.rb_node,
|
||||
struct afs_vnode, cb_promise);
|
||||
printk("\nUNPROMISE on %p\n", vnode);
|
||||
rb_erase(&vnode->cb_promise, &server->cb_promises);
|
||||
vnode->cb_promised = false;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
|
||||
struct afs_callback callbacks[])
|
||||
void afs_break_callbacks(struct afs_server *server, size_t count,
|
||||
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--) {
|
||||
struct afs_vnode *vnode = NULL;
|
||||
struct inode *inode = NULL;
|
||||
int valid = 0;
|
||||
|
||||
_debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
|
||||
callbacks->fid.vid,
|
||||
callbacks->fid.vnode,
|
||||
|
|
@ -101,66 +200,244 @@ int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
|
|||
callbacks->expiry,
|
||||
callbacks->type
|
||||
);
|
||||
|
||||
/* find the inode for this fid */
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
|
||||
list_for_each_entry(vnode,
|
||||
&afs_cb_hash(server, &callbacks->fid),
|
||||
cb_hash_link) {
|
||||
if (memcmp(&vnode->fid, &callbacks->fid,
|
||||
sizeof(struct afs_fid)) != 0)
|
||||
continue;
|
||||
|
||||
/* right vnode, but is it same server? */
|
||||
if (vnode->cb_server != server)
|
||||
break; /* no */
|
||||
|
||||
/* try and nail the inode down */
|
||||
inode = igrab(AFS_VNODE_TO_I(vnode));
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
|
||||
if (inode) {
|
||||
/* we've found the record for this vnode */
|
||||
spin_lock(&vnode->lock);
|
||||
if (vnode->cb_server == server) {
|
||||
/* the callback _is_ on the calling server */
|
||||
vnode->cb_server = NULL;
|
||||
valid = 1;
|
||||
|
||||
afs_kafstimod_del_timer(&vnode->cb_timeout);
|
||||
vnode->flags |= AFS_VNODE_CHANGED;
|
||||
|
||||
spin_lock(&server->cb_lock);
|
||||
list_del_init(&vnode->cb_link);
|
||||
spin_unlock(&server->cb_lock);
|
||||
|
||||
spin_lock(&afs_cb_hash_lock);
|
||||
list_del_init(&vnode->cb_hash_link);
|
||||
spin_unlock(&afs_cb_hash_lock);
|
||||
}
|
||||
spin_unlock(&vnode->lock);
|
||||
|
||||
if (valid) {
|
||||
invalidate_remote_inode(inode);
|
||||
afs_put_server(server);
|
||||
}
|
||||
iput(inode);
|
||||
}
|
||||
afs_break_one_callback(server, &callbacks->fid);
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
_leave("");
|
||||
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);
|
||||
return 0;
|
||||
struct afs_callback *cb;
|
||||
|
||||
_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);
|
||||
}
|
||||
|
|
|
|||
344
fs/afs/cell.c
344
fs/afs/cell.c
|
|
@ -11,15 +11,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <rxrpc/peer.h>
|
||||
#include <rxrpc/connection.h>
|
||||
#include "volume.h"
|
||||
#include "cell.h"
|
||||
#include "server.h"
|
||||
#include "transport.h"
|
||||
#include "vlclient.h"
|
||||
#include "kafstimod.h"
|
||||
#include "super.h"
|
||||
#include "internal.h"
|
||||
|
||||
DECLARE_RWSEM(afs_proc_cells_sem);
|
||||
|
|
@ -28,34 +19,21 @@ LIST_HEAD(afs_proc_cells);
|
|||
static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells);
|
||||
static DEFINE_RWLOCK(afs_cells_lock);
|
||||
static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
|
||||
static DECLARE_WAIT_QUEUE_HEAD(afs_cells_freeable_wq);
|
||||
static struct afs_cell *afs_cell_root;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
static cachefs_match_val_t afs_cell_cache_match(void *target,
|
||||
const void *entry);
|
||||
static void afs_cell_cache_update(void *source, void *entry);
|
||||
|
||||
struct cachefs_index_def afs_cache_cell_index_def = {
|
||||
.name = "cell_ix",
|
||||
.data_size = sizeof(struct afs_cache_cell),
|
||||
.keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
|
||||
.match = afs_cell_cache_match,
|
||||
.update = afs_cell_cache_update,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* create a cell record
|
||||
* - "name" is the name of the cell
|
||||
* - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
|
||||
*/
|
||||
int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
|
||||
struct afs_cell *afs_cell_create(const char *name, char *vllist)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
char *next;
|
||||
int ret;
|
||||
|
||||
_enter("%s", name);
|
||||
_enter("%s,%s", name, vllist);
|
||||
|
||||
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);
|
||||
if (!cell) {
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
down_write(&afs_cells_sem);
|
||||
|
||||
memset(cell, 0, sizeof(struct afs_cell));
|
||||
atomic_set(&cell->usage, 0);
|
||||
atomic_set(&cell->usage, 1);
|
||||
|
||||
INIT_LIST_HEAD(&cell->link);
|
||||
|
||||
rwlock_init(&cell->sv_lock);
|
||||
INIT_LIST_HEAD(&cell->sv_list);
|
||||
INIT_LIST_HEAD(&cell->sv_graveyard);
|
||||
spin_lock_init(&cell->sv_gylock);
|
||||
rwlock_init(&cell->servers_lock);
|
||||
INIT_LIST_HEAD(&cell->servers);
|
||||
|
||||
init_rwsem(&cell->vl_sem);
|
||||
INIT_LIST_HEAD(&cell->vl_list);
|
||||
INIT_LIST_HEAD(&cell->vl_graveyard);
|
||||
spin_lock_init(&cell->vl_gylock);
|
||||
spin_lock_init(&cell->vl_lock);
|
||||
|
||||
strcpy(cell->name,name);
|
||||
strcpy(cell->name, name);
|
||||
|
||||
/* fill in the VL server list from the rest of the string */
|
||||
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)
|
||||
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);
|
||||
if (ret < 0)
|
||||
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);
|
||||
list_add_tail(&cell->proc_link, &afs_proc_cells);
|
||||
up_write(&afs_proc_cells_sem);
|
||||
|
||||
*_cell = cell;
|
||||
up_write(&afs_cells_sem);
|
||||
|
||||
_leave(" = 0 (%p)", cell);
|
||||
return 0;
|
||||
_leave(" = %p", cell);
|
||||
return cell;
|
||||
|
||||
badaddr:
|
||||
printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist);
|
||||
printk(KERN_ERR "kAFS: bad VL server IP address\n");
|
||||
error:
|
||||
up_write(&afs_cells_sem);
|
||||
kfree(cell);
|
||||
_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)
|
||||
{
|
||||
struct afs_cell *old_root, *new_root;
|
||||
char *cp;
|
||||
int ret;
|
||||
|
||||
_enter("");
|
||||
|
||||
|
|
@ -160,79 +134,60 @@ int afs_cell_init(char *rootcell)
|
|||
/* module is loaded with no parameters, or built statically.
|
||||
* - in the future we might initialize cell DB here.
|
||||
*/
|
||||
_leave(" = 0 (but no root)");
|
||||
_leave(" = 0 [no root]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cp = strchr(rootcell, ':');
|
||||
if (!cp) {
|
||||
printk(KERN_ERR "kAFS: no VL server IP addresses specified\n");
|
||||
_leave(" = %d (no colon)", -EINVAL);
|
||||
_leave(" = -EINVAL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* allocate a cell record for the root cell */
|
||||
*cp++ = 0;
|
||||
ret = afs_cell_create(rootcell, cp, &new_root);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
new_root = afs_cell_create(rootcell, cp);
|
||||
if (IS_ERR(new_root)) {
|
||||
_leave(" = %ld", PTR_ERR(new_root));
|
||||
return PTR_ERR(new_root);
|
||||
}
|
||||
|
||||
/* as afs_put_cell() takes locks by itself, we have to do
|
||||
* a little gymnastics to be race-free.
|
||||
*/
|
||||
afs_get_cell(new_root);
|
||||
|
||||
/* install the new cell */
|
||||
write_lock(&afs_cells_lock);
|
||||
while (afs_cell_root) {
|
||||
old_root = afs_cell_root;
|
||||
afs_cell_root = NULL;
|
||||
write_unlock(&afs_cells_lock);
|
||||
afs_put_cell(old_root);
|
||||
write_lock(&afs_cells_lock);
|
||||
}
|
||||
old_root = afs_cell_root;
|
||||
afs_cell_root = new_root;
|
||||
write_unlock(&afs_cells_lock);
|
||||
afs_put_cell(old_root);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
int ret;
|
||||
|
||||
_enter("\"%*.*s\",", namesz, namesz, name ? name : "");
|
||||
|
||||
*_cell = NULL;
|
||||
down_read(&afs_cells_sem);
|
||||
read_lock(&afs_cells_lock);
|
||||
|
||||
if (name) {
|
||||
/* if the cell was named, look for it in the cell record list */
|
||||
ret = -ENOENT;
|
||||
cell = NULL;
|
||||
read_lock(&afs_cells_lock);
|
||||
|
||||
list_for_each_entry(cell, &afs_cells, link) {
|
||||
if (strncmp(cell->name, name, namesz) == 0) {
|
||||
afs_get_cell(cell);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
cell = NULL;
|
||||
cell = ERR_PTR(-ENOENT);
|
||||
found:
|
||||
|
||||
read_unlock(&afs_cells_lock);
|
||||
|
||||
if (cell)
|
||||
ret = 0;
|
||||
;
|
||||
} else {
|
||||
read_lock(&afs_cells_lock);
|
||||
|
||||
cell = afs_cell_root;
|
||||
if (!cell) {
|
||||
/* this should not happen unless user tries to mount
|
||||
|
|
@ -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
|
||||
* for other reasons.
|
||||
*/
|
||||
ret = -EDESTADDRREQ;
|
||||
cell = ERR_PTR(-EDESTADDRREQ);
|
||||
} else {
|
||||
afs_get_cell(cell);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
read_unlock(&afs_cells_lock);
|
||||
}
|
||||
|
||||
*_cell = cell;
|
||||
_leave(" = %d (%p)", ret, cell);
|
||||
return ret;
|
||||
read_unlock(&afs_cells_lock);
|
||||
up_read(&afs_cells_sem);
|
||||
_leave(" = %p", cell);
|
||||
return cell;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
cell = *_cell;
|
||||
if (cell && !list_empty(&cell->link))
|
||||
afs_get_cell(cell);
|
||||
else
|
||||
cell = NULL;
|
||||
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
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);
|
||||
|
||||
/* sanity check */
|
||||
BUG_ON(atomic_read(&cell->usage) <= 0);
|
||||
ASSERTCMP(atomic_read(&cell->usage), >, 0);
|
||||
|
||||
/* to prevent a race, the decrement and the dequeue must be effectively
|
||||
* atomic */
|
||||
|
|
@ -298,35 +247,49 @@ void afs_put_cell(struct afs_cell *cell)
|
|||
return;
|
||||
}
|
||||
|
||||
ASSERT(list_empty(&cell->servers));
|
||||
ASSERT(list_empty(&cell->vl_list));
|
||||
|
||||
write_unlock(&afs_cells_lock);
|
||||
|
||||
BUG_ON(!list_empty(&cell->sv_list));
|
||||
BUG_ON(!list_empty(&cell->sv_graveyard));
|
||||
BUG_ON(!list_empty(&cell->vl_list));
|
||||
BUG_ON(!list_empty(&cell->vl_graveyard));
|
||||
wake_up(&afs_cells_freeable_wq);
|
||||
|
||||
_leave(" [unused]");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
|
||||
|
||||
/* to prevent a race, the decrement and the dequeue must be effectively
|
||||
* atomic */
|
||||
write_lock(&afs_cells_lock);
|
||||
ASSERTCMP(atomic_read(&cell->usage), >=, 0);
|
||||
ASSERT(list_empty(&cell->link));
|
||||
|
||||
/* sanity check */
|
||||
BUG_ON(atomic_read(&cell->usage) != 0);
|
||||
/* wait for everyone to stop using the cell */
|
||||
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);
|
||||
|
||||
|
|
@ -338,101 +301,25 @@ static void afs_cell_destroy(struct afs_cell *cell)
|
|||
cachefs_relinquish_cookie(cell->cache, 0);
|
||||
#endif
|
||||
|
||||
up_write(&afs_cells_sem);
|
||||
|
||||
BUG_ON(!list_empty(&cell->sv_list));
|
||||
BUG_ON(!list_empty(&cell->sv_graveyard));
|
||||
BUG_ON(!list_empty(&cell->vl_list));
|
||||
BUG_ON(!list_empty(&cell->vl_graveyard));
|
||||
|
||||
/* finish cleaning up the cell */
|
||||
kfree(cell);
|
||||
|
||||
_leave(" [destroyed]");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* - the timeout daemon is stopped before calling this
|
||||
*/
|
||||
void afs_cell_purge(void)
|
||||
{
|
||||
struct afs_vlocation *vlocation;
|
||||
struct afs_cell *cell;
|
||||
|
||||
_enter("");
|
||||
|
||||
afs_put_cell(afs_cell_root);
|
||||
|
||||
down_write(&afs_cells_sem);
|
||||
|
||||
while (!list_empty(&afs_cells)) {
|
||||
cell = NULL;
|
||||
|
||||
|
|
@ -451,102 +338,11 @@ void afs_cell_purge(void)
|
|||
_debug("PURGING CELL %s (%d)",
|
||||
cell->name, atomic_read(&cell->usage));
|
||||
|
||||
BUG_ON(!list_empty(&cell->sv_list));
|
||||
BUG_ON(!list_empty(&cell->vl_list));
|
||||
|
||||
/* purge the cell's VL graveyard list */
|
||||
_debug(" - clearing VL graveyard");
|
||||
|
||||
spin_lock(&cell->vl_gylock);
|
||||
|
||||
while (!list_empty(&cell->vl_graveyard)) {
|
||||
vlocation = list_entry(cell->vl_graveyard.next,
|
||||
struct afs_vlocation,
|
||||
link);
|
||||
list_del_init(&vlocation->link);
|
||||
|
||||
afs_kafstimod_del_timer(&vlocation->timeout);
|
||||
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
afs_vlocation_do_timeout(vlocation);
|
||||
/* TODO: race if move to use krxtimod instead
|
||||
* of kafstimod */
|
||||
|
||||
spin_lock(&cell->vl_gylock);
|
||||
}
|
||||
|
||||
spin_unlock(&cell->vl_gylock);
|
||||
|
||||
/* purge the cell's server graveyard list */
|
||||
_debug(" - clearing server graveyard");
|
||||
|
||||
spin_lock(&cell->sv_gylock);
|
||||
|
||||
while (!list_empty(&cell->sv_graveyard)) {
|
||||
struct afs_server *server;
|
||||
|
||||
server = list_entry(cell->sv_graveyard.next,
|
||||
struct afs_server, link);
|
||||
list_del_init(&server->link);
|
||||
|
||||
afs_kafstimod_del_timer(&server->timeout);
|
||||
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
|
||||
afs_server_do_timeout(server);
|
||||
|
||||
spin_lock(&cell->sv_gylock);
|
||||
}
|
||||
|
||||
spin_unlock(&cell->sv_gylock);
|
||||
|
||||
/* now the cell should be left with no references */
|
||||
afs_cell_destroy(cell);
|
||||
}
|
||||
}
|
||||
|
||||
up_write(&afs_cells_sem);
|
||||
_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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -12,623 +12,316 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/completion.h>
|
||||
#include "server.h"
|
||||
#include "cell.h"
|
||||
#include "transport.h"
|
||||
#include <rxrpc/rxrpc.h>
|
||||
#include <rxrpc/transport.h>
|
||||
#include <rxrpc/connection.h>
|
||||
#include <rxrpc/call.h>
|
||||
#include "cmservice.h"
|
||||
#include <linux/ip.h>
|
||||
#include "internal.h"
|
||||
#include "afs_cm.h"
|
||||
|
||||
static unsigned afscm_usage; /* AFS cache manager usage count */
|
||||
static struct rw_semaphore afscm_sem; /* AFS cache manager start/stop semaphore */
|
||||
struct workqueue_struct *afs_cm_workqueue;
|
||||
|
||||
static int afscm_new_call(struct rxrpc_call *call);
|
||||
static void afscm_attention(struct rxrpc_call *call);
|
||||
static void afscm_error(struct rxrpc_call *call);
|
||||
static void afscm_aemap(struct rxrpc_call *call);
|
||||
static int afs_deliver_cb_init_call_back_state(struct afs_call *,
|
||||
struct sk_buff *, bool);
|
||||
static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool);
|
||||
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);
|
||||
static void _SRXAFSCM_Probe(struct rxrpc_call *call);
|
||||
|
||||
typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call);
|
||||
|
||||
static const struct rxrpc_operation AFSCM_ops[] = {
|
||||
{
|
||||
.id = 204,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "CallBack",
|
||||
.user = _SRXAFSCM_CallBack,
|
||||
},
|
||||
{
|
||||
.id = 205,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "InitCallBackState",
|
||||
.user = _SRXAFSCM_InitCallBackState,
|
||||
},
|
||||
{
|
||||
.id = 206,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "Probe",
|
||||
.user = _SRXAFSCM_Probe,
|
||||
},
|
||||
#if 0
|
||||
{
|
||||
.id = 207,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetLock",
|
||||
.user = _SRXAFSCM_GetLock,
|
||||
},
|
||||
{
|
||||
.id = 208,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetCE",
|
||||
.user = _SRXAFSCM_GetCE,
|
||||
},
|
||||
{
|
||||
.id = 209,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetXStatsVersion",
|
||||
.user = _SRXAFSCM_GetXStatsVersion,
|
||||
},
|
||||
{
|
||||
.id = 210,
|
||||
.asize = RXRPC_APP_MARK_EOF,
|
||||
.name = "GetXStats",
|
||||
.user = _SRXAFSCM_GetXStats,
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* CB.CallBack operation type
|
||||
*/
|
||||
static const struct afs_call_type afs_SRXCBCallBack = {
|
||||
.deliver = afs_deliver_cb_callback,
|
||||
.abort_to_error = afs_abort_to_error,
|
||||
.destructor = afs_cm_destructor,
|
||||
};
|
||||
|
||||
static struct rxrpc_service AFSCM_service = {
|
||||
.name = "AFS/CM",
|
||||
.owner = THIS_MODULE,
|
||||
.link = LIST_HEAD_INIT(AFSCM_service.link),
|
||||
.new_call = afscm_new_call,
|
||||
.service_id = 1,
|
||||
.attn_func = afscm_attention,
|
||||
.error_func = afscm_error,
|
||||
.aemap_func = afscm_aemap,
|
||||
.ops_begin = &AFSCM_ops[0],
|
||||
.ops_end = &AFSCM_ops[ARRAY_SIZE(AFSCM_ops)],
|
||||
/*
|
||||
* CB.InitCallBackState operation type
|
||||
*/
|
||||
static const struct afs_call_type afs_SRXCBInitCallBackState = {
|
||||
.deliver = afs_deliver_cb_init_call_back_state,
|
||||
.abort_to_error = afs_abort_to_error,
|
||||
.destructor = afs_cm_destructor,
|
||||
};
|
||||
|
||||
static DECLARE_COMPLETION(kafscmd_alive);
|
||||
static DECLARE_COMPLETION(kafscmd_dead);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq);
|
||||
static LIST_HEAD(kafscmd_attention_list);
|
||||
static LIST_HEAD(afscm_calls);
|
||||
static DEFINE_SPINLOCK(afscm_calls_lock);
|
||||
static DEFINE_SPINLOCK(kafscmd_attention_lock);
|
||||
static int kafscmd_die;
|
||||
/*
|
||||
* CB.Probe operation type
|
||||
*/
|
||||
static const struct afs_call_type afs_SRXCBProbe = {
|
||||
.deliver = afs_deliver_cb_probe,
|
||||
.abort_to_error = afs_abort_to_error,
|
||||
.destructor = afs_cm_destructor,
|
||||
};
|
||||
|
||||
/*
|
||||
* 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;
|
||||
_SRXAFSCM_xxxx_t func;
|
||||
int die;
|
||||
_enter("{CB.OP %u}", operation_id);
|
||||
|
||||
printk(KERN_INFO "kAFS: Started kafscmd %d\n", current->pid);
|
||||
|
||||
daemonize("kafscmd");
|
||||
|
||||
complete(&kafscmd_alive);
|
||||
|
||||
/* loop around looking for things to attend to */
|
||||
do {
|
||||
if (list_empty(&kafscmd_attention_list)) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&kafscmd_sleepq, &myself);
|
||||
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (!list_empty(&kafscmd_attention_list) ||
|
||||
signal_pending(current) ||
|
||||
kafscmd_die)
|
||||
break;
|
||||
|
||||
schedule();
|
||||
}
|
||||
|
||||
remove_wait_queue(&kafscmd_sleepq, &myself);
|
||||
set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
die = kafscmd_die;
|
||||
|
||||
/* dequeue the next call requiring attention */
|
||||
call = NULL;
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
|
||||
if (!list_empty(&kafscmd_attention_list)) {
|
||||
call = list_entry(kafscmd_attention_list.next,
|
||||
struct rxrpc_call,
|
||||
app_attn_link);
|
||||
list_del_init(&call->app_attn_link);
|
||||
die = 0;
|
||||
}
|
||||
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
if (call) {
|
||||
/* act upon it */
|
||||
_debug("@@@ Begin Attend Call %p", call);
|
||||
|
||||
func = call->app_user;
|
||||
if (func)
|
||||
func(call);
|
||||
|
||||
rxrpc_put_call(call);
|
||||
|
||||
_debug("@@@ End Attend Call %p", call);
|
||||
}
|
||||
|
||||
} while(!die);
|
||||
|
||||
/* and that's all */
|
||||
complete_and_exit(&kafscmd_dead, 0);
|
||||
switch (operation_id) {
|
||||
case CBCallBack:
|
||||
call->type = &afs_SRXCBCallBack;
|
||||
return true;
|
||||
case CBInitCallBackState:
|
||||
call->type = &afs_SRXCBInitCallBackState;
|
||||
return true;
|
||||
case CBProbe:
|
||||
call->type = &afs_SRXCBProbe;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle a call coming in to the cache manager
|
||||
* - if I want to keep the call, I must increment its usage count
|
||||
* - the return value will be negated and passed back in an abort packet if
|
||||
* non-zero
|
||||
* - serialised by virtue of there only being one krxiod
|
||||
* clean up a cache manager call
|
||||
*/
|
||||
static int afscm_new_call(struct rxrpc_call *call)
|
||||
static void afs_cm_destructor(struct afs_call *call)
|
||||
{
|
||||
_enter("%p{cid=%u u=%d}",
|
||||
call, ntohl(call->call_id), atomic_read(&call->usage));
|
||||
_enter("");
|
||||
|
||||
rxrpc_get_call(call);
|
||||
|
||||
/* add to my current call list */
|
||||
spin_lock(&afscm_calls_lock);
|
||||
list_add(&call->app_link,&afscm_calls);
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
afs_put_server(call->server);
|
||||
call->server = NULL;
|
||||
kfree(call->buffer);
|
||||
call->buffer = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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}",
|
||||
call, ntohl(call->call_id), atomic_read(&call->usage));
|
||||
struct afs_call *call = container_of(work, struct afs_call, work);
|
||||
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
_enter("");
|
||||
|
||||
if (list_empty(&call->app_attn_link)) {
|
||||
list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
|
||||
rxrpc_get_call(call);
|
||||
}
|
||||
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
wake_up(&kafscmd_sleepq);
|
||||
|
||||
_leave(" {u=%d}", atomic_read(&call->usage));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
/* be sure to send the reply *before* attempting to spam the AFS server
|
||||
* with FSFetchStatus requests on the vnodes with broken callbacks lest
|
||||
* 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 */
|
||||
afs_send_empty_reply(call);
|
||||
|
||||
afs_break_callbacks(call->server, call->count, call->request);
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* map afs abort codes to/from Linux error codes
|
||||
* - called with call->lock held
|
||||
* deliver request data to a CB.CallBack call
|
||||
*/
|
||||
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) {
|
||||
case RXRPC_ESTATE_LOCAL_ABORT:
|
||||
call->app_abort_code = -call->app_errno;
|
||||
break;
|
||||
case RXRPC_ESTATE_PEER_ABORT:
|
||||
call->app_errno = -ECONNABORTED;
|
||||
break;
|
||||
default:
|
||||
struct afs_callback *cb;
|
||||
struct afs_server *server;
|
||||
struct in_addr addr;
|
||||
__be32 *bp;
|
||||
u32 tmp;
|
||||
int ret, loop;
|
||||
|
||||
_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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* start the cache manager service if not already started
|
||||
*/
|
||||
int afscm_start(void)
|
||||
{
|
||||
int ret;
|
||||
if (!last)
|
||||
return 0;
|
||||
|
||||
down_write(&afscm_sem);
|
||||
if (!afscm_usage) {
|
||||
ret = kernel_thread(kafscmd, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
call->state = AFS_CALL_REPLYING;
|
||||
|
||||
wait_for_completion(&kafscmd_alive);
|
||||
|
||||
ret = rxrpc_add_service(afs_transport, &AFSCM_service);
|
||||
if (ret < 0)
|
||||
goto kill;
|
||||
|
||||
afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
|
||||
afs_mntpt_expiry_timeout * HZ);
|
||||
}
|
||||
|
||||
afscm_usage++;
|
||||
up_write(&afscm_sem);
|
||||
/* we'll need the file server record as that tells us which set of
|
||||
* vnodes to operate upon */
|
||||
memcpy(&addr, &ip_hdr(skb)->saddr, 4);
|
||||
server = afs_find_server(&addr);
|
||||
if (!server)
|
||||
return -ENOTCONN;
|
||||
call->server = server;
|
||||
|
||||
INIT_WORK(&call->work, SRXAFSCB_CallBack);
|
||||
schedule_work(&call->work);
|
||||
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);
|
||||
afscm_usage--;
|
||||
|
||||
if (afscm_usage == 0) {
|
||||
/* don't want more incoming calls */
|
||||
rxrpc_del_service(afs_transport, &AFSCM_service);
|
||||
|
||||
/* abort any calls I've still got open (the afscm_error() will
|
||||
* dequeue them) */
|
||||
spin_lock(&afscm_calls_lock);
|
||||
while (!list_empty(&afscm_calls)) {
|
||||
call = list_entry(afscm_calls.next,
|
||||
struct rxrpc_call,
|
||||
app_link);
|
||||
|
||||
list_del_init(&call->app_link);
|
||||
rxrpc_get_call(call);
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and
|
||||
* put */
|
||||
|
||||
_debug("nuking active call %08x.%d",
|
||||
ntohl(call->conn->conn_id),
|
||||
ntohl(call->call_id));
|
||||
rxrpc_put_call(call);
|
||||
rxrpc_put_call(call);
|
||||
|
||||
spin_lock(&afscm_calls_lock);
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
/* get rid of my daemon */
|
||||
kafscmd_die = 1;
|
||||
wake_up(&kafscmd_sleepq);
|
||||
wait_for_completion(&kafscmd_dead);
|
||||
|
||||
/* dispose of any calls waiting for attention */
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
while (!list_empty(&kafscmd_attention_list)) {
|
||||
call = list_entry(kafscmd_attention_list.next,
|
||||
struct rxrpc_call,
|
||||
app_attn_link);
|
||||
|
||||
list_del_init(&call->app_attn_link);
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
rxrpc_put_call(call);
|
||||
|
||||
spin_lock(&kafscmd_attention_lock);
|
||||
}
|
||||
spin_unlock(&kafscmd_attention_lock);
|
||||
|
||||
afs_kafstimod_del_timer(&afs_mntpt_expiry_timer);
|
||||
}
|
||||
|
||||
up_write(&afscm_sem);
|
||||
afs_init_callback_state(call->server);
|
||||
afs_send_empty_reply(call);
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
size_t count, qty, tmp;
|
||||
int ret = 0, removed;
|
||||
struct in_addr addr;
|
||||
|
||||
_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) {
|
||||
/* we've received the last packet
|
||||
* - drain all the data from the call and send the reply
|
||||
*/
|
||||
case RXRPC_CSTATE_SRVR_GOT_ARGS:
|
||||
ret = -EBADMSG;
|
||||
qty = call->app_ready_qty;
|
||||
if (qty < 8 || qty > 50 * (6 * 4) + 8)
|
||||
break;
|
||||
/* no unmarshalling required */
|
||||
call->state = AFS_CALL_REPLYING;
|
||||
|
||||
{
|
||||
struct afs_callback *cb, *pcb;
|
||||
int loop;
|
||||
__be32 *fp, *bp;
|
||||
/* we'll need the file server record as that tells us which set of
|
||||
* vnodes to operate upon */
|
||||
memcpy(&addr, &ip_hdr(skb)->saddr, 4);
|
||||
server = afs_find_server(&addr);
|
||||
if (!server)
|
||||
return -ENOTCONN;
|
||||
call->server = server;
|
||||
|
||||
fp = rxrpc_call_alloc_scratch(call, qty);
|
||||
|
||||
/* drag the entire argument block out to the scratch
|
||||
* space */
|
||||
ret = rxrpc_call_read_data(call, fp, qty, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/* and unmarshall the parameter block */
|
||||
ret = -EBADMSG;
|
||||
count = ntohl(*fp++);
|
||||
if (count>AFSCBMAX ||
|
||||
(count * (3 * 4) + 8 != qty &&
|
||||
count * (6 * 4) + 8 != qty))
|
||||
break;
|
||||
|
||||
bp = fp + count*3;
|
||||
tmp = ntohl(*bp++);
|
||||
if (tmp > 0 && tmp != count)
|
||||
break;
|
||||
if (tmp == 0)
|
||||
bp = NULL;
|
||||
|
||||
pcb = cb = rxrpc_call_alloc_scratch_s(
|
||||
call, struct afs_callback);
|
||||
|
||||
for (loop = count - 1; loop >= 0; loop--) {
|
||||
pcb->fid.vid = ntohl(*fp++);
|
||||
pcb->fid.vnode = ntohl(*fp++);
|
||||
pcb->fid.unique = ntohl(*fp++);
|
||||
if (bp) {
|
||||
pcb->version = ntohl(*bp++);
|
||||
pcb->expiry = ntohl(*bp++);
|
||||
pcb->type = ntohl(*bp++);
|
||||
} else {
|
||||
pcb->version = 0;
|
||||
pcb->expiry = 0;
|
||||
pcb->type = AFSCM_CB_UNTYPED;
|
||||
}
|
||||
pcb++;
|
||||
}
|
||||
|
||||
/* invoke the actual service routine */
|
||||
ret = SRXAFSCM_CallBack(server, count, cb);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* send the reply */
|
||||
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
|
||||
GFP_KERNEL, 0, &count);
|
||||
if (ret < 0)
|
||||
break;
|
||||
break;
|
||||
|
||||
/* operation complete */
|
||||
case RXRPC_CSTATE_COMPLETE:
|
||||
call->app_user = NULL;
|
||||
removed = 0;
|
||||
spin_lock(&afscm_calls_lock);
|
||||
if (!list_empty(&call->app_link)) {
|
||||
list_del_init(&call->app_link);
|
||||
removed = 1;
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
if (removed)
|
||||
rxrpc_put_call(call);
|
||||
break;
|
||||
|
||||
/* operation terminated on error */
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
call->app_user = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
rxrpc_call_abort(call, ret);
|
||||
|
||||
afs_put_server(server);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
INIT_WORK(&call->work, SRXAFSCB_InitCallBackState);
|
||||
schedule_work(&call->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
size_t count;
|
||||
int ret = 0, removed;
|
||||
struct afs_call *call = container_of(work, struct afs_call, work);
|
||||
|
||||
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
|
||||
|
||||
server = afs_server_get_from_peer(call->conn->peer);
|
||||
|
||||
switch (call->app_call_state) {
|
||||
/* we've received the last packet - drain all the data from the
|
||||
* call */
|
||||
case RXRPC_CSTATE_SRVR_GOT_ARGS:
|
||||
/* shouldn't be any args */
|
||||
ret = -EBADMSG;
|
||||
break;
|
||||
|
||||
/* send the reply when asked for it */
|
||||
case RXRPC_CSTATE_SRVR_SND_REPLY:
|
||||
/* invoke the actual service routine */
|
||||
ret = SRXAFSCM_InitCallBackState(server);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
|
||||
GFP_KERNEL, 0, &count);
|
||||
if (ret < 0)
|
||||
break;
|
||||
break;
|
||||
|
||||
/* operation complete */
|
||||
case RXRPC_CSTATE_COMPLETE:
|
||||
call->app_user = NULL;
|
||||
removed = 0;
|
||||
spin_lock(&afscm_calls_lock);
|
||||
if (!list_empty(&call->app_link)) {
|
||||
list_del_init(&call->app_link);
|
||||
removed = 1;
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
if (removed)
|
||||
rxrpc_put_call(call);
|
||||
break;
|
||||
|
||||
/* operation terminated on error */
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
call->app_user = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
rxrpc_call_abort(call, ret);
|
||||
|
||||
afs_put_server(server);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
_enter("");
|
||||
afs_send_empty_reply(call);
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
size_t count;
|
||||
int ret = 0, removed;
|
||||
_enter(",{%u},%d", skb->len, last);
|
||||
|
||||
_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) {
|
||||
/* we've received the last packet - drain all the data from the
|
||||
* call */
|
||||
case RXRPC_CSTATE_SRVR_GOT_ARGS:
|
||||
/* shouldn't be any args */
|
||||
ret = -EBADMSG;
|
||||
break;
|
||||
|
||||
/* send the reply when asked for it */
|
||||
case RXRPC_CSTATE_SRVR_SND_REPLY:
|
||||
/* invoke the actual service routine */
|
||||
ret = SRXAFSCM_Probe(server);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
|
||||
GFP_KERNEL, 0, &count);
|
||||
if (ret < 0)
|
||||
break;
|
||||
break;
|
||||
|
||||
/* operation complete */
|
||||
case RXRPC_CSTATE_COMPLETE:
|
||||
call->app_user = NULL;
|
||||
removed = 0;
|
||||
spin_lock(&afscm_calls_lock);
|
||||
if (!list_empty(&call->app_link)) {
|
||||
list_del_init(&call->app_link);
|
||||
removed = 1;
|
||||
}
|
||||
spin_unlock(&afscm_calls_lock);
|
||||
|
||||
if (removed)
|
||||
rxrpc_put_call(call);
|
||||
break;
|
||||
|
||||
/* operation terminated on error */
|
||||
case RXRPC_CSTATE_ERROR:
|
||||
call->app_user = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
rxrpc_call_abort(call, ret);
|
||||
|
||||
afs_put_server(server);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
INIT_WORK(&call->work, SRXAFSCB_Probe);
|
||||
schedule_work(&call->work);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
288
fs/afs/dir.c
288
fs/afs/dir.c
|
|
@ -15,11 +15,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include "vnode.h"
|
||||
#include "volume.h"
|
||||
#include <rxrpc/call.h>
|
||||
#include "super.h"
|
||||
#include "internal.h"
|
||||
|
||||
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
|
@ -127,9 +122,10 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page)
|
|||
if (qty == 0)
|
||||
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",
|
||||
__FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages));
|
||||
__FUNCTION__, dir->i_ino, qty,
|
||||
ntohs(dbuf->blocks[0].pagehdr.npages));
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -194,6 +190,7 @@ static struct page *afs_dir_get_page(struct inode *dir, unsigned long index)
|
|||
|
||||
fail:
|
||||
afs_dir_put_page(page);
|
||||
_leave(" = -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_dirent) != 32);
|
||||
|
||||
if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED)
|
||||
if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
|
||||
return -ENOENT;
|
||||
|
||||
_leave(" = 0");
|
||||
|
|
@ -242,7 +239,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
|
|||
/* skip entries marked unused in the bitmap */
|
||||
if (!(block->pagehdr.bitmap[offset / 8] &
|
||||
(1 << (offset % 8)))) {
|
||||
_debug("ENT[%Zu.%u]: unused\n",
|
||||
_debug("ENT[%Zu.%u]: unused",
|
||||
blkoff / sizeof(union afs_dir_block), offset);
|
||||
if (offset >= curr)
|
||||
*fpos = blkoff +
|
||||
|
|
@ -256,7 +253,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
|
|||
sizeof(*block) -
|
||||
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,
|
||||
(offset < curr ? "skip" : "fill"),
|
||||
nlen, dire->u.name);
|
||||
|
|
@ -266,7 +263,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
|
|||
if (next >= AFS_DIRENT_PER_BLOCK) {
|
||||
_debug("ENT[%Zu.%u]:"
|
||||
" %u travelled beyond end dir block"
|
||||
" (len %u/%Zu)\n",
|
||||
" (len %u/%Zu)",
|
||||
blkoff / sizeof(union afs_dir_block),
|
||||
offset, next, tmp, nlen);
|
||||
return -EIO;
|
||||
|
|
@ -274,13 +271,13 @@ static int afs_dir_iterate_block(unsigned *fpos,
|
|||
if (!(block->pagehdr.bitmap[next / 8] &
|
||||
(1 << (next % 8)))) {
|
||||
_debug("ENT[%Zu.%u]:"
|
||||
" %u unmarked extension (len %u/%Zu)\n",
|
||||
" %u unmarked extension (len %u/%Zu)",
|
||||
blkoff / sizeof(union afs_dir_block),
|
||||
offset, next, tmp, nlen);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
_debug("ENT[%Zu.%u]: ext %u/%Zu\n",
|
||||
_debug("ENT[%Zu.%u]: ext %u/%Zu",
|
||||
blkoff / sizeof(union afs_dir_block),
|
||||
next, tmp, nlen);
|
||||
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,
|
||||
filldir_t filldir)
|
||||
{
|
||||
union afs_dir_block *dblock;
|
||||
union afs_dir_block *dblock;
|
||||
struct afs_dir_page *dbuf;
|
||||
struct page *page;
|
||||
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);
|
||||
|
||||
if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
|
||||
if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
|
||||
_leave(" = -ESTALE");
|
||||
return -ESTALE;
|
||||
}
|
||||
|
|
@ -381,10 +378,12 @@ static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
|
|||
unsigned fpos;
|
||||
int ret;
|
||||
|
||||
_enter("{%Ld,{%lu}}", file->f_pos, file->f_path.dentry->d_inode->i_ino);
|
||||
_enter("{%Ld,{%lu}}",
|
||||
file->f_pos, file->f_path.dentry->d_inode->i_ino);
|
||||
|
||||
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;
|
||||
|
||||
_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;
|
||||
|
||||
_enter("{%s,%Zu},%s,%u,,%lu,%u",
|
||||
_enter("{%s,%Zu},%s,%u,,%llu,%u",
|
||||
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) {
|
||||
_leave(" = 0 [no]");
|
||||
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,
|
||||
struct nameidata *nd)
|
||||
static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct afs_fid *fid)
|
||||
{
|
||||
struct afs_dir_lookup_cookie cookie;
|
||||
struct afs_super_info *as;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
unsigned fpos;
|
||||
int ret;
|
||||
|
||||
_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);
|
||||
|
||||
/* insanity checks first */
|
||||
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);
|
||||
}
|
||||
_enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name);
|
||||
|
||||
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;
|
||||
ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
_leave(" = %d [iter]", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = -ENOENT;
|
||||
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);
|
||||
}
|
||||
|
||||
/* instantiate the dentry */
|
||||
ret = afs_iget(dir->i_sb, &cookie.fid, &inode);
|
||||
if (ret < 0) {
|
||||
_leave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
inode = afs_iget(dir->i_sb, &fid);
|
||||
if (IS_ERR(inode)) {
|
||||
_leave(" = %ld", PTR_ERR(inode));
|
||||
return ERR_PTR(PTR_ERR(inode));
|
||||
}
|
||||
|
||||
dentry->d_op = &afs_fs_dentry_operations;
|
||||
dentry->d_fsdata = (void *) (unsigned long) vnode->status.version;
|
||||
|
||||
d_add(dentry, inode);
|
||||
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }",
|
||||
cookie.fid.vnode,
|
||||
cookie.fid.unique,
|
||||
fid.vnode,
|
||||
fid.unique,
|
||||
dentry->d_inode->i_ino,
|
||||
dentry->d_inode->i_version);
|
||||
|
||||
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
|
||||
* - NOTE! the hit can be a negative hit too, so we can't assume we have an
|
||||
* 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)
|
||||
{
|
||||
struct afs_dir_lookup_cookie cookie;
|
||||
struct afs_vnode *vnode;
|
||||
struct afs_fid fid;
|
||||
struct dentry *parent;
|
||||
struct inode *inode, *dir;
|
||||
unsigned fpos;
|
||||
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 */
|
||||
parent = dget_parent(dentry->d_parent);
|
||||
parent = dget_parent(dentry);
|
||||
|
||||
dir = parent->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 */
|
||||
if (is_bad_inode(inode)) {
|
||||
printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
parent->d_name.name, dentry->d_name.name);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
/* force a full look up if the parent directory changed since last the
|
||||
* server was consulted
|
||||
* - otherwise this inode must still exist, even if the inode details
|
||||
* themselves have changed
|
||||
*/
|
||||
if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED)
|
||||
afs_vnode_fetch_status(AFS_FS_I(dir));
|
||||
|
||||
if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
|
||||
/* check that this dirent still exists if the directory's contents were
|
||||
* modified */
|
||||
if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
|
||||
_debug("%s: parent dir deleted", dentry->d_name.name);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) {
|
||||
_debug("%s: file already deleted", dentry->d_name.name);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if ((unsigned long) dentry->d_fsdata !=
|
||||
(unsigned long) AFS_FS_I(dir)->status.version) {
|
||||
_debug("%s: parent changed %lu -> %u",
|
||||
dentry->d_name.name,
|
||||
(unsigned long) dentry->d_fsdata,
|
||||
(unsigned) AFS_FS_I(dir)->status.version);
|
||||
if (test_and_clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags)) {
|
||||
/* rm/rmdir/rename may have occurred */
|
||||
_debug("dir modified");
|
||||
|
||||
/* search the directory for this vnode */
|
||||
cookie.name = dentry->d_name.name;
|
||||
cookie.nlen = dentry->d_name.len;
|
||||
cookie.fid.vid = AFS_FS_I(inode)->volume->vid;
|
||||
cookie.found = 0;
|
||||
|
||||
fpos = 0;
|
||||
ret = afs_dir_iterate(dir, &fpos, &cookie,
|
||||
afs_dir_lookup_filldir);
|
||||
ret = afs_do_lookup(dir, dentry, &fid);
|
||||
if (ret == -ENOENT) {
|
||||
_debug("%s: dirent not found", dentry->d_name.name);
|
||||
goto not_found;
|
||||
}
|
||||
if (ret < 0) {
|
||||
_debug("failed to iterate dir %s: %d",
|
||||
parent->d_name.name, ret);
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if (!cookie.found) {
|
||||
_debug("%s: dirent not found", dentry->d_name.name);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
/* if the vnode ID has changed, then the dirent points to a
|
||||
* different file */
|
||||
if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) {
|
||||
_debug("%s: dirent changed", dentry->d_name.name);
|
||||
if (fid.vnode != vnode->fid.vnode) {
|
||||
_debug("%s: dirent changed [%u != %u]",
|
||||
dentry->d_name.name, fid.vnode,
|
||||
vnode->fid.vnode);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
/* if the vnode ID uniqifier has changed, then the file has
|
||||
* been deleted */
|
||||
if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) {
|
||||
if (fid.unique != vnode->fid.unique) {
|
||||
_debug("%s: file deleted (uq %u -> %u I:%lu)",
|
||||
dentry->d_name.name,
|
||||
cookie.fid.unique,
|
||||
AFS_FS_I(inode)->fid.unique,
|
||||
inode->i_version);
|
||||
spin_lock(&AFS_FS_I(inode)->lock);
|
||||
AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED;
|
||||
spin_unlock(&AFS_FS_I(inode)->lock);
|
||||
dentry->d_name.name, fid.unique,
|
||||
vnode->fid.unique, inode->i_version);
|
||||
spin_lock(&vnode->lock);
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
spin_unlock(&vnode->lock);
|
||||
invalidate_remote_inode(inode);
|
||||
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:
|
||||
dput(parent);
|
||||
_leave(" = 1 [valid]");
|
||||
|
|
@ -610,12 +683,10 @@ out_bad:
|
|||
goto out_valid;
|
||||
}
|
||||
|
||||
shrink_dcache_parent(dentry);
|
||||
|
||||
_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);
|
||||
|
||||
dput(parent);
|
||||
|
||||
_leave(" = 0 [bad]");
|
||||
|
|
@ -635,10 +706,9 @@ static int afs_d_delete(struct dentry *dentry)
|
|||
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
|
||||
goto zap;
|
||||
|
||||
if (dentry->d_inode) {
|
||||
if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED)
|
||||
if (dentry->d_inode &&
|
||||
test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags))
|
||||
goto zap;
|
||||
}
|
||||
|
||||
_leave(" = 0 [keep]");
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
|
@ -15,9 +15,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "volume.h"
|
||||
#include "vnode.h"
|
||||
#include <rxrpc/call.h>
|
||||
#include "internal.h"
|
||||
|
||||
#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)
|
||||
{
|
||||
struct afs_rxfs_fetch_descriptor desc;
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
struct cachefs_page *pageio;
|
||||
#endif
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
size_t len;
|
||||
off_t offset;
|
||||
int ret;
|
||||
|
||||
inode = page->mapping->host;
|
||||
|
|
@ -97,14 +92,10 @@ static int afs_file_readpage(struct file *file, struct page *page)
|
|||
BUG_ON(!PageLocked(page));
|
||||
|
||||
ret = -ESTALE;
|
||||
if (vnode->flags & AFS_VNODE_DELETED)
|
||||
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
|
||||
goto error;
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* is it cached? */
|
||||
ret = cachefs_read_or_alloc_page(vnode->cache,
|
||||
page,
|
||||
|
|
@ -128,26 +119,19 @@ static int afs_file_readpage(struct file *file, struct page *page)
|
|||
case -ENOBUFS:
|
||||
case -ENODATA:
|
||||
default:
|
||||
desc.fid = vnode->fid;
|
||||
desc.offset = page->index << PAGE_CACHE_SHIFT;
|
||||
desc.size = min((size_t) (inode->i_size - desc.offset),
|
||||
(size_t) PAGE_SIZE);
|
||||
desc.buffer = kmap(page);
|
||||
|
||||
clear_page(desc.buffer);
|
||||
offset = page->index << PAGE_CACHE_SHIFT;
|
||||
len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
|
||||
|
||||
/* read the contents of the file from the server into the
|
||||
* page */
|
||||
ret = afs_vnode_fetch_data(vnode, &desc);
|
||||
kunmap(page);
|
||||
ret = afs_vnode_fetch_data(vnode, offset, len, page);
|
||||
if (ret < 0) {
|
||||
if (ret==-ENOENT) {
|
||||
if (ret == -ENOENT) {
|
||||
_debug("got NOENT from server"
|
||||
" - marking file deleted and stale");
|
||||
vnode->flags |= AFS_VNODE_DELETED;
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
ret = -ESTALE;
|
||||
}
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
cachefs_uncache_page(vnode->cache, page);
|
||||
#endif
|
||||
|
|
@ -174,10 +158,9 @@ static int afs_file_readpage(struct file *file, struct page *page)
|
|||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
error:
|
||||
SetPageError(page);
|
||||
unlock_page(page);
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
1076
fs/afs/fsclient.c
1076
fs/afs/fsclient.c
File diff suppressed because it is too large
Load diff
|
|
@ -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 */
|
||||
107
fs/afs/inode.c
107
fs/afs/inode.c
|
|
@ -19,9 +19,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "volume.h"
|
||||
#include "vnode.h"
|
||||
#include "super.h"
|
||||
#include "internal.h"
|
||||
|
||||
struct afs_iget_data {
|
||||
|
|
@ -40,7 +37,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
|
|||
vnode->status.type,
|
||||
vnode->status.nlink,
|
||||
vnode->status.size,
|
||||
vnode->status.version,
|
||||
vnode->status.data_version,
|
||||
vnode->status.mode);
|
||||
|
||||
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) {
|
||||
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_op = &afs_mntpt_inode_operations;
|
||||
inode->i_fop = &afs_mntpt_file_operations;
|
||||
|
|
@ -88,25 +85,6 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
|
|||
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
|
||||
*/
|
||||
|
|
@ -137,8 +115,7 @@ static int afs_iget5_set(struct inode *inode, void *opaque)
|
|||
/*
|
||||
* inode retrieval
|
||||
*/
|
||||
inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
|
||||
struct inode **_inode)
|
||||
inline struct inode *afs_iget(struct super_block *sb, struct afs_fid *fid)
|
||||
{
|
||||
struct afs_iget_data data = { .fid = *fid };
|
||||
struct afs_super_info *as;
|
||||
|
|
@ -155,20 +132,18 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
|
|||
&data);
|
||||
if (!inode) {
|
||||
_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);
|
||||
|
||||
/* deal with an existing inode */
|
||||
if (!(inode->i_state & I_NEW)) {
|
||||
ret = afs_vnode_fetch_status(vnode);
|
||||
if (ret == 0)
|
||||
*_inode = inode;
|
||||
else
|
||||
iput(inode);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
#ifdef AFS_CACHING_SUPPORT
|
||||
|
|
@ -181,21 +156,19 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
|
|||
#endif
|
||||
|
||||
/* okay... it's a new inode */
|
||||
inode->i_flags |= S_NOATIME;
|
||||
vnode->flags |= AFS_VNODE_CHANGED;
|
||||
ret = afs_inode_fetch_status(inode);
|
||||
if (ret<0)
|
||||
set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
|
||||
ret = afs_vnode_fetch_status(vnode);
|
||||
if (ret < 0)
|
||||
goto bad_inode;
|
||||
ret = afs_inode_map_status(vnode);
|
||||
if (ret < 0)
|
||||
goto bad_inode;
|
||||
|
||||
/* success */
|
||||
inode->i_flags |= S_NOATIME;
|
||||
unlock_new_inode(inode);
|
||||
|
||||
*_inode = inode;
|
||||
_leave(" = 0 [CB { v=%u x=%lu t=%u }]",
|
||||
vnode->cb_version,
|
||||
vnode->cb_timeout.timo_jif,
|
||||
vnode->cb_type);
|
||||
return 0;
|
||||
_leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type);
|
||||
return inode;
|
||||
|
||||
/* failure */
|
||||
bad_inode:
|
||||
|
|
@ -204,7 +177,7 @@ bad_inode:
|
|||
iput(inode);
|
||||
|
||||
_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,
|
||||
struct kstat *stat)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
|
||||
_enter("{ ino=%lu v=%lu }", inode->i_ino, inode->i_version);
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
ret = afs_inode_fetch_status(inode);
|
||||
if (ret == -ENOENT) {
|
||||
_leave(" = %d [%d %p]",
|
||||
ret, atomic_read(&dentry->d_count), dentry->d_inode);
|
||||
return ret;
|
||||
} else if (ret < 0) {
|
||||
make_bad_inode(inode);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* transfer attributes from the inode structure to the stat
|
||||
* structure */
|
||||
generic_fillattr(inode, stat);
|
||||
|
||||
_leave(" = 0 CB { v=%u x=%u t=%u }",
|
||||
vnode->cb_version,
|
||||
vnode->cb_expiry,
|
||||
vnode->cb_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -260,12 +210,23 @@ void afs_clear_inode(struct inode *inode)
|
|||
vnode->fid.vnode,
|
||||
vnode->cb_version,
|
||||
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
|
||||
cachefs_relinquish_cookie(vnode->cache, 0);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* 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)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef AFS_INTERNAL_H
|
||||
#define AFS_INTERNAL_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.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)
|
||||
#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ## a)
|
||||
#define kdebug(FMT, a...) printk(FMT"\n" , ## a)
|
||||
#define kproto(FMT, a...) printk("### "FMT"\n" , ## a)
|
||||
#define knet(FMT, a...) printk(FMT"\n" , ## a)
|
||||
struct afs_wait_mode {
|
||||
/* RxRPC received message notification */
|
||||
void (*rx_wakeup)(struct afs_call *call);
|
||||
|
||||
#ifdef __KDEBUG
|
||||
#define _enter(FMT, a...) kenter(FMT , ## a)
|
||||
#define _leave(FMT, a...) kleave(FMT , ## a)
|
||||
#define _debug(FMT, a...) kdebug(FMT , ## a)
|
||||
#define _proto(FMT, a...) kproto(FMT , ## a)
|
||||
#define _net(FMT, a...) knet(FMT , ## a)
|
||||
#else
|
||||
#define _enter(FMT, a...) do { } while(0)
|
||||
#define _leave(FMT, a...) do { } while(0)
|
||||
#define _debug(FMT, a...) do { } while(0)
|
||||
#define _proto(FMT, a...) do { } while(0)
|
||||
#define _net(FMT, a...) do { } while(0)
|
||||
/* synchronous call waiter and call dispatched notification */
|
||||
int (*wait)(struct afs_call *call);
|
||||
|
||||
/* asynchronous call completion */
|
||||
void (*async_complete)(void *reply, int error);
|
||||
};
|
||||
|
||||
extern const struct afs_wait_mode afs_sync_call;
|
||||
extern const struct afs_wait_mode afs_async_call;
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
static inline void afs_discard_my_signals(void)
|
||||
{
|
||||
while (signal_pending(current)) {
|
||||
siginfo_t sinfo;
|
||||
/* server record management */
|
||||
rwlock_t servers_lock; /* active server list lock */
|
||||
struct list_head servers; /* active server list */
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
dequeue_signal(current,¤t->blocked, &sinfo);
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
}
|
||||
/* volume location record management */
|
||||
struct rw_semaphore vl_sem; /* volume management serialisation semaphore */
|
||||
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
|
||||
|
|
@ -60,6 +333,19 @@ extern struct list_head afs_proc_cells;
|
|||
extern struct cachefs_index_def afs_cache_cell_index_def;
|
||||
#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
|
||||
*/
|
||||
|
|
@ -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 **);
|
||||
#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
|
||||
*/
|
||||
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 *,
|
||||
struct kstat *);
|
||||
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;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* misc.c
|
||||
*/
|
||||
extern int afs_abort_to_error(u32);
|
||||
|
||||
/*
|
||||
* mntpt.c
|
||||
*/
|
||||
extern const struct inode_operations afs_mntpt_inode_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 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
|
||||
|
|
@ -108,16 +412,6 @@ extern int afs_mntpt_check_symlink(struct afs_vnode *);
|
|||
extern int afs_fs_init(void);
|
||||
extern void afs_fs_exit(void);
|
||||
|
||||
#define AFS_CB_HASH_COUNT (PAGE_SIZE / sizeof(struct list_head))
|
||||
|
||||
extern struct list_head afs_cb_hash_tbl[];
|
||||
extern spinlock_t afs_cb_hash_lock;
|
||||
|
||||
#define afs_cb_hash(SRV, FID) \
|
||||
afs_cb_hash_tbl[((unsigned long)(SRV) + \
|
||||
(FID)->vid + (FID)->vnode + (FID)->unique) & \
|
||||
(AFS_CB_HASH_COUNT - 1)]
|
||||
|
||||
/*
|
||||
* proc.c
|
||||
*/
|
||||
|
|
@ -126,4 +420,217 @@ extern void afs_proc_cleanup(void);
|
|||
extern int afs_proc_cell_setup(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 */
|
||||
|
|
|
|||
|
|
@ -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("");
|
||||
}
|
||||
|
|
@ -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 */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue