mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02: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 4164 additions and 5393 deletions
				
			
		
							
								
								
									
										13
									
								
								fs/Kconfig
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								fs/Kconfig
									
									
									
									
									
								
							| 
						 | 
					@ -2019,7 +2019,7 @@ config CODA_FS_OLD_API
 | 
				
			||||||
config AFS_FS
 | 
					config AFS_FS
 | 
				
			||||||
	tristate "Andrew File System support (AFS) (EXPERIMENTAL)"
 | 
						tristate "Andrew File System support (AFS) (EXPERIMENTAL)"
 | 
				
			||||||
	depends on INET && EXPERIMENTAL
 | 
						depends on INET && EXPERIMENTAL
 | 
				
			||||||
	select RXRPC
 | 
						select AF_RXRPC
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  If you say Y here, you will get an experimental Andrew File System
 | 
						  If you say Y here, you will get an experimental Andrew File System
 | 
				
			||||||
	  driver. It currently only supports unsecured read-only AFS access.
 | 
						  driver. It currently only supports unsecured read-only AFS access.
 | 
				
			||||||
| 
						 | 
					@ -2028,6 +2028,17 @@ config AFS_FS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  If unsure, say N.
 | 
						  If unsure, say N.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config AFS_DEBUG
 | 
				
			||||||
 | 
						bool "AFS dynamic debugging"
 | 
				
			||||||
 | 
						depends on AFS_FS
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  Say Y here to make runtime controllable debugging messages appear.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  See <file:Documentation/filesystems/afs.txt> for more information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  If unsure, say N.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config RXRPC
 | 
					config RXRPC
 | 
				
			||||||
	tristate
 | 
						tristate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,12 +10,11 @@ kafs-objs := \
 | 
				
			||||||
	file.o \
 | 
						file.o \
 | 
				
			||||||
	fsclient.o \
 | 
						fsclient.o \
 | 
				
			||||||
	inode.o \
 | 
						inode.o \
 | 
				
			||||||
	kafsasyncd.o \
 | 
					 | 
				
			||||||
	kafstimod.o \
 | 
					 | 
				
			||||||
	main.o \
 | 
						main.o \
 | 
				
			||||||
	misc.o \
 | 
						misc.o \
 | 
				
			||||||
	mntpt.o \
 | 
						mntpt.o \
 | 
				
			||||||
	proc.o \
 | 
						proc.o \
 | 
				
			||||||
 | 
						rxrpc.o \
 | 
				
			||||||
	server.o \
 | 
						server.o \
 | 
				
			||||||
	super.o \
 | 
						super.o \
 | 
				
			||||||
	vlclient.o \
 | 
						vlclient.o \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* AFS types
 | 
					/* AFS common types
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,10 @@
 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 * 2 of the License, or (at your option) any later version.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef AFS_TYPES_H
 | 
					#ifndef AFS_H
 | 
				
			||||||
#define AFS_TYPES_H
 | 
					#define AFS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <rxrpc/types.h>
 | 
					#include <linux/in.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef unsigned			afs_volid_t;
 | 
					typedef unsigned			afs_volid_t;
 | 
				
			||||||
typedef unsigned			afs_vnodeid_t;
 | 
					typedef unsigned			afs_vnodeid_t;
 | 
				
			||||||
| 
						 | 
					@ -31,9 +31,6 @@ typedef enum {
 | 
				
			||||||
	AFS_FTYPE_SYMLINK	= 3,
 | 
						AFS_FTYPE_SYMLINK	= 3,
 | 
				
			||||||
} afs_file_type_t;
 | 
					} afs_file_type_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct afs_cell;
 | 
					 | 
				
			||||||
struct afs_vnode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * AFS file identifier
 | 
					 * AFS file identifier
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -54,14 +51,13 @@ typedef enum {
 | 
				
			||||||
} afs_callback_type_t;
 | 
					} afs_callback_type_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct afs_callback {
 | 
					struct afs_callback {
 | 
				
			||||||
	struct afs_server	*server;	/* server that made the promise */
 | 
					 | 
				
			||||||
	struct afs_fid		fid;		/* file identifier */
 | 
						struct afs_fid		fid;		/* file identifier */
 | 
				
			||||||
	unsigned		version;	/* callback version */
 | 
						unsigned		version;	/* callback version */
 | 
				
			||||||
	unsigned		expiry;		/* time at which expires */
 | 
						unsigned		expiry;		/* time at which expires */
 | 
				
			||||||
	afs_callback_type_t	type;		/* type of callback */
 | 
						afs_callback_type_t	type;		/* type of callback */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define AFSCBMAX 50
 | 
					#define AFSCBMAX 50	/* maximum callbacks transferred per bulk op */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * AFS volume information
 | 
					 * AFS volume information
 | 
				
			||||||
| 
						 | 
					@ -88,7 +84,7 @@ struct afs_file_status {
 | 
				
			||||||
	afs_file_type_t		type;		/* file type */
 | 
						afs_file_type_t		type;		/* file type */
 | 
				
			||||||
	unsigned		nlink;		/* link count */
 | 
						unsigned		nlink;		/* link count */
 | 
				
			||||||
	size_t			size;		/* file size */
 | 
						size_t			size;		/* file size */
 | 
				
			||||||
	afs_dataversion_t	version;	/* current data version */
 | 
						afs_dataversion_t	data_version;	/* current data version */
 | 
				
			||||||
	unsigned		author;		/* author ID */
 | 
						unsigned		author;		/* author ID */
 | 
				
			||||||
	unsigned		owner;		/* owner ID */
 | 
						unsigned		owner;		/* owner ID */
 | 
				
			||||||
	unsigned		caller_access;	/* access rights for authenticated caller */
 | 
						unsigned		caller_access;	/* access rights for authenticated caller */
 | 
				
			||||||
| 
						 | 
					@ -106,4 +102,4 @@ struct afs_volsync {
 | 
				
			||||||
	time_t			creation;	/* volume creation time */
 | 
						time_t			creation;	/* volume creation time */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* AFS_TYPES_H */
 | 
					#endif /* AFS_H */
 | 
				
			||||||
							
								
								
									
										28
									
								
								fs/afs/afs_cm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 */
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* AFS abort/error codes
 | 
					/* AFS File Service definitions
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -9,15 +9,22 @@
 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 * 2 of the License, or (at your option) any later version.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef AFS_ERRORS_H
 | 
					#ifndef AFS_FS_H
 | 
				
			||||||
#define AFS_ERRORS_H
 | 
					#define AFS_FS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "types.h"
 | 
					#define AFS_FS_PORT		7000	/* AFS file server port */
 | 
				
			||||||
 | 
					#define FS_SERVICE		1	/* AFS File Service ID */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					enum AFS_FS_Operations {
 | 
				
			||||||
 * file server abort codes
 | 
						FSFETCHSTATUS		= 132,	/* AFS Fetch file status */
 | 
				
			||||||
 */
 | 
						FSFETCHDATA		= 130,	/* AFS Fetch file data */
 | 
				
			||||||
typedef enum {
 | 
						FSGIVEUPCALLBACKS	= 147,	/* AFS Discard callback promises */
 | 
				
			||||||
 | 
						FSGETVOLUMEINFO		= 148,	/* AFS Get root volume information */
 | 
				
			||||||
 | 
						FSGETROOTVOLUME		= 151,	/* AFS Get root volume name */
 | 
				
			||||||
 | 
						FSLOOKUP		= 161	/* AFS lookup file in directory */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum AFS_FS_Errors {
 | 
				
			||||||
	VSALVAGE	= 101,	/* volume needs salvaging */
 | 
						VSALVAGE	= 101,	/* volume needs salvaging */
 | 
				
			||||||
	VNOVNODE	= 102,	/* no such file/dir (vnode) */
 | 
						VNOVNODE	= 102,	/* no such file/dir (vnode) */
 | 
				
			||||||
	VNOVOL		= 103,	/* no such volume or volume unavailable */
 | 
						VNOVOL		= 103,	/* no such volume or volume unavailable */
 | 
				
			||||||
| 
						 | 
					@ -29,8 +36,6 @@ typedef enum {
 | 
				
			||||||
	VOVERQUOTA	= 109,	/* volume's maximum quota exceeded */
 | 
						VOVERQUOTA	= 109,	/* volume's maximum quota exceeded */
 | 
				
			||||||
	VBUSY		= 110,	/* volume is temporarily unavailable */
 | 
						VBUSY		= 110,	/* volume is temporarily unavailable */
 | 
				
			||||||
	VMOVED		= 111,	/* volume moved to new server - ask this FS where */
 | 
						VMOVED		= 111,	/* volume moved to new server - ask this FS where */
 | 
				
			||||||
} afs_rxfs_abort_t;
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int afs_abort_to_error(int);
 | 
					#endif /* AFS_FS_H */
 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_ERRORS_H */
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* Volume Location Service client interface
 | 
					/* AFS Volume Location Service client interface
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,19 @@
 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 * 2 of the License, or (at your option) any later version.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef AFS_VLCLIENT_H
 | 
					#ifndef AFS_VL_H
 | 
				
			||||||
#define AFS_VLCLIENT_H
 | 
					#define AFS_VL_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "types.h"
 | 
					#include "afs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AFS_VL_PORT		7003	/* volume location service port */
 | 
				
			||||||
 | 
					#define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum AFSVL_Operations {
 | 
				
			||||||
 | 
						VLGETENTRYBYID		= 503,	/* AFS Get Cache Entry By ID operation ID */
 | 
				
			||||||
 | 
						VLGETENTRYBYNAME	= 504,	/* AFS Get Cache Entry By Name operation ID */
 | 
				
			||||||
 | 
						VLPROBE			= 514,	/* AFS Probe Volume Location Service operation ID */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum AFSVL_Errors {
 | 
					enum AFSVL_Errors {
 | 
				
			||||||
	AFSVL_IDEXIST 		= 363520,	/* Volume Id entry exists in vl database */
 | 
						AFSVL_IDEXIST 		= 363520,	/* Volume Id entry exists in vl database */
 | 
				
			||||||
| 
						 | 
					@ -40,14 +49,16 @@ enum AFSVL_Errors {
 | 
				
			||||||
	AFSVL_BADVOLOPER 	= 363542,	/* Bad volume operation code */
 | 
						AFSVL_BADVOLOPER 	= 363542,	/* Bad volume operation code */
 | 
				
			||||||
	AFSVL_BADRELLOCKTYPE 	= 363543,	/* Bad release lock type */
 | 
						AFSVL_BADRELLOCKTYPE 	= 363543,	/* Bad release lock type */
 | 
				
			||||||
	AFSVL_RERELEASE 	= 363544,	/* Status report: last release was aborted */
 | 
						AFSVL_RERELEASE 	= 363544,	/* Status report: last release was aborted */
 | 
				
			||||||
	AFSVL_BADSERVERFLAG 	= 363545,	/* Invalid replication site server °ag */
 | 
						AFSVL_BADSERVERFLAG 	= 363545,	/* Invalid replication site server °ag */
 | 
				
			||||||
	AFSVL_PERM 		= 363546,	/* No permission access */
 | 
						AFSVL_PERM 		= 363546,	/* No permission access */
 | 
				
			||||||
	AFSVL_NOMEM 		= 363547,	/* malloc/realloc failed to alloc enough memory */
 | 
						AFSVL_NOMEM 		= 363547,	/* malloc/realloc failed to alloc enough memory */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* maps to "struct vldbentry" in vvl-spec.pdf */
 | 
					/*
 | 
				
			||||||
 | 
					 * maps to "struct vldbentry" in vvl-spec.pdf
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
struct afs_vldbentry {
 | 
					struct afs_vldbentry {
 | 
				
			||||||
	char		name[65];		/* name of volume (including NUL char) */
 | 
						char		name[65];		/* name of volume (with NUL char) */
 | 
				
			||||||
	afs_voltype_t	type;			/* volume type */
 | 
						afs_voltype_t	type;			/* volume type */
 | 
				
			||||||
	unsigned	num_servers;		/* num servers that hold instances of this vol */
 | 
						unsigned	num_servers;		/* num servers that hold instances of this vol */
 | 
				
			||||||
	unsigned	clone_id;		/* cloning ID */
 | 
						unsigned	clone_id;		/* cloning ID */
 | 
				
			||||||
| 
						 | 
					@ -70,16 +81,4 @@ struct afs_vldbentry {
 | 
				
			||||||
	} servers[8];
 | 
						} servers[8];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int afs_rxvl_get_entry_by_name(struct afs_server *, const char *,
 | 
					#endif /* AFS_VL_H */
 | 
				
			||||||
				      unsigned, struct afs_cache_vlocation *);
 | 
					 | 
				
			||||||
extern int afs_rxvl_get_entry_by_id(struct afs_server *, afs_volid_t,
 | 
					 | 
				
			||||||
				    afs_voltype_t,
 | 
					 | 
				
			||||||
				    struct afs_cache_vlocation *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_rxvl_get_entry_by_id_async(struct afs_async_op *,
 | 
					 | 
				
			||||||
					  afs_volid_t, afs_voltype_t);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *,
 | 
					 | 
				
			||||||
					   struct afs_cache_vlocation *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_VLCLIENT_H */
 | 
					 | 
				
			||||||
							
								
								
									
										256
									
								
								fs/afs/cache.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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
 | 
					 * This software may be freely redistributed under the terms of the
 | 
				
			||||||
 * GNU General Public License.
 | 
					 * GNU General Public License.
 | 
				
			||||||
| 
						 | 
					@ -16,83 +16,182 @@
 | 
				
			||||||
#include <linux/kernel.h>
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/init.h>
 | 
					#include <linux/init.h>
 | 
				
			||||||
#include "server.h"
 | 
					#include <linux/circ_buf.h>
 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
#include "cmservice.h"
 | 
					
 | 
				
			||||||
 | 
					unsigned afs_vnode_update_timeout = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define afs_breakring_space(server) \
 | 
				
			||||||
 | 
						CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail,	\
 | 
				
			||||||
 | 
							   ARRAY_SIZE((server)->cb_break))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//static void afs_callback_updater(struct work_struct *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct workqueue_struct *afs_callback_update_worker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * allow the fileserver to request callback state (re-)initialisation
 | 
					 * allow the fileserver to request callback state (re-)initialisation
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int SRXAFSCM_InitCallBackState(struct afs_server *server)
 | 
					void afs_init_callback_state(struct afs_server *server)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct list_head callbacks;
 | 
						struct afs_vnode *vnode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p", server);
 | 
						_enter("{%p}", server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_LIST_HEAD(&callbacks);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* transfer the callback list from the server to a temp holding area */
 | 
					 | 
				
			||||||
	spin_lock(&server->cb_lock);
 | 
						spin_lock(&server->cb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_add(&callbacks, &server->cb_promises);
 | 
						/* kill all the promises on record from this server */
 | 
				
			||||||
	list_del_init(&server->cb_promises);
 | 
						while (!RB_EMPTY_ROOT(&server->cb_promises)) {
 | 
				
			||||||
 | 
							vnode = rb_entry(server->cb_promises.rb_node,
 | 
				
			||||||
	/* munch our way through the list, grabbing the inode, dropping all the
 | 
									 struct afs_vnode, cb_promise);
 | 
				
			||||||
	 * locks and regetting them in the right order
 | 
							printk("\nUNPROMISE on %p\n", vnode);
 | 
				
			||||||
	 */
 | 
							rb_erase(&vnode->cb_promise, &server->cb_promises);
 | 
				
			||||||
	while (!list_empty(&callbacks)) {
 | 
							vnode->cb_promised = false;
 | 
				
			||||||
		struct afs_vnode *vnode;
 | 
						}
 | 
				
			||||||
		struct inode *inode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		vnode = list_entry(callbacks.next, struct afs_vnode, cb_link);
 | 
					 | 
				
			||||||
		list_del_init(&vnode->cb_link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* try and grab the inode - may fail */
 | 
					 | 
				
			||||||
		inode = igrab(AFS_VNODE_TO_I(vnode));
 | 
					 | 
				
			||||||
		if (inode) {
 | 
					 | 
				
			||||||
			int release = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_unlock(&server->cb_lock);
 | 
						spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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);
 | 
							spin_lock(&vnode->lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (vnode->cb_server == server) {
 | 
							_debug("break callback");
 | 
				
			||||||
				vnode->cb_server = NULL;
 | 
					 | 
				
			||||||
				afs_kafstimod_del_timer(&vnode->cb_timeout);
 | 
					 | 
				
			||||||
				spin_lock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
				list_del_init(&vnode->cb_hash_link);
 | 
					 | 
				
			||||||
				spin_unlock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
				release = 1;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			spin_unlock(&vnode->lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			iput(inode);
 | 
					 | 
				
			||||||
			afs_put_server(server);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		spin_lock(&server->cb_lock);
 | 
							spin_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);
 | 
							spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = 0");
 | 
							queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
 | 
				
			||||||
	return 0;
 | 
							spin_unlock(&vnode->lock);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * allow the fileserver to explicitly break one callback
 | 
				
			||||||
 | 
					 * - happens when
 | 
				
			||||||
 | 
					 *   - the backing file is changed
 | 
				
			||||||
 | 
					 *   - a lock is released
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_break_one_callback(struct afs_server *server,
 | 
				
			||||||
 | 
									   struct afs_fid *fid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_vnode *vnode;
 | 
				
			||||||
 | 
						struct rb_node *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("find");
 | 
				
			||||||
 | 
						spin_lock(&server->fs_lock);
 | 
				
			||||||
 | 
						p = server->fs_vnodes.rb_node;
 | 
				
			||||||
 | 
						while (p) {
 | 
				
			||||||
 | 
							vnode = rb_entry(p, struct afs_vnode, server_rb);
 | 
				
			||||||
 | 
							if (fid->vid < vnode->fid.vid)
 | 
				
			||||||
 | 
								p = p->rb_left;
 | 
				
			||||||
 | 
							else if (fid->vid > vnode->fid.vid)
 | 
				
			||||||
 | 
								p = p->rb_right;
 | 
				
			||||||
 | 
							else if (fid->vnode < vnode->fid.vnode)
 | 
				
			||||||
 | 
								p = p->rb_left;
 | 
				
			||||||
 | 
							else if (fid->vnode > vnode->fid.vnode)
 | 
				
			||||||
 | 
								p = p->rb_right;
 | 
				
			||||||
 | 
							else if (fid->unique < vnode->fid.unique)
 | 
				
			||||||
 | 
								p = p->rb_left;
 | 
				
			||||||
 | 
							else if (fid->unique > vnode->fid.unique)
 | 
				
			||||||
 | 
								p = p->rb_right;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								goto found;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* not found so we just ignore it (it may have moved to another
 | 
				
			||||||
 | 
						 * server) */
 | 
				
			||||||
 | 
					not_available:
 | 
				
			||||||
 | 
						_debug("not avail");
 | 
				
			||||||
 | 
						spin_unlock(&server->fs_lock);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					found:
 | 
				
			||||||
 | 
						_debug("found");
 | 
				
			||||||
 | 
						ASSERTCMP(server, ==, vnode->server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!igrab(AFS_VNODE_TO_I(vnode)))
 | 
				
			||||||
 | 
							goto not_available;
 | 
				
			||||||
 | 
						spin_unlock(&server->fs_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_break_callback(server, vnode);
 | 
				
			||||||
 | 
						iput(&vnode->vfs_inode);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * allow the fileserver to break callback promises
 | 
					 * allow the fileserver to break callback promises
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
 | 
					void afs_break_callbacks(struct afs_server *server, size_t count,
 | 
				
			||||||
			 struct afs_callback callbacks[])
 | 
								 struct afs_callback callbacks[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	_enter("%p,%u,", server, count);
 | 
						_enter("%p,%zu,", server, count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(server != NULL);
 | 
				
			||||||
 | 
						ASSERTCMP(count, <=, AFSCBMAX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (; count > 0; callbacks++, count--) {
 | 
						for (; count > 0; callbacks++, count--) {
 | 
				
			||||||
		struct afs_vnode *vnode = NULL;
 | 
					 | 
				
			||||||
		struct inode *inode = NULL;
 | 
					 | 
				
			||||||
		int valid = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		_debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
 | 
							_debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
 | 
				
			||||||
		       callbacks->fid.vid,
 | 
							       callbacks->fid.vid,
 | 
				
			||||||
		       callbacks->fid.vnode,
 | 
							       callbacks->fid.vnode,
 | 
				
			||||||
| 
						 | 
					@ -101,66 +200,244 @@ int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
 | 
				
			||||||
		       callbacks->expiry,
 | 
							       callbacks->expiry,
 | 
				
			||||||
		       callbacks->type
 | 
							       callbacks->type
 | 
				
			||||||
		       );
 | 
							       );
 | 
				
			||||||
 | 
							afs_break_one_callback(server, &callbacks->fid);
 | 
				
			||||||
		/* find the inode for this fid */
 | 
					 | 
				
			||||||
		spin_lock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		list_for_each_entry(vnode,
 | 
					 | 
				
			||||||
				    &afs_cb_hash(server, &callbacks->fid),
 | 
					 | 
				
			||||||
				    cb_hash_link) {
 | 
					 | 
				
			||||||
			if (memcmp(&vnode->fid, &callbacks->fid,
 | 
					 | 
				
			||||||
				   sizeof(struct afs_fid)) != 0)
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* right vnode, but is it same server? */
 | 
					 | 
				
			||||||
			if (vnode->cb_server != server)
 | 
					 | 
				
			||||||
				break; /* no */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* try and nail the inode down */
 | 
					 | 
				
			||||||
			inode = igrab(AFS_VNODE_TO_I(vnode));
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		spin_unlock(&afs_cb_hash_lock);
 | 
						_leave("");
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
		if (inode) {
 | 
					 | 
				
			||||||
			/* we've found the record for this vnode */
 | 
					 | 
				
			||||||
			spin_lock(&vnode->lock);
 | 
					 | 
				
			||||||
			if (vnode->cb_server == server) {
 | 
					 | 
				
			||||||
				/* the callback _is_ on the calling server */
 | 
					 | 
				
			||||||
				vnode->cb_server = NULL;
 | 
					 | 
				
			||||||
				valid = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				afs_kafstimod_del_timer(&vnode->cb_timeout);
 | 
					 | 
				
			||||||
				vnode->flags |= AFS_VNODE_CHANGED;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				spin_lock(&server->cb_lock);
 | 
					 | 
				
			||||||
				list_del_init(&vnode->cb_link);
 | 
					 | 
				
			||||||
				spin_unlock(&server->cb_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				spin_lock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
				list_del_init(&vnode->cb_hash_link);
 | 
					 | 
				
			||||||
				spin_unlock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			spin_unlock(&vnode->lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (valid) {
 | 
					 | 
				
			||||||
				invalidate_remote_inode(inode);
 | 
					 | 
				
			||||||
				afs_put_server(server);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			iput(inode);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = 0");
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * allow the fileserver to see if the cache manager is still alive
 | 
					 * record the callback for breaking
 | 
				
			||||||
 | 
					 * - the caller must hold server->cb_lock
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int SRXAFSCM_Probe(struct afs_server *server)
 | 
					static void afs_do_give_up_callback(struct afs_server *server,
 | 
				
			||||||
 | 
									    struct afs_vnode *vnode)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	_debug("SRXAFSCM_Probe(%p)\n", server);
 | 
						struct afs_callback *cb;
 | 
				
			||||||
	return 0;
 | 
					
 | 
				
			||||||
 | 
						_enter("%p,%p", server, vnode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cb = &server->cb_break[server->cb_break_head];
 | 
				
			||||||
 | 
						cb->fid		= vnode->fid;
 | 
				
			||||||
 | 
						cb->version	= vnode->cb_version;
 | 
				
			||||||
 | 
						cb->expiry	= vnode->cb_expiry;
 | 
				
			||||||
 | 
						cb->type	= vnode->cb_type;
 | 
				
			||||||
 | 
						smp_wmb();
 | 
				
			||||||
 | 
						server->cb_break_head =
 | 
				
			||||||
 | 
							(server->cb_break_head + 1) &
 | 
				
			||||||
 | 
							(ARRAY_SIZE(server->cb_break) - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* defer the breaking of callbacks to try and collect as many as
 | 
				
			||||||
 | 
						 * possible to ship in one operation */
 | 
				
			||||||
 | 
						switch (atomic_inc_return(&server->cb_break_n)) {
 | 
				
			||||||
 | 
						case 1 ... AFSCBMAX - 1:
 | 
				
			||||||
 | 
							queue_delayed_work(afs_callback_update_worker,
 | 
				
			||||||
 | 
									   &server->cb_break_work, HZ * 2);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case AFSCBMAX:
 | 
				
			||||||
 | 
							afs_flush_callback_breaks(server);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(server->cb_promises.rb_node != NULL);
 | 
				
			||||||
 | 
						rb_erase(&vnode->cb_promise, &server->cb_promises);
 | 
				
			||||||
 | 
						vnode->cb_promised = false;
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * give up the callback registered for a vnode on the file server when the
 | 
				
			||||||
 | 
					 * inode is being cleared
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_give_up_callback(struct afs_vnode *vnode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *server = vnode->server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DECLARE_WAITQUEUE(myself, current);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%d", vnode->cb_promised);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("GIVE UP INODE %p", &vnode->vfs_inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!vnode->cb_promised) {
 | 
				
			||||||
 | 
							_leave(" [not promised]");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(server != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock(&server->cb_lock);
 | 
				
			||||||
 | 
						if (vnode->cb_promised && afs_breakring_space(server) == 0) {
 | 
				
			||||||
 | 
							add_wait_queue(&server->cb_break_waitq, &myself);
 | 
				
			||||||
 | 
							for (;;) {
 | 
				
			||||||
 | 
								set_current_state(TASK_UNINTERRUPTIBLE);
 | 
				
			||||||
 | 
								if (!vnode->cb_promised ||
 | 
				
			||||||
 | 
								    afs_breakring_space(server) != 0)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
								schedule();
 | 
				
			||||||
 | 
								spin_lock(&server->cb_lock);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							remove_wait_queue(&server->cb_break_waitq, &myself);
 | 
				
			||||||
 | 
							__set_current_state(TASK_RUNNING);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* of course, it's always possible for the server to break this vnode's
 | 
				
			||||||
 | 
						 * callback first... */
 | 
				
			||||||
 | 
						if (vnode->cb_promised)
 | 
				
			||||||
 | 
							afs_do_give_up_callback(server, vnode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * dispatch a deferred give up callbacks operation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_dispatch_give_up_callbacks(struct work_struct *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *server =
 | 
				
			||||||
 | 
							container_of(work, struct afs_server, cb_break_work.work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* tell the fileserver to discard the callback promises it has
 | 
				
			||||||
 | 
						 * - in the event of ENOMEM or some other error, we just forget that we
 | 
				
			||||||
 | 
						 *   had callbacks entirely, and the server will call us later to break
 | 
				
			||||||
 | 
						 *   them
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						afs_fs_give_up_callbacks(server, &afs_async_call);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * flush the outstanding callback breaks on a server
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_flush_callback_breaks(struct afs_server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cancel_delayed_work(&server->cb_break_work);
 | 
				
			||||||
 | 
						queue_delayed_work(afs_callback_update_worker,
 | 
				
			||||||
 | 
								   &server->cb_break_work, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * update a bunch of callbacks
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_callback_updater(struct work_struct *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *server;
 | 
				
			||||||
 | 
						struct afs_vnode *vnode, *xvnode;
 | 
				
			||||||
 | 
						time_t now;
 | 
				
			||||||
 | 
						long timeout;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server = container_of(work, struct afs_server, updater);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						now = get_seconds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* find the first vnode to update */
 | 
				
			||||||
 | 
						spin_lock(&server->cb_lock);
 | 
				
			||||||
 | 
						for (;;) {
 | 
				
			||||||
 | 
							if (RB_EMPTY_ROOT(&server->cb_promises)) {
 | 
				
			||||||
 | 
								spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
								_leave(" [nothing]");
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vnode = rb_entry(rb_first(&server->cb_promises),
 | 
				
			||||||
 | 
									 struct afs_vnode, cb_promise);
 | 
				
			||||||
 | 
							if (atomic_read(&vnode->usage) > 0)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							rb_erase(&vnode->cb_promise, &server->cb_promises);
 | 
				
			||||||
 | 
							vnode->cb_promised = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						timeout = vnode->update_at - now;
 | 
				
			||||||
 | 
						if (timeout > 0) {
 | 
				
			||||||
 | 
							queue_delayed_work(afs_vnode_update_worker,
 | 
				
			||||||
 | 
									   &afs_vnode_update, timeout * HZ);
 | 
				
			||||||
 | 
							spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
							_leave(" [nothing]");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_del_init(&vnode->update);
 | 
				
			||||||
 | 
						atomic_inc(&vnode->usage);
 | 
				
			||||||
 | 
						spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* we can now perform the update */
 | 
				
			||||||
 | 
						_debug("update %s", vnode->vldb.name);
 | 
				
			||||||
 | 
						vnode->state = AFS_VL_UPDATING;
 | 
				
			||||||
 | 
						vnode->upd_rej_cnt = 0;
 | 
				
			||||||
 | 
						vnode->upd_busy_cnt = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = afs_vnode_update_record(vl, &vldb);
 | 
				
			||||||
 | 
						switch (ret) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							afs_vnode_apply_update(vl, &vldb);
 | 
				
			||||||
 | 
							vnode->state = AFS_VL_UPDATING;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOMEDIUM:
 | 
				
			||||||
 | 
							vnode->state = AFS_VL_VOLUME_DELETED;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							vnode->state = AFS_VL_UNCERTAIN;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* and then reschedule */
 | 
				
			||||||
 | 
						_debug("reschedule");
 | 
				
			||||||
 | 
						vnode->update_at = get_seconds() + afs_vnode_update_timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock(&server->cb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!list_empty(&server->cb_promises)) {
 | 
				
			||||||
 | 
							/* next update in 10 minutes, but wait at least 1 second more
 | 
				
			||||||
 | 
							 * than the newest record already queued so that we don't spam
 | 
				
			||||||
 | 
							 * the VL server suddenly with lots of requests
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							xvnode = list_entry(server->cb_promises.prev,
 | 
				
			||||||
 | 
									    struct afs_vnode, update);
 | 
				
			||||||
 | 
							if (vnode->update_at <= xvnode->update_at)
 | 
				
			||||||
 | 
								vnode->update_at = xvnode->update_at + 1;
 | 
				
			||||||
 | 
							xvnode = list_entry(server->cb_promises.next,
 | 
				
			||||||
 | 
									    struct afs_vnode, update);
 | 
				
			||||||
 | 
							timeout = xvnode->update_at - now;
 | 
				
			||||||
 | 
							if (timeout < 0)
 | 
				
			||||||
 | 
								timeout = 0;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							timeout = afs_vnode_update_timeout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_add_tail(&vnode->update, &server->cb_promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("timeout %ld", timeout);
 | 
				
			||||||
 | 
						queue_delayed_work(afs_vnode_update_worker,
 | 
				
			||||||
 | 
								   &afs_vnode_update, timeout * HZ);
 | 
				
			||||||
 | 
						spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
						afs_put_vnode(vl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * initialise the callback update process
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int __init afs_callback_update_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						afs_callback_update_worker =
 | 
				
			||||||
 | 
							create_singlethread_workqueue("kafs_callbackd");
 | 
				
			||||||
 | 
						return afs_callback_update_worker ? 0 : -ENOMEM;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * shut down the callback update process
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void __exit afs_callback_update_kill(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						destroy_workqueue(afs_callback_update_worker);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										342
									
								
								fs/afs/cell.c
									
									
									
									
									
								
							
							
						
						
									
										342
									
								
								fs/afs/cell.c
									
									
									
									
									
								
							| 
						 | 
					@ -11,15 +11,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <rxrpc/peer.h>
 | 
					 | 
				
			||||||
#include <rxrpc/connection.h>
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "server.h"
 | 
					 | 
				
			||||||
#include "transport.h"
 | 
					 | 
				
			||||||
#include "vlclient.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include "super.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DECLARE_RWSEM(afs_proc_cells_sem);
 | 
					DECLARE_RWSEM(afs_proc_cells_sem);
 | 
				
			||||||
| 
						 | 
					@ -28,34 +19,21 @@ LIST_HEAD(afs_proc_cells);
 | 
				
			||||||
static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells);
 | 
					static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells);
 | 
				
			||||||
static DEFINE_RWLOCK(afs_cells_lock);
 | 
					static DEFINE_RWLOCK(afs_cells_lock);
 | 
				
			||||||
static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
 | 
					static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
 | 
				
			||||||
 | 
					static DECLARE_WAIT_QUEUE_HEAD(afs_cells_freeable_wq);
 | 
				
			||||||
static struct afs_cell *afs_cell_root;
 | 
					static struct afs_cell *afs_cell_root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static cachefs_match_val_t afs_cell_cache_match(void *target,
 | 
					 | 
				
			||||||
						const void *entry);
 | 
					 | 
				
			||||||
static void afs_cell_cache_update(void *source, void *entry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cachefs_index_def afs_cache_cell_index_def = {
 | 
					 | 
				
			||||||
	.name			= "cell_ix",
 | 
					 | 
				
			||||||
	.data_size		= sizeof(struct afs_cache_cell),
 | 
					 | 
				
			||||||
	.keys[0]		= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
 | 
					 | 
				
			||||||
	.match			= afs_cell_cache_match,
 | 
					 | 
				
			||||||
	.update			= afs_cell_cache_update,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * create a cell record
 | 
					 * create a cell record
 | 
				
			||||||
 * - "name" is the name of the cell
 | 
					 * - "name" is the name of the cell
 | 
				
			||||||
 * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
 | 
					 * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
 | 
					struct afs_cell *afs_cell_create(const char *name, char *vllist)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell;
 | 
						struct afs_cell *cell;
 | 
				
			||||||
	char *next;
 | 
						char *next;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%s", name);
 | 
						_enter("%s,%s", name, vllist);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
 | 
						BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,25 +41,22 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
 | 
				
			||||||
	cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
 | 
						cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
 | 
				
			||||||
	if (!cell) {
 | 
						if (!cell) {
 | 
				
			||||||
		_leave(" = -ENOMEM");
 | 
							_leave(" = -ENOMEM");
 | 
				
			||||||
		return -ENOMEM;
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_write(&afs_cells_sem);
 | 
						down_write(&afs_cells_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(cell, 0, sizeof(struct afs_cell));
 | 
						memset(cell, 0, sizeof(struct afs_cell));
 | 
				
			||||||
	atomic_set(&cell->usage, 0);
 | 
						atomic_set(&cell->usage, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_LIST_HEAD(&cell->link);
 | 
						INIT_LIST_HEAD(&cell->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rwlock_init(&cell->sv_lock);
 | 
						rwlock_init(&cell->servers_lock);
 | 
				
			||||||
	INIT_LIST_HEAD(&cell->sv_list);
 | 
						INIT_LIST_HEAD(&cell->servers);
 | 
				
			||||||
	INIT_LIST_HEAD(&cell->sv_graveyard);
 | 
					 | 
				
			||||||
	spin_lock_init(&cell->sv_gylock);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init_rwsem(&cell->vl_sem);
 | 
						init_rwsem(&cell->vl_sem);
 | 
				
			||||||
	INIT_LIST_HEAD(&cell->vl_list);
 | 
						INIT_LIST_HEAD(&cell->vl_list);
 | 
				
			||||||
	INIT_LIST_HEAD(&cell->vl_graveyard);
 | 
						spin_lock_init(&cell->vl_lock);
 | 
				
			||||||
	spin_lock_init(&cell->vl_gylock);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	strcpy(cell->name, name);
 | 
						strcpy(cell->name, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,9 +81,9 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
 | 
				
			||||||
		if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS)
 | 
							if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} while(vllist = next, vllist);
 | 
						} while ((vllist = next));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* add a proc dir for this cell */
 | 
						/* add a proc directory for this cell */
 | 
				
			||||||
	ret = afs_proc_cell_setup(cell);
 | 
						ret = afs_proc_cell_setup(cell);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
| 
						 | 
					@ -129,30 +104,29 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
 | 
				
			||||||
	down_write(&afs_proc_cells_sem);
 | 
						down_write(&afs_proc_cells_sem);
 | 
				
			||||||
	list_add_tail(&cell->proc_link, &afs_proc_cells);
 | 
						list_add_tail(&cell->proc_link, &afs_proc_cells);
 | 
				
			||||||
	up_write(&afs_proc_cells_sem);
 | 
						up_write(&afs_proc_cells_sem);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	*_cell = cell;
 | 
					 | 
				
			||||||
	up_write(&afs_cells_sem);
 | 
						up_write(&afs_cells_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = 0 (%p)", cell);
 | 
						_leave(" = %p", cell);
 | 
				
			||||||
	return 0;
 | 
						return cell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
badaddr:
 | 
					badaddr:
 | 
				
			||||||
	printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist);
 | 
						printk(KERN_ERR "kAFS: bad VL server IP address\n");
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	up_write(&afs_cells_sem);
 | 
						up_write(&afs_cells_sem);
 | 
				
			||||||
	kfree(cell);
 | 
						kfree(cell);
 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ret;
 | 
						return ERR_PTR(ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * initialise the cell database from module parameters
 | 
					 * set the root cell information
 | 
				
			||||||
 | 
					 * - can be called with a module parameter string
 | 
				
			||||||
 | 
					 * - can be called from a write to /proc/fs/afs/rootcell
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_cell_init(char *rootcell)
 | 
					int afs_cell_init(char *rootcell)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *old_root, *new_root;
 | 
						struct afs_cell *old_root, *new_root;
 | 
				
			||||||
	char *cp;
 | 
						char *cp;
 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("");
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,79 +134,60 @@ int afs_cell_init(char *rootcell)
 | 
				
			||||||
		/* module is loaded with no parameters, or built statically.
 | 
							/* module is loaded with no parameters, or built statically.
 | 
				
			||||||
		 * - in the future we might initialize cell DB here.
 | 
							 * - in the future we might initialize cell DB here.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		_leave(" = 0 (but no root)");
 | 
							_leave(" = 0 [no root]");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cp = strchr(rootcell, ':');
 | 
						cp = strchr(rootcell, ':');
 | 
				
			||||||
	if (!cp) {
 | 
						if (!cp) {
 | 
				
			||||||
		printk(KERN_ERR "kAFS: no VL server IP addresses specified\n");
 | 
							printk(KERN_ERR "kAFS: no VL server IP addresses specified\n");
 | 
				
			||||||
		_leave(" = %d (no colon)", -EINVAL);
 | 
							_leave(" = -EINVAL");
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* allocate a cell record for the root cell */
 | 
						/* allocate a cell record for the root cell */
 | 
				
			||||||
	*cp++ = 0;
 | 
						*cp++ = 0;
 | 
				
			||||||
	ret = afs_cell_create(rootcell, cp, &new_root);
 | 
						new_root = afs_cell_create(rootcell, cp);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (IS_ERR(new_root)) {
 | 
				
			||||||
		_leave(" = %d", ret);
 | 
							_leave(" = %ld", PTR_ERR(new_root));
 | 
				
			||||||
		return ret;
 | 
							return PTR_ERR(new_root);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* as afs_put_cell() takes locks by itself, we have to do
 | 
						/* install the new cell */
 | 
				
			||||||
	 * a little gymnastics to be race-free.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	afs_get_cell(new_root);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	write_lock(&afs_cells_lock);
 | 
						write_lock(&afs_cells_lock);
 | 
				
			||||||
	while (afs_cell_root) {
 | 
					 | 
				
			||||||
	old_root = afs_cell_root;
 | 
						old_root = afs_cell_root;
 | 
				
			||||||
		afs_cell_root = NULL;
 | 
					 | 
				
			||||||
		write_unlock(&afs_cells_lock);
 | 
					 | 
				
			||||||
		afs_put_cell(old_root);
 | 
					 | 
				
			||||||
		write_lock(&afs_cells_lock);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	afs_cell_root = new_root;
 | 
						afs_cell_root = new_root;
 | 
				
			||||||
	write_unlock(&afs_cells_lock);
 | 
						write_unlock(&afs_cells_lock);
 | 
				
			||||||
 | 
						afs_put_cell(old_root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = 0");
 | 
				
			||||||
	return ret;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * lookup a cell record
 | 
					 * lookup a cell record
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell)
 | 
					struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell;
 | 
						struct afs_cell *cell;
 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("\"%*.*s\",", namesz, namesz, name ? name : "");
 | 
						_enter("\"%*.*s\",", namesz, namesz, name ? name : "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*_cell = NULL;
 | 
						down_read(&afs_cells_sem);
 | 
				
			||||||
 | 
						read_lock(&afs_cells_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (name) {
 | 
						if (name) {
 | 
				
			||||||
		/* if the cell was named, look for it in the cell record list */
 | 
							/* if the cell was named, look for it in the cell record list */
 | 
				
			||||||
		ret = -ENOENT;
 | 
					 | 
				
			||||||
		cell = NULL;
 | 
					 | 
				
			||||||
		read_lock(&afs_cells_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		list_for_each_entry(cell, &afs_cells, link) {
 | 
							list_for_each_entry(cell, &afs_cells, link) {
 | 
				
			||||||
			if (strncmp(cell->name, name, namesz) == 0) {
 | 
								if (strncmp(cell->name, name, namesz) == 0) {
 | 
				
			||||||
				afs_get_cell(cell);
 | 
									afs_get_cell(cell);
 | 
				
			||||||
				goto found;
 | 
									goto found;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		cell = NULL;
 | 
							cell = ERR_PTR(-ENOENT);
 | 
				
			||||||
	found:
 | 
						found:
 | 
				
			||||||
 | 
							;
 | 
				
			||||||
		read_unlock(&afs_cells_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (cell)
 | 
					 | 
				
			||||||
			ret = 0;
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		read_lock(&afs_cells_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		cell = afs_cell_root;
 | 
							cell = afs_cell_root;
 | 
				
			||||||
		if (!cell) {
 | 
							if (!cell) {
 | 
				
			||||||
			/* this should not happen unless user tries to mount
 | 
								/* this should not happen unless user tries to mount
 | 
				
			||||||
| 
						 | 
					@ -241,37 +196,32 @@ int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell)
 | 
				
			||||||
			 * ENOENT might be "more appropriate" but they happen
 | 
								 * ENOENT might be "more appropriate" but they happen
 | 
				
			||||||
			 * for other reasons.
 | 
								 * for other reasons.
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			ret = -EDESTADDRREQ;
 | 
								cell = ERR_PTR(-EDESTADDRREQ);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			afs_get_cell(cell);
 | 
								afs_get_cell(cell);
 | 
				
			||||||
			ret = 0;
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	read_unlock(&afs_cells_lock);
 | 
						read_unlock(&afs_cells_lock);
 | 
				
			||||||
	}
 | 
						up_read(&afs_cells_sem);
 | 
				
			||||||
 | 
						_leave(" = %p", cell);
 | 
				
			||||||
	*_cell = cell;
 | 
						return cell;
 | 
				
			||||||
	_leave(" = %d (%p)", ret, cell);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * try and get a cell record
 | 
					 * try and get a cell record
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell)
 | 
					struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	write_lock(&afs_cells_lock);
 | 
						write_lock(&afs_cells_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cell = *_cell;
 | 
					 | 
				
			||||||
	if (cell && !list_empty(&cell->link))
 | 
						if (cell && !list_empty(&cell->link))
 | 
				
			||||||
		afs_get_cell(cell);
 | 
							afs_get_cell(cell);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		cell = NULL;
 | 
							cell = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	write_unlock(&afs_cells_lock);
 | 
						write_unlock(&afs_cells_lock);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return cell;
 | 
						return cell;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -285,8 +235,7 @@ void afs_put_cell(struct afs_cell *cell)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
 | 
						_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* sanity check */
 | 
						ASSERTCMP(atomic_read(&cell->usage), >, 0);
 | 
				
			||||||
	BUG_ON(atomic_read(&cell->usage) <= 0);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* to prevent a race, the decrement and the dequeue must be effectively
 | 
						/* to prevent a race, the decrement and the dequeue must be effectively
 | 
				
			||||||
	 * atomic */
 | 
						 * atomic */
 | 
				
			||||||
| 
						 | 
					@ -298,35 +247,49 @@ void afs_put_cell(struct afs_cell *cell)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(list_empty(&cell->servers));
 | 
				
			||||||
 | 
						ASSERT(list_empty(&cell->vl_list));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	write_unlock(&afs_cells_lock);
 | 
						write_unlock(&afs_cells_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(!list_empty(&cell->sv_list));
 | 
						wake_up(&afs_cells_freeable_wq);
 | 
				
			||||||
	BUG_ON(!list_empty(&cell->sv_graveyard));
 | 
					 | 
				
			||||||
	BUG_ON(!list_empty(&cell->vl_list));
 | 
					 | 
				
			||||||
	BUG_ON(!list_empty(&cell->vl_graveyard));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" [unused]");
 | 
						_leave(" [unused]");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * destroy a cell record
 | 
					 * destroy a cell record
 | 
				
			||||||
 | 
					 * - must be called with the afs_cells_sem write-locked
 | 
				
			||||||
 | 
					 * - cell->link should have been broken by the caller
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_cell_destroy(struct afs_cell *cell)
 | 
					static void afs_cell_destroy(struct afs_cell *cell)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
 | 
						_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* to prevent a race, the decrement and the dequeue must be effectively
 | 
						ASSERTCMP(atomic_read(&cell->usage), >=, 0);
 | 
				
			||||||
	 * atomic */
 | 
						ASSERT(list_empty(&cell->link));
 | 
				
			||||||
	write_lock(&afs_cells_lock);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* sanity check */
 | 
						/* wait for everyone to stop using the cell */
 | 
				
			||||||
	BUG_ON(atomic_read(&cell->usage) != 0);
 | 
						if (atomic_read(&cell->usage) > 0) {
 | 
				
			||||||
 | 
							DECLARE_WAITQUEUE(myself, current);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_del_init(&cell->link);
 | 
							_debug("wait for cell %s", cell->name);
 | 
				
			||||||
 | 
							set_current_state(TASK_UNINTERRUPTIBLE);
 | 
				
			||||||
 | 
							add_wait_queue(&afs_cells_freeable_wq, &myself);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	write_unlock(&afs_cells_lock);
 | 
							while (atomic_read(&cell->usage) > 0) {
 | 
				
			||||||
 | 
								schedule();
 | 
				
			||||||
 | 
								set_current_state(TASK_UNINTERRUPTIBLE);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_write(&afs_cells_sem);
 | 
							remove_wait_queue(&afs_cells_freeable_wq, &myself);
 | 
				
			||||||
 | 
							set_current_state(TASK_RUNNING);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("cell dead");
 | 
				
			||||||
 | 
						ASSERTCMP(atomic_read(&cell->usage), ==, 0);
 | 
				
			||||||
 | 
						ASSERT(list_empty(&cell->servers));
 | 
				
			||||||
 | 
						ASSERT(list_empty(&cell->vl_list));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_proc_cell_remove(cell);
 | 
						afs_proc_cell_remove(cell);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -338,101 +301,25 @@ static void afs_cell_destroy(struct afs_cell *cell)
 | 
				
			||||||
	cachefs_relinquish_cookie(cell->cache, 0);
 | 
						cachefs_relinquish_cookie(cell->cache, 0);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	up_write(&afs_cells_sem);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	BUG_ON(!list_empty(&cell->sv_list));
 | 
					 | 
				
			||||||
	BUG_ON(!list_empty(&cell->sv_graveyard));
 | 
					 | 
				
			||||||
	BUG_ON(!list_empty(&cell->vl_list));
 | 
					 | 
				
			||||||
	BUG_ON(!list_empty(&cell->vl_graveyard));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* finish cleaning up the cell */
 | 
					 | 
				
			||||||
	kfree(cell);
 | 
						kfree(cell);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" [destroyed]");
 | 
						_leave(" [destroyed]");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * lookup the server record corresponding to an Rx RPC peer
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_server_find_by_peer(const struct rxrpc_peer *peer,
 | 
					 | 
				
			||||||
			    struct afs_server **_server)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_server *server;
 | 
					 | 
				
			||||||
	struct afs_cell *cell;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p{a=%08x},", peer, ntohl(peer->addr.s_addr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* search the cell list */
 | 
					 | 
				
			||||||
	read_lock(&afs_cells_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	list_for_each_entry(cell, &afs_cells, link) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		_debug("? cell %s",cell->name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		write_lock(&cell->sv_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* check the active list */
 | 
					 | 
				
			||||||
		list_for_each_entry(server, &cell->sv_list, link) {
 | 
					 | 
				
			||||||
			_debug("?? server %08x", ntohl(server->addr.s_addr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (memcmp(&server->addr, &peer->addr,
 | 
					 | 
				
			||||||
				   sizeof(struct in_addr)) == 0)
 | 
					 | 
				
			||||||
				goto found_server;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* check the inactive list */
 | 
					 | 
				
			||||||
		spin_lock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
		list_for_each_entry(server, &cell->sv_graveyard, link) {
 | 
					 | 
				
			||||||
			_debug("?? dead server %08x",
 | 
					 | 
				
			||||||
			       ntohl(server->addr.s_addr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (memcmp(&server->addr, &peer->addr,
 | 
					 | 
				
			||||||
				   sizeof(struct in_addr)) == 0)
 | 
					 | 
				
			||||||
				goto found_dead_server;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		spin_unlock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		write_unlock(&cell->sv_lock);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	read_unlock(&afs_cells_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = -ENOENT");
 | 
					 | 
				
			||||||
	return -ENOENT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* we found it in the graveyard - resurrect it */
 | 
					 | 
				
			||||||
found_dead_server:
 | 
					 | 
				
			||||||
	list_move_tail(&server->link, &cell->sv_list);
 | 
					 | 
				
			||||||
	afs_get_server(server);
 | 
					 | 
				
			||||||
	afs_kafstimod_del_timer(&server->timeout);
 | 
					 | 
				
			||||||
	spin_unlock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
	goto success;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* we found it - increment its ref count and return it */
 | 
					 | 
				
			||||||
found_server:
 | 
					 | 
				
			||||||
	afs_get_server(server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
success:
 | 
					 | 
				
			||||||
	write_unlock(&cell->sv_lock);
 | 
					 | 
				
			||||||
	read_unlock(&afs_cells_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	*_server = server;
 | 
					 | 
				
			||||||
	_leave(" = 0 (s=%p c=%p)", server, cell);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * purge in-memory cell database on module unload or afs_init() failure
 | 
					 * purge in-memory cell database on module unload or afs_init() failure
 | 
				
			||||||
 * - the timeout daemon is stopped before calling this
 | 
					 * - the timeout daemon is stopped before calling this
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void afs_cell_purge(void)
 | 
					void afs_cell_purge(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vlocation *vlocation;
 | 
					 | 
				
			||||||
	struct afs_cell *cell;
 | 
						struct afs_cell *cell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("");
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_put_cell(afs_cell_root);
 | 
						afs_put_cell(afs_cell_root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						down_write(&afs_cells_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (!list_empty(&afs_cells)) {
 | 
						while (!list_empty(&afs_cells)) {
 | 
				
			||||||
		cell = NULL;
 | 
							cell = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -451,102 +338,11 @@ void afs_cell_purge(void)
 | 
				
			||||||
			_debug("PURGING CELL %s (%d)",
 | 
								_debug("PURGING CELL %s (%d)",
 | 
				
			||||||
			       cell->name, atomic_read(&cell->usage));
 | 
								       cell->name, atomic_read(&cell->usage));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			BUG_ON(!list_empty(&cell->sv_list));
 | 
					 | 
				
			||||||
			BUG_ON(!list_empty(&cell->vl_list));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* purge the cell's VL graveyard list */
 | 
					 | 
				
			||||||
			_debug(" - clearing VL graveyard");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			spin_lock(&cell->vl_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			while (!list_empty(&cell->vl_graveyard)) {
 | 
					 | 
				
			||||||
				vlocation = list_entry(cell->vl_graveyard.next,
 | 
					 | 
				
			||||||
						       struct afs_vlocation,
 | 
					 | 
				
			||||||
						       link);
 | 
					 | 
				
			||||||
				list_del_init(&vlocation->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				afs_kafstimod_del_timer(&vlocation->timeout);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				spin_unlock(&cell->vl_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				afs_vlocation_do_timeout(vlocation);
 | 
					 | 
				
			||||||
				/* TODO: race if move to use krxtimod instead
 | 
					 | 
				
			||||||
				 * of kafstimod */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				spin_lock(&cell->vl_gylock);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			spin_unlock(&cell->vl_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* purge the cell's server graveyard list */
 | 
					 | 
				
			||||||
			_debug(" - clearing server graveyard");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			spin_lock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			while (!list_empty(&cell->sv_graveyard)) {
 | 
					 | 
				
			||||||
				struct afs_server *server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				server = list_entry(cell->sv_graveyard.next,
 | 
					 | 
				
			||||||
						    struct afs_server, link);
 | 
					 | 
				
			||||||
				list_del_init(&server->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				afs_kafstimod_del_timer(&server->timeout);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				spin_unlock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				afs_server_do_timeout(server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				spin_lock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			spin_unlock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* now the cell should be left with no references */
 | 
								/* now the cell should be left with no references */
 | 
				
			||||||
			afs_cell_destroy(cell);
 | 
								afs_cell_destroy(cell);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						up_write(&afs_cells_sem);
 | 
				
			||||||
	_leave("");
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * match a cell record obtained from the cache
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static cachefs_match_val_t afs_cell_cache_match(void *target,
 | 
					 | 
				
			||||||
						const void *entry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const struct afs_cache_cell *ccell = entry;
 | 
					 | 
				
			||||||
	struct afs_cell *cell = target;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("{%s},{%s}", ccell->name, cell->name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) {
 | 
					 | 
				
			||||||
		_leave(" = SUCCESS");
 | 
					 | 
				
			||||||
		return CACHEFS_MATCH_SUCCESS;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = FAILED");
 | 
					 | 
				
			||||||
	return CACHEFS_MATCH_FAILED;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * update a cell record in the cache
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static void afs_cell_cache_update(void *source, void *entry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_cache_cell *ccell = entry;
 | 
					 | 
				
			||||||
	struct afs_cell *cell = source;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p,%p", source, entry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	strncpy(ccell->name, cell->name, sizeof(ccell->name));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memcpy(ccell->vl_servers,
 | 
					 | 
				
			||||||
	       cell->vl_addrs,
 | 
					 | 
				
			||||||
	       min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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/module.h>
 | 
				
			||||||
#include <linux/init.h>
 | 
					#include <linux/init.h>
 | 
				
			||||||
#include <linux/sched.h>
 | 
					#include <linux/sched.h>
 | 
				
			||||||
#include <linux/completion.h>
 | 
					#include <linux/ip.h>
 | 
				
			||||||
#include "server.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "transport.h"
 | 
					 | 
				
			||||||
#include <rxrpc/rxrpc.h>
 | 
					 | 
				
			||||||
#include <rxrpc/transport.h>
 | 
					 | 
				
			||||||
#include <rxrpc/connection.h>
 | 
					 | 
				
			||||||
#include <rxrpc/call.h>
 | 
					 | 
				
			||||||
#include "cmservice.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					#include "afs_cm.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static unsigned afscm_usage;		/* AFS cache manager usage count */
 | 
					struct workqueue_struct *afs_cm_workqueue;
 | 
				
			||||||
static struct rw_semaphore afscm_sem;	/* AFS cache manager start/stop semaphore */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int afscm_new_call(struct rxrpc_call *call);
 | 
					static int afs_deliver_cb_init_call_back_state(struct afs_call *,
 | 
				
			||||||
static void afscm_attention(struct rxrpc_call *call);
 | 
										       struct sk_buff *, bool);
 | 
				
			||||||
static void afscm_error(struct rxrpc_call *call);
 | 
					static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool);
 | 
				
			||||||
static void afscm_aemap(struct rxrpc_call *call);
 | 
					static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool);
 | 
				
			||||||
 | 
					static void afs_cm_destructor(struct afs_call *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _SRXAFSCM_CallBack(struct rxrpc_call *call);
 | 
					/*
 | 
				
			||||||
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call);
 | 
					 * CB.CallBack operation type
 | 
				
			||||||
static void _SRXAFSCM_Probe(struct rxrpc_call *call);
 | 
					 */
 | 
				
			||||||
 | 
					static const struct afs_call_type afs_SRXCBCallBack = {
 | 
				
			||||||
typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call);
 | 
						.deliver	= afs_deliver_cb_callback,
 | 
				
			||||||
 | 
						.abort_to_error	= afs_abort_to_error,
 | 
				
			||||||
static const struct rxrpc_operation AFSCM_ops[] = {
 | 
						.destructor	= afs_cm_destructor,
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.id	= 204,
 | 
					 | 
				
			||||||
		.asize	= RXRPC_APP_MARK_EOF,
 | 
					 | 
				
			||||||
		.name	= "CallBack",
 | 
					 | 
				
			||||||
		.user	= _SRXAFSCM_CallBack,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.id	= 205,
 | 
					 | 
				
			||||||
		.asize	= RXRPC_APP_MARK_EOF,
 | 
					 | 
				
			||||||
		.name	= "InitCallBackState",
 | 
					 | 
				
			||||||
		.user	= _SRXAFSCM_InitCallBackState,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.id	= 206,
 | 
					 | 
				
			||||||
		.asize	= RXRPC_APP_MARK_EOF,
 | 
					 | 
				
			||||||
		.name	= "Probe",
 | 
					 | 
				
			||||||
		.user	= _SRXAFSCM_Probe,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.id	= 207,
 | 
					 | 
				
			||||||
		.asize	= RXRPC_APP_MARK_EOF,
 | 
					 | 
				
			||||||
		.name	= "GetLock",
 | 
					 | 
				
			||||||
		.user	= _SRXAFSCM_GetLock,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.id	= 208,
 | 
					 | 
				
			||||||
		.asize	= RXRPC_APP_MARK_EOF,
 | 
					 | 
				
			||||||
		.name	= "GetCE",
 | 
					 | 
				
			||||||
		.user	= _SRXAFSCM_GetCE,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.id	= 209,
 | 
					 | 
				
			||||||
		.asize	= RXRPC_APP_MARK_EOF,
 | 
					 | 
				
			||||||
		.name	= "GetXStatsVersion",
 | 
					 | 
				
			||||||
		.user	= _SRXAFSCM_GetXStatsVersion,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.id	= 210,
 | 
					 | 
				
			||||||
		.asize	= RXRPC_APP_MARK_EOF,
 | 
					 | 
				
			||||||
		.name	= "GetXStats",
 | 
					 | 
				
			||||||
		.user	= _SRXAFSCM_GetXStats,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct rxrpc_service AFSCM_service = {
 | 
					/*
 | 
				
			||||||
	.name		= "AFS/CM",
 | 
					 * CB.InitCallBackState operation type
 | 
				
			||||||
	.owner		= THIS_MODULE,
 | 
					 */
 | 
				
			||||||
	.link		= LIST_HEAD_INIT(AFSCM_service.link),
 | 
					static const struct afs_call_type afs_SRXCBInitCallBackState = {
 | 
				
			||||||
	.new_call	= afscm_new_call,
 | 
						.deliver	= afs_deliver_cb_init_call_back_state,
 | 
				
			||||||
	.service_id	= 1,
 | 
						.abort_to_error	= afs_abort_to_error,
 | 
				
			||||||
	.attn_func	= afscm_attention,
 | 
						.destructor	= afs_cm_destructor,
 | 
				
			||||||
	.error_func	= afscm_error,
 | 
					 | 
				
			||||||
	.aemap_func	= afscm_aemap,
 | 
					 | 
				
			||||||
	.ops_begin	= &AFSCM_ops[0],
 | 
					 | 
				
			||||||
	.ops_end	= &AFSCM_ops[ARRAY_SIZE(AFSCM_ops)],
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static DECLARE_COMPLETION(kafscmd_alive);
 | 
					/*
 | 
				
			||||||
static DECLARE_COMPLETION(kafscmd_dead);
 | 
					 * CB.Probe operation type
 | 
				
			||||||
static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq);
 | 
					 */
 | 
				
			||||||
static LIST_HEAD(kafscmd_attention_list);
 | 
					static const struct afs_call_type afs_SRXCBProbe = {
 | 
				
			||||||
static LIST_HEAD(afscm_calls);
 | 
						.deliver	= afs_deliver_cb_probe,
 | 
				
			||||||
static DEFINE_SPINLOCK(afscm_calls_lock);
 | 
						.abort_to_error	= afs_abort_to_error,
 | 
				
			||||||
static DEFINE_SPINLOCK(kafscmd_attention_lock);
 | 
						.destructor	= afs_cm_destructor,
 | 
				
			||||||
static int kafscmd_die;
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * AFS Cache Manager kernel thread
 | 
					 * route an incoming cache manager call
 | 
				
			||||||
 | 
					 * - return T if supported, F if not
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int kafscmd(void *arg)
 | 
					bool afs_cm_incoming_call(struct afs_call *call)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	DECLARE_WAITQUEUE(myself, current);
 | 
						u32 operation_id = ntohl(call->operation_ID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct rxrpc_call *call;
 | 
						_enter("{CB.OP %u}", operation_id);
 | 
				
			||||||
	_SRXAFSCM_xxxx_t func;
 | 
					 | 
				
			||||||
	int die;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printk(KERN_INFO "kAFS: Started kafscmd %d\n", current->pid);
 | 
						switch (operation_id) {
 | 
				
			||||||
 | 
						case CBCallBack:
 | 
				
			||||||
	daemonize("kafscmd");
 | 
							call->type = &afs_SRXCBCallBack;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
	complete(&kafscmd_alive);
 | 
						case CBInitCallBackState:
 | 
				
			||||||
 | 
							call->type = &afs_SRXCBInitCallBackState;
 | 
				
			||||||
	/* loop around looking for things to attend to */
 | 
							return true;
 | 
				
			||||||
	do {
 | 
						case CBProbe:
 | 
				
			||||||
		if (list_empty(&kafscmd_attention_list)) {
 | 
							call->type = &afs_SRXCBProbe;
 | 
				
			||||||
			set_current_state(TASK_INTERRUPTIBLE);
 | 
							return true;
 | 
				
			||||||
			add_wait_queue(&kafscmd_sleepq, &myself);
 | 
						default:
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
			for (;;) {
 | 
					 | 
				
			||||||
				set_current_state(TASK_INTERRUPTIBLE);
 | 
					 | 
				
			||||||
				if (!list_empty(&kafscmd_attention_list) ||
 | 
					 | 
				
			||||||
				    signal_pending(current) ||
 | 
					 | 
				
			||||||
				    kafscmd_die)
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				schedule();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			remove_wait_queue(&kafscmd_sleepq, &myself);
 | 
					 | 
				
			||||||
			set_current_state(TASK_RUNNING);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		die = kafscmd_die;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* dequeue the next call requiring attention */
 | 
					 | 
				
			||||||
		call = NULL;
 | 
					 | 
				
			||||||
		spin_lock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!list_empty(&kafscmd_attention_list)) {
 | 
					 | 
				
			||||||
			call = list_entry(kafscmd_attention_list.next,
 | 
					 | 
				
			||||||
					  struct rxrpc_call,
 | 
					 | 
				
			||||||
					  app_attn_link);
 | 
					 | 
				
			||||||
			list_del_init(&call->app_attn_link);
 | 
					 | 
				
			||||||
			die = 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		spin_unlock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (call) {
 | 
					 | 
				
			||||||
			/* act upon it */
 | 
					 | 
				
			||||||
			_debug("@@@ Begin Attend Call %p", call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			func = call->app_user;
 | 
					 | 
				
			||||||
			if (func)
 | 
					 | 
				
			||||||
				func(call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			rxrpc_put_call(call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			_debug("@@@ End Attend Call %p", call);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	} while(!die);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* and that's all */
 | 
					 | 
				
			||||||
	complete_and_exit(&kafscmd_dead, 0);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * handle a call coming in to the cache manager
 | 
					 * clean up a cache manager call
 | 
				
			||||||
 * - if I want to keep the call, I must increment its usage count
 | 
					 | 
				
			||||||
 * - the return value will be negated and passed back in an abort packet if
 | 
					 | 
				
			||||||
 *   non-zero
 | 
					 | 
				
			||||||
 * - serialised by virtue of there only being one krxiod
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afscm_new_call(struct rxrpc_call *call)
 | 
					static void afs_cm_destructor(struct afs_call *call)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	_enter("%p{cid=%u u=%d}",
 | 
						_enter("");
 | 
				
			||||||
	       call, ntohl(call->call_id), atomic_read(&call->usage));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rxrpc_get_call(call);
 | 
						afs_put_server(call->server);
 | 
				
			||||||
 | 
						call->server = NULL;
 | 
				
			||||||
	/* add to my current call list */
 | 
						kfree(call->buffer);
 | 
				
			||||||
	spin_lock(&afscm_calls_lock);
 | 
						call->buffer = NULL;
 | 
				
			||||||
	list_add(&call->app_link,&afscm_calls);
 | 
					 | 
				
			||||||
	spin_unlock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = 0");
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * queue on the kafscmd queue for attention
 | 
					 * allow the fileserver to see if the cache manager is still alive
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afscm_attention(struct rxrpc_call *call)
 | 
					static void SRXAFSCB_CallBack(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	_enter("%p{cid=%u u=%d}",
 | 
						struct afs_call *call = container_of(work, struct afs_call, work);
 | 
				
			||||||
	       call, ntohl(call->call_id), atomic_read(&call->usage));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&kafscmd_attention_lock);
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (list_empty(&call->app_attn_link)) {
 | 
						/* be sure to send the reply *before* attempting to spam the AFS server
 | 
				
			||||||
		list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
 | 
						 * with FSFetchStatus requests on the vnodes with broken callbacks lest
 | 
				
			||||||
		rxrpc_get_call(call);
 | 
						 * the AFS server get into a vicious cycle of trying to break further
 | 
				
			||||||
	}
 | 
						 * callbacks because it hadn't received completion of the CBCallBack op
 | 
				
			||||||
 | 
						 * yet */
 | 
				
			||||||
	spin_unlock(&kafscmd_attention_lock);
 | 
						afs_send_empty_reply(call);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	wake_up(&kafscmd_sleepq);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" {u=%d}", atomic_read(&call->usage));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * handle my call being aborted
 | 
					 | 
				
			||||||
 * - clean up, dequeue and put my ref to the call
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void afscm_error(struct rxrpc_call *call)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int removed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p{est=%s ac=%u er=%d}",
 | 
					 | 
				
			||||||
	       call,
 | 
					 | 
				
			||||||
	       rxrpc_call_error_states[call->app_err_state],
 | 
					 | 
				
			||||||
	       call->app_abort_code,
 | 
					 | 
				
			||||||
	       call->app_errno);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_lock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (list_empty(&call->app_attn_link)) {
 | 
					 | 
				
			||||||
		list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
 | 
					 | 
				
			||||||
		rxrpc_get_call(call);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_unlock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	removed = 0;
 | 
					 | 
				
			||||||
	spin_lock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
	if (!list_empty(&call->app_link)) {
 | 
					 | 
				
			||||||
		list_del_init(&call->app_link);
 | 
					 | 
				
			||||||
		removed = 1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	spin_unlock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (removed)
 | 
					 | 
				
			||||||
		rxrpc_put_call(call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wake_up(&kafscmd_sleepq);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_break_callbacks(call->server, call->count, call->request);
 | 
				
			||||||
	_leave("");
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * map afs abort codes to/from Linux error codes
 | 
					 * deliver request data to a CB.CallBack call
 | 
				
			||||||
 * - called with call->lock held
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afscm_aemap(struct rxrpc_call *call)
 | 
					static int afs_deliver_cb_callback(struct afs_call *call, struct sk_buff *skb,
 | 
				
			||||||
 | 
									   bool last)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (call->app_err_state) {
 | 
						struct afs_callback *cb;
 | 
				
			||||||
	case RXRPC_ESTATE_LOCAL_ABORT:
 | 
						struct afs_server *server;
 | 
				
			||||||
		call->app_abort_code = -call->app_errno;
 | 
						struct in_addr addr;
 | 
				
			||||||
		break;
 | 
						__be32 *bp;
 | 
				
			||||||
	case RXRPC_ESTATE_PEER_ABORT:
 | 
						u32 tmp;
 | 
				
			||||||
		call->app_errno = -ECONNABORTED;
 | 
						int ret, loop;
 | 
				
			||||||
		break;
 | 
					
 | 
				
			||||||
	default:
 | 
						_enter("{%u},{%u},%d", call->unmarshall, skb->len, last);
 | 
				
			||||||
		break;
 | 
					
 | 
				
			||||||
	}
 | 
						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);
 | 
				
			||||||
 * start the cache manager service if not already started
 | 
							_debug("FID count: %u", call->count);
 | 
				
			||||||
 */
 | 
							if (call->count > AFSCBMAX)
 | 
				
			||||||
int afscm_start(void)
 | 
								return -EBADMSG;
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_write(&afscm_sem);
 | 
							call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL);
 | 
				
			||||||
	if (!afscm_usage) {
 | 
							if (!call->buffer)
 | 
				
			||||||
		ret = kernel_thread(kafscmd, NULL, 0);
 | 
								return -ENOMEM;
 | 
				
			||||||
		if (ret < 0)
 | 
							call->offset = 0;
 | 
				
			||||||
			goto out;
 | 
							call->unmarshall++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		wait_for_completion(&kafscmd_alive);
 | 
						case 2:
 | 
				
			||||||
 | 
							_debug("extract FID array");
 | 
				
			||||||
		ret = rxrpc_add_service(afs_transport, &AFSCM_service);
 | 
							ret = afs_extract_data(call, skb, last, call->buffer,
 | 
				
			||||||
		if (ret < 0)
 | 
									       call->count * 3 * 4);
 | 
				
			||||||
			goto kill;
 | 
							switch (ret) {
 | 
				
			||||||
 | 
							case 0:		break;
 | 
				
			||||||
		afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
 | 
							case -EAGAIN:	return 0;
 | 
				
			||||||
					afs_mntpt_expiry_timeout * HZ);
 | 
							default:	return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afscm_usage++;
 | 
							_debug("unmarshall FID array");
 | 
				
			||||||
	up_write(&afscm_sem);
 | 
							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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!last)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
kill:
 | 
						call->state = AFS_CALL_REPLYING;
 | 
				
			||||||
	kafscmd_die = 1;
 | 
					 | 
				
			||||||
	wake_up(&kafscmd_sleepq);
 | 
					 | 
				
			||||||
	wait_for_completion(&kafscmd_dead);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
						/* we'll need the file server record as that tells us which set of
 | 
				
			||||||
	up_write(&afscm_sem);
 | 
						 * vnodes to operate upon */
 | 
				
			||||||
	return ret;
 | 
						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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * stop the cache manager service
 | 
					 * allow the fileserver to request callback state (re-)initialisation
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void afscm_stop(void)
 | 
					static void SRXAFSCB_InitCallBackState(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rxrpc_call *call;
 | 
						struct afs_call *call = container_of(work, struct afs_call, work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_write(&afscm_sem);
 | 
						_enter("{%p}", call->server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(afscm_usage == 0);
 | 
						afs_init_callback_state(call->server);
 | 
				
			||||||
	afscm_usage--;
 | 
						afs_send_empty_reply(call);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
	if (afscm_usage == 0) {
 | 
					 | 
				
			||||||
		/* don't want more incoming calls */
 | 
					 | 
				
			||||||
		rxrpc_del_service(afs_transport, &AFSCM_service);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* abort any calls I've still got open (the afscm_error() will
 | 
					 | 
				
			||||||
		 * dequeue them) */
 | 
					 | 
				
			||||||
		spin_lock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
		while (!list_empty(&afscm_calls)) {
 | 
					 | 
				
			||||||
			call = list_entry(afscm_calls.next,
 | 
					 | 
				
			||||||
					  struct rxrpc_call,
 | 
					 | 
				
			||||||
					  app_link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			list_del_init(&call->app_link);
 | 
					 | 
				
			||||||
			rxrpc_get_call(call);
 | 
					 | 
				
			||||||
			spin_unlock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and
 | 
					 | 
				
			||||||
							 * put */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			_debug("nuking active call %08x.%d",
 | 
					 | 
				
			||||||
			       ntohl(call->conn->conn_id),
 | 
					 | 
				
			||||||
			       ntohl(call->call_id));
 | 
					 | 
				
			||||||
			rxrpc_put_call(call);
 | 
					 | 
				
			||||||
			rxrpc_put_call(call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			spin_lock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		spin_unlock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* get rid of my daemon */
 | 
					 | 
				
			||||||
		kafscmd_die = 1;
 | 
					 | 
				
			||||||
		wake_up(&kafscmd_sleepq);
 | 
					 | 
				
			||||||
		wait_for_completion(&kafscmd_dead);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* dispose of any calls waiting for attention */
 | 
					 | 
				
			||||||
		spin_lock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
		while (!list_empty(&kafscmd_attention_list)) {
 | 
					 | 
				
			||||||
			call = list_entry(kafscmd_attention_list.next,
 | 
					 | 
				
			||||||
					  struct rxrpc_call,
 | 
					 | 
				
			||||||
					  app_attn_link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			list_del_init(&call->app_attn_link);
 | 
					 | 
				
			||||||
			spin_unlock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			rxrpc_put_call(call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			spin_lock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		spin_unlock(&kafscmd_attention_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		afs_kafstimod_del_timer(&afs_mntpt_expiry_timer);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	up_write(&afscm_sem);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * handle the fileserver breaking a set of callbacks
 | 
					 * deliver request data to a CB.InitCallBackState call
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void _SRXAFSCM_CallBack(struct rxrpc_call *call)
 | 
					static int afs_deliver_cb_init_call_back_state(struct afs_call *call,
 | 
				
			||||||
 | 
										       struct sk_buff *skb,
 | 
				
			||||||
 | 
										       bool last)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server *server;
 | 
						struct afs_server *server;
 | 
				
			||||||
	size_t count, qty, tmp;
 | 
						struct in_addr addr;
 | 
				
			||||||
	int ret = 0, removed;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
 | 
						_enter(",{%u},%d", skb->len, last);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server = afs_server_get_from_peer(call->conn->peer);
 | 
						if (skb->len > 0)
 | 
				
			||||||
 | 
							return -EBADMSG;
 | 
				
			||||||
 | 
						if (!last)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (call->app_call_state) {
 | 
						/* no unmarshalling required */
 | 
				
			||||||
		/* we've received the last packet
 | 
						call->state = AFS_CALL_REPLYING;
 | 
				
			||||||
		 * - drain all the data from the call and send the reply
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_SRVR_GOT_ARGS:
 | 
					 | 
				
			||||||
		ret = -EBADMSG;
 | 
					 | 
				
			||||||
		qty = call->app_ready_qty;
 | 
					 | 
				
			||||||
		if (qty < 8 || qty > 50 * (6 * 4) + 8)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
						/* we'll need the file server record as that tells us which set of
 | 
				
			||||||
			struct afs_callback *cb, *pcb;
 | 
						 * vnodes to operate upon */
 | 
				
			||||||
			int loop;
 | 
						memcpy(&addr, &ip_hdr(skb)->saddr, 4);
 | 
				
			||||||
			__be32 *fp, *bp;
 | 
						server = afs_find_server(&addr);
 | 
				
			||||||
 | 
						if (!server)
 | 
				
			||||||
 | 
							return -ENOTCONN;
 | 
				
			||||||
 | 
						call->server = server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fp = rxrpc_call_alloc_scratch(call, qty);
 | 
						INIT_WORK(&call->work, SRXAFSCB_InitCallBackState);
 | 
				
			||||||
 | 
						schedule_work(&call->work);
 | 
				
			||||||
			/* drag the entire argument block out to the scratch
 | 
						return 0;
 | 
				
			||||||
			 * space */
 | 
					 | 
				
			||||||
			ret = rxrpc_call_read_data(call, fp, qty, 0);
 | 
					 | 
				
			||||||
			if (ret < 0)
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* and unmarshall the parameter block */
 | 
					 | 
				
			||||||
			ret = -EBADMSG;
 | 
					 | 
				
			||||||
			count = ntohl(*fp++);
 | 
					 | 
				
			||||||
			if (count>AFSCBMAX ||
 | 
					 | 
				
			||||||
			    (count * (3 * 4) + 8 != qty &&
 | 
					 | 
				
			||||||
			     count * (6 * 4) + 8 != qty))
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			bp = fp + count*3;
 | 
					 | 
				
			||||||
			tmp = ntohl(*bp++);
 | 
					 | 
				
			||||||
			if (tmp > 0 && tmp != count)
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			if (tmp == 0)
 | 
					 | 
				
			||||||
				bp = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			pcb = cb = rxrpc_call_alloc_scratch_s(
 | 
					 | 
				
			||||||
				call, struct afs_callback);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			for (loop = count - 1; loop >= 0; loop--) {
 | 
					 | 
				
			||||||
				pcb->fid.vid	= ntohl(*fp++);
 | 
					 | 
				
			||||||
				pcb->fid.vnode	= ntohl(*fp++);
 | 
					 | 
				
			||||||
				pcb->fid.unique	= ntohl(*fp++);
 | 
					 | 
				
			||||||
				if (bp) {
 | 
					 | 
				
			||||||
					pcb->version	= ntohl(*bp++);
 | 
					 | 
				
			||||||
					pcb->expiry	= ntohl(*bp++);
 | 
					 | 
				
			||||||
					pcb->type	= ntohl(*bp++);
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					pcb->version	= 0;
 | 
					 | 
				
			||||||
					pcb->expiry	= 0;
 | 
					 | 
				
			||||||
					pcb->type	= AFSCM_CB_UNTYPED;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				pcb++;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* invoke the actual service routine */
 | 
					 | 
				
			||||||
			ret = SRXAFSCM_CallBack(server, count, cb);
 | 
					 | 
				
			||||||
			if (ret < 0)
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* send the reply */
 | 
					 | 
				
			||||||
		ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
 | 
					 | 
				
			||||||
					    GFP_KERNEL, 0, &count);
 | 
					 | 
				
			||||||
		if (ret < 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* operation complete */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_COMPLETE:
 | 
					 | 
				
			||||||
		call->app_user = NULL;
 | 
					 | 
				
			||||||
		removed = 0;
 | 
					 | 
				
			||||||
		spin_lock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
		if (!list_empty(&call->app_link)) {
 | 
					 | 
				
			||||||
			list_del_init(&call->app_link);
 | 
					 | 
				
			||||||
			removed = 1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		spin_unlock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (removed)
 | 
					 | 
				
			||||||
			rxrpc_put_call(call);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* operation terminated on error */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_ERROR:
 | 
					 | 
				
			||||||
		call->app_user = NULL;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		rxrpc_call_abort(call, ret);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_put_server(server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * handle the fileserver asking us to initialise our callback state
 | 
					 * allow the fileserver to see if the cache manager is still alive
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call)
 | 
					static void SRXAFSCB_Probe(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server *server;
 | 
						struct afs_call *call = container_of(work, struct afs_call, work);
 | 
				
			||||||
	size_t count;
 | 
					 | 
				
			||||||
	int ret = 0, removed;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
 | 
						_enter("");
 | 
				
			||||||
 | 
						afs_send_empty_reply(call);
 | 
				
			||||||
	server = afs_server_get_from_peer(call->conn->peer);
 | 
						_leave("");
 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (call->app_call_state) {
 | 
					 | 
				
			||||||
		/* we've received the last packet - drain all the data from the
 | 
					 | 
				
			||||||
		 * call */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_SRVR_GOT_ARGS:
 | 
					 | 
				
			||||||
		/* shouldn't be any args */
 | 
					 | 
				
			||||||
		ret = -EBADMSG;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* send the reply when asked for it */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_SRVR_SND_REPLY:
 | 
					 | 
				
			||||||
		/* invoke the actual service routine */
 | 
					 | 
				
			||||||
		ret = SRXAFSCM_InitCallBackState(server);
 | 
					 | 
				
			||||||
		if (ret < 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
 | 
					 | 
				
			||||||
					    GFP_KERNEL, 0, &count);
 | 
					 | 
				
			||||||
		if (ret < 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* operation complete */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_COMPLETE:
 | 
					 | 
				
			||||||
		call->app_user = NULL;
 | 
					 | 
				
			||||||
		removed = 0;
 | 
					 | 
				
			||||||
		spin_lock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
		if (!list_empty(&call->app_link)) {
 | 
					 | 
				
			||||||
			list_del_init(&call->app_link);
 | 
					 | 
				
			||||||
			removed = 1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		spin_unlock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (removed)
 | 
					 | 
				
			||||||
			rxrpc_put_call(call);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* operation terminated on error */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_ERROR:
 | 
					 | 
				
			||||||
		call->app_user = NULL;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		rxrpc_call_abort(call, ret);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_put_server(server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * handle a probe from a fileserver
 | 
					 * deliver request data to a CB.Probe call
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void _SRXAFSCM_Probe(struct rxrpc_call *call)
 | 
					static int afs_deliver_cb_probe(struct afs_call *call, struct sk_buff *skb,
 | 
				
			||||||
 | 
									bool last)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server *server;
 | 
						_enter(",{%u},%d", skb->len, last);
 | 
				
			||||||
	size_t count;
 | 
					 | 
				
			||||||
	int ret = 0, removed;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
 | 
						if (skb->len > 0)
 | 
				
			||||||
 | 
							return -EBADMSG;
 | 
				
			||||||
 | 
						if (!last)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server = afs_server_get_from_peer(call->conn->peer);
 | 
						/* no unmarshalling required */
 | 
				
			||||||
 | 
						call->state = AFS_CALL_REPLYING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (call->app_call_state) {
 | 
						INIT_WORK(&call->work, SRXAFSCB_Probe);
 | 
				
			||||||
		/* we've received the last packet - drain all the data from the
 | 
						schedule_work(&call->work);
 | 
				
			||||||
		 * call */
 | 
						return 0;
 | 
				
			||||||
	case RXRPC_CSTATE_SRVR_GOT_ARGS:
 | 
					 | 
				
			||||||
		/* shouldn't be any args */
 | 
					 | 
				
			||||||
		ret = -EBADMSG;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* send the reply when asked for it */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_SRVR_SND_REPLY:
 | 
					 | 
				
			||||||
		/* invoke the actual service routine */
 | 
					 | 
				
			||||||
		ret = SRXAFSCM_Probe(server);
 | 
					 | 
				
			||||||
		if (ret < 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
 | 
					 | 
				
			||||||
					    GFP_KERNEL, 0, &count);
 | 
					 | 
				
			||||||
		if (ret < 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* operation complete */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_COMPLETE:
 | 
					 | 
				
			||||||
		call->app_user = NULL;
 | 
					 | 
				
			||||||
		removed = 0;
 | 
					 | 
				
			||||||
		spin_lock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
		if (!list_empty(&call->app_link)) {
 | 
					 | 
				
			||||||
			list_del_init(&call->app_link);
 | 
					 | 
				
			||||||
			removed = 1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		spin_unlock(&afscm_calls_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (removed)
 | 
					 | 
				
			||||||
			rxrpc_put_call(call);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* operation terminated on error */
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_ERROR:
 | 
					 | 
				
			||||||
		call->app_user = NULL;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		rxrpc_call_abort(call, ret);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_put_server(server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
					 | 
				
			||||||
							
								
								
									
										284
									
								
								fs/afs/dir.c
									
									
									
									
									
								
							
							
						
						
									
										284
									
								
								fs/afs/dir.c
									
									
									
									
									
								
							| 
						 | 
					@ -15,11 +15,6 @@
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
#include <linux/pagemap.h>
 | 
					#include <linux/pagemap.h>
 | 
				
			||||||
#include <linux/smp_lock.h>
 | 
					 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include <rxrpc/call.h>
 | 
					 | 
				
			||||||
#include "super.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
 | 
					static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
| 
						 | 
					@ -129,7 +124,8 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) {
 | 
						if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) {
 | 
				
			||||||
		printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",
 | 
							printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",
 | 
				
			||||||
		       __FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages));
 | 
							       __FUNCTION__, dir->i_ino, qty,
 | 
				
			||||||
 | 
							       ntohs(dbuf->blocks[0].pagehdr.npages));
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -194,6 +190,7 @@ static struct page *afs_dir_get_page(struct inode *dir, unsigned long index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fail:
 | 
					fail:
 | 
				
			||||||
	afs_dir_put_page(page);
 | 
						afs_dir_put_page(page);
 | 
				
			||||||
 | 
						_leave(" = -EIO");
 | 
				
			||||||
	return ERR_PTR(-EIO);
 | 
						return ERR_PTR(-EIO);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,7 +204,7 @@ static int afs_dir_open(struct inode *inode, struct file *file)
 | 
				
			||||||
	BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
 | 
						BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
 | 
				
			||||||
	BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
 | 
						BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED)
 | 
						if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = 0");
 | 
						_leave(" = 0");
 | 
				
			||||||
| 
						 | 
					@ -242,7 +239,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
 | 
				
			||||||
		/* skip entries marked unused in the bitmap */
 | 
							/* skip entries marked unused in the bitmap */
 | 
				
			||||||
		if (!(block->pagehdr.bitmap[offset / 8] &
 | 
							if (!(block->pagehdr.bitmap[offset / 8] &
 | 
				
			||||||
		      (1 << (offset % 8)))) {
 | 
							      (1 << (offset % 8)))) {
 | 
				
			||||||
			_debug("ENT[%Zu.%u]: unused\n",
 | 
								_debug("ENT[%Zu.%u]: unused",
 | 
				
			||||||
			       blkoff / sizeof(union afs_dir_block), offset);
 | 
								       blkoff / sizeof(union afs_dir_block), offset);
 | 
				
			||||||
			if (offset >= curr)
 | 
								if (offset >= curr)
 | 
				
			||||||
				*fpos = blkoff +
 | 
									*fpos = blkoff +
 | 
				
			||||||
| 
						 | 
					@ -256,7 +253,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
 | 
				
			||||||
			       sizeof(*block) -
 | 
								       sizeof(*block) -
 | 
				
			||||||
			       offset * sizeof(union afs_dirent));
 | 
								       offset * sizeof(union afs_dirent));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n",
 | 
							_debug("ENT[%Zu.%u]: %s %Zu \"%s\"",
 | 
				
			||||||
		       blkoff / sizeof(union afs_dir_block), offset,
 | 
							       blkoff / sizeof(union afs_dir_block), offset,
 | 
				
			||||||
		       (offset < curr ? "skip" : "fill"),
 | 
							       (offset < curr ? "skip" : "fill"),
 | 
				
			||||||
		       nlen, dire->u.name);
 | 
							       nlen, dire->u.name);
 | 
				
			||||||
| 
						 | 
					@ -266,7 +263,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
 | 
				
			||||||
			if (next >= AFS_DIRENT_PER_BLOCK) {
 | 
								if (next >= AFS_DIRENT_PER_BLOCK) {
 | 
				
			||||||
				_debug("ENT[%Zu.%u]:"
 | 
									_debug("ENT[%Zu.%u]:"
 | 
				
			||||||
				       " %u travelled beyond end dir block"
 | 
									       " %u travelled beyond end dir block"
 | 
				
			||||||
				       " (len %u/%Zu)\n",
 | 
									       " (len %u/%Zu)",
 | 
				
			||||||
				       blkoff / sizeof(union afs_dir_block),
 | 
									       blkoff / sizeof(union afs_dir_block),
 | 
				
			||||||
				       offset, next, tmp, nlen);
 | 
									       offset, next, tmp, nlen);
 | 
				
			||||||
				return -EIO;
 | 
									return -EIO;
 | 
				
			||||||
| 
						 | 
					@ -274,13 +271,13 @@ static int afs_dir_iterate_block(unsigned *fpos,
 | 
				
			||||||
			if (!(block->pagehdr.bitmap[next / 8] &
 | 
								if (!(block->pagehdr.bitmap[next / 8] &
 | 
				
			||||||
			      (1 << (next % 8)))) {
 | 
								      (1 << (next % 8)))) {
 | 
				
			||||||
				_debug("ENT[%Zu.%u]:"
 | 
									_debug("ENT[%Zu.%u]:"
 | 
				
			||||||
				       " %u unmarked extension (len %u/%Zu)\n",
 | 
									       " %u unmarked extension (len %u/%Zu)",
 | 
				
			||||||
				       blkoff / sizeof(union afs_dir_block),
 | 
									       blkoff / sizeof(union afs_dir_block),
 | 
				
			||||||
				       offset, next, tmp, nlen);
 | 
									       offset, next, tmp, nlen);
 | 
				
			||||||
				return -EIO;
 | 
									return -EIO;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			_debug("ENT[%Zu.%u]: ext %u/%Zu\n",
 | 
								_debug("ENT[%Zu.%u]: ext %u/%Zu",
 | 
				
			||||||
			       blkoff / sizeof(union afs_dir_block),
 | 
								       blkoff / sizeof(union afs_dir_block),
 | 
				
			||||||
			       next, tmp, nlen);
 | 
								       next, tmp, nlen);
 | 
				
			||||||
			next++;
 | 
								next++;
 | 
				
			||||||
| 
						 | 
					@ -311,7 +308,7 @@ static int afs_dir_iterate_block(unsigned *fpos,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * read an AFS directory
 | 
					 * iterate through the data blob that lists the contents of an AFS directory
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
 | 
					static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
 | 
				
			||||||
			   filldir_t filldir)
 | 
								   filldir_t filldir)
 | 
				
			||||||
| 
						 | 
					@ -324,7 +321,7 @@ static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%lu},%u,,", dir->i_ino, *fpos);
 | 
						_enter("{%lu},%u,,", dir->i_ino, *fpos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
 | 
						if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
 | 
				
			||||||
		_leave(" = -ESTALE");
 | 
							_leave(" = -ESTALE");
 | 
				
			||||||
		return -ESTALE;
 | 
							return -ESTALE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -381,10 +378,12 @@ static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
 | 
				
			||||||
	unsigned fpos;
 | 
						unsigned fpos;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%Ld,{%lu}}", file->f_pos, file->f_path.dentry->d_inode->i_ino);
 | 
						_enter("{%Ld,{%lu}}",
 | 
				
			||||||
 | 
						       file->f_pos, file->f_path.dentry->d_inode->i_ino);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fpos = file->f_pos;
 | 
						fpos = file->f_pos;
 | 
				
			||||||
	ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, cookie, filldir);
 | 
						ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos,
 | 
				
			||||||
 | 
								      cookie, filldir);
 | 
				
			||||||
	file->f_pos = fpos;
 | 
						file->f_pos = fpos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
| 
						 | 
					@ -401,9 +400,13 @@ static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_dir_lookup_cookie *cookie = _cookie;
 | 
						struct afs_dir_lookup_cookie *cookie = _cookie;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%s,%Zu},%s,%u,,%lu,%u",
 | 
						_enter("{%s,%Zu},%s,%u,,%llu,%u",
 | 
				
			||||||
	       cookie->name, cookie->nlen, name, nlen, ino, dtype);
 | 
						       cookie->name, cookie->nlen, name, nlen, ino, dtype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* insanity checks first */
 | 
				
			||||||
 | 
						BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
 | 
				
			||||||
 | 
						BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) {
 | 
						if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) {
 | 
				
			||||||
		_leave(" = 0 [no]");
 | 
							_leave(" = 0 [no]");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
| 
						 | 
					@ -418,34 +421,17 @@ static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * look up an entry in a directory
 | 
					 * do a lookup in a directory
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
 | 
					static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
				     struct nameidata *nd)
 | 
								 struct afs_fid *fid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_dir_lookup_cookie cookie;
 | 
						struct afs_dir_lookup_cookie cookie;
 | 
				
			||||||
	struct afs_super_info *as;
 | 
						struct afs_super_info *as;
 | 
				
			||||||
	struct afs_vnode *vnode;
 | 
					 | 
				
			||||||
	struct inode *inode;
 | 
					 | 
				
			||||||
	unsigned fpos;
 | 
						unsigned fpos;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);
 | 
						_enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* insanity checks first */
 | 
					 | 
				
			||||||
	BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
 | 
					 | 
				
			||||||
	BUILD_BUG_ON(sizeof(union afs_dirent) != 32);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (dentry->d_name.len > 255) {
 | 
					 | 
				
			||||||
		_leave(" = -ENAMETOOLONG");
 | 
					 | 
				
			||||||
		return ERR_PTR(-ENAMETOOLONG);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vnode = AFS_FS_I(dir);
 | 
					 | 
				
			||||||
	if (vnode->flags & AFS_VNODE_DELETED) {
 | 
					 | 
				
			||||||
		_leave(" = -ESTALE");
 | 
					 | 
				
			||||||
		return ERR_PTR(-ESTALE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	as = dir->i_sb->s_fs_info;
 | 
						as = dir->i_sb->s_fs_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -458,54 +444,130 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
	fpos = 0;
 | 
						fpos = 0;
 | 
				
			||||||
	ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
 | 
						ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
		_leave(" = %d", ret);
 | 
							_leave(" = %d [iter]", ret);
 | 
				
			||||||
		return ERR_PTR(ret);
 | 
							return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = -ENOENT;
 | 
						ret = -ENOENT;
 | 
				
			||||||
	if (!cookie.found) {
 | 
						if (!cookie.found) {
 | 
				
			||||||
		_leave(" = %d", ret);
 | 
							_leave(" = -ENOENT [not found]");
 | 
				
			||||||
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*fid = cookie.fid;
 | 
				
			||||||
 | 
						_leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * look up an entry in a directory
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
 | 
									     struct nameidata *nd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_vnode *vnode;
 | 
				
			||||||
 | 
						struct afs_fid fid;
 | 
				
			||||||
 | 
						struct inode *inode;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dentry->d_name.len > 255) {
 | 
				
			||||||
 | 
							_leave(" = -ENAMETOOLONG");
 | 
				
			||||||
 | 
							return ERR_PTR(-ENAMETOOLONG);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vnode = AFS_FS_I(dir);
 | 
				
			||||||
 | 
						if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
 | 
				
			||||||
 | 
							_leave(" = -ESTALE");
 | 
				
			||||||
 | 
							return ERR_PTR(-ESTALE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = afs_do_lookup(dir, dentry, &fid);
 | 
				
			||||||
 | 
						if (ret < 0) {
 | 
				
			||||||
 | 
							_leave(" = %d [do]", ret);
 | 
				
			||||||
		return ERR_PTR(ret);
 | 
							return ERR_PTR(ret);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* instantiate the dentry */
 | 
						/* instantiate the dentry */
 | 
				
			||||||
	ret = afs_iget(dir->i_sb, &cookie.fid, &inode);
 | 
						inode = afs_iget(dir->i_sb, &fid);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (IS_ERR(inode)) {
 | 
				
			||||||
		_leave(" = %d", ret);
 | 
							_leave(" = %ld", PTR_ERR(inode));
 | 
				
			||||||
		return ERR_PTR(ret);
 | 
							return ERR_PTR(PTR_ERR(inode));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dentry->d_op = &afs_fs_dentry_operations;
 | 
						dentry->d_op = &afs_fs_dentry_operations;
 | 
				
			||||||
	dentry->d_fsdata = (void *) (unsigned long) vnode->status.version;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	d_add(dentry, inode);
 | 
						d_add(dentry, inode);
 | 
				
			||||||
	_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }",
 | 
						_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }",
 | 
				
			||||||
	       cookie.fid.vnode,
 | 
						       fid.vnode,
 | 
				
			||||||
	       cookie.fid.unique,
 | 
						       fid.unique,
 | 
				
			||||||
	       dentry->d_inode->i_ino,
 | 
						       dentry->d_inode->i_ino,
 | 
				
			||||||
	       dentry->d_inode->i_version);
 | 
						       dentry->d_inode->i_version);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * propagate changed and modified flags on a directory to all the children of
 | 
				
			||||||
 | 
					 * that directory as they may indicate that the ACL on the dir has changed,
 | 
				
			||||||
 | 
					 * potentially rendering the child inaccessible or that a file has been deleted
 | 
				
			||||||
 | 
					 * or renamed
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_propagate_dir_changes(struct dentry *dir)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dentry *child;
 | 
				
			||||||
 | 
						bool c, m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c = test_bit(AFS_VNODE_CHANGED, &AFS_FS_I(dir->d_inode)->flags);
 | 
				
			||||||
 | 
						m = test_bit(AFS_VNODE_MODIFIED, &AFS_FS_I(dir->d_inode)->flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("{%d,%d}", c, m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock(&dir->d_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) {
 | 
				
			||||||
 | 
							if (child->d_inode) {
 | 
				
			||||||
 | 
								struct afs_vnode *vnode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_debug("tag %s", child->d_name.name);
 | 
				
			||||||
 | 
								vnode = AFS_FS_I(child->d_inode);
 | 
				
			||||||
 | 
								if (c)
 | 
				
			||||||
 | 
									set_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags);
 | 
				
			||||||
 | 
								if (m)
 | 
				
			||||||
 | 
									set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_unlock(&dir->d_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * check that a dentry lookup hit has found a valid entry
 | 
					 * check that a dentry lookup hit has found a valid entry
 | 
				
			||||||
 * - NOTE! the hit can be a negative hit too, so we can't assume we have an
 | 
					 * - NOTE! the hit can be a negative hit too, so we can't assume we have an
 | 
				
			||||||
 *   inode
 | 
					 *   inode
 | 
				
			||||||
 * (derived from nfs_lookup_revalidate)
 | 
					 * - there are several things we need to check
 | 
				
			||||||
 | 
					 *   - parent dir data changes (rm, rmdir, rename, mkdir, create, link,
 | 
				
			||||||
 | 
					 *     symlink)
 | 
				
			||||||
 | 
					 *   - parent dir metadata changed (security changes)
 | 
				
			||||||
 | 
					 *   - dentry data changed (write, truncate)
 | 
				
			||||||
 | 
					 *   - dentry metadata changed (security changes)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 | 
					static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_dir_lookup_cookie cookie;
 | 
						struct afs_vnode *vnode;
 | 
				
			||||||
 | 
						struct afs_fid fid;
 | 
				
			||||||
	struct dentry *parent;
 | 
						struct dentry *parent;
 | 
				
			||||||
	struct inode *inode, *dir;
 | 
						struct inode *inode, *dir;
 | 
				
			||||||
	unsigned fpos;
 | 
					 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name);
 | 
						vnode = AFS_FS_I(dentry->d_inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("{sb=%p n=%s fl=%lx},",
 | 
				
			||||||
 | 
						       dentry->d_sb, dentry->d_name.name, vnode->flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* lock down the parent dentry so we can peer at it */
 | 
						/* lock down the parent dentry so we can peer at it */
 | 
				
			||||||
	parent = dget_parent(dentry->d_parent);
 | 
						parent = dget_parent(dentry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dir = parent->d_inode;
 | 
						dir = parent->d_inode;
 | 
				
			||||||
	inode = dentry->d_inode;
 | 
						inode = dentry->d_inode;
 | 
				
			||||||
| 
						 | 
					@ -517,81 +579,92 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 | 
				
			||||||
	/* handle a bad inode */
 | 
						/* handle a bad inode */
 | 
				
			||||||
	if (is_bad_inode(inode)) {
 | 
						if (is_bad_inode(inode)) {
 | 
				
			||||||
		printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
 | 
							printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
 | 
				
			||||||
		       dentry->d_parent->d_name.name, dentry->d_name.name);
 | 
							       parent->d_name.name, dentry->d_name.name);
 | 
				
			||||||
		goto out_bad;
 | 
							goto out_bad;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* force a full look up if the parent directory changed since last the
 | 
						/* check that this dirent still exists if the directory's contents were
 | 
				
			||||||
	 * server was consulted
 | 
						 * modified */
 | 
				
			||||||
	 * - otherwise this inode must still exist, even if the inode details
 | 
						if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
 | 
				
			||||||
	 *   themselves have changed
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED)
 | 
					 | 
				
			||||||
		afs_vnode_fetch_status(AFS_FS_I(dir));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
 | 
					 | 
				
			||||||
		_debug("%s: parent dir deleted", dentry->d_name.name);
 | 
							_debug("%s: parent dir deleted", dentry->d_name.name);
 | 
				
			||||||
		goto out_bad;
 | 
							goto out_bad;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) {
 | 
						if (test_and_clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags)) {
 | 
				
			||||||
		_debug("%s: file already deleted", dentry->d_name.name);
 | 
							/* rm/rmdir/rename may have occurred */
 | 
				
			||||||
		goto out_bad;
 | 
							_debug("dir modified");
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((unsigned long) dentry->d_fsdata !=
 | 
					 | 
				
			||||||
	    (unsigned long) AFS_FS_I(dir)->status.version) {
 | 
					 | 
				
			||||||
		_debug("%s: parent changed %lu -> %u",
 | 
					 | 
				
			||||||
		       dentry->d_name.name,
 | 
					 | 
				
			||||||
		       (unsigned long) dentry->d_fsdata,
 | 
					 | 
				
			||||||
		       (unsigned) AFS_FS_I(dir)->status.version);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* search the directory for this vnode */
 | 
							/* search the directory for this vnode */
 | 
				
			||||||
		cookie.name	= dentry->d_name.name;
 | 
							ret = afs_do_lookup(dir, dentry, &fid);
 | 
				
			||||||
		cookie.nlen	= dentry->d_name.len;
 | 
							if (ret == -ENOENT) {
 | 
				
			||||||
		cookie.fid.vid	= AFS_FS_I(inode)->volume->vid;
 | 
								_debug("%s: dirent not found", dentry->d_name.name);
 | 
				
			||||||
		cookie.found	= 0;
 | 
								goto not_found;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		fpos = 0;
 | 
					 | 
				
			||||||
		ret = afs_dir_iterate(dir, &fpos, &cookie,
 | 
					 | 
				
			||||||
				      afs_dir_lookup_filldir);
 | 
					 | 
				
			||||||
		if (ret < 0) {
 | 
							if (ret < 0) {
 | 
				
			||||||
			_debug("failed to iterate dir %s: %d",
 | 
								_debug("failed to iterate dir %s: %d",
 | 
				
			||||||
			       parent->d_name.name, ret);
 | 
								       parent->d_name.name, ret);
 | 
				
			||||||
			goto out_bad;
 | 
								goto out_bad;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!cookie.found) {
 | 
					 | 
				
			||||||
			_debug("%s: dirent not found", dentry->d_name.name);
 | 
					 | 
				
			||||||
			goto not_found;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* if the vnode ID has changed, then the dirent points to a
 | 
							/* if the vnode ID has changed, then the dirent points to a
 | 
				
			||||||
		 * different file */
 | 
							 * different file */
 | 
				
			||||||
		if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) {
 | 
							if (fid.vnode != vnode->fid.vnode) {
 | 
				
			||||||
			_debug("%s: dirent changed", dentry->d_name.name);
 | 
								_debug("%s: dirent changed [%u != %u]",
 | 
				
			||||||
 | 
								       dentry->d_name.name, fid.vnode,
 | 
				
			||||||
 | 
								       vnode->fid.vnode);
 | 
				
			||||||
			goto not_found;
 | 
								goto not_found;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* if the vnode ID uniqifier has changed, then the file has
 | 
							/* if the vnode ID uniqifier has changed, then the file has
 | 
				
			||||||
		 * been deleted */
 | 
							 * been deleted */
 | 
				
			||||||
		if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) {
 | 
							if (fid.unique != vnode->fid.unique) {
 | 
				
			||||||
			_debug("%s: file deleted (uq %u -> %u I:%lu)",
 | 
								_debug("%s: file deleted (uq %u -> %u I:%lu)",
 | 
				
			||||||
			       dentry->d_name.name,
 | 
								       dentry->d_name.name, fid.unique,
 | 
				
			||||||
			       cookie.fid.unique,
 | 
								       vnode->fid.unique, inode->i_version);
 | 
				
			||||||
			       AFS_FS_I(inode)->fid.unique,
 | 
								spin_lock(&vnode->lock);
 | 
				
			||||||
			       inode->i_version);
 | 
								set_bit(AFS_VNODE_DELETED, &vnode->flags);
 | 
				
			||||||
			spin_lock(&AFS_FS_I(inode)->lock);
 | 
								spin_unlock(&vnode->lock);
 | 
				
			||||||
			AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED;
 | 
					 | 
				
			||||||
			spin_unlock(&AFS_FS_I(inode)->lock);
 | 
					 | 
				
			||||||
			invalidate_remote_inode(inode);
 | 
								invalidate_remote_inode(inode);
 | 
				
			||||||
			goto out_bad;
 | 
								goto out_bad;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		dentry->d_fsdata =
 | 
					 | 
				
			||||||
			(void *) (unsigned long) AFS_FS_I(dir)->status.version;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* if the directory's metadata were changed then the security may be
 | 
				
			||||||
 | 
						 * different and we may no longer have access */
 | 
				
			||||||
 | 
						mutex_lock(&vnode->cb_broken_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_and_clear_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags) ||
 | 
				
			||||||
 | 
						    test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
 | 
				
			||||||
 | 
							_debug("%s: changed", dentry->d_name.name);
 | 
				
			||||||
 | 
							set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
 | 
				
			||||||
 | 
							if (afs_vnode_fetch_status(vnode) < 0) {
 | 
				
			||||||
 | 
								mutex_unlock(&vnode->cb_broken_lock);
 | 
				
			||||||
 | 
								goto out_bad;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
 | 
				
			||||||
 | 
							_debug("%s: file already deleted", dentry->d_name.name);
 | 
				
			||||||
 | 
							mutex_unlock(&vnode->cb_broken_lock);
 | 
				
			||||||
 | 
							goto out_bad;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* if the vnode's data version number changed then its contents are
 | 
				
			||||||
 | 
						 * different */
 | 
				
			||||||
 | 
						if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
 | 
				
			||||||
 | 
							_debug("zap data");
 | 
				
			||||||
 | 
							invalidate_remote_inode(inode);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (S_ISDIR(inode->i_mode) &&
 | 
				
			||||||
 | 
						    (test_bit(AFS_VNODE_CHANGED, &vnode->flags) ||
 | 
				
			||||||
 | 
						     test_bit(AFS_VNODE_MODIFIED, &vnode->flags)))
 | 
				
			||||||
 | 
							afs_propagate_dir_changes(dentry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clear_bit(AFS_VNODE_CHANGED, &vnode->flags);
 | 
				
			||||||
 | 
						clear_bit(AFS_VNODE_MODIFIED, &vnode->flags);
 | 
				
			||||||
 | 
						mutex_unlock(&vnode->cb_broken_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_valid:
 | 
					out_valid:
 | 
				
			||||||
	dput(parent);
 | 
						dput(parent);
 | 
				
			||||||
	_leave(" = 1 [valid]");
 | 
						_leave(" = 1 [valid]");
 | 
				
			||||||
| 
						 | 
					@ -610,12 +683,10 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 | 
				
			||||||
			goto out_valid;
 | 
								goto out_valid;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	shrink_dcache_parent(dentry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_debug("dropping dentry %s/%s",
 | 
						_debug("dropping dentry %s/%s",
 | 
				
			||||||
	       dentry->d_parent->d_name.name, dentry->d_name.name);
 | 
						       parent->d_name.name, dentry->d_name.name);
 | 
				
			||||||
 | 
						shrink_dcache_parent(dentry);
 | 
				
			||||||
	d_drop(dentry);
 | 
						d_drop(dentry);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	dput(parent);
 | 
						dput(parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = 0 [bad]");
 | 
						_leave(" = 0 [bad]");
 | 
				
			||||||
| 
						 | 
					@ -635,10 +706,9 @@ static int afs_d_delete(struct dentry *dentry)
 | 
				
			||||||
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
 | 
						if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
 | 
				
			||||||
		goto zap;
 | 
							goto zap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dentry->d_inode) {
 | 
						if (dentry->d_inode &&
 | 
				
			||||||
		if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED)
 | 
						    test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags))
 | 
				
			||||||
			goto zap;
 | 
								goto zap;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = 0 [keep]");
 | 
						_leave(" = 0 [keep]");
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* file.c: AFS filesystem file handling
 | 
					/* AFS filesystem file handling
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -15,9 +15,6 @@
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
#include <linux/pagemap.h>
 | 
					#include <linux/pagemap.h>
 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include <rxrpc/call.h>
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
| 
						 | 
					@ -80,12 +77,10 @@ static void afs_file_readpage_write_complete(void *cookie_data,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_file_readpage(struct file *file, struct page *page)
 | 
					static int afs_file_readpage(struct file *file, struct page *page)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_rxfs_fetch_descriptor desc;
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
	struct cachefs_page *pageio;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	struct afs_vnode *vnode;
 | 
						struct afs_vnode *vnode;
 | 
				
			||||||
	struct inode *inode;
 | 
						struct inode *inode;
 | 
				
			||||||
 | 
						size_t len;
 | 
				
			||||||
 | 
						off_t offset;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inode = page->mapping->host;
 | 
						inode = page->mapping->host;
 | 
				
			||||||
| 
						 | 
					@ -97,14 +92,10 @@ static int afs_file_readpage(struct file *file, struct page *page)
 | 
				
			||||||
	BUG_ON(!PageLocked(page));
 | 
						BUG_ON(!PageLocked(page));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = -ESTALE;
 | 
						ret = -ESTALE;
 | 
				
			||||||
	if (vnode->flags & AFS_VNODE_DELETED)
 | 
						if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
	ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* is it cached? */
 | 
						/* is it cached? */
 | 
				
			||||||
	ret = cachefs_read_or_alloc_page(vnode->cache,
 | 
						ret = cachefs_read_or_alloc_page(vnode->cache,
 | 
				
			||||||
					 page,
 | 
										 page,
 | 
				
			||||||
| 
						 | 
					@ -128,26 +119,19 @@ static int afs_file_readpage(struct file *file, struct page *page)
 | 
				
			||||||
	case -ENOBUFS:
 | 
						case -ENOBUFS:
 | 
				
			||||||
	case -ENODATA:
 | 
						case -ENODATA:
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		desc.fid	= vnode->fid;
 | 
							offset = page->index << PAGE_CACHE_SHIFT;
 | 
				
			||||||
		desc.offset	= page->index << PAGE_CACHE_SHIFT;
 | 
							len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
 | 
				
			||||||
		desc.size	= min((size_t) (inode->i_size - desc.offset),
 | 
					 | 
				
			||||||
				      (size_t) PAGE_SIZE);
 | 
					 | 
				
			||||||
		desc.buffer	= kmap(page);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		clear_page(desc.buffer);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* read the contents of the file from the server into the
 | 
							/* read the contents of the file from the server into the
 | 
				
			||||||
		 * page */
 | 
							 * page */
 | 
				
			||||||
		ret = afs_vnode_fetch_data(vnode, &desc);
 | 
							ret = afs_vnode_fetch_data(vnode, offset, len, page);
 | 
				
			||||||
		kunmap(page);
 | 
					 | 
				
			||||||
		if (ret < 0) {
 | 
							if (ret < 0) {
 | 
				
			||||||
			if (ret == -ENOENT) {
 | 
								if (ret == -ENOENT) {
 | 
				
			||||||
				_debug("got NOENT from server"
 | 
									_debug("got NOENT from server"
 | 
				
			||||||
				       " - marking file deleted and stale");
 | 
									       " - marking file deleted and stale");
 | 
				
			||||||
				vnode->flags |= AFS_VNODE_DELETED;
 | 
									set_bit(AFS_VNODE_DELETED, &vnode->flags);
 | 
				
			||||||
				ret = -ESTALE;
 | 
									ret = -ESTALE;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
			cachefs_uncache_page(vnode->cache, page);
 | 
								cachefs_uncache_page(vnode->cache, page);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -177,7 +161,6 @@ static int afs_file_readpage(struct file *file, struct page *page)
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	SetPageError(page);
 | 
						SetPageError(page);
 | 
				
			||||||
	unlock_page(page);
 | 
						unlock_page(page);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1038
									
								
								fs/afs/fsclient.c
									
									
									
									
									
								
							
							
						
						
									
										1038
									
								
								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 */
 | 
					 | 
				
			||||||
							
								
								
									
										105
									
								
								fs/afs/inode.c
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								fs/afs/inode.c
									
									
									
									
									
								
							| 
						 | 
					@ -19,9 +19,6 @@
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
#include <linux/pagemap.h>
 | 
					#include <linux/pagemap.h>
 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include "super.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct afs_iget_data {
 | 
					struct afs_iget_data {
 | 
				
			||||||
| 
						 | 
					@ -40,7 +37,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
 | 
				
			||||||
	       vnode->status.type,
 | 
						       vnode->status.type,
 | 
				
			||||||
	       vnode->status.nlink,
 | 
						       vnode->status.nlink,
 | 
				
			||||||
	       vnode->status.size,
 | 
						       vnode->status.size,
 | 
				
			||||||
	       vnode->status.version,
 | 
						       vnode->status.data_version,
 | 
				
			||||||
	       vnode->status.mode);
 | 
						       vnode->status.mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (vnode->status.type) {
 | 
						switch (vnode->status.type) {
 | 
				
			||||||
| 
						 | 
					@ -78,7 +75,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
 | 
				
			||||||
	if (vnode->status.type == AFS_FTYPE_SYMLINK) {
 | 
						if (vnode->status.type == AFS_FTYPE_SYMLINK) {
 | 
				
			||||||
		afs_mntpt_check_symlink(vnode);
 | 
							afs_mntpt_check_symlink(vnode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (vnode->flags & AFS_VNODE_MOUNTPOINT) {
 | 
							if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) {
 | 
				
			||||||
			inode->i_mode	= S_IFDIR | vnode->status.mode;
 | 
								inode->i_mode	= S_IFDIR | vnode->status.mode;
 | 
				
			||||||
			inode->i_op	= &afs_mntpt_inode_operations;
 | 
								inode->i_op	= &afs_mntpt_inode_operations;
 | 
				
			||||||
			inode->i_fop	= &afs_mntpt_file_operations;
 | 
								inode->i_fop	= &afs_mntpt_file_operations;
 | 
				
			||||||
| 
						 | 
					@ -88,25 +85,6 @@ static int afs_inode_map_status(struct afs_vnode *vnode)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * attempt to fetch the status of an inode, coelescing multiple simultaneous
 | 
					 | 
				
			||||||
 * fetches
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static int afs_inode_fetch_status(struct inode *inode)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_vnode *vnode;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vnode = AFS_FS_I(inode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = afs_vnode_fetch_status(vnode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ret == 0)
 | 
					 | 
				
			||||||
		ret = afs_inode_map_status(vnode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * iget5() comparator
 | 
					 * iget5() comparator
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -137,8 +115,7 @@ static int afs_iget5_set(struct inode *inode, void *opaque)
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * inode retrieval
 | 
					 * inode retrieval
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
 | 
					inline struct inode *afs_iget(struct super_block *sb, struct afs_fid *fid)
 | 
				
			||||||
		    struct inode **_inode)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_iget_data data = { .fid = *fid };
 | 
						struct afs_iget_data data = { .fid = *fid };
 | 
				
			||||||
	struct afs_super_info *as;
 | 
						struct afs_super_info *as;
 | 
				
			||||||
| 
						 | 
					@ -155,20 +132,18 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
 | 
				
			||||||
			     &data);
 | 
								     &data);
 | 
				
			||||||
	if (!inode) {
 | 
						if (!inode) {
 | 
				
			||||||
		_leave(" = -ENOMEM");
 | 
							_leave(" = -ENOMEM");
 | 
				
			||||||
		return -ENOMEM;
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("GOT INODE %p { vl=%x vn=%x, u=%x }",
 | 
				
			||||||
 | 
						       inode, fid->vid, fid->vnode, fid->unique);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vnode = AFS_FS_I(inode);
 | 
						vnode = AFS_FS_I(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* deal with an existing inode */
 | 
						/* deal with an existing inode */
 | 
				
			||||||
	if (!(inode->i_state & I_NEW)) {
 | 
						if (!(inode->i_state & I_NEW)) {
 | 
				
			||||||
		ret = afs_vnode_fetch_status(vnode);
 | 
							_leave(" = %p", inode);
 | 
				
			||||||
		if (ret == 0)
 | 
							return inode;
 | 
				
			||||||
			*_inode = inode;
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			iput(inode);
 | 
					 | 
				
			||||||
		_leave(" = %d", ret);
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
| 
						 | 
					@ -181,21 +156,19 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* okay... it's a new inode */
 | 
						/* okay... it's a new inode */
 | 
				
			||||||
	inode->i_flags |= S_NOATIME;
 | 
						set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
 | 
				
			||||||
	vnode->flags |= AFS_VNODE_CHANGED;
 | 
						ret = afs_vnode_fetch_status(vnode);
 | 
				
			||||||
	ret = afs_inode_fetch_status(inode);
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							goto bad_inode;
 | 
				
			||||||
 | 
						ret = afs_inode_map_status(vnode);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		goto bad_inode;
 | 
							goto bad_inode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* success */
 | 
						/* success */
 | 
				
			||||||
 | 
						inode->i_flags |= S_NOATIME;
 | 
				
			||||||
	unlock_new_inode(inode);
 | 
						unlock_new_inode(inode);
 | 
				
			||||||
 | 
						_leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type);
 | 
				
			||||||
	*_inode = inode;
 | 
						return inode;
 | 
				
			||||||
	_leave(" = 0 [CB { v=%u x=%lu t=%u }]",
 | 
					 | 
				
			||||||
	       vnode->cb_version,
 | 
					 | 
				
			||||||
	       vnode->cb_timeout.timo_jif,
 | 
					 | 
				
			||||||
	       vnode->cb_type);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* failure */
 | 
						/* failure */
 | 
				
			||||||
bad_inode:
 | 
					bad_inode:
 | 
				
			||||||
| 
						 | 
					@ -204,7 +177,7 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
 | 
				
			||||||
	iput(inode);
 | 
						iput(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave(" = %d [bad]", ret);
 | 
						_leave(" = %d [bad]", ret);
 | 
				
			||||||
	return ret;
 | 
						return ERR_PTR(ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -213,36 +186,13 @@ inline int afs_iget(struct super_block *sb, struct afs_fid *fid,
 | 
				
			||||||
int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry,
 | 
					int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry,
 | 
				
			||||||
		      struct kstat *stat)
 | 
							      struct kstat *stat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vnode *vnode;
 | 
					 | 
				
			||||||
	struct inode *inode;
 | 
						struct inode *inode;
 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inode = dentry->d_inode;
 | 
						inode = dentry->d_inode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{ ino=%lu v=%lu }", inode->i_ino, inode->i_version);
 | 
						_enter("{ ino=%lu v=%lu }", inode->i_ino, inode->i_version);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vnode = AFS_FS_I(inode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = afs_inode_fetch_status(inode);
 | 
					 | 
				
			||||||
	if (ret == -ENOENT) {
 | 
					 | 
				
			||||||
		_leave(" = %d [%d %p]",
 | 
					 | 
				
			||||||
		       ret, atomic_read(&dentry->d_count), dentry->d_inode);
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
	} else if (ret < 0) {
 | 
					 | 
				
			||||||
		make_bad_inode(inode);
 | 
					 | 
				
			||||||
		_leave(" = %d", ret);
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* transfer attributes from the inode structure to the stat
 | 
					 | 
				
			||||||
	 * structure */
 | 
					 | 
				
			||||||
	generic_fillattr(inode, stat);
 | 
						generic_fillattr(inode, stat);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = 0 CB { v=%u x=%u t=%u }",
 | 
					 | 
				
			||||||
	       vnode->cb_version,
 | 
					 | 
				
			||||||
	       vnode->cb_expiry,
 | 
					 | 
				
			||||||
	       vnode->cb_type);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -260,12 +210,23 @@ void afs_clear_inode(struct inode *inode)
 | 
				
			||||||
	       vnode->fid.vnode,
 | 
						       vnode->fid.vnode,
 | 
				
			||||||
	       vnode->cb_version,
 | 
						       vnode->cb_version,
 | 
				
			||||||
	       vnode->cb_expiry,
 | 
						       vnode->cb_expiry,
 | 
				
			||||||
	       vnode->cb_type
 | 
						       vnode->cb_type);
 | 
				
			||||||
	       );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(inode->i_ino != vnode->fid.vnode);
 | 
						_debug("CLEAR INODE %p", inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_vnode_give_up_callback(vnode);
 | 
						ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_give_up_callback(vnode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (vnode->server) {
 | 
				
			||||||
 | 
							spin_lock(&vnode->server->fs_lock);
 | 
				
			||||||
 | 
							rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes);
 | 
				
			||||||
 | 
							spin_unlock(&vnode->server->fs_lock);
 | 
				
			||||||
 | 
							afs_put_server(vnode->server);
 | 
				
			||||||
 | 
							vnode->server = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(!vnode->cb_promised);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
	cachefs_relinquish_cookie(vnode->cache, 0);
 | 
						cachefs_relinquish_cookie(vnode->cache, 0);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* internal AFS stuff
 | 
					/* internal AFS stuff
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -9,47 +9,320 @@
 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 * 2 of the License, or (at your option) any later version.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef AFS_INTERNAL_H
 | 
					 | 
				
			||||||
#define AFS_INTERNAL_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <linux/compiler.h>
 | 
					#include <linux/compiler.h>
 | 
				
			||||||
#include <linux/kernel.h>
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
#include <linux/pagemap.h>
 | 
					#include <linux/pagemap.h>
 | 
				
			||||||
 | 
					#include <linux/skbuff.h>
 | 
				
			||||||
 | 
					#include <linux/rxrpc.h>
 | 
				
			||||||
 | 
					#include "afs.h"
 | 
				
			||||||
 | 
					#include "afs_vl.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AFS_CELL_MAX_ADDRS 15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct afs_call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
						AFS_VL_NEW,			/* new, uninitialised record */
 | 
				
			||||||
 | 
						AFS_VL_CREATING,		/* creating record */
 | 
				
			||||||
 | 
						AFS_VL_VALID,			/* record is pending */
 | 
				
			||||||
 | 
						AFS_VL_NO_VOLUME,		/* no such volume available */
 | 
				
			||||||
 | 
						AFS_VL_UPDATING,		/* update in progress */
 | 
				
			||||||
 | 
						AFS_VL_VOLUME_DELETED,		/* volume was deleted */
 | 
				
			||||||
 | 
						AFS_VL_UNCERTAIN,		/* uncertain state (update failed) */
 | 
				
			||||||
 | 
					} __attribute__((packed)) afs_vlocation_state_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * debug tracing
 | 
					 * definition of how to wait for the completion of an operation
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#define kenter(FMT, a...)	printk("==> %s("FMT")\n",__FUNCTION__ , ## a)
 | 
					struct afs_wait_mode {
 | 
				
			||||||
#define kleave(FMT, a...)	printk("<== %s()"FMT"\n",__FUNCTION__ , ## a)
 | 
						/* RxRPC received message notification */
 | 
				
			||||||
#define kdebug(FMT, a...)	printk(FMT"\n" , ## a)
 | 
						void (*rx_wakeup)(struct afs_call *call);
 | 
				
			||||||
#define kproto(FMT, a...)	printk("### "FMT"\n" , ## a)
 | 
					 | 
				
			||||||
#define knet(FMT, a...)		printk(FMT"\n" , ## a)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __KDEBUG
 | 
						/* synchronous call waiter and call dispatched notification */
 | 
				
			||||||
#define _enter(FMT, a...)	kenter(FMT , ## a)
 | 
						int (*wait)(struct afs_call *call);
 | 
				
			||||||
#define _leave(FMT, a...)	kleave(FMT , ## a)
 | 
					
 | 
				
			||||||
#define _debug(FMT, a...)	kdebug(FMT , ## a)
 | 
						/* asynchronous call completion */
 | 
				
			||||||
#define _proto(FMT, a...)	kproto(FMT , ## a)
 | 
						void (*async_complete)(void *reply, int error);
 | 
				
			||||||
#define _net(FMT, a...)		knet(FMT , ## a)
 | 
					};
 | 
				
			||||||
#else
 | 
					
 | 
				
			||||||
#define _enter(FMT, a...)	do { } while(0)
 | 
					extern const struct afs_wait_mode afs_sync_call;
 | 
				
			||||||
#define _leave(FMT, a...)	do { } while(0)
 | 
					extern const struct afs_wait_mode afs_async_call;
 | 
				
			||||||
#define _debug(FMT, a...)	do { } while(0)
 | 
					
 | 
				
			||||||
#define _proto(FMT, a...)	do { } while(0)
 | 
					/*
 | 
				
			||||||
#define _net(FMT, a...)		do { } while(0)
 | 
					 * a record of an in-progress RxRPC call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_call {
 | 
				
			||||||
 | 
						const struct afs_call_type *type;	/* type of call */
 | 
				
			||||||
 | 
						const struct afs_wait_mode *wait_mode;	/* completion wait mode */
 | 
				
			||||||
 | 
						wait_queue_head_t	waitq;		/* processes awaiting completion */
 | 
				
			||||||
 | 
						struct work_struct	async_work;	/* asynchronous work processor */
 | 
				
			||||||
 | 
						struct work_struct	work;		/* actual work processor */
 | 
				
			||||||
 | 
						struct sk_buff_head	rx_queue;	/* received packets */
 | 
				
			||||||
 | 
						struct rxrpc_call	*rxcall;	/* RxRPC call handle */
 | 
				
			||||||
 | 
						struct key		*key;		/* security for this call */
 | 
				
			||||||
 | 
						struct afs_server	*server;	/* server affected by incoming CM call */
 | 
				
			||||||
 | 
						void			*request;	/* request data (first part) */
 | 
				
			||||||
 | 
						void			*request2;	/* request data (second part) */
 | 
				
			||||||
 | 
						void			*buffer;	/* reply receive buffer */
 | 
				
			||||||
 | 
						void			*reply;		/* reply buffer (first part) */
 | 
				
			||||||
 | 
						void			*reply2;	/* reply buffer (second part) */
 | 
				
			||||||
 | 
						void			*reply3;	/* reply buffer (third part) */
 | 
				
			||||||
 | 
						enum {					/* call state */
 | 
				
			||||||
 | 
							AFS_CALL_REQUESTING,	/* request is being sent for outgoing call */
 | 
				
			||||||
 | 
							AFS_CALL_AWAIT_REPLY,	/* awaiting reply to outgoing call */
 | 
				
			||||||
 | 
							AFS_CALL_AWAIT_OP_ID,	/* awaiting op ID on incoming call */
 | 
				
			||||||
 | 
							AFS_CALL_AWAIT_REQUEST,	/* awaiting request data on incoming call */
 | 
				
			||||||
 | 
							AFS_CALL_REPLYING,	/* replying to incoming call */
 | 
				
			||||||
 | 
							AFS_CALL_AWAIT_ACK,	/* awaiting final ACK of incoming call */
 | 
				
			||||||
 | 
							AFS_CALL_COMPLETE,	/* successfully completed */
 | 
				
			||||||
 | 
							AFS_CALL_BUSY,		/* server was busy */
 | 
				
			||||||
 | 
							AFS_CALL_ABORTED,	/* call was aborted */
 | 
				
			||||||
 | 
							AFS_CALL_ERROR,		/* call failed due to error */
 | 
				
			||||||
 | 
						}			state;
 | 
				
			||||||
 | 
						int			error;		/* error code */
 | 
				
			||||||
 | 
						unsigned		request_size;	/* size of request data */
 | 
				
			||||||
 | 
						unsigned		reply_max;	/* maximum size of reply */
 | 
				
			||||||
 | 
						unsigned		reply_size;	/* current size of reply */
 | 
				
			||||||
 | 
						unsigned short		offset;		/* offset into received data store */
 | 
				
			||||||
 | 
						unsigned char		unmarshall;	/* unmarshalling phase */
 | 
				
			||||||
 | 
						bool			incoming;	/* T if incoming call */
 | 
				
			||||||
 | 
						u16			service_id;	/* RxRPC service ID to call */
 | 
				
			||||||
 | 
						__be16			port;		/* target UDP port */
 | 
				
			||||||
 | 
						__be32			operation_ID;	/* operation ID for an incoming call */
 | 
				
			||||||
 | 
						u32			count;		/* count for use in unmarshalling */
 | 
				
			||||||
 | 
						__be32			tmp;		/* place to extract temporary data */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct afs_call_type {
 | 
				
			||||||
 | 
						/* deliver request or reply data to an call
 | 
				
			||||||
 | 
						 * - returning an error will cause the call to be aborted
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						int (*deliver)(struct afs_call *call, struct sk_buff *skb,
 | 
				
			||||||
 | 
							       bool last);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* map an abort code to an error number */
 | 
				
			||||||
 | 
						int (*abort_to_error)(u32 abort_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* clean up a call */
 | 
				
			||||||
 | 
						void (*destructor)(struct afs_call *call);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * AFS superblock private data
 | 
				
			||||||
 | 
					 * - there's one superblock per volume
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_super_info {
 | 
				
			||||||
 | 
						struct afs_volume	*volume;	/* volume record */
 | 
				
			||||||
 | 
						char			rwparent;	/* T if parent is R/W AFS volume */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return sb->s_fs_info;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern struct file_system_type afs_fs_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * entry in the cached cell catalogue
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_cache_cell {
 | 
				
			||||||
 | 
						char			name[64];	/* cell name (padded with NULs) */
 | 
				
			||||||
 | 
						struct in_addr		vl_servers[15];	/* cached cell VL servers */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * AFS cell record
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_cell {
 | 
				
			||||||
 | 
						atomic_t		usage;
 | 
				
			||||||
 | 
						struct list_head	link;		/* main cell list link */
 | 
				
			||||||
 | 
						struct list_head	proc_link;	/* /proc cell list link */
 | 
				
			||||||
 | 
						struct proc_dir_entry	*proc_dir;	/* /proc dir for this cell */
 | 
				
			||||||
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
 | 
						struct cachefs_cookie	*cache;		/* caching cookie */
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void afs_discard_my_signals(void)
 | 
						/* server record management */
 | 
				
			||||||
{
 | 
						rwlock_t		servers_lock;	/* active server list lock */
 | 
				
			||||||
	while (signal_pending(current)) {
 | 
						struct list_head	servers;	/* active server list */
 | 
				
			||||||
		siginfo_t sinfo;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		spin_lock_irq(¤t->sighand->siglock);
 | 
						/* volume location record management */
 | 
				
			||||||
		dequeue_signal(current,¤t->blocked, &sinfo);
 | 
						struct rw_semaphore	vl_sem;		/* volume management serialisation semaphore */
 | 
				
			||||||
		spin_unlock_irq(¤t->sighand->siglock);
 | 
						struct list_head	vl_list;	/* cell's active VL record list */
 | 
				
			||||||
	}
 | 
						spinlock_t		vl_lock;	/* vl_list lock */
 | 
				
			||||||
}
 | 
						unsigned short		vl_naddrs;	/* number of VL servers in addr list */
 | 
				
			||||||
 | 
						unsigned short		vl_curr_svix;	/* current server index */
 | 
				
			||||||
 | 
						struct in_addr		vl_addrs[AFS_CELL_MAX_ADDRS];	/* cell VL server addresses */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char			name[0];	/* cell name - must go last */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * entry in the cached volume location catalogue
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_cache_vlocation {
 | 
				
			||||||
 | 
						uint8_t			name[64 + 1];	/* volume name (lowercase, padded with NULs) */
 | 
				
			||||||
 | 
						uint8_t			nservers;	/* number of entries used in servers[] */
 | 
				
			||||||
 | 
						uint8_t			vidmask;	/* voltype mask for vid[] */
 | 
				
			||||||
 | 
						uint8_t			srvtmask[8];	/* voltype masks for servers[] */
 | 
				
			||||||
 | 
					#define AFS_VOL_VTM_RW	0x01 /* R/W version of the volume is available (on this server) */
 | 
				
			||||||
 | 
					#define AFS_VOL_VTM_RO	0x02 /* R/O version of the volume is available (on this server) */
 | 
				
			||||||
 | 
					#define AFS_VOL_VTM_BAK	0x04 /* backup version of the volume is available (on this server) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_volid_t		vid[3];		/* volume IDs for R/W, R/O and Bak volumes */
 | 
				
			||||||
 | 
						struct in_addr		servers[8];	/* fileserver addresses */
 | 
				
			||||||
 | 
						time_t			rtime;		/* last retrieval time */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * volume -> vnode hash table entry
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_cache_vhash {
 | 
				
			||||||
 | 
						afs_voltype_t		vtype;		/* which volume variation */
 | 
				
			||||||
 | 
						uint8_t			hash_bucket;	/* which hash bucket this represents */
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * AFS volume location record
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_vlocation {
 | 
				
			||||||
 | 
						atomic_t		usage;
 | 
				
			||||||
 | 
						time_t			time_of_death;	/* time at which put reduced usage to 0 */
 | 
				
			||||||
 | 
						struct list_head	link;		/* link in cell volume location list */
 | 
				
			||||||
 | 
						struct list_head	grave;		/* link in master graveyard list */
 | 
				
			||||||
 | 
						struct list_head	update;		/* link in master update list */
 | 
				
			||||||
 | 
						struct afs_cell		*cell;		/* cell to which volume belongs */
 | 
				
			||||||
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
 | 
						struct cachefs_cookie	*cache;		/* caching cookie */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						struct afs_cache_vlocation vldb;	/* volume information DB record */
 | 
				
			||||||
 | 
						struct afs_volume	*vols[3];	/* volume access record pointer (index by type) */
 | 
				
			||||||
 | 
						wait_queue_head_t	waitq;		/* status change waitqueue */
 | 
				
			||||||
 | 
						time_t			update_at;	/* time at which record should be updated */
 | 
				
			||||||
 | 
						rwlock_t		lock;		/* access lock */
 | 
				
			||||||
 | 
						afs_vlocation_state_t	state;		/* volume location state */
 | 
				
			||||||
 | 
						unsigned short		upd_rej_cnt;	/* ENOMEDIUM count during update */
 | 
				
			||||||
 | 
						unsigned short		upd_busy_cnt;	/* EBUSY count during update */
 | 
				
			||||||
 | 
						bool			valid;		/* T if valid */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * AFS fileserver record
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_server {
 | 
				
			||||||
 | 
						atomic_t		usage;
 | 
				
			||||||
 | 
						time_t			time_of_death;	/* time at which put reduced usage to 0 */
 | 
				
			||||||
 | 
						struct in_addr		addr;		/* server address */
 | 
				
			||||||
 | 
						struct afs_cell		*cell;		/* cell in which server resides */
 | 
				
			||||||
 | 
						struct list_head	link;		/* link in cell's server list */
 | 
				
			||||||
 | 
						struct list_head	grave;		/* link in master graveyard list */
 | 
				
			||||||
 | 
						struct rb_node		master_rb;	/* link in master by-addr tree */
 | 
				
			||||||
 | 
						struct rw_semaphore	sem;		/* access lock */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* file service access */
 | 
				
			||||||
 | 
						struct rb_root		fs_vnodes;	/* vnodes backed by this server (ordered by FID) */
 | 
				
			||||||
 | 
						unsigned long		fs_act_jif;	/* time at which last activity occurred */
 | 
				
			||||||
 | 
						unsigned long		fs_dead_jif;	/* time at which no longer to be considered dead */
 | 
				
			||||||
 | 
						spinlock_t		fs_lock;	/* access lock */
 | 
				
			||||||
 | 
						int			fs_state;      	/* 0 or reason FS currently marked dead (-errno) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* callback promise management */
 | 
				
			||||||
 | 
						struct rb_root		cb_promises;	/* vnode expiration list (ordered earliest first) */
 | 
				
			||||||
 | 
						struct delayed_work	cb_updater;	/* callback updater */
 | 
				
			||||||
 | 
						struct delayed_work	cb_break_work;	/* collected break dispatcher */
 | 
				
			||||||
 | 
						wait_queue_head_t	cb_break_waitq;	/* space available in cb_break waitqueue */
 | 
				
			||||||
 | 
						spinlock_t		cb_lock;	/* access lock */
 | 
				
			||||||
 | 
						struct afs_callback	cb_break[64];	/* ring of callbacks awaiting breaking */
 | 
				
			||||||
 | 
						atomic_t		cb_break_n;	/* number of pending breaks */
 | 
				
			||||||
 | 
						u8			cb_break_head;	/* head of callback breaking ring */
 | 
				
			||||||
 | 
						u8			cb_break_tail;	/* tail of callback breaking ring */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * AFS volume access record
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_volume {
 | 
				
			||||||
 | 
						atomic_t		usage;
 | 
				
			||||||
 | 
						struct afs_cell		*cell;		/* cell to which belongs (unrefd ptr) */
 | 
				
			||||||
 | 
						struct afs_vlocation	*vlocation;	/* volume location */
 | 
				
			||||||
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
 | 
						struct cachefs_cookie	*cache;		/* caching cookie */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						afs_volid_t		vid;		/* volume ID */
 | 
				
			||||||
 | 
						afs_voltype_t		type;		/* type of volume */
 | 
				
			||||||
 | 
						char			type_force;	/* force volume type (suppress R/O -> R/W) */
 | 
				
			||||||
 | 
						unsigned short		nservers;	/* number of server slots filled */
 | 
				
			||||||
 | 
						unsigned short		rjservers;	/* number of servers discarded due to -ENOMEDIUM */
 | 
				
			||||||
 | 
						struct afs_server	*servers[8];	/* servers on which volume resides (ordered) */
 | 
				
			||||||
 | 
						struct rw_semaphore	server_sem;	/* lock for accessing current server */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * vnode catalogue entry
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_cache_vnode {
 | 
				
			||||||
 | 
						afs_vnodeid_t		vnode_id;	/* vnode ID */
 | 
				
			||||||
 | 
						unsigned		vnode_unique;	/* vnode ID uniquifier */
 | 
				
			||||||
 | 
						afs_dataversion_t	data_version;	/* data version */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * AFS inode private data
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_vnode {
 | 
				
			||||||
 | 
						struct inode		vfs_inode;	/* the VFS's inode record */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct afs_volume	*volume;	/* volume on which vnode resides */
 | 
				
			||||||
 | 
						struct afs_server	*server;	/* server currently supplying this file */
 | 
				
			||||||
 | 
						struct afs_fid		fid;		/* the file identifier for this inode */
 | 
				
			||||||
 | 
						struct afs_file_status	status;		/* AFS status info for this file */
 | 
				
			||||||
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
 | 
						struct cachefs_cookie	*cache;		/* caching cookie */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wait_queue_head_t	update_waitq;	/* status fetch waitqueue */
 | 
				
			||||||
 | 
						unsigned		update_cnt;	/* number of outstanding ops that will update the
 | 
				
			||||||
 | 
											 * status */
 | 
				
			||||||
 | 
						spinlock_t		lock;		/* waitqueue/flags lock */
 | 
				
			||||||
 | 
						unsigned long		flags;
 | 
				
			||||||
 | 
					#define AFS_VNODE_CB_BROKEN	0		/* set if vnode's callback was broken */
 | 
				
			||||||
 | 
					#define AFS_VNODE_CHANGED	1		/* set if vnode's metadata changed */
 | 
				
			||||||
 | 
					#define AFS_VNODE_MODIFIED	2		/* set if vnode's data modified */
 | 
				
			||||||
 | 
					#define AFS_VNODE_ZAP_DATA	3		/* set if vnode's data should be invalidated */
 | 
				
			||||||
 | 
					#define AFS_VNODE_DELETED	4		/* set if vnode deleted on server */
 | 
				
			||||||
 | 
					#define AFS_VNODE_MOUNTPOINT	5		/* set if vnode is a mountpoint symlink */
 | 
				
			||||||
 | 
					#define AFS_VNODE_DIR_CHANGED	6		/* set if vnode's parent dir metadata changed */
 | 
				
			||||||
 | 
					#define AFS_VNODE_DIR_MODIFIED	7		/* set if vnode's parent dir data modified */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* outstanding callback notification on this file */
 | 
				
			||||||
 | 
						struct rb_node		server_rb;	/* link in server->fs_vnodes */
 | 
				
			||||||
 | 
						struct rb_node		cb_promise;	/* link in server->cb_promises */
 | 
				
			||||||
 | 
						struct work_struct	cb_broken_work;	/* work to be done on callback break */
 | 
				
			||||||
 | 
						struct mutex		cb_broken_lock;	/* lock against multiple attempts to fix break */
 | 
				
			||||||
 | 
					//	struct list_head	cb_hash_link;	/* link in master callback hash */
 | 
				
			||||||
 | 
						time_t			cb_expires;	/* time at which callback expires */
 | 
				
			||||||
 | 
						time_t			cb_expires_at;	/* time used to order cb_promise */
 | 
				
			||||||
 | 
						unsigned		cb_version;	/* callback version */
 | 
				
			||||||
 | 
						unsigned		cb_expiry;	/* callback expiry time */
 | 
				
			||||||
 | 
						afs_callback_type_t	cb_type;	/* type of callback */
 | 
				
			||||||
 | 
						bool			cb_promised;	/* true if promise still holds */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*****************************************************************************/
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * callback.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern void afs_init_callback_state(struct afs_server *);
 | 
				
			||||||
 | 
					extern void afs_broken_callback_work(struct work_struct *);
 | 
				
			||||||
 | 
					extern void afs_break_callbacks(struct afs_server *, size_t,
 | 
				
			||||||
 | 
									struct afs_callback[]);
 | 
				
			||||||
 | 
					extern void afs_give_up_callback(struct afs_vnode *);
 | 
				
			||||||
 | 
					extern void afs_dispatch_give_up_callbacks(struct work_struct *);
 | 
				
			||||||
 | 
					extern void afs_flush_callback_breaks(struct afs_server *);
 | 
				
			||||||
 | 
					extern int __init afs_callback_update_init(void);
 | 
				
			||||||
 | 
					extern void __exit afs_callback_update_kill(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * cell.c
 | 
					 * cell.c
 | 
				
			||||||
| 
						 | 
					@ -60,6 +333,19 @@ extern struct list_head afs_proc_cells;
 | 
				
			||||||
extern struct cachefs_index_def afs_cache_cell_index_def;
 | 
					extern struct cachefs_index_def afs_cache_cell_index_def;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
 | 
				
			||||||
 | 
					extern int afs_cell_init(char *);
 | 
				
			||||||
 | 
					extern struct afs_cell *afs_cell_create(const char *, char *);
 | 
				
			||||||
 | 
					extern struct afs_cell *afs_cell_lookup(const char *, unsigned);
 | 
				
			||||||
 | 
					extern struct afs_cell *afs_grab_cell(struct afs_cell *);
 | 
				
			||||||
 | 
					extern void afs_put_cell(struct afs_cell *);
 | 
				
			||||||
 | 
					extern void afs_cell_purge(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * cmservice.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern bool afs_cm_incoming_call(struct afs_call *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * dir.c
 | 
					 * dir.c
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -76,10 +362,23 @@ extern const struct inode_operations afs_file_inode_operations;
 | 
				
			||||||
extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **);
 | 
					extern int afs_cache_get_page_cookie(struct page *, struct cachefs_page **);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * fsclient.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern int afs_fs_fetch_file_status(struct afs_server *,
 | 
				
			||||||
 | 
									    struct afs_vnode *,
 | 
				
			||||||
 | 
									    struct afs_volsync *,
 | 
				
			||||||
 | 
									    const struct afs_wait_mode *);
 | 
				
			||||||
 | 
					extern int afs_fs_give_up_callbacks(struct afs_server *,
 | 
				
			||||||
 | 
									    const struct afs_wait_mode *);
 | 
				
			||||||
 | 
					extern int afs_fs_fetch_data(struct afs_server *, struct afs_vnode *, off_t,
 | 
				
			||||||
 | 
								     size_t, struct page *, struct afs_volsync *,
 | 
				
			||||||
 | 
								     const struct afs_wait_mode *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * inode.c
 | 
					 * inode.c
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
extern int afs_iget(struct super_block *, struct afs_fid *, struct inode **);
 | 
					extern struct inode *afs_iget(struct super_block *, struct afs_fid *);
 | 
				
			||||||
extern int afs_inode_getattr(struct vfsmount *, struct dentry *,
 | 
					extern int afs_inode_getattr(struct vfsmount *, struct dentry *,
 | 
				
			||||||
			     struct kstat *);
 | 
								     struct kstat *);
 | 
				
			||||||
extern void afs_clear_inode(struct inode *);
 | 
					extern void afs_clear_inode(struct inode *);
 | 
				
			||||||
| 
						 | 
					@ -91,16 +390,21 @@ extern void afs_clear_inode(struct inode *);
 | 
				
			||||||
extern struct cachefs_netfs afs_cache_netfs;
 | 
					extern struct cachefs_netfs afs_cache_netfs;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * misc.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern int afs_abort_to_error(u32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * mntpt.c
 | 
					 * mntpt.c
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
extern const struct inode_operations afs_mntpt_inode_operations;
 | 
					extern const struct inode_operations afs_mntpt_inode_operations;
 | 
				
			||||||
extern const struct file_operations afs_mntpt_file_operations;
 | 
					extern const struct file_operations afs_mntpt_file_operations;
 | 
				
			||||||
extern struct afs_timer afs_mntpt_expiry_timer;
 | 
					 | 
				
			||||||
extern struct afs_timer_ops afs_mntpt_expiry_timer_ops;
 | 
					 | 
				
			||||||
extern unsigned long afs_mntpt_expiry_timeout;
 | 
					extern unsigned long afs_mntpt_expiry_timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int afs_mntpt_check_symlink(struct afs_vnode *);
 | 
					extern int afs_mntpt_check_symlink(struct afs_vnode *);
 | 
				
			||||||
 | 
					extern void afs_mntpt_kill_timer(void);
 | 
				
			||||||
 | 
					extern void afs_umount_begin(struct vfsmount *, int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * super.c
 | 
					 * super.c
 | 
				
			||||||
| 
						 | 
					@ -108,16 +412,6 @@ extern int afs_mntpt_check_symlink(struct afs_vnode *);
 | 
				
			||||||
extern int afs_fs_init(void);
 | 
					extern int afs_fs_init(void);
 | 
				
			||||||
extern void afs_fs_exit(void);
 | 
					extern void afs_fs_exit(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define AFS_CB_HASH_COUNT (PAGE_SIZE / sizeof(struct list_head))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern struct list_head afs_cb_hash_tbl[];
 | 
					 | 
				
			||||||
extern spinlock_t afs_cb_hash_lock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define afs_cb_hash(SRV, FID)						\
 | 
					 | 
				
			||||||
	afs_cb_hash_tbl[((unsigned long)(SRV) +				\
 | 
					 | 
				
			||||||
			 (FID)->vid + (FID)->vnode + (FID)->unique) &	\
 | 
					 | 
				
			||||||
			(AFS_CB_HASH_COUNT - 1)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * proc.c
 | 
					 * proc.c
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -126,4 +420,217 @@ extern void afs_proc_cleanup(void);
 | 
				
			||||||
extern int afs_proc_cell_setup(struct afs_cell *);
 | 
					extern int afs_proc_cell_setup(struct afs_cell *);
 | 
				
			||||||
extern void afs_proc_cell_remove(struct afs_cell *);
 | 
					extern void afs_proc_cell_remove(struct afs_cell *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* AFS_INTERNAL_H */
 | 
					/*
 | 
				
			||||||
 | 
					 * rxrpc.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern int afs_open_socket(void);
 | 
				
			||||||
 | 
					extern void afs_close_socket(void);
 | 
				
			||||||
 | 
					extern int afs_make_call(struct in_addr *, struct afs_call *, gfp_t,
 | 
				
			||||||
 | 
								 const struct afs_wait_mode *);
 | 
				
			||||||
 | 
					extern struct afs_call *afs_alloc_flat_call(const struct afs_call_type *,
 | 
				
			||||||
 | 
										    size_t, size_t);
 | 
				
			||||||
 | 
					extern void afs_flat_call_destructor(struct afs_call *);
 | 
				
			||||||
 | 
					extern void afs_transfer_reply(struct afs_call *, struct sk_buff *);
 | 
				
			||||||
 | 
					extern void afs_send_empty_reply(struct afs_call *);
 | 
				
			||||||
 | 
					extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *,
 | 
				
			||||||
 | 
								    size_t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * server.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern spinlock_t afs_server_peer_lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern struct afs_server *afs_lookup_server(struct afs_cell *,
 | 
				
			||||||
 | 
										    const struct in_addr *);
 | 
				
			||||||
 | 
					extern struct afs_server *afs_find_server(const struct in_addr *);
 | 
				
			||||||
 | 
					extern void afs_put_server(struct afs_server *);
 | 
				
			||||||
 | 
					extern void __exit afs_purge_servers(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * vlclient.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
 | 
					extern struct cachefs_index_def afs_vlocation_cache_index_def;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int afs_vl_get_entry_by_name(struct in_addr *, const char *,
 | 
				
			||||||
 | 
									    struct afs_cache_vlocation *,
 | 
				
			||||||
 | 
									    const struct afs_wait_mode *);
 | 
				
			||||||
 | 
					extern int afs_vl_get_entry_by_id(struct in_addr *, afs_volid_t, afs_voltype_t,
 | 
				
			||||||
 | 
									  struct afs_cache_vlocation *,
 | 
				
			||||||
 | 
									  const struct afs_wait_mode *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * vlocation.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int __init afs_vlocation_update_init(void);
 | 
				
			||||||
 | 
					extern struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *,
 | 
				
			||||||
 | 
											  const char *, size_t);
 | 
				
			||||||
 | 
					extern void afs_put_vlocation(struct afs_vlocation *);
 | 
				
			||||||
 | 
					extern void __exit afs_vlocation_purge(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * vnode.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
 | 
					extern struct cachefs_index_def afs_vnode_cache_index_def;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct afs_vnode *AFS_FS_I(struct inode *inode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return container_of(inode, struct afs_vnode, vfs_inode);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return &vnode->vfs_inode;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int afs_vnode_fetch_status(struct afs_vnode *);
 | 
				
			||||||
 | 
					extern int afs_vnode_fetch_data(struct afs_vnode *vnode, off_t, size_t,
 | 
				
			||||||
 | 
									struct page *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * volume.c
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
 | 
					extern struct cachefs_index_def afs_volume_cache_index_def;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern void afs_put_volume(struct afs_volume *);
 | 
				
			||||||
 | 
					extern struct afs_volume *afs_volume_lookup(const char *, struct afs_cell *,
 | 
				
			||||||
 | 
										    int);
 | 
				
			||||||
 | 
					extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *);
 | 
				
			||||||
 | 
					extern int afs_volume_release_fileserver(struct afs_vnode *,
 | 
				
			||||||
 | 
										 struct afs_server *, int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*****************************************************************************/
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * debug tracing
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern unsigned afs_debug;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define dbgprintk(FMT,...) \
 | 
				
			||||||
 | 
						printk("[%x%-6.6s] "FMT"\n", smp_processor_id(), current->comm ,##__VA_ARGS__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* make sure we maintain the format strings, even when debugging is disabled */
 | 
				
			||||||
 | 
					static inline __attribute__((format(printf,1,2)))
 | 
				
			||||||
 | 
					void _dbprintk(const char *fmt, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define kenter(FMT,...)	dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define kleave(FMT,...)	dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define kdebug(FMT,...)	dbgprintk("    "FMT ,##__VA_ARGS__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(__KDEBUG)
 | 
				
			||||||
 | 
					#define _enter(FMT,...)	kenter(FMT,##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define _leave(FMT,...)	kleave(FMT,##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define _debug(FMT,...)	kdebug(FMT,##__VA_ARGS__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#elif defined(CONFIG_AFS_DEBUG)
 | 
				
			||||||
 | 
					#define AFS_DEBUG_KENTER	0x01
 | 
				
			||||||
 | 
					#define AFS_DEBUG_KLEAVE	0x02
 | 
				
			||||||
 | 
					#define AFS_DEBUG_KDEBUG	0x04
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define _enter(FMT,...)					\
 | 
				
			||||||
 | 
					do {							\
 | 
				
			||||||
 | 
						if (unlikely(afs_debug & AFS_DEBUG_KENTER))	\
 | 
				
			||||||
 | 
							kenter(FMT,##__VA_ARGS__);		\
 | 
				
			||||||
 | 
					} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define _leave(FMT,...)					\
 | 
				
			||||||
 | 
					do {							\
 | 
				
			||||||
 | 
						if (unlikely(afs_debug & AFS_DEBUG_KLEAVE))	\
 | 
				
			||||||
 | 
							kleave(FMT,##__VA_ARGS__);		\
 | 
				
			||||||
 | 
					} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define _debug(FMT,...)					\
 | 
				
			||||||
 | 
					do {							\
 | 
				
			||||||
 | 
						if (unlikely(afs_debug & AFS_DEBUG_KDEBUG))	\
 | 
				
			||||||
 | 
							kdebug(FMT,##__VA_ARGS__);		\
 | 
				
			||||||
 | 
					} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define _enter(FMT,...)	_dbprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define _leave(FMT,...)	_dbprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define _debug(FMT,...)	_dbprintk("    "FMT ,##__VA_ARGS__)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * debug assertion checking
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#if 1 // defined(__KDEBUGALL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERT(X)						\
 | 
				
			||||||
 | 
					do {								\
 | 
				
			||||||
 | 
						if (unlikely(!(X))) {					\
 | 
				
			||||||
 | 
							printk(KERN_ERR "\n");				\
 | 
				
			||||||
 | 
							printk(KERN_ERR "AFS: Assertion failed\n");	\
 | 
				
			||||||
 | 
							BUG();						\
 | 
				
			||||||
 | 
						}							\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERTCMP(X, OP, Y)						\
 | 
				
			||||||
 | 
					do {									\
 | 
				
			||||||
 | 
						if (unlikely(!((X) OP (Y)))) {					\
 | 
				
			||||||
 | 
							printk(KERN_ERR "\n");					\
 | 
				
			||||||
 | 
							printk(KERN_ERR "AFS: Assertion failed\n");		\
 | 
				
			||||||
 | 
							printk(KERN_ERR "%lu " #OP " %lu is false\n",		\
 | 
				
			||||||
 | 
							       (unsigned long)(X), (unsigned long)(Y));		\
 | 
				
			||||||
 | 
							printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n",	\
 | 
				
			||||||
 | 
							       (unsigned long)(X), (unsigned long)(Y));		\
 | 
				
			||||||
 | 
							BUG();							\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERTIF(C, X)						\
 | 
				
			||||||
 | 
					do {								\
 | 
				
			||||||
 | 
						if (unlikely((C) && !(X))) {				\
 | 
				
			||||||
 | 
							printk(KERN_ERR "\n");				\
 | 
				
			||||||
 | 
							printk(KERN_ERR "AFS: Assertion failed\n");	\
 | 
				
			||||||
 | 
							BUG();						\
 | 
				
			||||||
 | 
						}							\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERTIFCMP(C, X, OP, Y)					\
 | 
				
			||||||
 | 
					do {									\
 | 
				
			||||||
 | 
						if (unlikely((C) && !((X) OP (Y)))) {				\
 | 
				
			||||||
 | 
							printk(KERN_ERR "\n");					\
 | 
				
			||||||
 | 
							printk(KERN_ERR "AFS: Assertion failed\n");		\
 | 
				
			||||||
 | 
							printk(KERN_ERR "%lu " #OP " %lu is false\n",		\
 | 
				
			||||||
 | 
							       (unsigned long)(X), (unsigned long)(Y));		\
 | 
				
			||||||
 | 
							printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n",	\
 | 
				
			||||||
 | 
							       (unsigned long)(X), (unsigned long)(Y));		\
 | 
				
			||||||
 | 
							BUG();							\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERT(X)				\
 | 
				
			||||||
 | 
					do {						\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERTCMP(X, OP, Y)			\
 | 
				
			||||||
 | 
					do {						\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERTIF(C, X)				\
 | 
				
			||||||
 | 
					do {						\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ASSERTIFCMP(C, X, OP, Y)		\
 | 
				
			||||||
 | 
					do {						\
 | 
				
			||||||
 | 
					} while(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* __KDEBUGALL */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,194 +0,0 @@
 | 
				
			||||||
/* AFS timeout daemon
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <linux/module.h>
 | 
					 | 
				
			||||||
#include <linux/init.h>
 | 
					 | 
				
			||||||
#include <linux/sched.h>
 | 
					 | 
				
			||||||
#include <linux/completion.h>
 | 
					 | 
				
			||||||
#include <linux/freezer.h>
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include <asm/errno.h>
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static DECLARE_COMPLETION(kafstimod_alive);
 | 
					 | 
				
			||||||
static DECLARE_COMPLETION(kafstimod_dead);
 | 
					 | 
				
			||||||
static DECLARE_WAIT_QUEUE_HEAD(kafstimod_sleepq);
 | 
					 | 
				
			||||||
static int kafstimod_die;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static LIST_HEAD(kafstimod_list);
 | 
					 | 
				
			||||||
static DEFINE_SPINLOCK(kafstimod_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int kafstimod(void *arg);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * start the timeout daemon
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_kafstimod_start(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = kernel_thread(kafstimod, NULL, 0);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wait_for_completion(&kafstimod_alive);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * stop the timeout daemon
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void afs_kafstimod_stop(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* get rid of my daemon */
 | 
					 | 
				
			||||||
	kafstimod_die = 1;
 | 
					 | 
				
			||||||
	wake_up(&kafstimod_sleepq);
 | 
					 | 
				
			||||||
	wait_for_completion(&kafstimod_dead);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * timeout processing daemon
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static int kafstimod(void *arg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_timer *timer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DECLARE_WAITQUEUE(myself, current);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	printk("kAFS: Started kafstimod %d\n", current->pid);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	daemonize("kafstimod");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	complete(&kafstimod_alive);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* loop around looking for things to attend to */
 | 
					 | 
				
			||||||
loop:
 | 
					 | 
				
			||||||
	set_current_state(TASK_INTERRUPTIBLE);
 | 
					 | 
				
			||||||
	add_wait_queue(&kafstimod_sleepq, &myself);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (;;) {
 | 
					 | 
				
			||||||
		unsigned long jif;
 | 
					 | 
				
			||||||
		signed long timeout;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* deal with the server being asked to die */
 | 
					 | 
				
			||||||
		if (kafstimod_die) {
 | 
					 | 
				
			||||||
			remove_wait_queue(&kafstimod_sleepq, &myself);
 | 
					 | 
				
			||||||
			_leave("");
 | 
					 | 
				
			||||||
			complete_and_exit(&kafstimod_dead, 0);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		try_to_freeze();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* discard pending signals */
 | 
					 | 
				
			||||||
		afs_discard_my_signals();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* work out the time to elapse before the next event */
 | 
					 | 
				
			||||||
		spin_lock(&kafstimod_lock);
 | 
					 | 
				
			||||||
		if (list_empty(&kafstimod_list)) {
 | 
					 | 
				
			||||||
			timeout = MAX_SCHEDULE_TIMEOUT;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			timer = list_entry(kafstimod_list.next,
 | 
					 | 
				
			||||||
					   struct afs_timer, link);
 | 
					 | 
				
			||||||
			timeout = timer->timo_jif;
 | 
					 | 
				
			||||||
			jif = jiffies;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (time_before_eq((unsigned long) timeout, jif))
 | 
					 | 
				
			||||||
				goto immediate;
 | 
					 | 
				
			||||||
			timeout = (long) timeout - (long) jiffies;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		spin_unlock(&kafstimod_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		schedule_timeout(timeout);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		set_current_state(TASK_INTERRUPTIBLE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* the thing on the front of the queue needs processing
 | 
					 | 
				
			||||||
	 * - we come here with the lock held and timer pointing to the expired
 | 
					 | 
				
			||||||
	 *   entry
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
immediate:
 | 
					 | 
				
			||||||
	remove_wait_queue(&kafstimod_sleepq, &myself);
 | 
					 | 
				
			||||||
	set_current_state(TASK_RUNNING);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_debug("@@@ Begin Timeout of %p", timer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* dequeue the timer */
 | 
					 | 
				
			||||||
	list_del_init(&timer->link);
 | 
					 | 
				
			||||||
	spin_unlock(&kafstimod_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* call the timeout function */
 | 
					 | 
				
			||||||
	timer->ops->timed_out(timer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_debug("@@@ End Timeout");
 | 
					 | 
				
			||||||
	goto loop;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * (re-)queue a timer
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void afs_kafstimod_add_timer(struct afs_timer *timer, unsigned long timeout)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_timer *ptimer;
 | 
					 | 
				
			||||||
	struct list_head *_p;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p,%lu", timer, timeout);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_lock(&kafstimod_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	list_del(&timer->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* the timer was deferred or reset - put it back in the queue at the
 | 
					 | 
				
			||||||
	 * right place */
 | 
					 | 
				
			||||||
	timer->timo_jif = jiffies + timeout;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	list_for_each(_p, &kafstimod_list) {
 | 
					 | 
				
			||||||
		ptimer = list_entry(_p, struct afs_timer, link);
 | 
					 | 
				
			||||||
		if (time_before(timer->timo_jif, ptimer->timo_jif))
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	list_add_tail(&timer->link, _p); /* insert before stopping point */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_unlock(&kafstimod_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wake_up(&kafstimod_sleepq);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave("");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * dequeue a timer
 | 
					 | 
				
			||||||
 * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_kafstimod_del_timer(struct afs_timer *timer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int ret = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p", timer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_lock(&kafstimod_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (list_empty(&timer->link))
 | 
					 | 
				
			||||||
		ret = -ENOENT;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		list_del_init(&timer->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_unlock(&kafstimod_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wake_up(&kafstimod_sleepq);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,45 +0,0 @@
 | 
				
			||||||
/* AFS timeout daemon
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef AFS_KAFSTIMOD_H
 | 
					 | 
				
			||||||
#define AFS_KAFSTIMOD_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "types.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct afs_timer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct afs_timer_ops {
 | 
					 | 
				
			||||||
	/* called when the front of the timer queue has timed out */
 | 
					 | 
				
			||||||
	void (*timed_out)(struct afs_timer *);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * AFS timer/timeout record
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_timer {
 | 
					 | 
				
			||||||
	struct list_head		link;		/* link in timer queue */
 | 
					 | 
				
			||||||
	unsigned long			timo_jif;	/* timeout time */
 | 
					 | 
				
			||||||
	const struct afs_timer_ops	*ops;		/* timeout expiry function */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline void afs_timer_init(struct afs_timer *timer,
 | 
					 | 
				
			||||||
				  const struct afs_timer_ops *ops)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	INIT_LIST_HEAD(&timer->link);
 | 
					 | 
				
			||||||
	timer->ops = ops;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_kafstimod_start(void);
 | 
					 | 
				
			||||||
extern void afs_kafstimod_stop(void);
 | 
					 | 
				
			||||||
extern void afs_kafstimod_add_timer(struct afs_timer *, unsigned long);
 | 
					 | 
				
			||||||
extern int afs_kafstimod_del_timer(struct afs_timer *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_KAFSTIMOD_H */
 | 
					 | 
				
			||||||
							
								
								
									
										135
									
								
								fs/afs/main.c
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								fs/afs/main.c
									
									
									
									
									
								
							| 
						 | 
					@ -13,43 +13,21 @@
 | 
				
			||||||
#include <linux/moduleparam.h>
 | 
					#include <linux/moduleparam.h>
 | 
				
			||||||
#include <linux/init.h>
 | 
					#include <linux/init.h>
 | 
				
			||||||
#include <linux/completion.h>
 | 
					#include <linux/completion.h>
 | 
				
			||||||
#include <rxrpc/rxrpc.h>
 | 
					 | 
				
			||||||
#include <rxrpc/transport.h>
 | 
					 | 
				
			||||||
#include <rxrpc/call.h>
 | 
					 | 
				
			||||||
#include <rxrpc/peer.h>
 | 
					 | 
				
			||||||
#include "cache.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "server.h"
 | 
					 | 
				
			||||||
#include "fsclient.h"
 | 
					 | 
				
			||||||
#include "cmservice.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include "kafsasyncd.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct rxrpc_transport *afs_transport;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int afs_adding_peer(struct rxrpc_peer *peer);
 | 
					 | 
				
			||||||
static void afs_discarding_peer(struct rxrpc_peer *peer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
MODULE_DESCRIPTION("AFS Client File System");
 | 
					MODULE_DESCRIPTION("AFS Client File System");
 | 
				
			||||||
MODULE_AUTHOR("Red Hat, Inc.");
 | 
					MODULE_AUTHOR("Red Hat, Inc.");
 | 
				
			||||||
MODULE_LICENSE("GPL");
 | 
					MODULE_LICENSE("GPL");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned afs_debug;
 | 
				
			||||||
 | 
					module_param_named(debug, afs_debug, uint, S_IWUSR | S_IRUGO);
 | 
				
			||||||
 | 
					MODULE_PARM_DESC(afs_debug, "AFS debugging mask");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char *rootcell;
 | 
					static char *rootcell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module_param(rootcell, charp, 0);
 | 
					module_param(rootcell, charp, 0);
 | 
				
			||||||
MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
 | 
					MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct rxrpc_peer_ops afs_peer_ops = {
 | 
					 | 
				
			||||||
	.adding		= afs_adding_peer,
 | 
					 | 
				
			||||||
	.discarding	= afs_discarding_peer,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct list_head afs_cb_hash_tbl[AFS_CB_HASH_COUNT];
 | 
					 | 
				
			||||||
DEFINE_SPINLOCK(afs_cb_hash_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
static struct cachefs_netfs_operations afs_cache_ops = {
 | 
					static struct cachefs_netfs_operations afs_cache_ops = {
 | 
				
			||||||
	.get_page_cookie	= afs_cache_get_page_cookie,
 | 
						.get_page_cookie	= afs_cache_get_page_cookie,
 | 
				
			||||||
| 
						 | 
					@ -67,15 +45,10 @@ struct cachefs_netfs afs_cache_netfs = {
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int __init afs_init(void)
 | 
					static int __init afs_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int loop, ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n");
 | 
						printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* initialise the callback hash table */
 | 
					 | 
				
			||||||
	spin_lock_init(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
	for (loop = AFS_CB_HASH_COUNT - 1; loop >= 0; loop--)
 | 
					 | 
				
			||||||
		INIT_LIST_HEAD(&afs_cb_hash_tbl[loop]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* register the /proc stuff */
 | 
						/* register the /proc stuff */
 | 
				
			||||||
	ret = afs_proc_init();
 | 
						ret = afs_proc_init();
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
| 
						 | 
					@ -94,22 +67,18 @@ static int __init afs_init(void)
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		goto error_cell_init;
 | 
							goto error_cell_init;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* start the timeout daemon */
 | 
						/* initialise the VL update process */
 | 
				
			||||||
	ret = afs_kafstimod_start();
 | 
						ret = afs_vlocation_update_init();
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		goto error_kafstimod;
 | 
							goto error_vl_update_init;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* start the async operation daemon */
 | 
						/* initialise the callback update process */
 | 
				
			||||||
	ret = afs_kafsasyncd_start();
 | 
						ret = afs_callback_update_init();
 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto error_kafsasyncd;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* create the RxRPC transport */
 | 
						/* create the RxRPC transport */
 | 
				
			||||||
	ret = rxrpc_create_transport(7001, &afs_transport);
 | 
						ret = afs_open_socket();
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		goto error_transport;
 | 
							goto error_open_socket;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_transport->peer_ops = &afs_peer_ops;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* register the filesystems */
 | 
						/* register the filesystems */
 | 
				
			||||||
	ret = afs_fs_init();
 | 
						ret = afs_fs_init();
 | 
				
			||||||
| 
						 | 
					@ -119,17 +88,16 @@ static int __init afs_init(void)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
error_fs:
 | 
					error_fs:
 | 
				
			||||||
	rxrpc_put_transport(afs_transport);
 | 
						afs_close_socket();
 | 
				
			||||||
error_transport:
 | 
					error_open_socket:
 | 
				
			||||||
	afs_kafsasyncd_stop();
 | 
					error_vl_update_init:
 | 
				
			||||||
error_kafsasyncd:
 | 
					 | 
				
			||||||
	afs_kafstimod_stop();
 | 
					 | 
				
			||||||
error_kafstimod:
 | 
					 | 
				
			||||||
error_cell_init:
 | 
					error_cell_init:
 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
	cachefs_unregister_netfs(&afs_cache_netfs);
 | 
						cachefs_unregister_netfs(&afs_cache_netfs);
 | 
				
			||||||
error_cache:
 | 
					error_cache:
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						afs_callback_update_kill();
 | 
				
			||||||
 | 
						afs_vlocation_purge();
 | 
				
			||||||
	afs_cell_purge();
 | 
						afs_cell_purge();
 | 
				
			||||||
	afs_proc_cleanup();
 | 
						afs_proc_cleanup();
 | 
				
			||||||
	printk(KERN_ERR "kAFS: failed to register: %d\n", ret);
 | 
						printk(KERN_ERR "kAFS: failed to register: %d\n", ret);
 | 
				
			||||||
| 
						 | 
					@ -149,9 +117,11 @@ static void __exit afs_exit(void)
 | 
				
			||||||
	printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
 | 
						printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_fs_exit();
 | 
						afs_fs_exit();
 | 
				
			||||||
	rxrpc_put_transport(afs_transport);
 | 
						afs_close_socket();
 | 
				
			||||||
	afs_kafstimod_stop();
 | 
						afs_purge_servers();
 | 
				
			||||||
	afs_kafsasyncd_stop();
 | 
						afs_callback_update_kill();
 | 
				
			||||||
 | 
						afs_vlocation_purge();
 | 
				
			||||||
 | 
						flush_scheduled_work();
 | 
				
			||||||
	afs_cell_purge();
 | 
						afs_cell_purge();
 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					#ifdef AFS_CACHING_SUPPORT
 | 
				
			||||||
	cachefs_unregister_netfs(&afs_cache_netfs);
 | 
						cachefs_unregister_netfs(&afs_cache_netfs);
 | 
				
			||||||
| 
						 | 
					@ -160,64 +130,3 @@ static void __exit afs_exit(void)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module_exit(afs_exit);
 | 
					module_exit(afs_exit);
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * notification that new peer record is being added
 | 
					 | 
				
			||||||
 * - called from krxsecd
 | 
					 | 
				
			||||||
 * - return an error to induce an abort
 | 
					 | 
				
			||||||
 * - mustn't sleep (caller holds an rwlock)
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static int afs_adding_peer(struct rxrpc_peer *peer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_server *server;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_debug("kAFS: Adding new peer %08x\n", ntohl(peer->addr.s_addr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* determine which server the peer resides in (if any) */
 | 
					 | 
				
			||||||
	ret = afs_server_find_by_peer(peer, &server);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		return ret; /* none that we recognise, so abort */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_debug("Server %p{u=%d}\n", server, atomic_read(&server->usage));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_debug("Cell %p{u=%d}\n",
 | 
					 | 
				
			||||||
	       server->cell, atomic_read(&server->cell->usage));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* cross-point the structs under a global lock */
 | 
					 | 
				
			||||||
	spin_lock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
	peer->user = server;
 | 
					 | 
				
			||||||
	server->peer = peer;
 | 
					 | 
				
			||||||
	spin_unlock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_put_server(server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * notification that a peer record is being discarded
 | 
					 | 
				
			||||||
 * - called from krxiod or krxsecd
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void afs_discarding_peer(struct rxrpc_peer *peer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_server *server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p",peer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_debug("Discarding peer %08x (rtt=%lu.%lumS)\n",
 | 
					 | 
				
			||||||
	       ntohl(peer->addr.s_addr),
 | 
					 | 
				
			||||||
	       (long) (peer->rtt / 1000),
 | 
					 | 
				
			||||||
	       (long) (peer->rtt % 1000));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* uncross-point the structs under a global lock */
 | 
					 | 
				
			||||||
	spin_lock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
	server = peer->user;
 | 
					 | 
				
			||||||
	if (server) {
 | 
					 | 
				
			||||||
		peer->user = NULL;
 | 
					 | 
				
			||||||
		server->peer = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	spin_unlock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave("");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* miscellaneous bits
 | 
					/* miscellaneous bits
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -12,18 +12,19 @@
 | 
				
			||||||
#include <linux/kernel.h>
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/errno.h>
 | 
					#include <linux/errno.h>
 | 
				
			||||||
#include "errors.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					#include "afs_fs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * convert an AFS abort code to a Linux error number
 | 
					 * convert an AFS abort code to a Linux error number
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_abort_to_error(int abortcode)
 | 
					int afs_abort_to_error(u32 abort_code)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (abortcode) {
 | 
						switch (abort_code) {
 | 
				
			||||||
 | 
						case 13:		return -EACCES;
 | 
				
			||||||
	case VSALVAGE:		return -EIO;
 | 
						case VSALVAGE:		return -EIO;
 | 
				
			||||||
	case VNOVNODE:		return -ENOENT;
 | 
						case VNOVNODE:		return -ENOENT;
 | 
				
			||||||
	case VNOVOL:		return -ENXIO;
 | 
						case VNOVOL:		return -ENOMEDIUM;
 | 
				
			||||||
	case VVOLEXISTS:	return -EEXIST;
 | 
						case VVOLEXISTS:	return -EEXIST;
 | 
				
			||||||
	case VNOSERVICE:	return -EIO;
 | 
						case VNOSERVICE:	return -EIO;
 | 
				
			||||||
	case VOFFLINE:		return -ENOENT;
 | 
						case VOFFLINE:		return -ENOENT;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										104
									
								
								fs/afs/mntpt.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								fs/afs/mntpt.c
									
									
									
									
									
								
							| 
						 | 
					@ -18,10 +18,6 @@
 | 
				
			||||||
#include <linux/mount.h>
 | 
					#include <linux/mount.h>
 | 
				
			||||||
#include <linux/namei.h>
 | 
					#include <linux/namei.h>
 | 
				
			||||||
#include <linux/mnt_namespace.h>
 | 
					#include <linux/mnt_namespace.h>
 | 
				
			||||||
#include "super.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +26,7 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
 | 
				
			||||||
				       struct nameidata *nd);
 | 
									       struct nameidata *nd);
 | 
				
			||||||
static int afs_mntpt_open(struct inode *inode, struct file *file);
 | 
					static int afs_mntpt_open(struct inode *inode, struct file *file);
 | 
				
			||||||
static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
 | 
					static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
 | 
				
			||||||
 | 
					static void afs_mntpt_expiry_timed_out(struct work_struct *work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct file_operations afs_mntpt_file_operations = {
 | 
					const struct file_operations afs_mntpt_file_operations = {
 | 
				
			||||||
	.open		= afs_mntpt_open,
 | 
						.open		= afs_mntpt_open,
 | 
				
			||||||
| 
						 | 
					@ -43,16 +40,9 @@ const struct inode_operations afs_mntpt_inode_operations = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static LIST_HEAD(afs_vfsmounts);
 | 
					static LIST_HEAD(afs_vfsmounts);
 | 
				
			||||||
 | 
					static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void afs_mntpt_expiry_timed_out(struct afs_timer *timer);
 | 
					unsigned long afs_mntpt_expiry_timeout = 10 * 60;
 | 
				
			||||||
 | 
					 | 
				
			||||||
struct afs_timer_ops afs_mntpt_expiry_timer_ops = {
 | 
					 | 
				
			||||||
	.timed_out	= afs_mntpt_expiry_timed_out,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct afs_timer afs_mntpt_expiry_timer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
unsigned long afs_mntpt_expiry_timeout = 20;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * check a symbolic link to see whether it actually encodes a mountpoint
 | 
					 * check a symbolic link to see whether it actually encodes a mountpoint
 | 
				
			||||||
| 
						 | 
					@ -84,7 +74,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* examine the symlink's contents */
 | 
						/* examine the symlink's contents */
 | 
				
			||||||
	size = vnode->status.size;
 | 
						size = vnode->status.size;
 | 
				
			||||||
	_debug("symlink to %*.*s", size, (int) size, buf);
 | 
						_debug("symlink to %*.*s", (int) size, (int) size, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (size > 2 &&
 | 
						if (size > 2 &&
 | 
				
			||||||
	    (buf[0] == '%' || buf[0] == '#') &&
 | 
						    (buf[0] == '%' || buf[0] == '#') &&
 | 
				
			||||||
| 
						 | 
					@ -92,7 +82,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode)
 | 
				
			||||||
	    ) {
 | 
						    ) {
 | 
				
			||||||
		_debug("symlink is a mountpoint");
 | 
							_debug("symlink is a mountpoint");
 | 
				
			||||||
		spin_lock(&vnode->lock);
 | 
							spin_lock(&vnode->lock);
 | 
				
			||||||
		vnode->flags |= AFS_VNODE_MOUNTPOINT;
 | 
							set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
 | 
				
			||||||
		spin_unlock(&vnode->lock);
 | 
							spin_unlock(&vnode->lock);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,7 +103,7 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
 | 
				
			||||||
				       struct dentry *dentry,
 | 
									       struct dentry *dentry,
 | 
				
			||||||
				       struct nameidata *nd)
 | 
									       struct nameidata *nd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	kenter("%p,%p{%p{%s},%s}",
 | 
						_enter("%p,%p{%p{%s},%s}",
 | 
				
			||||||
	       dir,
 | 
						       dir,
 | 
				
			||||||
	       dentry,
 | 
						       dentry,
 | 
				
			||||||
	       dentry->d_parent,
 | 
						       dentry->d_parent,
 | 
				
			||||||
| 
						 | 
					@ -129,7 +119,7 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_mntpt_open(struct inode *inode, struct file *file)
 | 
					static int afs_mntpt_open(struct inode *inode, struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	kenter("%p,%p{%p{%s},%s}",
 | 
						_enter("%p,%p{%p{%s},%s}",
 | 
				
			||||||
	       inode, file,
 | 
						       inode, file,
 | 
				
			||||||
	       file->f_path.dentry->d_parent,
 | 
						       file->f_path.dentry->d_parent,
 | 
				
			||||||
	       file->f_path.dentry->d_parent ?
 | 
						       file->f_path.dentry->d_parent ?
 | 
				
			||||||
| 
						 | 
					@ -152,7 +142,7 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
 | 
				
			||||||
	char *buf, *devname = NULL, *options = NULL;
 | 
						char *buf, *devname = NULL, *options = NULL;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kenter("{%s}", mntpt->d_name.name);
 | 
						_enter("{%s}", mntpt->d_name.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(!mntpt->d_inode);
 | 
						BUG_ON(!mntpt->d_inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -196,13 +186,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
 | 
				
			||||||
		strcat(options, ",rwpath");
 | 
							strcat(options, ",rwpath");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* try and do the mount */
 | 
						/* try and do the mount */
 | 
				
			||||||
	kdebug("--- attempting mount %s -o %s ---", devname, options);
 | 
						_debug("--- attempting mount %s -o %s ---", devname, options);
 | 
				
			||||||
	mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options);
 | 
						mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options);
 | 
				
			||||||
	kdebug("--- mount result %p ---", mnt);
 | 
						_debug("--- mount result %p ---", mnt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free_page((unsigned long) devname);
 | 
						free_page((unsigned long) devname);
 | 
				
			||||||
	free_page((unsigned long) options);
 | 
						free_page((unsigned long) options);
 | 
				
			||||||
	kleave(" = %p", mnt);
 | 
						_leave(" = %p", mnt);
 | 
				
			||||||
	return mnt;
 | 
						return mnt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
| 
						 | 
					@ -212,7 +202,7 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
 | 
				
			||||||
		free_page((unsigned long) devname);
 | 
							free_page((unsigned long) devname);
 | 
				
			||||||
	if (options)
 | 
						if (options)
 | 
				
			||||||
		free_page((unsigned long) options);
 | 
							free_page((unsigned long) options);
 | 
				
			||||||
	kleave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ERR_PTR(ret);
 | 
						return ERR_PTR(ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,51 +212,81 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
 | 
				
			||||||
static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
 | 
					static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct vfsmount *newmnt;
 | 
						struct vfsmount *newmnt;
 | 
				
			||||||
	struct dentry *old_dentry;
 | 
					 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kenter("%p{%s},{%s:%p{%s}}",
 | 
						_enter("%p{%s},{%s:%p{%s}}",
 | 
				
			||||||
	       dentry,
 | 
						       dentry,
 | 
				
			||||||
	       dentry->d_name.name,
 | 
						       dentry->d_name.name,
 | 
				
			||||||
	       nd->mnt->mnt_devname,
 | 
						       nd->mnt->mnt_devname,
 | 
				
			||||||
	       dentry,
 | 
						       dentry,
 | 
				
			||||||
	       nd->dentry->d_name.name);
 | 
						       nd->dentry->d_name.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newmnt = afs_mntpt_do_automount(dentry);
 | 
						dput(nd->dentry);
 | 
				
			||||||
 | 
						nd->dentry = dget(dentry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newmnt = afs_mntpt_do_automount(nd->dentry);
 | 
				
			||||||
	if (IS_ERR(newmnt)) {
 | 
						if (IS_ERR(newmnt)) {
 | 
				
			||||||
		path_release(nd);
 | 
							path_release(nd);
 | 
				
			||||||
		return (void *)newmnt;
 | 
							return (void *)newmnt;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	old_dentry = nd->dentry;
 | 
					 | 
				
			||||||
	nd->dentry = dentry;
 | 
					 | 
				
			||||||
	err = do_add_mount(newmnt, nd, 0, &afs_vfsmounts);
 | 
					 | 
				
			||||||
	nd->dentry = old_dentry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	path_release(nd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!err) {
 | 
					 | 
				
			||||||
	mntget(newmnt);
 | 
						mntget(newmnt);
 | 
				
			||||||
 | 
						err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts);
 | 
				
			||||||
 | 
						switch (err) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							path_release(nd);
 | 
				
			||||||
		nd->mnt = newmnt;
 | 
							nd->mnt = newmnt;
 | 
				
			||||||
		dget(newmnt->mnt_root);
 | 
							nd->dentry = dget(newmnt->mnt_root);
 | 
				
			||||||
		nd->dentry = newmnt->mnt_root;
 | 
							schedule_delayed_work(&afs_mntpt_expiry_timer,
 | 
				
			||||||
 | 
									      afs_mntpt_expiry_timeout * HZ);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EBUSY:
 | 
				
			||||||
 | 
							/* someone else made a mount here whilst we were busy */
 | 
				
			||||||
 | 
							while (d_mountpoint(nd->dentry) &&
 | 
				
			||||||
 | 
							       follow_down(&nd->mnt, &nd->dentry))
 | 
				
			||||||
 | 
								;
 | 
				
			||||||
 | 
							err = 0;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							mntput(newmnt);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kleave(" = %d", err);
 | 
						_leave(" = %d", err);
 | 
				
			||||||
	return ERR_PTR(err);
 | 
						return ERR_PTR(err);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * handle mountpoint expiry timer going off
 | 
					 * handle mountpoint expiry timer going off
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_mntpt_expiry_timed_out(struct afs_timer *timer)
 | 
					static void afs_mntpt_expiry_timed_out(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	kenter("");
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!list_empty(&afs_vfsmounts)) {
 | 
				
			||||||
		mark_mounts_for_expiry(&afs_vfsmounts);
 | 
							mark_mounts_for_expiry(&afs_vfsmounts);
 | 
				
			||||||
 | 
							schedule_delayed_work(&afs_mntpt_expiry_timer,
 | 
				
			||||||
	afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
 | 
					 | 
				
			||||||
				      afs_mntpt_expiry_timeout * HZ);
 | 
									      afs_mntpt_expiry_timeout * HZ);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	kleave("");
 | 
					
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * kill the AFS mountpoint timer if it's still running
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_mntpt_kill_timer(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(list_empty(&afs_vfsmounts));
 | 
				
			||||||
 | 
						cancel_delayed_work(&afs_mntpt_expiry_timer);
 | 
				
			||||||
 | 
						flush_scheduled_work();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * begin unmount by attempting to remove all automounted mountpoints we added
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_umount_begin(struct vfsmount *vfsmnt, int flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						shrink_submounts(vfsmnt, &afs_vfsmounts);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +0,0 @@
 | 
				
			||||||
/* mount parameters
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef AFS_MOUNT_H
 | 
					 | 
				
			||||||
#define AFS_MOUNT_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct afs_mountdata {
 | 
					 | 
				
			||||||
	const char		*volume;	/* name of volume */
 | 
					 | 
				
			||||||
	const char		*cell;		/* name of cell containing volume */
 | 
					 | 
				
			||||||
	const char		*cache;		/* name of cache block device */
 | 
					 | 
				
			||||||
	size_t			nservers;	/* number of server addresses listed */
 | 
					 | 
				
			||||||
	uint32_t		servers[10];	/* IP addresses of servers in this cell */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_MOUNT_H */
 | 
					 | 
				
			||||||
| 
						 | 
					@ -13,8 +13,6 @@
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/proc_fs.h>
 | 
					#include <linux/proc_fs.h>
 | 
				
			||||||
#include <linux/seq_file.h>
 | 
					#include <linux/seq_file.h>
 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include <asm/uaccess.h>
 | 
					#include <asm/uaccess.h>
 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -315,10 +313,14 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strcmp(kbuf, "add") == 0) {
 | 
						if (strcmp(kbuf, "add") == 0) {
 | 
				
			||||||
		struct afs_cell *cell;
 | 
							struct afs_cell *cell;
 | 
				
			||||||
		ret = afs_cell_create(name, args, &cell);
 | 
					 | 
				
			||||||
		if (ret < 0)
 | 
					 | 
				
			||||||
			goto done;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cell = afs_cell_create(name, args);
 | 
				
			||||||
 | 
							if (IS_ERR(cell)) {
 | 
				
			||||||
 | 
								ret = PTR_ERR(cell);
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							afs_put_cell(cell);
 | 
				
			||||||
		printk("kAFS: Added new cell '%s'\n", name);
 | 
							printk("kAFS: Added new cell '%s'\n", name);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		goto inval;
 | 
							goto inval;
 | 
				
			||||||
| 
						 | 
					@ -472,7 +474,7 @@ static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
 | 
				
			||||||
	struct seq_file *m;
 | 
						struct seq_file *m;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
 | 
						cell = PDE(inode)->data;
 | 
				
			||||||
	if (!cell)
 | 
						if (!cell)
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -491,13 +493,7 @@ static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file)
 | 
					static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell = PDE(inode)->data;
 | 
						return seq_release(inode, file);
 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = seq_release(inode, file);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_put_cell(cell);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -557,6 +553,16 @@ static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
 | 
				
			||||||
	up_read(&cell->vl_sem);
 | 
						up_read(&cell->vl_sem);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char afs_vlocation_states[][4] = {
 | 
				
			||||||
 | 
						[AFS_VL_NEW]			= "New",
 | 
				
			||||||
 | 
						[AFS_VL_CREATING]		= "Crt",
 | 
				
			||||||
 | 
						[AFS_VL_VALID]			= "Val",
 | 
				
			||||||
 | 
						[AFS_VL_NO_VOLUME]		= "NoV",
 | 
				
			||||||
 | 
						[AFS_VL_UPDATING]		= "Upd",
 | 
				
			||||||
 | 
						[AFS_VL_VOLUME_DELETED]		= "Del",
 | 
				
			||||||
 | 
						[AFS_VL_UNCERTAIN]		= "Unc",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * display a header line followed by a load of volume lines
 | 
					 * display a header line followed by a load of volume lines
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -567,13 +573,14 @@ static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* display header on line 1 */
 | 
						/* display header on line 1 */
 | 
				
			||||||
	if (v == (void *) 1) {
 | 
						if (v == (void *) 1) {
 | 
				
			||||||
		seq_puts(m, "USE VLID[0]  VLID[1]  VLID[2]  NAME\n");
 | 
							seq_puts(m, "USE STT VLID[0]  VLID[1]  VLID[2]  NAME\n");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* display one cell per line on subsequent lines */
 | 
						/* display one cell per line on subsequent lines */
 | 
				
			||||||
	seq_printf(m, "%3d %08x %08x %08x %s\n",
 | 
						seq_printf(m, "%3d %s %08x %08x %08x %s\n",
 | 
				
			||||||
		   atomic_read(&vlocation->usage),
 | 
							   atomic_read(&vlocation->usage),
 | 
				
			||||||
 | 
							   afs_vlocation_states[vlocation->state],
 | 
				
			||||||
		   vlocation->vldb.vid[0],
 | 
							   vlocation->vldb.vid[0],
 | 
				
			||||||
		   vlocation->vldb.vid[1],
 | 
							   vlocation->vldb.vid[1],
 | 
				
			||||||
		   vlocation->vldb.vid[2],
 | 
							   vlocation->vldb.vid[2],
 | 
				
			||||||
| 
						 | 
					@ -592,7 +599,7 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
 | 
				
			||||||
	struct seq_file *m;
 | 
						struct seq_file *m;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cell = afs_get_cell_maybe((struct afs_cell**)&PDE(inode)->data);
 | 
						cell = PDE(inode)->data;
 | 
				
			||||||
	if (!cell)
 | 
						if (!cell)
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -612,13 +619,7 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
 | 
				
			||||||
static int afs_proc_cell_vlservers_release(struct inode *inode,
 | 
					static int afs_proc_cell_vlservers_release(struct inode *inode,
 | 
				
			||||||
					   struct file *file)
 | 
										   struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell = PDE(inode)->data;
 | 
						return seq_release(inode, file);
 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = seq_release(inode,file);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_put_cell(cell);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -703,7 +704,7 @@ static int afs_proc_cell_servers_open(struct inode *inode, struct file *file)
 | 
				
			||||||
	struct seq_file *m;
 | 
						struct seq_file *m;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
 | 
						cell = PDE(inode)->data;
 | 
				
			||||||
	if (!cell)
 | 
						if (!cell)
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -722,13 +723,7 @@ static int afs_proc_cell_servers_open(struct inode *inode, struct file *file)
 | 
				
			||||||
static int afs_proc_cell_servers_release(struct inode *inode,
 | 
					static int afs_proc_cell_servers_release(struct inode *inode,
 | 
				
			||||||
					 struct file *file)
 | 
										 struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell = PDE(inode)->data;
 | 
						return seq_release(inode, file);
 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = seq_release(inode, file);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_put_cell(cell);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -736,7 +731,7 @@ static int afs_proc_cell_servers_release(struct inode *inode,
 | 
				
			||||||
 * first item
 | 
					 * first item
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
 | 
					static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
 | 
				
			||||||
	__acquires(m->private->sv_lock)
 | 
						__acquires(m->private->servers_lock)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct list_head *_p;
 | 
						struct list_head *_p;
 | 
				
			||||||
	struct afs_cell *cell = m->private;
 | 
						struct afs_cell *cell = m->private;
 | 
				
			||||||
| 
						 | 
					@ -745,7 +740,7 @@ static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
 | 
				
			||||||
	_enter("cell=%p pos=%Ld", cell, *_pos);
 | 
						_enter("cell=%p pos=%Ld", cell, *_pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* lock the list against modification */
 | 
						/* lock the list against modification */
 | 
				
			||||||
	read_lock(&cell->sv_lock);
 | 
						read_lock(&cell->servers_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* allow for the header line */
 | 
						/* allow for the header line */
 | 
				
			||||||
	if (!pos)
 | 
						if (!pos)
 | 
				
			||||||
| 
						 | 
					@ -753,11 +748,11 @@ static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
 | 
				
			||||||
	pos--;
 | 
						pos--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* find the n'th element in the list */
 | 
						/* find the n'th element in the list */
 | 
				
			||||||
	list_for_each(_p, &cell->sv_list)
 | 
						list_for_each(_p, &cell->servers)
 | 
				
			||||||
		if (!pos--)
 | 
							if (!pos--)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return _p != &cell->sv_list ? _p : NULL;
 | 
						return _p != &cell->servers ? _p : NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -774,20 +769,20 @@ static void *afs_proc_cell_servers_next(struct seq_file *p, void *v,
 | 
				
			||||||
	(*_pos)++;
 | 
						(*_pos)++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_p = v;
 | 
						_p = v;
 | 
				
			||||||
	_p = v == (void *) 1 ? cell->sv_list.next : _p->next;
 | 
						_p = v == (void *) 1 ? cell->servers.next : _p->next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return _p != &cell->sv_list ? _p : NULL;
 | 
						return _p != &cell->servers ? _p : NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * clean up after reading from the cells list
 | 
					 * clean up after reading from the cells list
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_proc_cell_servers_stop(struct seq_file *p, void *v)
 | 
					static void afs_proc_cell_servers_stop(struct seq_file *p, void *v)
 | 
				
			||||||
	__releases(p->private->sv_lock)
 | 
						__releases(p->private->servers_lock)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell = p->private;
 | 
						struct afs_cell *cell = p->private;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	read_unlock(&cell->sv_lock);
 | 
						read_unlock(&cell->servers_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										666
									
								
								fs/afs/rxrpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										666
									
								
								fs/afs/rxrpc.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,666 @@
 | 
				
			||||||
 | 
					/* Maintain an RxRPC server socket to do AFS communications through
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
 | 
					 * modify it under the terms of the GNU General Public License
 | 
				
			||||||
 | 
					 * as published by the Free Software Foundation; either version
 | 
				
			||||||
 | 
					 * 2 of the License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <net/sock.h>
 | 
				
			||||||
 | 
					#include <net/af_rxrpc.h>
 | 
				
			||||||
 | 
					#include <rxrpc/packet.h>
 | 
				
			||||||
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					#include "afs_cm.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct socket *afs_socket; /* my RxRPC socket */
 | 
				
			||||||
 | 
					static struct workqueue_struct *afs_async_calls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void afs_wake_up_call_waiter(struct afs_call *);
 | 
				
			||||||
 | 
					static int afs_wait_for_call_to_complete(struct afs_call *);
 | 
				
			||||||
 | 
					static void afs_wake_up_async_call(struct afs_call *);
 | 
				
			||||||
 | 
					static int afs_dont_wait_for_call_to_complete(struct afs_call *);
 | 
				
			||||||
 | 
					static void afs_process_async_call(struct work_struct *);
 | 
				
			||||||
 | 
					static void afs_rx_interceptor(struct sock *, unsigned long, struct sk_buff *);
 | 
				
			||||||
 | 
					static int afs_deliver_cm_op_id(struct afs_call *, struct sk_buff *, bool);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* synchronous call management */
 | 
				
			||||||
 | 
					const struct afs_wait_mode afs_sync_call = {
 | 
				
			||||||
 | 
						.rx_wakeup	= afs_wake_up_call_waiter,
 | 
				
			||||||
 | 
						.wait		= afs_wait_for_call_to_complete,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* asynchronous call management */
 | 
				
			||||||
 | 
					const struct afs_wait_mode afs_async_call = {
 | 
				
			||||||
 | 
						.rx_wakeup	= afs_wake_up_async_call,
 | 
				
			||||||
 | 
						.wait		= afs_dont_wait_for_call_to_complete,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* asynchronous incoming call management */
 | 
				
			||||||
 | 
					static const struct afs_wait_mode afs_async_incoming_call = {
 | 
				
			||||||
 | 
						.rx_wakeup	= afs_wake_up_async_call,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* asynchronous incoming call initial processing */
 | 
				
			||||||
 | 
					static const struct afs_call_type afs_RXCMxxxx = {
 | 
				
			||||||
 | 
						.deliver	= afs_deliver_cm_op_id,
 | 
				
			||||||
 | 
						.abort_to_error	= afs_abort_to_error,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void afs_collect_incoming_call(struct work_struct *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct sk_buff_head afs_incoming_calls;
 | 
				
			||||||
 | 
					static DECLARE_WORK(afs_collect_incoming_call_work, afs_collect_incoming_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * open an RxRPC socket and bind it to be a server for callback notifications
 | 
				
			||||||
 | 
					 * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int afs_open_socket(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sockaddr_rxrpc srx;
 | 
				
			||||||
 | 
						struct socket *socket;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb_queue_head_init(&afs_incoming_calls);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_async_calls = create_singlethread_workqueue("kafsd");
 | 
				
			||||||
 | 
						if (!afs_async_calls) {
 | 
				
			||||||
 | 
							_leave(" = -ENOMEM [wq]");
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = sock_create_kern(AF_RXRPC, SOCK_DGRAM, PF_INET, &socket);
 | 
				
			||||||
 | 
						if (ret < 0) {
 | 
				
			||||||
 | 
							destroy_workqueue(afs_async_calls);
 | 
				
			||||||
 | 
							_leave(" = %d [socket]", ret);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						socket->sk->sk_allocation = GFP_NOFS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* bind the callback manager's address to make this a server socket */
 | 
				
			||||||
 | 
						srx.srx_family			= AF_RXRPC;
 | 
				
			||||||
 | 
						srx.srx_service			= CM_SERVICE;
 | 
				
			||||||
 | 
						srx.transport_type		= SOCK_DGRAM;
 | 
				
			||||||
 | 
						srx.transport_len		= sizeof(srx.transport.sin);
 | 
				
			||||||
 | 
						srx.transport.sin.sin_family	= AF_INET;
 | 
				
			||||||
 | 
						srx.transport.sin.sin_port	= htons(AFS_CM_PORT);
 | 
				
			||||||
 | 
						memset(&srx.transport.sin.sin_addr, 0,
 | 
				
			||||||
 | 
						       sizeof(srx.transport.sin.sin_addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
 | 
				
			||||||
 | 
						if (ret < 0) {
 | 
				
			||||||
 | 
							sock_release(socket);
 | 
				
			||||||
 | 
							_leave(" = %d [bind]", ret);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rxrpc_kernel_intercept_rx_messages(socket, afs_rx_interceptor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_socket = socket;
 | 
				
			||||||
 | 
						_leave(" = 0");
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * close the RxRPC socket AFS was using
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_close_socket(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sock_release(afs_socket);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("dework");
 | 
				
			||||||
 | 
						destroy_workqueue(afs_async_calls);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * allocate a call with flat request and reply buffers
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_call *afs_alloc_flat_call(const struct afs_call_type *type,
 | 
				
			||||||
 | 
									     size_t request_size, size_t reply_size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_call *call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call = kzalloc(sizeof(*call), GFP_NOFS);
 | 
				
			||||||
 | 
						if (!call)
 | 
				
			||||||
 | 
							goto nomem_call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (request_size) {
 | 
				
			||||||
 | 
							call->request = kmalloc(request_size, GFP_NOFS);
 | 
				
			||||||
 | 
							if (!call->request)
 | 
				
			||||||
 | 
								goto nomem_request;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (reply_size) {
 | 
				
			||||||
 | 
							call->buffer = kmalloc(reply_size, GFP_NOFS);
 | 
				
			||||||
 | 
							if (!call->buffer)
 | 
				
			||||||
 | 
								goto nomem_buffer;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call->type = type;
 | 
				
			||||||
 | 
						call->request_size = request_size;
 | 
				
			||||||
 | 
						call->reply_max = reply_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init_waitqueue_head(&call->waitq);
 | 
				
			||||||
 | 
						skb_queue_head_init(&call->rx_queue);
 | 
				
			||||||
 | 
						return call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nomem_buffer:
 | 
				
			||||||
 | 
						kfree(call->request);
 | 
				
			||||||
 | 
					nomem_request:
 | 
				
			||||||
 | 
						kfree(call);
 | 
				
			||||||
 | 
					nomem_call:
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * clean up a call with flat buffer
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_flat_call_destructor(struct afs_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kfree(call->request);
 | 
				
			||||||
 | 
						call->request = NULL;
 | 
				
			||||||
 | 
						kfree(call->buffer);
 | 
				
			||||||
 | 
						call->buffer = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * initiate a call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
 | 
				
			||||||
 | 
							  const struct afs_wait_mode *wait_mode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sockaddr_rxrpc srx;
 | 
				
			||||||
 | 
						struct rxrpc_call *rxcall;
 | 
				
			||||||
 | 
						struct msghdr msg;
 | 
				
			||||||
 | 
						struct kvec iov[1];
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%x,{%d},", addr->s_addr, ntohs(call->port));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call->wait_mode = wait_mode;
 | 
				
			||||||
 | 
						INIT_WORK(&call->async_work, afs_process_async_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&srx, 0, sizeof(srx));
 | 
				
			||||||
 | 
						srx.srx_family = AF_RXRPC;
 | 
				
			||||||
 | 
						srx.srx_service = call->service_id;
 | 
				
			||||||
 | 
						srx.transport_type = SOCK_DGRAM;
 | 
				
			||||||
 | 
						srx.transport_len = sizeof(srx.transport.sin);
 | 
				
			||||||
 | 
						srx.transport.sin.sin_family = AF_INET;
 | 
				
			||||||
 | 
						srx.transport.sin.sin_port = call->port;
 | 
				
			||||||
 | 
						memcpy(&srx.transport.sin.sin_addr, addr, 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create a call */
 | 
				
			||||||
 | 
						rxcall = rxrpc_kernel_begin_call(afs_socket, &srx, call->key,
 | 
				
			||||||
 | 
										 (unsigned long) call, gfp);
 | 
				
			||||||
 | 
						if (IS_ERR(rxcall)) {
 | 
				
			||||||
 | 
							ret = PTR_ERR(rxcall);
 | 
				
			||||||
 | 
							goto error_kill_call;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call->rxcall = rxcall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* send the request */
 | 
				
			||||||
 | 
						iov[0].iov_base	= call->request;
 | 
				
			||||||
 | 
						iov[0].iov_len	= call->request_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg.msg_name		= NULL;
 | 
				
			||||||
 | 
						msg.msg_namelen		= 0;
 | 
				
			||||||
 | 
						msg.msg_iov		= (struct iovec *) iov;
 | 
				
			||||||
 | 
						msg.msg_iovlen		= 1;
 | 
				
			||||||
 | 
						msg.msg_control		= NULL;
 | 
				
			||||||
 | 
						msg.msg_controllen	= 0;
 | 
				
			||||||
 | 
						msg.msg_flags		= 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* have to change the state *before* sending the last packet as RxRPC
 | 
				
			||||||
 | 
						 * might give us the reply before it returns from sending the
 | 
				
			||||||
 | 
						 * request */
 | 
				
			||||||
 | 
						call->state = AFS_CALL_AWAIT_REPLY;
 | 
				
			||||||
 | 
						ret = rxrpc_kernel_send_data(rxcall, &msg, call->request_size);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							goto error_do_abort;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* at this point, an async call may no longer exist as it may have
 | 
				
			||||||
 | 
						 * already completed */
 | 
				
			||||||
 | 
						return wait_mode->wait(call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error_do_abort:
 | 
				
			||||||
 | 
						rxrpc_kernel_abort_call(rxcall, RX_USER_ABORT);
 | 
				
			||||||
 | 
						rxrpc_kernel_end_call(rxcall);
 | 
				
			||||||
 | 
					error_kill_call:
 | 
				
			||||||
 | 
						call->type->destructor(call);
 | 
				
			||||||
 | 
						ASSERT(skb_queue_empty(&call->rx_queue));
 | 
				
			||||||
 | 
						kfree(call);
 | 
				
			||||||
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * handles intercepted messages that were arriving in the socket's Rx queue
 | 
				
			||||||
 | 
					 * - called with the socket receive queue lock held to ensure message ordering
 | 
				
			||||||
 | 
					 * - called with softirqs disabled
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_rx_interceptor(struct sock *sk, unsigned long user_call_ID,
 | 
				
			||||||
 | 
								       struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_call *call = (struct afs_call *) user_call_ID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%p,,%u", call, skb->mark);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERTCMP(sk, ==, afs_socket->sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!call) {
 | 
				
			||||||
 | 
							/* its an incoming call for our callback service */
 | 
				
			||||||
 | 
							__skb_queue_tail(&afs_incoming_calls, skb);
 | 
				
			||||||
 | 
							schedule_work(&afs_collect_incoming_call_work);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* route the messages directly to the appropriate call */
 | 
				
			||||||
 | 
							__skb_queue_tail(&call->rx_queue, skb);
 | 
				
			||||||
 | 
							call->wait_mode->rx_wakeup(call);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * deliver messages to a call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_deliver_to_call(struct afs_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sk_buff *skb;
 | 
				
			||||||
 | 
						bool last;
 | 
				
			||||||
 | 
						u32 abort_code;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while ((call->state == AFS_CALL_AWAIT_REPLY ||
 | 
				
			||||||
 | 
							call->state == AFS_CALL_AWAIT_OP_ID ||
 | 
				
			||||||
 | 
							call->state == AFS_CALL_AWAIT_REQUEST ||
 | 
				
			||||||
 | 
							call->state == AFS_CALL_AWAIT_ACK) &&
 | 
				
			||||||
 | 
						       (skb = skb_dequeue(&call->rx_queue))) {
 | 
				
			||||||
 | 
							switch (skb->mark) {
 | 
				
			||||||
 | 
							case RXRPC_SKB_MARK_DATA:
 | 
				
			||||||
 | 
								_debug("Rcv DATA");
 | 
				
			||||||
 | 
								last = rxrpc_kernel_is_data_last(skb);
 | 
				
			||||||
 | 
								ret = call->type->deliver(call, skb, last);
 | 
				
			||||||
 | 
								switch (ret) {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									if (last &&
 | 
				
			||||||
 | 
									    call->state == AFS_CALL_AWAIT_REPLY)
 | 
				
			||||||
 | 
										call->state = AFS_CALL_COMPLETE;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case -ENOTCONN:
 | 
				
			||||||
 | 
									abort_code = RX_CALL_DEAD;
 | 
				
			||||||
 | 
									goto do_abort;
 | 
				
			||||||
 | 
								case -ENOTSUPP:
 | 
				
			||||||
 | 
									abort_code = RX_INVALID_OPERATION;
 | 
				
			||||||
 | 
									goto do_abort;
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									abort_code = RXGEN_CC_UNMARSHAL;
 | 
				
			||||||
 | 
									if (call->state != AFS_CALL_AWAIT_REPLY)
 | 
				
			||||||
 | 
										abort_code = RXGEN_SS_UNMARSHAL;
 | 
				
			||||||
 | 
								do_abort:
 | 
				
			||||||
 | 
									rxrpc_kernel_abort_call(call->rxcall,
 | 
				
			||||||
 | 
												abort_code);
 | 
				
			||||||
 | 
									call->error = ret;
 | 
				
			||||||
 | 
									call->state = AFS_CALL_ERROR;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								rxrpc_kernel_data_delivered(skb);
 | 
				
			||||||
 | 
								skb = NULL;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case RXRPC_SKB_MARK_FINAL_ACK:
 | 
				
			||||||
 | 
								_debug("Rcv ACK");
 | 
				
			||||||
 | 
								call->state = AFS_CALL_COMPLETE;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case RXRPC_SKB_MARK_BUSY:
 | 
				
			||||||
 | 
								_debug("Rcv BUSY");
 | 
				
			||||||
 | 
								call->error = -EBUSY;
 | 
				
			||||||
 | 
								call->state = AFS_CALL_BUSY;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case RXRPC_SKB_MARK_REMOTE_ABORT:
 | 
				
			||||||
 | 
								abort_code = rxrpc_kernel_get_abort_code(skb);
 | 
				
			||||||
 | 
								call->error = call->type->abort_to_error(abort_code);
 | 
				
			||||||
 | 
								call->state = AFS_CALL_ABORTED;
 | 
				
			||||||
 | 
								_debug("Rcv ABORT %u -> %d", abort_code, call->error);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case RXRPC_SKB_MARK_NET_ERROR:
 | 
				
			||||||
 | 
								call->error = -rxrpc_kernel_get_error_number(skb);
 | 
				
			||||||
 | 
								call->state = AFS_CALL_ERROR;
 | 
				
			||||||
 | 
								_debug("Rcv NET ERROR %d", call->error);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case RXRPC_SKB_MARK_LOCAL_ERROR:
 | 
				
			||||||
 | 
								call->error = -rxrpc_kernel_get_error_number(skb);
 | 
				
			||||||
 | 
								call->state = AFS_CALL_ERROR;
 | 
				
			||||||
 | 
								_debug("Rcv LOCAL ERROR %d", call->error);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								BUG();
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rxrpc_kernel_free_skb(skb);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* make sure the queue is empty if the call is done with (we might have
 | 
				
			||||||
 | 
						 * aborted the call early because of an unmarshalling error) */
 | 
				
			||||||
 | 
						if (call->state >= AFS_CALL_COMPLETE) {
 | 
				
			||||||
 | 
							while ((skb = skb_dequeue(&call->rx_queue)))
 | 
				
			||||||
 | 
								rxrpc_kernel_free_skb(skb);
 | 
				
			||||||
 | 
							if (call->incoming) {
 | 
				
			||||||
 | 
								rxrpc_kernel_end_call(call->rxcall);
 | 
				
			||||||
 | 
								call->type->destructor(call);
 | 
				
			||||||
 | 
								ASSERT(skb_queue_empty(&call->rx_queue));
 | 
				
			||||||
 | 
								kfree(call);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * wait synchronously for a call to complete
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int afs_wait_for_call_to_complete(struct afs_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sk_buff *skb;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DECLARE_WAITQUEUE(myself, current);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						add_wait_queue(&call->waitq, &myself);
 | 
				
			||||||
 | 
						for (;;) {
 | 
				
			||||||
 | 
							set_current_state(TASK_INTERRUPTIBLE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* deliver any messages that are in the queue */
 | 
				
			||||||
 | 
							if (!skb_queue_empty(&call->rx_queue)) {
 | 
				
			||||||
 | 
								__set_current_state(TASK_RUNNING);
 | 
				
			||||||
 | 
								afs_deliver_to_call(call);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = call->error;
 | 
				
			||||||
 | 
							if (call->state >= AFS_CALL_COMPLETE)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							ret = -EINTR;
 | 
				
			||||||
 | 
							if (signal_pending(current))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							schedule();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						remove_wait_queue(&call->waitq, &myself);
 | 
				
			||||||
 | 
						__set_current_state(TASK_RUNNING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* kill the call */
 | 
				
			||||||
 | 
						if (call->state < AFS_CALL_COMPLETE) {
 | 
				
			||||||
 | 
							_debug("call incomplete");
 | 
				
			||||||
 | 
							rxrpc_kernel_abort_call(call->rxcall, RX_CALL_DEAD);
 | 
				
			||||||
 | 
							while ((skb = skb_dequeue(&call->rx_queue)))
 | 
				
			||||||
 | 
								rxrpc_kernel_free_skb(skb);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("call complete");
 | 
				
			||||||
 | 
						rxrpc_kernel_end_call(call->rxcall);
 | 
				
			||||||
 | 
						call->type->destructor(call);
 | 
				
			||||||
 | 
						ASSERT(skb_queue_empty(&call->rx_queue));
 | 
				
			||||||
 | 
						kfree(call);
 | 
				
			||||||
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * wake up a waiting call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_wake_up_call_waiter(struct afs_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						wake_up(&call->waitq);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * wake up an asynchronous call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_wake_up_async_call(struct afs_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
						queue_work(afs_async_calls, &call->async_work);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * put a call into asynchronous mode
 | 
				
			||||||
 | 
					 * - mustn't touch the call descriptor as the call my have completed by the
 | 
				
			||||||
 | 
					 *   time we get here
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int afs_dont_wait_for_call_to_complete(struct afs_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
						return -EINPROGRESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * delete an asynchronous call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_delete_async_call(struct work_struct *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_call *call =
 | 
				
			||||||
 | 
							container_of(work, struct afs_call, async_work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(skb_queue_empty(&call->rx_queue));
 | 
				
			||||||
 | 
						ASSERT(!work_pending(&call->async_work));
 | 
				
			||||||
 | 
						kfree(call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * perform processing on an asynchronous call
 | 
				
			||||||
 | 
					 * - on a multiple-thread workqueue this work item may try to run on several
 | 
				
			||||||
 | 
					 *   CPUs at the same time
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_process_async_call(struct work_struct *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_call *call =
 | 
				
			||||||
 | 
							container_of(work, struct afs_call, async_work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!skb_queue_empty(&call->rx_queue))
 | 
				
			||||||
 | 
							afs_deliver_to_call(call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (call->state >= AFS_CALL_COMPLETE && call->wait_mode) {
 | 
				
			||||||
 | 
							if (call->wait_mode->async_complete)
 | 
				
			||||||
 | 
								call->wait_mode->async_complete(call->reply,
 | 
				
			||||||
 | 
												call->error);
 | 
				
			||||||
 | 
							call->reply = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* kill the call */
 | 
				
			||||||
 | 
							rxrpc_kernel_end_call(call->rxcall);
 | 
				
			||||||
 | 
							if (call->type->destructor)
 | 
				
			||||||
 | 
								call->type->destructor(call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* we can't just delete the call because the work item may be
 | 
				
			||||||
 | 
							 * queued */
 | 
				
			||||||
 | 
							PREPARE_WORK(&call->async_work, afs_delete_async_call);
 | 
				
			||||||
 | 
							queue_work(afs_async_calls, &call->async_work);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * empty a socket buffer into a flat reply buffer
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_transfer_reply(struct afs_call *call, struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t len = skb->len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (skb_copy_bits(skb, 0, call->buffer + call->reply_size, len) < 0)
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						call->reply_size += len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * accept the backlog of incoming calls
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_collect_incoming_call(struct work_struct *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct rxrpc_call *rxcall;
 | 
				
			||||||
 | 
						struct afs_call *call = NULL;
 | 
				
			||||||
 | 
						struct sk_buff *skb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while ((skb = skb_dequeue(&afs_incoming_calls))) {
 | 
				
			||||||
 | 
							_debug("new call");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* don't need the notification */
 | 
				
			||||||
 | 
							rxrpc_kernel_free_skb(skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!call) {
 | 
				
			||||||
 | 
								call = kzalloc(sizeof(struct afs_call), GFP_KERNEL);
 | 
				
			||||||
 | 
								if (!call) {
 | 
				
			||||||
 | 
									rxrpc_kernel_reject_call(afs_socket);
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								INIT_WORK(&call->async_work, afs_process_async_call);
 | 
				
			||||||
 | 
								call->wait_mode = &afs_async_incoming_call;
 | 
				
			||||||
 | 
								call->type = &afs_RXCMxxxx;
 | 
				
			||||||
 | 
								init_waitqueue_head(&call->waitq);
 | 
				
			||||||
 | 
								skb_queue_head_init(&call->rx_queue);
 | 
				
			||||||
 | 
								call->state = AFS_CALL_AWAIT_OP_ID;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rxcall = rxrpc_kernel_accept_call(afs_socket,
 | 
				
			||||||
 | 
											  (unsigned long) call);
 | 
				
			||||||
 | 
							if (!IS_ERR(rxcall)) {
 | 
				
			||||||
 | 
								call->rxcall = rxcall;
 | 
				
			||||||
 | 
								call = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kfree(call);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * grab the operation ID from an incoming cache manager call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int afs_deliver_cm_op_id(struct afs_call *call, struct sk_buff *skb,
 | 
				
			||||||
 | 
									bool last)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t len = skb->len;
 | 
				
			||||||
 | 
						void *oibuf = (void *) &call->operation_ID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("{%u},{%zu},%d", call->offset, len, last);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERTCMP(call->offset, <, 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* the operation ID forms the first four bytes of the request data */
 | 
				
			||||||
 | 
						len = min_t(size_t, len, 4 - call->offset);
 | 
				
			||||||
 | 
						if (skb_copy_bits(skb, 0, oibuf + call->offset, len) < 0)
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						if (!pskb_pull(skb, len))
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						call->offset += len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (call->offset < 4) {
 | 
				
			||||||
 | 
							if (last) {
 | 
				
			||||||
 | 
								_leave(" = -EBADMSG [op ID short]");
 | 
				
			||||||
 | 
								return -EBADMSG;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_leave(" = 0 [incomplete]");
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call->state = AFS_CALL_AWAIT_REQUEST;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* ask the cache manager to route the call (it'll change the call type
 | 
				
			||||||
 | 
						 * if successful) */
 | 
				
			||||||
 | 
						if (!afs_cm_incoming_call(call))
 | 
				
			||||||
 | 
							return -ENOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* pass responsibility for the remainer of this message off to the
 | 
				
			||||||
 | 
						 * cache manager op */
 | 
				
			||||||
 | 
						return call->type->deliver(call, skb, last);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * send an empty reply
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void afs_send_empty_reply(struct afs_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msghdr msg;
 | 
				
			||||||
 | 
						struct iovec iov[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iov[0].iov_base		= NULL;
 | 
				
			||||||
 | 
						iov[0].iov_len		= 0;
 | 
				
			||||||
 | 
						msg.msg_name		= NULL;
 | 
				
			||||||
 | 
						msg.msg_namelen		= 0;
 | 
				
			||||||
 | 
						msg.msg_iov		= iov;
 | 
				
			||||||
 | 
						msg.msg_iovlen		= 0;
 | 
				
			||||||
 | 
						msg.msg_control		= NULL;
 | 
				
			||||||
 | 
						msg.msg_controllen	= 0;
 | 
				
			||||||
 | 
						msg.msg_flags		= 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call->state = AFS_CALL_AWAIT_ACK;
 | 
				
			||||||
 | 
						switch (rxrpc_kernel_send_data(call->rxcall, &msg, 0)) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							_leave(" [replied]");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case -ENOMEM:
 | 
				
			||||||
 | 
							_debug("oom");
 | 
				
			||||||
 | 
							rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT);
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							rxrpc_kernel_end_call(call->rxcall);
 | 
				
			||||||
 | 
							call->rxcall = NULL;
 | 
				
			||||||
 | 
							call->type->destructor(call);
 | 
				
			||||||
 | 
							ASSERT(skb_queue_empty(&call->rx_queue));
 | 
				
			||||||
 | 
							kfree(call);
 | 
				
			||||||
 | 
							_leave(" [error]");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * extract a piece of data from the received data socket buffers
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int afs_extract_data(struct afs_call *call, struct sk_buff *skb,
 | 
				
			||||||
 | 
							     bool last, void *buf, size_t count)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t len = skb->len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("{%u},{%zu},%d,,%zu", call->offset, len, last, count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERTCMP(call->offset, <, count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						len = min_t(size_t, len, count - call->offset);
 | 
				
			||||||
 | 
						if (skb_copy_bits(skb, 0, buf + call->offset, len) < 0 ||
 | 
				
			||||||
 | 
						    !pskb_pull(skb, len))
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						call->offset += len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (call->offset < count) {
 | 
				
			||||||
 | 
							if (last) {
 | 
				
			||||||
 | 
								_leave(" = -EBADMSG [%d < %lu]", call->offset, count);
 | 
				
			||||||
 | 
								return -EBADMSG;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_leave(" = -EAGAIN");
 | 
				
			||||||
 | 
							return -EAGAIN;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										614
									
								
								fs/afs/server.c
									
									
									
									
									
								
							
							
						
						
									
										614
									
								
								fs/afs/server.c
									
									
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* AFS server record management
 | 
					/* AFS server record management
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -11,127 +11,205 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/sched.h>
 | 
					#include <linux/sched.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <rxrpc/peer.h>
 | 
					 | 
				
			||||||
#include <rxrpc/connection.h>
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "server.h"
 | 
					 | 
				
			||||||
#include "transport.h"
 | 
					 | 
				
			||||||
#include "vlclient.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEFINE_SPINLOCK(afs_server_peer_lock);
 | 
					unsigned afs_server_timeout = 10;	/* server timeout in seconds */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define FS_SERVICE_ID		1	/* AFS Volume Location Service ID */
 | 
					static void afs_reap_server(struct work_struct *);
 | 
				
			||||||
#define VL_SERVICE_ID		52	/* AFS Volume Location Service ID */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __afs_server_timeout(struct afs_timer *timer)
 | 
					/* tree of all the servers, indexed by IP address */
 | 
				
			||||||
{
 | 
					static struct rb_root afs_servers = RB_ROOT;
 | 
				
			||||||
	struct afs_server *server =
 | 
					static DEFINE_RWLOCK(afs_servers_lock);
 | 
				
			||||||
		list_entry(timer, struct afs_server, timeout);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_debug("SERVER TIMEOUT [%p{u=%d}]",
 | 
					/* LRU list of all the servers not currently in use */
 | 
				
			||||||
	       server, atomic_read(&server->usage));
 | 
					static LIST_HEAD(afs_server_graveyard);
 | 
				
			||||||
 | 
					static DEFINE_SPINLOCK(afs_server_graveyard_lock);
 | 
				
			||||||
	afs_server_do_timeout(server);
 | 
					static DECLARE_DELAYED_WORK(afs_server_reaper, afs_reap_server);
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct afs_timer_ops afs_server_timer_ops = {
 | 
					 | 
				
			||||||
	.timed_out	= __afs_server_timeout,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * lookup a server record in a cell
 | 
					 * install a server record in the master tree
 | 
				
			||||||
 * - TODO: search the cell's server list
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr,
 | 
					static int afs_install_server(struct afs_server *server)
 | 
				
			||||||
		      struct afs_server **_server)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server *server, *active, *zombie;
 | 
						struct afs_server *xserver;
 | 
				
			||||||
	int loop;
 | 
						struct rb_node **pp, *p;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p,%08x,", cell, ntohl(addr->s_addr));
 | 
						_enter("%p", server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* allocate and initialise a server record */
 | 
						write_lock(&afs_servers_lock);
 | 
				
			||||||
	server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
 | 
					
 | 
				
			||||||
	if (!server) {
 | 
						ret = -EEXIST;
 | 
				
			||||||
		_leave(" = -ENOMEM");
 | 
						pp = &afs_servers.rb_node;
 | 
				
			||||||
		return -ENOMEM;
 | 
						p = NULL;
 | 
				
			||||||
 | 
						while (*pp) {
 | 
				
			||||||
 | 
							p = *pp;
 | 
				
			||||||
 | 
							_debug("- consider %p", p);
 | 
				
			||||||
 | 
							xserver = rb_entry(p, struct afs_server, master_rb);
 | 
				
			||||||
 | 
							if (server->addr.s_addr < xserver->addr.s_addr)
 | 
				
			||||||
 | 
								pp = &(*pp)->rb_left;
 | 
				
			||||||
 | 
							else if (server->addr.s_addr > xserver->addr.s_addr)
 | 
				
			||||||
 | 
								pp = &(*pp)->rb_right;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rb_link_node(&server->master_rb, p, pp);
 | 
				
			||||||
 | 
						rb_insert_color(&server->master_rb, &afs_servers);
 | 
				
			||||||
 | 
						ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error:
 | 
				
			||||||
 | 
						write_unlock(&afs_servers_lock);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * allocate a new server record
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static struct afs_server *afs_alloc_server(struct afs_cell *cell,
 | 
				
			||||||
 | 
										   const struct in_addr *addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (server) {
 | 
				
			||||||
		atomic_set(&server->usage, 1);
 | 
							atomic_set(&server->usage, 1);
 | 
				
			||||||
 | 
							server->cell = cell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		INIT_LIST_HEAD(&server->link);
 | 
							INIT_LIST_HEAD(&server->link);
 | 
				
			||||||
 | 
							INIT_LIST_HEAD(&server->grave);
 | 
				
			||||||
		init_rwsem(&server->sem);
 | 
							init_rwsem(&server->sem);
 | 
				
			||||||
	INIT_LIST_HEAD(&server->fs_callq);
 | 
					 | 
				
			||||||
		spin_lock_init(&server->fs_lock);
 | 
							spin_lock_init(&server->fs_lock);
 | 
				
			||||||
	INIT_LIST_HEAD(&server->cb_promises);
 | 
							server->fs_vnodes = RB_ROOT;
 | 
				
			||||||
 | 
							server->cb_promises = RB_ROOT;
 | 
				
			||||||
		spin_lock_init(&server->cb_lock);
 | 
							spin_lock_init(&server->cb_lock);
 | 
				
			||||||
 | 
							init_waitqueue_head(&server->cb_break_waitq);
 | 
				
			||||||
	for (loop = 0; loop < AFS_SERVER_CONN_LIST_SIZE; loop++)
 | 
							INIT_DELAYED_WORK(&server->cb_break_work,
 | 
				
			||||||
		server->fs_conn_cnt[loop] = 4;
 | 
									  afs_dispatch_give_up_callbacks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		memcpy(&server->addr, addr, sizeof(struct in_addr));
 | 
							memcpy(&server->addr, addr, sizeof(struct in_addr));
 | 
				
			||||||
		server->addr.s_addr = addr->s_addr;
 | 
							server->addr.s_addr = addr->s_addr;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_timer_init(&server->timeout, &afs_server_timer_ops);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* add to the cell */
 | 
					 | 
				
			||||||
	write_lock(&cell->sv_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* check the active list */
 | 
					 | 
				
			||||||
	list_for_each_entry(active, &cell->sv_list, link) {
 | 
					 | 
				
			||||||
		if (active->addr.s_addr == addr->s_addr)
 | 
					 | 
				
			||||||
			goto use_active_server;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* check the inactive list */
 | 
						_leave(" = %p{%d}", server, atomic_read(&server->usage));
 | 
				
			||||||
	spin_lock(&cell->sv_gylock);
 | 
						return server;
 | 
				
			||||||
	list_for_each_entry(zombie, &cell->sv_graveyard, link) {
 | 
					 | 
				
			||||||
		if (zombie->addr.s_addr == addr->s_addr)
 | 
					 | 
				
			||||||
			goto resurrect_server;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
	spin_unlock(&cell->sv_gylock);
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * get an FS-server record for a cell
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_server *afs_lookup_server(struct afs_cell *cell,
 | 
				
			||||||
 | 
									     const struct in_addr *addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *server, *candidate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%p,"NIPQUAD_FMT, cell, NIPQUAD(addr->s_addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* quick scan of the list to see if we already have the server */
 | 
				
			||||||
 | 
						read_lock(&cell->servers_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry(server, &cell->servers, link) {
 | 
				
			||||||
 | 
							if (server->addr.s_addr == addr->s_addr)
 | 
				
			||||||
 | 
								goto found_server_quickly;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						read_unlock(&cell->servers_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						candidate = afs_alloc_server(cell, addr);
 | 
				
			||||||
 | 
						if (!candidate) {
 | 
				
			||||||
 | 
							_leave(" = -ENOMEM");
 | 
				
			||||||
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						write_lock(&cell->servers_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* check the cell's server list again */
 | 
				
			||||||
 | 
						list_for_each_entry(server, &cell->servers, link) {
 | 
				
			||||||
 | 
							if (server->addr.s_addr == addr->s_addr)
 | 
				
			||||||
 | 
								goto found_server;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("new");
 | 
				
			||||||
 | 
						server = candidate;
 | 
				
			||||||
 | 
						if (afs_install_server(server) < 0)
 | 
				
			||||||
 | 
							goto server_in_two_cells;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_get_cell(cell);
 | 
						afs_get_cell(cell);
 | 
				
			||||||
	server->cell = cell;
 | 
						list_add_tail(&server->link, &cell->servers);
 | 
				
			||||||
	list_add_tail(&server->link, &cell->sv_list);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	write_unlock(&cell->sv_lock);
 | 
						write_unlock(&cell->servers_lock);
 | 
				
			||||||
 | 
						_leave(" = %p{%d}", server, atomic_read(&server->usage));
 | 
				
			||||||
 | 
						return server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*_server = server;
 | 
						/* found a matching server quickly */
 | 
				
			||||||
	_leave(" = 0 (%p)", server);
 | 
					found_server_quickly:
 | 
				
			||||||
	return 0;
 | 
						_debug("found quickly");
 | 
				
			||||||
 | 
						afs_get_server(server);
 | 
				
			||||||
 | 
						read_unlock(&cell->servers_lock);
 | 
				
			||||||
 | 
					no_longer_unused:
 | 
				
			||||||
 | 
						if (!list_empty(&server->grave)) {
 | 
				
			||||||
 | 
							spin_lock(&afs_server_graveyard_lock);
 | 
				
			||||||
 | 
							list_del_init(&server->grave);
 | 
				
			||||||
 | 
							spin_unlock(&afs_server_graveyard_lock);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_leave(" = %p{%d}", server, atomic_read(&server->usage));
 | 
				
			||||||
 | 
						return server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* found a matching active server */
 | 
						/* found a matching server on the second pass */
 | 
				
			||||||
use_active_server:
 | 
					found_server:
 | 
				
			||||||
	_debug("active server");
 | 
						_debug("found");
 | 
				
			||||||
	afs_get_server(active);
 | 
						afs_get_server(server);
 | 
				
			||||||
	write_unlock(&cell->sv_lock);
 | 
						write_unlock(&cell->servers_lock);
 | 
				
			||||||
 | 
						kfree(candidate);
 | 
				
			||||||
 | 
						goto no_longer_unused;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(server);
 | 
						/* found a server that seems to be in two cells */
 | 
				
			||||||
 | 
					server_in_two_cells:
 | 
				
			||||||
 | 
						write_unlock(&cell->servers_lock);
 | 
				
			||||||
 | 
						kfree(candidate);
 | 
				
			||||||
 | 
						printk(KERN_NOTICE "kAFS:"
 | 
				
			||||||
 | 
						       " Server "NIPQUAD_FMT" appears to be in two cells\n",
 | 
				
			||||||
 | 
						       NIPQUAD(*addr));
 | 
				
			||||||
 | 
						_leave(" = -EEXIST");
 | 
				
			||||||
 | 
						return ERR_PTR(-EEXIST);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*_server = active;
 | 
					/*
 | 
				
			||||||
	_leave(" = 0 (%p)", active);
 | 
					 * look up a server by its IP address
 | 
				
			||||||
	return 0;
 | 
					 */
 | 
				
			||||||
 | 
					struct afs_server *afs_find_server(const struct in_addr *_addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *server = NULL;
 | 
				
			||||||
 | 
						struct rb_node *p;
 | 
				
			||||||
 | 
						struct in_addr addr = *_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* found a matching server in the graveyard, so resurrect it and
 | 
						_enter(NIPQUAD_FMT, NIPQUAD(addr.s_addr));
 | 
				
			||||||
	 * dispose of the new record */
 | 
					 | 
				
			||||||
resurrect_server:
 | 
					 | 
				
			||||||
	_debug("resurrecting server");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_move_tail(&zombie->link, &cell->sv_list);
 | 
						read_lock(&afs_servers_lock);
 | 
				
			||||||
	afs_get_server(zombie);
 | 
					 | 
				
			||||||
	afs_kafstimod_del_timer(&zombie->timeout);
 | 
					 | 
				
			||||||
	spin_unlock(&cell->sv_gylock);
 | 
					 | 
				
			||||||
	write_unlock(&cell->sv_lock);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(server);
 | 
						p = afs_servers.rb_node;
 | 
				
			||||||
 | 
						while (p) {
 | 
				
			||||||
 | 
							server = rb_entry(p, struct afs_server, master_rb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*_server = zombie;
 | 
							_debug("- consider %p", p);
 | 
				
			||||||
	_leave(" = 0 (%p)", zombie);
 | 
					
 | 
				
			||||||
	return 0;
 | 
							if (addr.s_addr < server->addr.s_addr) {
 | 
				
			||||||
 | 
								p = p->rb_left;
 | 
				
			||||||
 | 
							} else if (addr.s_addr > server->addr.s_addr) {
 | 
				
			||||||
 | 
								p = p->rb_right;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								afs_get_server(server);
 | 
				
			||||||
 | 
								goto found;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server = NULL;
 | 
				
			||||||
 | 
					found:
 | 
				
			||||||
 | 
						read_unlock(&afs_servers_lock);
 | 
				
			||||||
 | 
						ASSERTIFCMP(server, server->addr.s_addr, ==, addr.s_addr);
 | 
				
			||||||
 | 
						_leave(" = %p", server);
 | 
				
			||||||
 | 
						return server;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -140,347 +218,105 @@ int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void afs_put_server(struct afs_server *server)
 | 
					void afs_put_server(struct afs_server *server)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_cell *cell;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!server)
 | 
						if (!server)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p", server);
 | 
						_enter("%p{%d}", server, atomic_read(&server->usage));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cell = server->cell;
 | 
						ASSERTCMP(atomic_read(&server->usage), >, 0);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* sanity check */
 | 
					 | 
				
			||||||
	BUG_ON(atomic_read(&server->usage) <= 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* to prevent a race, the decrement and the dequeue must be effectively
 | 
					 | 
				
			||||||
	 * atomic */
 | 
					 | 
				
			||||||
	write_lock(&cell->sv_lock);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (likely(!atomic_dec_and_test(&server->usage))) {
 | 
						if (likely(!atomic_dec_and_test(&server->usage))) {
 | 
				
			||||||
		write_unlock(&cell->sv_lock);
 | 
					 | 
				
			||||||
		_leave("");
 | 
							_leave("");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&cell->sv_gylock);
 | 
						afs_flush_callback_breaks(server);
 | 
				
			||||||
	list_move_tail(&server->link, &cell->sv_graveyard);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* time out in 10 secs */
 | 
						spin_lock(&afs_server_graveyard_lock);
 | 
				
			||||||
	afs_kafstimod_add_timer(&server->timeout, 10 * HZ);
 | 
						if (atomic_read(&server->usage) == 0) {
 | 
				
			||||||
 | 
							list_move_tail(&server->grave, &afs_server_graveyard);
 | 
				
			||||||
	spin_unlock(&cell->sv_gylock);
 | 
							server->time_of_death = get_seconds();
 | 
				
			||||||
	write_unlock(&cell->sv_lock);
 | 
							schedule_delayed_work(&afs_server_reaper,
 | 
				
			||||||
 | 
									      afs_server_timeout * HZ);
 | 
				
			||||||
	_leave(" [killed]");
 | 
						}
 | 
				
			||||||
 | 
						spin_unlock(&afs_server_graveyard_lock);
 | 
				
			||||||
 | 
						_leave(" [dead]");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * timeout server record
 | 
					 * destroy a dead server
 | 
				
			||||||
 * - removes from the cell's graveyard if the usage count is zero
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void afs_server_do_timeout(struct afs_server *server)
 | 
					static void afs_destroy_server(struct afs_server *server)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rxrpc_peer *peer;
 | 
					 | 
				
			||||||
	struct afs_cell *cell;
 | 
					 | 
				
			||||||
	int loop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p", server);
 | 
						_enter("%p", server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cell = server->cell;
 | 
						ASSERTCMP(server->fs_vnodes.rb_node, ==, NULL);
 | 
				
			||||||
 | 
						ASSERTCMP(server->cb_promises.rb_node, ==, NULL);
 | 
				
			||||||
	BUG_ON(atomic_read(&server->usage) < 0);
 | 
						ASSERTCMP(server->cb_break_head, ==, server->cb_break_tail);
 | 
				
			||||||
 | 
						ASSERTCMP(atomic_read(&server->cb_break_n), ==, 0);
 | 
				
			||||||
	/* remove from graveyard if still dead */
 | 
					 | 
				
			||||||
	spin_lock(&cell->vl_gylock);
 | 
					 | 
				
			||||||
	if (atomic_read(&server->usage) == 0)
 | 
					 | 
				
			||||||
		list_del_init(&server->link);
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		server = NULL;
 | 
					 | 
				
			||||||
	spin_unlock(&cell->vl_gylock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!server) {
 | 
					 | 
				
			||||||
		_leave("");
 | 
					 | 
				
			||||||
		return; /* resurrected */
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* we can now destroy it properly */
 | 
					 | 
				
			||||||
	afs_put_cell(cell);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* uncross-point the structs under a global lock */
 | 
					 | 
				
			||||||
	spin_lock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
	peer = server->peer;
 | 
					 | 
				
			||||||
	if (peer) {
 | 
					 | 
				
			||||||
		server->peer = NULL;
 | 
					 | 
				
			||||||
		peer->user = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	spin_unlock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* finish cleaning up the server */
 | 
					 | 
				
			||||||
	for (loop = AFS_SERVER_CONN_LIST_SIZE - 1; loop >= 0; loop--)
 | 
					 | 
				
			||||||
		if (server->fs_conn[loop])
 | 
					 | 
				
			||||||
			rxrpc_put_connection(server->fs_conn[loop]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (server->vlserver)
 | 
					 | 
				
			||||||
		rxrpc_put_connection(server->vlserver);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_put_cell(server->cell);
 | 
				
			||||||
	kfree(server);
 | 
						kfree(server);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" [destroyed]");
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * get a callslot on a connection to the fileserver on the specified server
 | 
					 * reap dead server records
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_server_request_callslot(struct afs_server *server,
 | 
					static void afs_reap_server(struct work_struct *work)
 | 
				
			||||||
				struct afs_server_callslot *callslot)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server_callslot *pcallslot;
 | 
						LIST_HEAD(corpses);
 | 
				
			||||||
	struct rxrpc_connection *conn;
 | 
						struct afs_server *server;
 | 
				
			||||||
	int nconn, ret;
 | 
						unsigned long delay, expiry;
 | 
				
			||||||
 | 
						time_t now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p,",server);
 | 
						now = get_seconds();
 | 
				
			||||||
 | 
						spin_lock(&afs_server_graveyard_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_LIST_HEAD(&callslot->link);
 | 
						while (!list_empty(&afs_server_graveyard)) {
 | 
				
			||||||
	callslot->task = current;
 | 
							server = list_entry(afs_server_graveyard.next,
 | 
				
			||||||
	callslot->conn = NULL;
 | 
									    struct afs_server, grave);
 | 
				
			||||||
	callslot->nconn = -1;
 | 
					 | 
				
			||||||
	callslot->ready = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = 0;
 | 
							/* the queue is ordered most dead first */
 | 
				
			||||||
	conn = NULL;
 | 
							expiry = server->time_of_death + afs_server_timeout;
 | 
				
			||||||
 | 
							if (expiry > now) {
 | 
				
			||||||
	/* get hold of a callslot first */
 | 
								delay = (expiry - now) * HZ;
 | 
				
			||||||
	spin_lock(&server->fs_lock);
 | 
								if (!schedule_delayed_work(&afs_server_reaper, delay)) {
 | 
				
			||||||
 | 
									cancel_delayed_work(&afs_server_reaper);
 | 
				
			||||||
	/* resurrect the server if it's death timeout has expired */
 | 
									schedule_delayed_work(&afs_server_reaper,
 | 
				
			||||||
	if (server->fs_state) {
 | 
											      delay);
 | 
				
			||||||
		if (time_before(jiffies, server->fs_dead_jif)) {
 | 
								}
 | 
				
			||||||
			ret = server->fs_state;
 | 
								break;
 | 
				
			||||||
			spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
			_leave(" = %d [still dead]", ret);
 | 
					 | 
				
			||||||
			return ret;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		server->fs_state = 0;
 | 
							write_lock(&server->cell->servers_lock);
 | 
				
			||||||
	}
 | 
							write_lock(&afs_servers_lock);
 | 
				
			||||||
 | 
							if (atomic_read(&server->usage) > 0) {
 | 
				
			||||||
	/* try and find a connection that has spare callslots */
 | 
								list_del_init(&server->grave);
 | 
				
			||||||
	for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) {
 | 
					 | 
				
			||||||
		if (server->fs_conn_cnt[nconn] > 0) {
 | 
					 | 
				
			||||||
			server->fs_conn_cnt[nconn]--;
 | 
					 | 
				
			||||||
			spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
			callslot->nconn = nconn;
 | 
					 | 
				
			||||||
			goto obtained_slot;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* none were available - wait interruptibly for one to become
 | 
					 | 
				
			||||||
	 * available */
 | 
					 | 
				
			||||||
	set_current_state(TASK_INTERRUPTIBLE);
 | 
					 | 
				
			||||||
	list_add_tail(&callslot->link, &server->fs_callq);
 | 
					 | 
				
			||||||
	spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (!callslot->ready && !signal_pending(current)) {
 | 
					 | 
				
			||||||
		schedule();
 | 
					 | 
				
			||||||
		set_current_state(TASK_INTERRUPTIBLE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	set_current_state(TASK_RUNNING);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* even if we were interrupted we may still be queued */
 | 
					 | 
				
			||||||
	if (!callslot->ready) {
 | 
					 | 
				
			||||||
		spin_lock(&server->fs_lock);
 | 
					 | 
				
			||||||
		list_del_init(&callslot->link);
 | 
					 | 
				
			||||||
		spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	nconn = callslot->nconn;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* if interrupted, we must release any slot we also got before
 | 
					 | 
				
			||||||
	 * returning an error */
 | 
					 | 
				
			||||||
	if (signal_pending(current)) {
 | 
					 | 
				
			||||||
		ret = -EINTR;
 | 
					 | 
				
			||||||
		goto error_release;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* if we were woken up with an error, then pass that error back to the
 | 
					 | 
				
			||||||
	 * called */
 | 
					 | 
				
			||||||
	if (nconn < 0) {
 | 
					 | 
				
			||||||
		_leave(" = %d", callslot->errno);
 | 
					 | 
				
			||||||
		return callslot->errno;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* were we given a connection directly? */
 | 
					 | 
				
			||||||
	if (callslot->conn) {
 | 
					 | 
				
			||||||
		/* yes - use it */
 | 
					 | 
				
			||||||
		_leave(" = 0 (nc=%d)", nconn);
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* got a callslot, but no connection */
 | 
					 | 
				
			||||||
obtained_slot:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* need to get hold of the RxRPC connection */
 | 
					 | 
				
			||||||
	down_write(&server->sem);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* quick check to see if there's an outstanding error */
 | 
					 | 
				
			||||||
	ret = server->fs_state;
 | 
					 | 
				
			||||||
	if (ret)
 | 
					 | 
				
			||||||
		goto error_release_upw;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (server->fs_conn[nconn]) {
 | 
					 | 
				
			||||||
		/* reuse an existing connection */
 | 
					 | 
				
			||||||
		rxrpc_get_connection(server->fs_conn[nconn]);
 | 
					 | 
				
			||||||
		callslot->conn = server->fs_conn[nconn];
 | 
					 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
		/* create a new connection */
 | 
								list_move_tail(&server->grave, &corpses);
 | 
				
			||||||
		ret = rxrpc_create_connection(afs_transport,
 | 
								list_del_init(&server->link);
 | 
				
			||||||
					      htons(7000),
 | 
								rb_erase(&server->master_rb, &afs_servers);
 | 
				
			||||||
					      server->addr.s_addr,
 | 
							}
 | 
				
			||||||
					      FS_SERVICE_ID,
 | 
							write_unlock(&afs_servers_lock);
 | 
				
			||||||
					      NULL,
 | 
							write_unlock(&server->cell->servers_lock);
 | 
				
			||||||
					      &server->fs_conn[nconn]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (ret < 0)
 | 
					 | 
				
			||||||
			goto error_release_upw;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		callslot->conn = server->fs_conn[0];
 | 
					 | 
				
			||||||
		rxrpc_get_connection(callslot->conn);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	up_write(&server->sem);
 | 
						spin_unlock(&afs_server_graveyard_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	_leave(" = 0");
 | 
						/* now reap the corpses we've extracted */
 | 
				
			||||||
	return 0;
 | 
						while (!list_empty(&corpses)) {
 | 
				
			||||||
 | 
							server = list_entry(corpses.next, struct afs_server, grave);
 | 
				
			||||||
	/* handle an error occurring */
 | 
							list_del(&server->grave);
 | 
				
			||||||
error_release_upw:
 | 
							afs_destroy_server(server);
 | 
				
			||||||
	up_write(&server->sem);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
error_release:
 | 
					 | 
				
			||||||
	/* either release the callslot or pass it along to another deserving
 | 
					 | 
				
			||||||
	 * task */
 | 
					 | 
				
			||||||
	spin_lock(&server->fs_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (nconn < 0) {
 | 
					 | 
				
			||||||
		/* no callslot allocated */
 | 
					 | 
				
			||||||
	} else if (list_empty(&server->fs_callq)) {
 | 
					 | 
				
			||||||
		/* no one waiting */
 | 
					 | 
				
			||||||
		server->fs_conn_cnt[nconn]++;
 | 
					 | 
				
			||||||
		spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* someone's waiting - dequeue them and wake them up */
 | 
					 | 
				
			||||||
		pcallslot = list_entry(server->fs_callq.next,
 | 
					 | 
				
			||||||
				       struct afs_server_callslot, link);
 | 
					 | 
				
			||||||
		list_del_init(&pcallslot->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		pcallslot->errno = server->fs_state;
 | 
					 | 
				
			||||||
		if (!pcallslot->errno) {
 | 
					 | 
				
			||||||
			/* pass them out callslot details */
 | 
					 | 
				
			||||||
			callslot->conn = xchg(&pcallslot->conn,
 | 
					 | 
				
			||||||
					      callslot->conn);
 | 
					 | 
				
			||||||
			pcallslot->nconn = nconn;
 | 
					 | 
				
			||||||
			callslot->nconn = nconn = -1;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		pcallslot->ready = 1;
 | 
					 | 
				
			||||||
		wake_up_process(pcallslot->task);
 | 
					 | 
				
			||||||
		spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rxrpc_put_connection(callslot->conn);
 | 
					 | 
				
			||||||
	callslot->conn = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * release a callslot back to the server
 | 
					 * discard all the server records for rmmod
 | 
				
			||||||
 * - transfers the RxRPC connection to the next pending callslot if possible
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void afs_server_release_callslot(struct afs_server *server,
 | 
					void __exit afs_purge_servers(void)
 | 
				
			||||||
				 struct afs_server_callslot *callslot)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server_callslot *pcallslot;
 | 
						afs_server_timeout = 0;
 | 
				
			||||||
 | 
						cancel_delayed_work(&afs_server_reaper);
 | 
				
			||||||
	_enter("{ad=%08x,cnt=%u},{%d}",
 | 
						schedule_delayed_work(&afs_server_reaper, 0);
 | 
				
			||||||
	       ntohl(server->addr.s_addr),
 | 
					 | 
				
			||||||
	       server->fs_conn_cnt[callslot->nconn],
 | 
					 | 
				
			||||||
	       callslot->nconn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	BUG_ON(callslot->nconn < 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_lock(&server->fs_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (list_empty(&server->fs_callq)) {
 | 
					 | 
				
			||||||
		/* no one waiting */
 | 
					 | 
				
			||||||
		server->fs_conn_cnt[callslot->nconn]++;
 | 
					 | 
				
			||||||
		spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* someone's waiting - dequeue them and wake them up */
 | 
					 | 
				
			||||||
		pcallslot = list_entry(server->fs_callq.next,
 | 
					 | 
				
			||||||
				       struct afs_server_callslot, link);
 | 
					 | 
				
			||||||
		list_del_init(&pcallslot->link);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		pcallslot->errno = server->fs_state;
 | 
					 | 
				
			||||||
		if (!pcallslot->errno) {
 | 
					 | 
				
			||||||
			/* pass them out callslot details */
 | 
					 | 
				
			||||||
			callslot->conn = xchg(&pcallslot->conn, callslot->conn);
 | 
					 | 
				
			||||||
			pcallslot->nconn = callslot->nconn;
 | 
					 | 
				
			||||||
			callslot->nconn = -1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		pcallslot->ready = 1;
 | 
					 | 
				
			||||||
		wake_up_process(pcallslot->task);
 | 
					 | 
				
			||||||
		spin_unlock(&server->fs_lock);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rxrpc_put_connection(callslot->conn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave("");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * get a handle to a connection to the vlserver (volume location) on the
 | 
					 | 
				
			||||||
 * specified server
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_server_get_vlconn(struct afs_server *server,
 | 
					 | 
				
			||||||
			  struct rxrpc_connection **_conn)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct rxrpc_connection *conn;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%p,", server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = 0;
 | 
					 | 
				
			||||||
	conn = NULL;
 | 
					 | 
				
			||||||
	down_read(&server->sem);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (server->vlserver) {
 | 
					 | 
				
			||||||
		/* reuse an existing connection */
 | 
					 | 
				
			||||||
		rxrpc_get_connection(server->vlserver);
 | 
					 | 
				
			||||||
		conn = server->vlserver;
 | 
					 | 
				
			||||||
		up_read(&server->sem);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* create a new connection */
 | 
					 | 
				
			||||||
		up_read(&server->sem);
 | 
					 | 
				
			||||||
		down_write(&server->sem);
 | 
					 | 
				
			||||||
		if (!server->vlserver) {
 | 
					 | 
				
			||||||
			ret = rxrpc_create_connection(afs_transport,
 | 
					 | 
				
			||||||
						      htons(7003),
 | 
					 | 
				
			||||||
						      server->addr.s_addr,
 | 
					 | 
				
			||||||
						      VL_SERVICE_ID,
 | 
					 | 
				
			||||||
						      NULL,
 | 
					 | 
				
			||||||
						      &server->vlserver);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (ret == 0) {
 | 
					 | 
				
			||||||
			rxrpc_get_connection(server->vlserver);
 | 
					 | 
				
			||||||
			conn = server->vlserver;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		up_write(&server->sem);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	*_conn = conn;
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,97 +0,0 @@
 | 
				
			||||||
/* AFS server record
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef AFS_SERVER_H
 | 
					 | 
				
			||||||
#define AFS_SERVER_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "types.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include <rxrpc/peer.h>
 | 
					 | 
				
			||||||
#include <linux/rwsem.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern spinlock_t afs_server_peer_lock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * AFS server record
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_server {
 | 
					 | 
				
			||||||
	atomic_t		usage;
 | 
					 | 
				
			||||||
	struct afs_cell		*cell;		/* cell in which server resides */
 | 
					 | 
				
			||||||
	struct list_head	link;		/* link in cell's server list */
 | 
					 | 
				
			||||||
	struct rw_semaphore	sem;		/* access lock */
 | 
					 | 
				
			||||||
	struct afs_timer	timeout;	/* graveyard timeout */
 | 
					 | 
				
			||||||
	struct in_addr		addr;		/* server address */
 | 
					 | 
				
			||||||
	struct rxrpc_peer	*peer;		/* peer record for this server */
 | 
					 | 
				
			||||||
	struct rxrpc_connection	*vlserver;	/* connection to the volume location service */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* file service access */
 | 
					 | 
				
			||||||
#define AFS_SERVER_CONN_LIST_SIZE 2
 | 
					 | 
				
			||||||
	struct rxrpc_connection	*fs_conn[AFS_SERVER_CONN_LIST_SIZE]; /* FS connections */
 | 
					 | 
				
			||||||
	unsigned		fs_conn_cnt[AFS_SERVER_CONN_LIST_SIZE];	/* per conn call count */
 | 
					 | 
				
			||||||
	struct list_head	fs_callq;	/* queue of processes waiting to make a call */
 | 
					 | 
				
			||||||
	spinlock_t		fs_lock;	/* access lock */
 | 
					 | 
				
			||||||
	int			fs_state;      	/* 0 or reason FS currently marked dead (-errno) */
 | 
					 | 
				
			||||||
	unsigned		fs_rtt;		/* FS round trip time */
 | 
					 | 
				
			||||||
	unsigned long		fs_act_jif;	/* time at which last activity occurred */
 | 
					 | 
				
			||||||
	unsigned long		fs_dead_jif;	/* time at which no longer to be considered dead */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* callback promise management */
 | 
					 | 
				
			||||||
	struct list_head	cb_promises;	/* as yet unbroken promises from this server */
 | 
					 | 
				
			||||||
	spinlock_t		cb_lock;	/* access lock */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_server_lookup(struct afs_cell *, const struct in_addr *,
 | 
					 | 
				
			||||||
			     struct afs_server **);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern void afs_put_server(struct afs_server *);
 | 
					 | 
				
			||||||
extern void afs_server_do_timeout(struct afs_server *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_server_find_by_peer(const struct rxrpc_peer *,
 | 
					 | 
				
			||||||
				   struct afs_server **);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_server_get_vlconn(struct afs_server *,
 | 
					 | 
				
			||||||
				 struct rxrpc_connection **);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline
 | 
					 | 
				
			||||||
struct afs_server *afs_server_get_from_peer(struct rxrpc_peer *peer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_server *server;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_lock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
	server = peer->user;
 | 
					 | 
				
			||||||
	if (server)
 | 
					 | 
				
			||||||
		afs_get_server(server);
 | 
					 | 
				
			||||||
	spin_unlock(&afs_server_peer_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return server;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * AFS server callslot grant record
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_server_callslot {
 | 
					 | 
				
			||||||
	struct list_head	link;		/* link in server's list */
 | 
					 | 
				
			||||||
	struct task_struct	*task;		/* process waiting to make call */
 | 
					 | 
				
			||||||
	struct rxrpc_connection	*conn;		/* connection to use (or NULL on error) */
 | 
					 | 
				
			||||||
	short			nconn;		/* connection slot number (-1 on error) */
 | 
					 | 
				
			||||||
	char			ready;		/* T when ready */
 | 
					 | 
				
			||||||
	int			errno;		/* error number if nconn==-1 */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_server_request_callslot(struct afs_server *,
 | 
					 | 
				
			||||||
				       struct afs_server_callslot *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern void afs_server_release_callslot(struct afs_server *,
 | 
					 | 
				
			||||||
					struct afs_server_callslot *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_SERVER_H */
 | 
					 | 
				
			||||||
							
								
								
									
										102
									
								
								fs/afs/super.c
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								fs/afs/super.c
									
									
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* AFS superblock handling
 | 
					/* AFS superblock handling
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
 | 
					 * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This software may be freely redistributed under the terms of the
 | 
					 * This software may be freely redistributed under the terms of the
 | 
				
			||||||
 * GNU General Public License.
 | 
					 * GNU General Public License.
 | 
				
			||||||
| 
						 | 
					@ -20,12 +20,6 @@
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
#include <linux/pagemap.h>
 | 
					#include <linux/pagemap.h>
 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "cmservice.h"
 | 
					 | 
				
			||||||
#include "fsclient.h"
 | 
					 | 
				
			||||||
#include "super.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */
 | 
					#define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */
 | 
				
			||||||
| 
						 | 
					@ -63,6 +57,7 @@ static const struct super_operations afs_super_ops = {
 | 
				
			||||||
	.drop_inode	= generic_delete_inode,
 | 
						.drop_inode	= generic_delete_inode,
 | 
				
			||||||
	.destroy_inode	= afs_destroy_inode,
 | 
						.destroy_inode	= afs_destroy_inode,
 | 
				
			||||||
	.clear_inode	= afs_clear_inode,
 | 
						.clear_inode	= afs_clear_inode,
 | 
				
			||||||
 | 
						.umount_begin	= afs_umount_begin,
 | 
				
			||||||
	.put_super	= afs_put_super,
 | 
						.put_super	= afs_put_super,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,8 +73,6 @@ int __init afs_fs_init(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("");
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_timer_init(&afs_mntpt_expiry_timer, &afs_mntpt_expiry_timer_ops);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* create ourselves an inode cache */
 | 
						/* create ourselves an inode cache */
 | 
				
			||||||
	atomic_set(&afs_count_active_inodes, 0);
 | 
						atomic_set(&afs_count_active_inodes, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,11 +92,11 @@ int __init afs_fs_init(void)
 | 
				
			||||||
	ret = register_filesystem(&afs_fs_type);
 | 
						ret = register_filesystem(&afs_fs_type);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
		kmem_cache_destroy(afs_inode_cachep);
 | 
							kmem_cache_destroy(afs_inode_cachep);
 | 
				
			||||||
		kleave(" = %d", ret);
 | 
							_leave(" = %d", ret);
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kleave(" = 0");
 | 
						_leave(" = 0");
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,6 +105,9 @@ int __init afs_fs_init(void)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void __exit afs_fs_exit(void)
 | 
					void __exit afs_fs_exit(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_mntpt_kill_timer();
 | 
				
			||||||
	unregister_filesystem(&afs_fs_type);
 | 
						unregister_filesystem(&afs_fs_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (atomic_read(&afs_count_active_inodes) != 0) {
 | 
						if (atomic_read(&afs_count_active_inodes) != 0) {
 | 
				
			||||||
| 
						 | 
					@ -121,6 +117,7 @@ void __exit afs_fs_exit(void)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kmem_cache_destroy(afs_inode_cachep);
 | 
						kmem_cache_destroy(afs_inode_cachep);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -154,9 +151,9 @@ static int want_no_value(char *const *_value, const char *option)
 | 
				
			||||||
 *   shamelessly adapted it from the msdos fs
 | 
					 *   shamelessly adapted it from the msdos fs
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_super_parse_options(struct afs_mount_params *params,
 | 
					static int afs_super_parse_options(struct afs_mount_params *params,
 | 
				
			||||||
				   char *options,
 | 
									   char *options, const char **devname)
 | 
				
			||||||
				   const char **devname)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct afs_cell *cell;
 | 
				
			||||||
	char *key, *value;
 | 
						char *key, *value;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,43 +162,37 @@ static int afs_super_parse_options(struct afs_mount_params *params,
 | 
				
			||||||
	options[PAGE_SIZE - 1] = 0;
 | 
						options[PAGE_SIZE - 1] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = 0;
 | 
						ret = 0;
 | 
				
			||||||
	while ((key = strsep(&options, ",")) != 0)
 | 
						while ((key = strsep(&options, ","))) {
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		value = strchr(key, '=');
 | 
							value = strchr(key, '=');
 | 
				
			||||||
		if (value)
 | 
							if (value)
 | 
				
			||||||
			*value++ = 0;
 | 
								*value++ = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		printk("kAFS: KEY: %s, VAL:%s\n", key, value ?: "-");
 | 
							_debug("kAFS: KEY: %s, VAL:%s", key, value ?: "-");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (strcmp(key, "rwpath") == 0) {
 | 
							if (strcmp(key, "rwpath") == 0) {
 | 
				
			||||||
			if (!want_no_value(&value, "rwpath"))
 | 
								if (!want_no_value(&value, "rwpath"))
 | 
				
			||||||
				return -EINVAL;
 | 
									return -EINVAL;
 | 
				
			||||||
			params->rwpath = 1;
 | 
								params->rwpath = 1;
 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		} else if (strcmp(key, "vol") == 0) {
 | 
							} else if (strcmp(key, "vol") == 0) {
 | 
				
			||||||
			if (!want_arg(&value, "vol"))
 | 
								if (!want_arg(&value, "vol"))
 | 
				
			||||||
				return -EINVAL;
 | 
									return -EINVAL;
 | 
				
			||||||
			*devname = value;
 | 
								*devname = value;
 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		} else if (strcmp(key, "cell") == 0) {
 | 
							} else if (strcmp(key, "cell") == 0) {
 | 
				
			||||||
			if (!want_arg(&value, "cell"))
 | 
								if (!want_arg(&value, "cell"))
 | 
				
			||||||
				return -EINVAL;
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
								cell = afs_cell_lookup(value, strlen(value));
 | 
				
			||||||
 | 
								if (IS_ERR(cell))
 | 
				
			||||||
 | 
									return PTR_ERR(cell);
 | 
				
			||||||
			afs_put_cell(params->default_cell);
 | 
								afs_put_cell(params->default_cell);
 | 
				
			||||||
			ret = afs_cell_lookup(value,
 | 
								params->default_cell = cell;
 | 
				
			||||||
					      strlen(value),
 | 
							} else {
 | 
				
			||||||
					      ¶ms->default_cell);
 | 
					 | 
				
			||||||
			if (ret < 0)
 | 
					 | 
				
			||||||
				return -EINVAL;
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			printk("kAFS: Unknown mount option: '%s'\n",  key);
 | 
								printk("kAFS: Unknown mount option: '%s'\n",  key);
 | 
				
			||||||
			ret = -EINVAL;
 | 
								ret = -EINVAL;
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = 0;
 | 
						ret = 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					@ -230,7 +221,7 @@ static int afs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
	struct inode *inode = NULL;
 | 
						struct inode *inode = NULL;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kenter("");
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* allocate a superblock info record */
 | 
						/* allocate a superblock info record */
 | 
				
			||||||
	as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
 | 
						as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
 | 
				
			||||||
| 
						 | 
					@ -253,9 +244,9 @@ static int afs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
	fid.vid		= as->volume->vid;
 | 
						fid.vid		= as->volume->vid;
 | 
				
			||||||
	fid.vnode	= 1;
 | 
						fid.vnode	= 1;
 | 
				
			||||||
	fid.unique	= 1;
 | 
						fid.unique	= 1;
 | 
				
			||||||
	ret = afs_iget(sb, &fid, &inode);
 | 
						inode = afs_iget(sb, &fid);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (IS_ERR(inode))
 | 
				
			||||||
		goto error;
 | 
							goto error_inode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = -ENOMEM;
 | 
						ret = -ENOMEM;
 | 
				
			||||||
	root = d_alloc_root(inode);
 | 
						root = d_alloc_root(inode);
 | 
				
			||||||
| 
						 | 
					@ -264,9 +255,12 @@ static int afs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sb->s_root = root;
 | 
						sb->s_root = root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kleave(" = 0");
 | 
						_leave(" = 0");
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error_inode:
 | 
				
			||||||
 | 
						ret = PTR_ERR(inode);
 | 
				
			||||||
 | 
						inode = NULL;
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	iput(inode);
 | 
						iput(inode);
 | 
				
			||||||
	afs_put_volume(as->volume);
 | 
						afs_put_volume(as->volume);
 | 
				
			||||||
| 
						 | 
					@ -274,7 +268,7 @@ static int afs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sb->s_fs_info = NULL;
 | 
						sb->s_fs_info = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kleave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -290,19 +284,13 @@ static int afs_get_sb(struct file_system_type *fs_type,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_mount_params params;
 | 
						struct afs_mount_params params;
 | 
				
			||||||
	struct super_block *sb;
 | 
						struct super_block *sb;
 | 
				
			||||||
 | 
						struct afs_volume *vol;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter(",,%s,%p", dev_name, options);
 | 
						_enter(",,%s,%p", dev_name, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(¶ms, 0, sizeof(params));
 | 
						memset(¶ms, 0, sizeof(params));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* start the cache manager */
 | 
					 | 
				
			||||||
	ret = afscm_start();
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		_leave(" = %d", ret);
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* parse the options */
 | 
						/* parse the options */
 | 
				
			||||||
	if (options) {
 | 
						if (options) {
 | 
				
			||||||
		ret = afs_super_parse_options(¶ms, options, &dev_name);
 | 
							ret = afs_super_parse_options(¶ms, options, &dev_name);
 | 
				
			||||||
| 
						 | 
					@ -316,17 +304,20 @@ static int afs_get_sb(struct file_system_type *fs_type,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* parse the device name */
 | 
						/* parse the device name */
 | 
				
			||||||
	ret = afs_volume_lookup(dev_name,
 | 
						vol = afs_volume_lookup(dev_name, params.default_cell, params.rwpath);
 | 
				
			||||||
				params.default_cell,
 | 
						if (IS_ERR(vol)) {
 | 
				
			||||||
				params.rwpath,
 | 
							ret = PTR_ERR(vol);
 | 
				
			||||||
				¶ms.volume);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params.volume = vol;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* allocate a deviceless superblock */
 | 
						/* allocate a deviceless superblock */
 | 
				
			||||||
	sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms);
 | 
						sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms);
 | 
				
			||||||
	if (IS_ERR(sb))
 | 
						if (IS_ERR(sb)) {
 | 
				
			||||||
 | 
							ret = PTR_ERR(sb);
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sb->s_flags = flags;
 | 
						sb->s_flags = flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -341,13 +332,12 @@ static int afs_get_sb(struct file_system_type *fs_type,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_put_volume(params.volume);
 | 
						afs_put_volume(params.volume);
 | 
				
			||||||
	afs_put_cell(params.default_cell);
 | 
						afs_put_cell(params.default_cell);
 | 
				
			||||||
	_leave(" = 0 [%p]", 0, sb);
 | 
						_leave(" = 0 [%p]", sb);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	afs_put_volume(params.volume);
 | 
						afs_put_volume(params.volume);
 | 
				
			||||||
	afs_put_cell(params.default_cell);
 | 
						afs_put_cell(params.default_cell);
 | 
				
			||||||
	afscm_stop();
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -362,7 +352,6 @@ static void afs_put_super(struct super_block *sb)
 | 
				
			||||||
	_enter("");
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_put_volume(as->volume);
 | 
						afs_put_volume(as->volume);
 | 
				
			||||||
	afscm_stop();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave("");
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -381,10 +370,8 @@ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep,
 | 
				
			||||||
		inode_init_once(&vnode->vfs_inode);
 | 
							inode_init_once(&vnode->vfs_inode);
 | 
				
			||||||
		init_waitqueue_head(&vnode->update_waitq);
 | 
							init_waitqueue_head(&vnode->update_waitq);
 | 
				
			||||||
		spin_lock_init(&vnode->lock);
 | 
							spin_lock_init(&vnode->lock);
 | 
				
			||||||
		INIT_LIST_HEAD(&vnode->cb_link);
 | 
							INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work);
 | 
				
			||||||
		INIT_LIST_HEAD(&vnode->cb_hash_link);
 | 
							mutex_init(&vnode->cb_broken_lock);
 | 
				
			||||||
		afs_timer_init(&vnode->cb_timeout,
 | 
					 | 
				
			||||||
			       &afs_vnode_cb_timed_out_ops);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -407,6 +394,7 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
 | 
				
			||||||
	vnode->volume		= NULL;
 | 
						vnode->volume		= NULL;
 | 
				
			||||||
	vnode->update_cnt	= 0;
 | 
						vnode->update_cnt	= 0;
 | 
				
			||||||
	vnode->flags		= 0;
 | 
						vnode->flags		= 0;
 | 
				
			||||||
 | 
						vnode->cb_promised	= false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &vnode->vfs_inode;
 | 
						return &vnode->vfs_inode;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -416,8 +404,14 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_destroy_inode(struct inode *inode)
 | 
					static void afs_destroy_inode(struct inode *inode)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct afs_vnode *vnode = AFS_FS_I(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%lu}", inode->i_ino);
 | 
						_enter("{%lu}", inode->i_ino);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode));
 | 
						_debug("DESTROY INODE %p", inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERTCMP(vnode->server, ==, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kmem_cache_free(afs_inode_cachep, vnode);
 | 
				
			||||||
	atomic_dec(&afs_count_active_inodes);
 | 
						atomic_dec(&afs_count_active_inodes);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,39 +0,0 @@
 | 
				
			||||||
/* AFS filesystem internal private data
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This software may be freely redistributed under the terms of the
 | 
					 | 
				
			||||||
 * GNU General Public License.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU General Public License
 | 
					 | 
				
			||||||
 * along with this program; if not, write to the Free Software
 | 
					 | 
				
			||||||
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
 | 
					 | 
				
			||||||
 *          David Howells <dhowells@redhat.com>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef AFS_SUPER_H
 | 
					 | 
				
			||||||
#define AFS_SUPER_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <linux/fs.h>
 | 
					 | 
				
			||||||
#include "server.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * AFS superblock private data
 | 
					 | 
				
			||||||
 * - there's one superblock per volume
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_super_info {
 | 
					 | 
				
			||||||
	struct afs_volume	*volume;	/* volume record */
 | 
					 | 
				
			||||||
	char			rwparent;	/* T if parent is R/W AFS volume */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return sb->s_fs_info;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern struct file_system_type afs_fs_type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_SUPER_H */
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,21 +0,0 @@
 | 
				
			||||||
/* AFS transport management
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef AFS_TRANSPORT_H
 | 
					 | 
				
			||||||
#define AFS_TRANSPORT_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "types.h"
 | 
					 | 
				
			||||||
#include <rxrpc/transport.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* the cache manager transport endpoint */
 | 
					 | 
				
			||||||
extern struct rxrpc_transport *afs_transport;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_TRANSPORT_H */
 | 
					 | 
				
			||||||
| 
						 | 
					@ -11,243 +11,76 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/init.h>
 | 
					#include <linux/init.h>
 | 
				
			||||||
#include <linux/sched.h>
 | 
					#include <linux/sched.h>
 | 
				
			||||||
#include <rxrpc/rxrpc.h>
 | 
					 | 
				
			||||||
#include <rxrpc/transport.h>
 | 
					 | 
				
			||||||
#include <rxrpc/connection.h>
 | 
					 | 
				
			||||||
#include <rxrpc/call.h>
 | 
					 | 
				
			||||||
#include "server.h"
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "vlclient.h"
 | 
					 | 
				
			||||||
#include "kafsasyncd.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include "errors.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define VLGETENTRYBYID		503	/* AFS Get Cache Entry By ID operation ID */
 | 
					/*
 | 
				
			||||||
#define VLGETENTRYBYNAME	504	/* AFS Get Cache Entry By Name operation ID */
 | 
					 * map volume locator abort codes to error codes
 | 
				
			||||||
#define VLPROBE			514	/* AFS Probe Volume Location Service operation ID */
 | 
					 */
 | 
				
			||||||
 | 
					static int afs_vl_abort_to_error(u32 abort_code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_enter("%u", abort_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call);
 | 
						switch (abort_code) {
 | 
				
			||||||
static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call);
 | 
						case AFSVL_IDEXIST:		return -EEXIST;
 | 
				
			||||||
 | 
						case AFSVL_IO:			return -EREMOTEIO;
 | 
				
			||||||
 | 
						case AFSVL_NAMEEXIST:		return -EEXIST;
 | 
				
			||||||
 | 
						case AFSVL_CREATEFAIL:		return -EREMOTEIO;
 | 
				
			||||||
 | 
						case AFSVL_NOENT:		return -ENOMEDIUM;
 | 
				
			||||||
 | 
						case AFSVL_EMPTY:		return -ENOMEDIUM;
 | 
				
			||||||
 | 
						case AFSVL_ENTDELETED:		return -ENOMEDIUM;
 | 
				
			||||||
 | 
						case AFSVL_BADNAME:		return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_BADINDEX:		return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_BADVOLTYPE:		return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_BADSERVER:		return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_BADPARTITION:	return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_REPSFULL:		return -EFBIG;
 | 
				
			||||||
 | 
						case AFSVL_NOREPSERVER:		return -ENOENT;
 | 
				
			||||||
 | 
						case AFSVL_DUPREPSERVER:	return -EEXIST;
 | 
				
			||||||
 | 
						case AFSVL_RWNOTFOUND:		return -ENOENT;
 | 
				
			||||||
 | 
						case AFSVL_BADREFCOUNT:		return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_SIZEEXCEEDED:	return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_BADENTRY:		return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_BADVOLIDBUMP:	return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_IDALREADYHASHED:	return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_ENTRYLOCKED:		return -EBUSY;
 | 
				
			||||||
 | 
						case AFSVL_BADVOLOPER:		return -EBADRQC;
 | 
				
			||||||
 | 
						case AFSVL_BADRELLOCKTYPE:	return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_RERELEASE:		return -EREMOTEIO;
 | 
				
			||||||
 | 
						case AFSVL_BADSERVERFLAG:	return -EINVAL;
 | 
				
			||||||
 | 
						case AFSVL_PERM:		return -EACCES;
 | 
				
			||||||
 | 
						case AFSVL_NOMEM:		return -EREMOTEIO;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return afs_abort_to_error(abort_code);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * map afs VL abort codes to/from Linux error codes
 | 
					 * deliver reply data to a VL.GetEntryByXXX call
 | 
				
			||||||
 * - called with call->lock held
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_rxvl_aemap(struct rxrpc_call *call)
 | 
					static int afs_deliver_vl_get_entry_by_xxx(struct afs_call *call,
 | 
				
			||||||
 | 
										   struct sk_buff *skb, bool last)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err;
 | 
						struct afs_cache_vlocation *entry;
 | 
				
			||||||
 | 
						__be32 *bp;
 | 
				
			||||||
 | 
						u32 tmp;
 | 
				
			||||||
 | 
						int loop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%u,%u,%d}",
 | 
						_enter(",,%u", last);
 | 
				
			||||||
	       call->app_err_state, call->app_abort_code, call->app_errno);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (call->app_err_state) {
 | 
						afs_transfer_reply(call, skb);
 | 
				
			||||||
	case RXRPC_ESTATE_LOCAL_ABORT:
 | 
						if (!last)
 | 
				
			||||||
		call->app_abort_code = -call->app_errno;
 | 
							return 0;
 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case RXRPC_ESTATE_PEER_ABORT:
 | 
						if (call->reply_size != call->reply_max)
 | 
				
			||||||
		switch (call->app_abort_code) {
 | 
							return -EBADMSG;
 | 
				
			||||||
		case AFSVL_IDEXIST:		err = -EEXIST;		break;
 | 
					 | 
				
			||||||
		case AFSVL_IO:			err = -EREMOTEIO;	break;
 | 
					 | 
				
			||||||
		case AFSVL_NAMEEXIST:		err = -EEXIST;		break;
 | 
					 | 
				
			||||||
		case AFSVL_CREATEFAIL:		err = -EREMOTEIO;	break;
 | 
					 | 
				
			||||||
		case AFSVL_NOENT:		err = -ENOMEDIUM;	break;
 | 
					 | 
				
			||||||
		case AFSVL_EMPTY:		err = -ENOMEDIUM;	break;
 | 
					 | 
				
			||||||
		case AFSVL_ENTDELETED:		err = -ENOMEDIUM;	break;
 | 
					 | 
				
			||||||
		case AFSVL_BADNAME:		err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADINDEX:		err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADVOLTYPE:		err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADSERVER:		err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADPARTITION:	err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_REPSFULL:		err = -EFBIG;		break;
 | 
					 | 
				
			||||||
		case AFSVL_NOREPSERVER:		err = -ENOENT;		break;
 | 
					 | 
				
			||||||
		case AFSVL_DUPREPSERVER:	err = -EEXIST;		break;
 | 
					 | 
				
			||||||
		case AFSVL_RWNOTFOUND:		err = -ENOENT;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADREFCOUNT:		err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_SIZEEXCEEDED:	err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADENTRY:		err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADVOLIDBUMP:	err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_IDALREADYHASHED:	err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_ENTRYLOCKED:		err = -EBUSY;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADVOLOPER:		err = -EBADRQC;		break;
 | 
					 | 
				
			||||||
		case AFSVL_BADRELLOCKTYPE:	err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_RERELEASE:		err = -EREMOTEIO;	break;
 | 
					 | 
				
			||||||
		case AFSVL_BADSERVERFLAG:	err = -EINVAL;		break;
 | 
					 | 
				
			||||||
		case AFSVL_PERM:		err = -EACCES;		break;
 | 
					 | 
				
			||||||
		case AFSVL_NOMEM:		err = -EREMOTEIO;	break;
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			err = afs_abort_to_error(call->app_abort_code);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		call->app_errno = err;
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						/* unmarshall the reply once we've received all of it */
 | 
				
			||||||
		return;
 | 
						entry = call->reply;
 | 
				
			||||||
	}
 | 
						bp = call->buffer;
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * probe a volume location server to see if it is still alive -- unused
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static int afs_rxvl_probe(struct afs_server *server, int alloc_flags)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct rxrpc_connection *conn;
 | 
					 | 
				
			||||||
	struct rxrpc_call *call;
 | 
					 | 
				
			||||||
	struct kvec piov[1];
 | 
					 | 
				
			||||||
	size_t sent;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
	__be32 param[1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DECLARE_WAITQUEUE(myself, current);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* get hold of the vlserver connection */
 | 
					 | 
				
			||||||
	ret = afs_server_get_vlconn(server, &conn);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* create a call through that connection */
 | 
					 | 
				
			||||||
	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		printk("kAFS: Unable to create call: %d\n", ret);
 | 
					 | 
				
			||||||
		goto out_put_conn;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	call->app_opcode = VLPROBE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* we want to get event notifications from the call */
 | 
					 | 
				
			||||||
	add_wait_queue(&call->waitq, &myself);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* marshall the parameters */
 | 
					 | 
				
			||||||
	param[0] = htonl(VLPROBE);
 | 
					 | 
				
			||||||
	piov[0].iov_len = sizeof(param);
 | 
					 | 
				
			||||||
	piov[0].iov_base = param;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* send the parameters to the server */
 | 
					 | 
				
			||||||
	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET,
 | 
					 | 
				
			||||||
				    alloc_flags, 0, &sent);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto abort;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* wait for the reply to completely arrive */
 | 
					 | 
				
			||||||
	for (;;) {
 | 
					 | 
				
			||||||
		set_current_state(TASK_INTERRUPTIBLE);
 | 
					 | 
				
			||||||
		if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY ||
 | 
					 | 
				
			||||||
		    signal_pending(current))
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		schedule();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	set_current_state(TASK_RUNNING);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = -EINTR;
 | 
					 | 
				
			||||||
	if (signal_pending(current))
 | 
					 | 
				
			||||||
		goto abort;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (call->app_call_state) {
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_ERROR:
 | 
					 | 
				
			||||||
		ret = call->app_errno;
 | 
					 | 
				
			||||||
		goto out_unwait;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_CLNT_GOT_REPLY:
 | 
					 | 
				
			||||||
		ret = 0;
 | 
					 | 
				
			||||||
		goto out_unwait;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		BUG();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
abort:
 | 
					 | 
				
			||||||
	set_current_state(TASK_UNINTERRUPTIBLE);
 | 
					 | 
				
			||||||
	rxrpc_call_abort(call, ret);
 | 
					 | 
				
			||||||
	schedule();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
out_unwait:
 | 
					 | 
				
			||||||
	set_current_state(TASK_RUNNING);
 | 
					 | 
				
			||||||
	remove_wait_queue(&call->waitq, &myself);
 | 
					 | 
				
			||||||
	rxrpc_put_call(call);
 | 
					 | 
				
			||||||
out_put_conn:
 | 
					 | 
				
			||||||
	rxrpc_put_connection(conn);
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * look up a volume location database entry by name
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_rxvl_get_entry_by_name(struct afs_server *server,
 | 
					 | 
				
			||||||
			       const char *volname,
 | 
					 | 
				
			||||||
			       unsigned volnamesz,
 | 
					 | 
				
			||||||
			       struct afs_cache_vlocation *entry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	DECLARE_WAITQUEUE(myself, current);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct rxrpc_connection *conn;
 | 
					 | 
				
			||||||
	struct rxrpc_call *call;
 | 
					 | 
				
			||||||
	struct kvec piov[3];
 | 
					 | 
				
			||||||
	unsigned tmp;
 | 
					 | 
				
			||||||
	size_t sent;
 | 
					 | 
				
			||||||
	int ret, loop;
 | 
					 | 
				
			||||||
	__be32 *bp, param[2], zero;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter(",%*.*s,%u,", volnamesz, volnamesz, volname, volnamesz);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memset(entry, 0, sizeof(*entry));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* get hold of the vlserver connection */
 | 
					 | 
				
			||||||
	ret = afs_server_get_vlconn(server, &conn);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* create a call through that connection */
 | 
					 | 
				
			||||||
	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		printk("kAFS: Unable to create call: %d\n", ret);
 | 
					 | 
				
			||||||
		goto out_put_conn;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	call->app_opcode = VLGETENTRYBYNAME;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* we want to get event notifications from the call */
 | 
					 | 
				
			||||||
	add_wait_queue(&call->waitq, &myself);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* marshall the parameters */
 | 
					 | 
				
			||||||
	piov[1].iov_len = volnamesz;
 | 
					 | 
				
			||||||
	piov[1].iov_base = (char *) volname;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	zero = 0;
 | 
					 | 
				
			||||||
	piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3;
 | 
					 | 
				
			||||||
	piov[2].iov_base = &zero;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	param[0] = htonl(VLGETENTRYBYNAME);
 | 
					 | 
				
			||||||
	param[1] = htonl(piov[1].iov_len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	piov[0].iov_len = sizeof(param);
 | 
					 | 
				
			||||||
	piov[0].iov_base = param;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* send the parameters to the server */
 | 
					 | 
				
			||||||
	ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS,
 | 
					 | 
				
			||||||
				    0, &sent);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto abort;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* wait for the reply to completely arrive */
 | 
					 | 
				
			||||||
	bp = rxrpc_call_alloc_scratch(call, 384);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = rxrpc_call_read_data(call, bp, 384,
 | 
					 | 
				
			||||||
				   RXRPC_CALL_READ_BLOCK |
 | 
					 | 
				
			||||||
				   RXRPC_CALL_READ_ALL);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		if (ret == -ECONNABORTED) {
 | 
					 | 
				
			||||||
			ret = call->app_errno;
 | 
					 | 
				
			||||||
			goto out_unwait;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		goto abort;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* unmarshall the reply */
 | 
					 | 
				
			||||||
	for (loop = 0; loop < 64; loop++)
 | 
						for (loop = 0; loop < 64; loop++)
 | 
				
			||||||
		entry->name[loop] = ntohl(*bp++);
 | 
							entry->name[loop] = ntohl(*bp++);
 | 
				
			||||||
 | 
						entry->name[loop] = 0;
 | 
				
			||||||
	bp++; /* final NUL */
 | 
						bp++; /* final NUL */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bp++; /* type */
 | 
						bp++; /* type */
 | 
				
			||||||
| 
						 | 
					@ -260,6 +93,7 @@ int afs_rxvl_get_entry_by_name(struct afs_server *server,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (loop = 0; loop < 8; loop++) {
 | 
						for (loop = 0; loop < 8; loop++) {
 | 
				
			||||||
		tmp = ntohl(*bp++);
 | 
							tmp = ntohl(*bp++);
 | 
				
			||||||
 | 
							entry->srvtmask[loop] = 0;
 | 
				
			||||||
		if (tmp & AFS_VLSF_RWVOL)
 | 
							if (tmp & AFS_VLSF_RWVOL)
 | 
				
			||||||
			entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
 | 
								entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
 | 
				
			||||||
		if (tmp & AFS_VLSF_ROVOL)
 | 
							if (tmp & AFS_VLSF_ROVOL)
 | 
				
			||||||
| 
						 | 
					@ -275,409 +109,104 @@ int afs_rxvl_get_entry_by_name(struct afs_server *server,
 | 
				
			||||||
	bp++; /* clone ID */
 | 
						bp++; /* clone ID */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tmp = ntohl(*bp++); /* flags */
 | 
						tmp = ntohl(*bp++); /* flags */
 | 
				
			||||||
 | 
						entry->vidmask = 0;
 | 
				
			||||||
	if (tmp & AFS_VLF_RWEXISTS)
 | 
						if (tmp & AFS_VLF_RWEXISTS)
 | 
				
			||||||
		entry->vidmask |= AFS_VOL_VTM_RW;
 | 
							entry->vidmask |= AFS_VOL_VTM_RW;
 | 
				
			||||||
	if (tmp & AFS_VLF_ROEXISTS)
 | 
						if (tmp & AFS_VLF_ROEXISTS)
 | 
				
			||||||
		entry->vidmask |= AFS_VOL_VTM_RO;
 | 
							entry->vidmask |= AFS_VOL_VTM_RO;
 | 
				
			||||||
	if (tmp & AFS_VLF_BACKEXISTS)
 | 
						if (tmp & AFS_VLF_BACKEXISTS)
 | 
				
			||||||
		entry->vidmask |= AFS_VOL_VTM_BAK;
 | 
							entry->vidmask |= AFS_VOL_VTM_BAK;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = -ENOMEDIUM;
 | 
					 | 
				
			||||||
	if (!entry->vidmask)
 | 
						if (!entry->vidmask)
 | 
				
			||||||
		goto abort;
 | 
							return -EBADMSG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* success */
 | 
						_leave(" = 0 [done]");
 | 
				
			||||||
	entry->rtime = get_seconds();
 | 
						return 0;
 | 
				
			||||||
	ret = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
out_unwait:
 | 
					 | 
				
			||||||
	set_current_state(TASK_RUNNING);
 | 
					 | 
				
			||||||
	remove_wait_queue(&call->waitq, &myself);
 | 
					 | 
				
			||||||
	rxrpc_put_call(call);
 | 
					 | 
				
			||||||
out_put_conn:
 | 
					 | 
				
			||||||
	rxrpc_put_connection(conn);
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
abort:
 | 
					 | 
				
			||||||
	set_current_state(TASK_UNINTERRUPTIBLE);
 | 
					 | 
				
			||||||
	rxrpc_call_abort(call, ret);
 | 
					 | 
				
			||||||
	schedule();
 | 
					 | 
				
			||||||
	goto out_unwait;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * look up a volume location database entry by ID
 | 
					 * VL.GetEntryByName operation type
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_rxvl_get_entry_by_id(struct afs_server *server,
 | 
					static const struct afs_call_type afs_RXVLGetEntryByName = {
 | 
				
			||||||
 | 
						.deliver	= afs_deliver_vl_get_entry_by_xxx,
 | 
				
			||||||
 | 
						.abort_to_error	= afs_vl_abort_to_error,
 | 
				
			||||||
 | 
						.destructor	= afs_flat_call_destructor,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * VL.GetEntryById operation type
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static const struct afs_call_type afs_RXVLGetEntryById = {
 | 
				
			||||||
 | 
						.deliver	= afs_deliver_vl_get_entry_by_xxx,
 | 
				
			||||||
 | 
						.abort_to_error	= afs_vl_abort_to_error,
 | 
				
			||||||
 | 
						.destructor	= afs_flat_call_destructor,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * dispatch a get volume entry by name operation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int afs_vl_get_entry_by_name(struct in_addr *addr,
 | 
				
			||||||
 | 
								     const char *volname,
 | 
				
			||||||
 | 
								     struct afs_cache_vlocation *entry,
 | 
				
			||||||
 | 
								     const struct afs_wait_mode *wait_mode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_call *call;
 | 
				
			||||||
 | 
						size_t volnamesz, reqsz, padsz;
 | 
				
			||||||
 | 
						__be32 *bp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						volnamesz = strlen(volname);
 | 
				
			||||||
 | 
						padsz = (4 - (volnamesz & 3)) & 3;
 | 
				
			||||||
 | 
						reqsz = 8 + volnamesz + padsz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call = afs_alloc_flat_call(&afs_RXVLGetEntryByName, reqsz, 384);
 | 
				
			||||||
 | 
						if (!call)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						call->reply = entry;
 | 
				
			||||||
 | 
						call->service_id = VL_SERVICE;
 | 
				
			||||||
 | 
						call->port = htons(AFS_VL_PORT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* marshall the parameters */
 | 
				
			||||||
 | 
						bp = call->request;
 | 
				
			||||||
 | 
						*bp++ = htonl(VLGETENTRYBYNAME);
 | 
				
			||||||
 | 
						*bp++ = htonl(volnamesz);
 | 
				
			||||||
 | 
						memcpy(bp, volname, volnamesz);
 | 
				
			||||||
 | 
						if (padsz > 0)
 | 
				
			||||||
 | 
							memset((void *) bp + volnamesz, 0, padsz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* initiate the call */
 | 
				
			||||||
 | 
						return afs_make_call(addr, call, GFP_KERNEL, wait_mode);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * dispatch a get volume entry by ID operation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int afs_vl_get_entry_by_id(struct in_addr *addr,
 | 
				
			||||||
			   afs_volid_t volid,
 | 
								   afs_volid_t volid,
 | 
				
			||||||
			   afs_voltype_t voltype,
 | 
								   afs_voltype_t voltype,
 | 
				
			||||||
			     struct afs_cache_vlocation *entry)
 | 
								   struct afs_cache_vlocation *entry,
 | 
				
			||||||
{
 | 
								   const struct afs_wait_mode *wait_mode)
 | 
				
			||||||
	DECLARE_WAITQUEUE(myself, current);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct rxrpc_connection *conn;
 | 
					 | 
				
			||||||
	struct rxrpc_call *call;
 | 
					 | 
				
			||||||
	struct kvec piov[1];
 | 
					 | 
				
			||||||
	unsigned tmp;
 | 
					 | 
				
			||||||
	size_t sent;
 | 
					 | 
				
			||||||
	int ret, loop;
 | 
					 | 
				
			||||||
	__be32 *bp, param[3];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter(",%x,%d,", volid, voltype);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memset(entry, 0, sizeof(*entry));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* get hold of the vlserver connection */
 | 
					 | 
				
			||||||
	ret = afs_server_get_vlconn(server, &conn);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* create a call through that connection */
 | 
					 | 
				
			||||||
	ret = rxrpc_create_call(conn, NULL, NULL, afs_rxvl_aemap, &call);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		printk("kAFS: Unable to create call: %d\n", ret);
 | 
					 | 
				
			||||||
		goto out_put_conn;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	call->app_opcode = VLGETENTRYBYID;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* we want to get event notifications from the call */
 | 
					 | 
				
			||||||
	add_wait_queue(&call->waitq, &myself);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* marshall the parameters */
 | 
					 | 
				
			||||||
	param[0] = htonl(VLGETENTRYBYID);
 | 
					 | 
				
			||||||
	param[1] = htonl(volid);
 | 
					 | 
				
			||||||
	param[2] = htonl(voltype);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	piov[0].iov_len = sizeof(param);
 | 
					 | 
				
			||||||
	piov[0].iov_base = param;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* send the parameters to the server */
 | 
					 | 
				
			||||||
	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
 | 
					 | 
				
			||||||
				    0, &sent);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		goto abort;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* wait for the reply to completely arrive */
 | 
					 | 
				
			||||||
	bp = rxrpc_call_alloc_scratch(call, 384);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = rxrpc_call_read_data(call, bp, 384,
 | 
					 | 
				
			||||||
				   RXRPC_CALL_READ_BLOCK |
 | 
					 | 
				
			||||||
				   RXRPC_CALL_READ_ALL);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		if (ret == -ECONNABORTED) {
 | 
					 | 
				
			||||||
			ret = call->app_errno;
 | 
					 | 
				
			||||||
			goto out_unwait;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		goto abort;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* unmarshall the reply */
 | 
					 | 
				
			||||||
	for (loop = 0; loop < 64; loop++)
 | 
					 | 
				
			||||||
		entry->name[loop] = ntohl(*bp++);
 | 
					 | 
				
			||||||
	bp++; /* final NUL */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bp++; /* type */
 | 
					 | 
				
			||||||
	entry->nservers = ntohl(*bp++);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (loop = 0; loop < 8; loop++)
 | 
					 | 
				
			||||||
		entry->servers[loop].s_addr = *bp++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bp += 8; /* partition IDs */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (loop = 0; loop < 8; loop++) {
 | 
					 | 
				
			||||||
		tmp = ntohl(*bp++);
 | 
					 | 
				
			||||||
		if (tmp & AFS_VLSF_RWVOL)
 | 
					 | 
				
			||||||
			entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
 | 
					 | 
				
			||||||
		if (tmp & AFS_VLSF_ROVOL)
 | 
					 | 
				
			||||||
			entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
		if (tmp & AFS_VLSF_BACKVOL)
 | 
					 | 
				
			||||||
			entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	entry->vid[0] = ntohl(*bp++);
 | 
					 | 
				
			||||||
	entry->vid[1] = ntohl(*bp++);
 | 
					 | 
				
			||||||
	entry->vid[2] = ntohl(*bp++);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bp++; /* clone ID */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tmp = ntohl(*bp++); /* flags */
 | 
					 | 
				
			||||||
	if (tmp & AFS_VLF_RWEXISTS)
 | 
					 | 
				
			||||||
		entry->vidmask |= AFS_VOL_VTM_RW;
 | 
					 | 
				
			||||||
	if (tmp & AFS_VLF_ROEXISTS)
 | 
					 | 
				
			||||||
		entry->vidmask |= AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
	if (tmp & AFS_VLF_BACKEXISTS)
 | 
					 | 
				
			||||||
		entry->vidmask |= AFS_VOL_VTM_BAK;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = -ENOMEDIUM;
 | 
					 | 
				
			||||||
	if (!entry->vidmask)
 | 
					 | 
				
			||||||
		goto abort;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if 0 /* TODO: remove */
 | 
					 | 
				
			||||||
	entry->nservers = 3;
 | 
					 | 
				
			||||||
	entry->servers[0].s_addr = htonl(0xac101249);
 | 
					 | 
				
			||||||
	entry->servers[1].s_addr = htonl(0xac101243);
 | 
					 | 
				
			||||||
	entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	entry->srvtmask[0] = AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
	entry->srvtmask[1] = AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
	entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* success */
 | 
					 | 
				
			||||||
	entry->rtime = get_seconds();
 | 
					 | 
				
			||||||
	ret = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
out_unwait:
 | 
					 | 
				
			||||||
	set_current_state(TASK_RUNNING);
 | 
					 | 
				
			||||||
	remove_wait_queue(&call->waitq, &myself);
 | 
					 | 
				
			||||||
	rxrpc_put_call(call);
 | 
					 | 
				
			||||||
out_put_conn:
 | 
					 | 
				
			||||||
	rxrpc_put_connection(conn);
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
abort:
 | 
					 | 
				
			||||||
	set_current_state(TASK_UNINTERRUPTIBLE);
 | 
					 | 
				
			||||||
	rxrpc_call_abort(call, ret);
 | 
					 | 
				
			||||||
	schedule();
 | 
					 | 
				
			||||||
	goto out_unwait;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * look up a volume location database entry by ID asynchronously
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_rxvl_get_entry_by_id_async(struct afs_async_op *op,
 | 
					 | 
				
			||||||
				   afs_volid_t volid,
 | 
					 | 
				
			||||||
				   afs_voltype_t voltype)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct rxrpc_connection *conn;
 | 
					 | 
				
			||||||
	struct rxrpc_call *call;
 | 
					 | 
				
			||||||
	struct kvec piov[1];
 | 
					 | 
				
			||||||
	size_t sent;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
	__be32 param[3];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter(",%x,%d,", volid, voltype);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* get hold of the vlserver connection */
 | 
					 | 
				
			||||||
	ret = afs_server_get_vlconn(op->server, &conn);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		_leave(" = %d", ret);
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* create a call through that connection */
 | 
					 | 
				
			||||||
	ret = rxrpc_create_call(conn,
 | 
					 | 
				
			||||||
				afs_rxvl_get_entry_by_id_attn,
 | 
					 | 
				
			||||||
				afs_rxvl_get_entry_by_id_error,
 | 
					 | 
				
			||||||
				afs_rxvl_aemap,
 | 
					 | 
				
			||||||
				&op->call);
 | 
					 | 
				
			||||||
	rxrpc_put_connection(conn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		printk("kAFS: Unable to create call: %d\n", ret);
 | 
					 | 
				
			||||||
		_leave(" = %d", ret);
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	op->call->app_opcode = VLGETENTRYBYID;
 | 
					 | 
				
			||||||
	op->call->app_user = op;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	call = op->call;
 | 
					 | 
				
			||||||
	rxrpc_get_call(call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* send event notifications from the call to kafsasyncd */
 | 
					 | 
				
			||||||
	afs_kafsasyncd_begin_op(op);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* marshall the parameters */
 | 
					 | 
				
			||||||
	param[0] = htonl(VLGETENTRYBYID);
 | 
					 | 
				
			||||||
	param[1] = htonl(volid);
 | 
					 | 
				
			||||||
	param[2] = htonl(voltype);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	piov[0].iov_len = sizeof(param);
 | 
					 | 
				
			||||||
	piov[0].iov_base = param;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* allocate result read buffer in scratch space */
 | 
					 | 
				
			||||||
	call->app_scr_ptr = rxrpc_call_alloc_scratch(op->call, 384);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* send the parameters to the server */
 | 
					 | 
				
			||||||
	ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS,
 | 
					 | 
				
			||||||
				    0, &sent);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		rxrpc_call_abort(call, ret); /* handle from kafsasyncd */
 | 
					 | 
				
			||||||
		ret = 0;
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* wait for the reply to completely arrive */
 | 
					 | 
				
			||||||
	ret = rxrpc_call_read_data(call, call->app_scr_ptr, 384, 0);
 | 
					 | 
				
			||||||
	switch (ret) {
 | 
					 | 
				
			||||||
	case 0:
 | 
					 | 
				
			||||||
	case -EAGAIN:
 | 
					 | 
				
			||||||
	case -ECONNABORTED:
 | 
					 | 
				
			||||||
		ret = 0;
 | 
					 | 
				
			||||||
		break;	/* all handled by kafsasyncd */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		rxrpc_call_abort(call, ret); /* make kafsasyncd handle it */
 | 
					 | 
				
			||||||
		ret = 0;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	rxrpc_put_call(call);
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * attend to the asynchronous get VLDB entry by ID
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_rxvl_get_entry_by_id_async2(struct afs_async_op *op,
 | 
					 | 
				
			||||||
				    struct afs_cache_vlocation *entry)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct afs_call *call;
 | 
				
			||||||
	__be32 *bp;
 | 
						__be32 *bp;
 | 
				
			||||||
	__u32 tmp;
 | 
					 | 
				
			||||||
	int loop, ret;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{op=%p cst=%u}", op, op->call->app_call_state);
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(entry, 0, sizeof(*entry));
 | 
						call = afs_alloc_flat_call(&afs_RXVLGetEntryById, 12, 384);
 | 
				
			||||||
 | 
						if (!call)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (op->call->app_call_state == RXRPC_CSTATE_COMPLETE) {
 | 
						call->reply = entry;
 | 
				
			||||||
		/* operation finished */
 | 
						call->service_id = VL_SERVICE;
 | 
				
			||||||
		afs_kafsasyncd_terminate_op(op);
 | 
						call->port = htons(AFS_VL_PORT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bp = op->call->app_scr_ptr;
 | 
						/* marshall the parameters */
 | 
				
			||||||
 | 
						bp = call->request;
 | 
				
			||||||
 | 
						*bp++ = htonl(VLGETENTRYBYID);
 | 
				
			||||||
 | 
						*bp++ = htonl(volid);
 | 
				
			||||||
 | 
						*bp   = htonl(voltype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* unmarshall the reply */
 | 
						/* initiate the call */
 | 
				
			||||||
		for (loop = 0; loop < 64; loop++)
 | 
						return afs_make_call(addr, call, GFP_KERNEL, wait_mode);
 | 
				
			||||||
			entry->name[loop] = ntohl(*bp++);
 | 
					 | 
				
			||||||
		bp++; /* final NUL */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		bp++; /* type */
 | 
					 | 
				
			||||||
		entry->nservers = ntohl(*bp++);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (loop = 0; loop < 8; loop++)
 | 
					 | 
				
			||||||
			entry->servers[loop].s_addr = *bp++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		bp += 8; /* partition IDs */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (loop = 0; loop < 8; loop++) {
 | 
					 | 
				
			||||||
			tmp = ntohl(*bp++);
 | 
					 | 
				
			||||||
			if (tmp & AFS_VLSF_RWVOL)
 | 
					 | 
				
			||||||
				entry->srvtmask[loop] |= AFS_VOL_VTM_RW;
 | 
					 | 
				
			||||||
			if (tmp & AFS_VLSF_ROVOL)
 | 
					 | 
				
			||||||
				entry->srvtmask[loop] |= AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
			if (tmp & AFS_VLSF_BACKVOL)
 | 
					 | 
				
			||||||
				entry->srvtmask[loop] |= AFS_VOL_VTM_BAK;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		entry->vid[0] = ntohl(*bp++);
 | 
					 | 
				
			||||||
		entry->vid[1] = ntohl(*bp++);
 | 
					 | 
				
			||||||
		entry->vid[2] = ntohl(*bp++);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		bp++; /* clone ID */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		tmp = ntohl(*bp++); /* flags */
 | 
					 | 
				
			||||||
		if (tmp & AFS_VLF_RWEXISTS)
 | 
					 | 
				
			||||||
			entry->vidmask |= AFS_VOL_VTM_RW;
 | 
					 | 
				
			||||||
		if (tmp & AFS_VLF_ROEXISTS)
 | 
					 | 
				
			||||||
			entry->vidmask |= AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
		if (tmp & AFS_VLF_BACKEXISTS)
 | 
					 | 
				
			||||||
			entry->vidmask |= AFS_VOL_VTM_BAK;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ret = -ENOMEDIUM;
 | 
					 | 
				
			||||||
		if (!entry->vidmask) {
 | 
					 | 
				
			||||||
			rxrpc_call_abort(op->call, ret);
 | 
					 | 
				
			||||||
			goto done;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if 0 /* TODO: remove */
 | 
					 | 
				
			||||||
		entry->nservers = 3;
 | 
					 | 
				
			||||||
		entry->servers[0].s_addr = htonl(0xac101249);
 | 
					 | 
				
			||||||
		entry->servers[1].s_addr = htonl(0xac101243);
 | 
					 | 
				
			||||||
		entry->servers[2].s_addr = htonl(0xac10125b /*0xac10125b*/);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		entry->srvtmask[0] = AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
		entry->srvtmask[1] = AFS_VOL_VTM_RO;
 | 
					 | 
				
			||||||
		entry->srvtmask[2] = AFS_VOL_VTM_RO | AFS_VOL_VTM_RW;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* success */
 | 
					 | 
				
			||||||
		entry->rtime = get_seconds();
 | 
					 | 
				
			||||||
		ret = 0;
 | 
					 | 
				
			||||||
		goto done;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (op->call->app_call_state == RXRPC_CSTATE_ERROR) {
 | 
					 | 
				
			||||||
		/* operation error */
 | 
					 | 
				
			||||||
		ret = op->call->app_errno;
 | 
					 | 
				
			||||||
		goto done;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = -EAGAIN");
 | 
					 | 
				
			||||||
	return -EAGAIN;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
done:
 | 
					 | 
				
			||||||
	rxrpc_put_call(op->call);
 | 
					 | 
				
			||||||
	op->call = NULL;
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * handle attention events on an async get-entry-by-ID op
 | 
					 | 
				
			||||||
 * - called from krxiod
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void afs_rxvl_get_entry_by_id_attn(struct rxrpc_call *call)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_async_op *op = call->app_user;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("{op=%p cst=%u}", op, call->app_call_state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (call->app_call_state) {
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_COMPLETE:
 | 
					 | 
				
			||||||
		afs_kafsasyncd_attend_op(op);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_CLNT_RCV_REPLY:
 | 
					 | 
				
			||||||
		if (call->app_async_read)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
	case RXRPC_CSTATE_CLNT_GOT_REPLY:
 | 
					 | 
				
			||||||
		if (call->app_read_count == 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		printk("kAFS: Reply bigger than expected"
 | 
					 | 
				
			||||||
		       " {cst=%u asyn=%d mark=%Zu rdy=%Zu pr=%u%s}",
 | 
					 | 
				
			||||||
		       call->app_call_state,
 | 
					 | 
				
			||||||
		       call->app_async_read,
 | 
					 | 
				
			||||||
		       call->app_mark,
 | 
					 | 
				
			||||||
		       call->app_ready_qty,
 | 
					 | 
				
			||||||
		       call->pkt_rcv_count,
 | 
					 | 
				
			||||||
		       call->app_last_rcv ? " last" : "");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		rxrpc_call_abort(call, -EBADMSG);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		BUG();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave("");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * handle error events on an async get-entry-by-ID op
 | 
					 | 
				
			||||||
 * - called from krxiod
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void afs_rxvl_get_entry_by_id_error(struct rxrpc_call *call)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_async_op *op = call->app_user;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("{op=%p cst=%u}", op, call->app_call_state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_kafsasyncd_attend_op(op);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave("");
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1301
									
								
								fs/afs/vlocation.c
									
									
									
									
									
								
							
							
						
						
									
										1301
									
								
								fs/afs/vlocation.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										396
									
								
								fs/afs/vnode.c
									
									
									
									
									
								
							
							
						
						
									
										396
									
								
								fs/afs/vnode.c
									
									
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* AFS vnode management
 | 
					/* AFS vnode management
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -14,71 +14,182 @@
 | 
				
			||||||
#include <linux/init.h>
 | 
					#include <linux/init.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
#include <linux/pagemap.h>
 | 
					 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "cmservice.h"
 | 
					 | 
				
			||||||
#include "fsclient.h"
 | 
					 | 
				
			||||||
#include "vlclient.h"
 | 
					 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void afs_vnode_cb_timed_out(struct afs_timer *timer);
 | 
					#if 0
 | 
				
			||||||
 | 
					static noinline bool dump_tree_aux(struct rb_node *node, struct rb_node *parent,
 | 
				
			||||||
 | 
									   int depth, char lr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_vnode *vnode;
 | 
				
			||||||
 | 
						bool bad = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct afs_timer_ops afs_vnode_cb_timed_out_ops = {
 | 
						if (!node)
 | 
				
			||||||
	.timed_out	= afs_vnode_cb_timed_out,
 | 
							return false;
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
						if (node->rb_left)
 | 
				
			||||||
static cachefs_match_val_t afs_vnode_cache_match(void *target,
 | 
							bad = dump_tree_aux(node->rb_left, node, depth + 2, '/');
 | 
				
			||||||
						 const void *entry);
 | 
					 | 
				
			||||||
static void afs_vnode_cache_update(void *source, void *entry);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct cachefs_index_def afs_vnode_cache_index_def = {
 | 
						vnode = rb_entry(node, struct afs_vnode, cb_promise);
 | 
				
			||||||
	.name		= "vnode",
 | 
						kdebug("%c %*.*s%c%p {%d}",
 | 
				
			||||||
	.data_size	= sizeof(struct afs_cache_vnode),
 | 
						       rb_is_red(node) ? 'R' : 'B',
 | 
				
			||||||
	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 4 },
 | 
						       depth, depth, "", lr,
 | 
				
			||||||
	.match		= afs_vnode_cache_match,
 | 
						       vnode, vnode->cb_expires_at);
 | 
				
			||||||
	.update		= afs_vnode_cache_update,
 | 
						if (rb_parent(node) != parent) {
 | 
				
			||||||
};
 | 
							printk("BAD: %p != %p\n", rb_parent(node), parent);
 | 
				
			||||||
 | 
							bad = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (node->rb_right)
 | 
				
			||||||
 | 
							bad |= dump_tree_aux(node->rb_right, node, depth + 2, '\\');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bad;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static noinline void dump_tree(const char *name, struct afs_server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						kenter("%s", name);
 | 
				
			||||||
 | 
						if (dump_tree_aux(server->cb_promises.rb_node, NULL, 0, '-'))
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * handle a callback timing out
 | 
					 * insert a vnode into the backing server's vnode tree
 | 
				
			||||||
 * TODO: retain a ref to vnode struct for an outstanding callback timeout
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_vnode_cb_timed_out(struct afs_timer *timer)
 | 
					static void afs_install_vnode(struct afs_vnode *vnode,
 | 
				
			||||||
 | 
								      struct afs_server *server)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server *oldserver;
 | 
						struct afs_server *old_server = vnode->server;
 | 
				
			||||||
	struct afs_vnode *vnode;
 | 
						struct afs_vnode *xvnode;
 | 
				
			||||||
 | 
						struct rb_node *parent, **p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vnode = list_entry(timer, struct afs_vnode, cb_timeout);
 | 
						_enter("%p,%p", vnode, server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p", vnode);
 | 
						if (old_server) {
 | 
				
			||||||
 | 
							spin_lock(&old_server->fs_lock);
 | 
				
			||||||
	/* set the changed flag in the vnode and release the server */
 | 
							rb_erase(&vnode->server_rb, &old_server->fs_vnodes);
 | 
				
			||||||
	spin_lock(&vnode->lock);
 | 
							spin_unlock(&old_server->fs_lock);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	oldserver = xchg(&vnode->cb_server, NULL);
 | 
					 | 
				
			||||||
	if (oldserver) {
 | 
					 | 
				
			||||||
		vnode->flags |= AFS_VNODE_CHANGED;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		spin_lock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
		list_del_init(&vnode->cb_hash_link);
 | 
					 | 
				
			||||||
		spin_unlock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		spin_lock(&oldserver->cb_lock);
 | 
					 | 
				
			||||||
		list_del_init(&vnode->cb_link);
 | 
					 | 
				
			||||||
		spin_unlock(&oldserver->cb_lock);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_unlock(&vnode->lock);
 | 
						afs_get_server(server);
 | 
				
			||||||
 | 
						vnode->server = server;
 | 
				
			||||||
 | 
						afs_put_server(old_server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	afs_put_server(oldserver);
 | 
						/* insert into the server's vnode tree in FID order */
 | 
				
			||||||
 | 
						spin_lock(&server->fs_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parent = NULL;
 | 
				
			||||||
 | 
						p = &server->fs_vnodes.rb_node;
 | 
				
			||||||
 | 
						while (*p) {
 | 
				
			||||||
 | 
							parent = *p;
 | 
				
			||||||
 | 
							xvnode = rb_entry(parent, struct afs_vnode, server_rb);
 | 
				
			||||||
 | 
							if (vnode->fid.vid < xvnode->fid.vid)
 | 
				
			||||||
 | 
								p = &(*p)->rb_left;
 | 
				
			||||||
 | 
							else if (vnode->fid.vid > xvnode->fid.vid)
 | 
				
			||||||
 | 
								p = &(*p)->rb_right;
 | 
				
			||||||
 | 
							else if (vnode->fid.vnode < xvnode->fid.vnode)
 | 
				
			||||||
 | 
								p = &(*p)->rb_left;
 | 
				
			||||||
 | 
							else if (vnode->fid.vnode > xvnode->fid.vnode)
 | 
				
			||||||
 | 
								p = &(*p)->rb_right;
 | 
				
			||||||
 | 
							else if (vnode->fid.unique < xvnode->fid.unique)
 | 
				
			||||||
 | 
								p = &(*p)->rb_left;
 | 
				
			||||||
 | 
							else if (vnode->fid.unique > xvnode->fid.unique)
 | 
				
			||||||
 | 
								p = &(*p)->rb_right;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								BUG(); /* can't happen unless afs_iget() malfunctions */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rb_link_node(&vnode->server_rb, parent, p);
 | 
				
			||||||
 | 
						rb_insert_color(&vnode->server_rb, &server->fs_vnodes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_unlock(&server->fs_lock);
 | 
				
			||||||
	_leave("");
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * insert a vnode into the promising server's update/expiration tree
 | 
				
			||||||
 | 
					 * - caller must hold vnode->lock
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_vnode_note_promise(struct afs_vnode *vnode,
 | 
				
			||||||
 | 
									   struct afs_server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *old_server;
 | 
				
			||||||
 | 
						struct afs_vnode *xvnode;
 | 
				
			||||||
 | 
						struct rb_node *parent, **p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%p,%p", vnode, server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASSERT(server != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						old_server = vnode->server;
 | 
				
			||||||
 | 
						if (vnode->cb_promised) {
 | 
				
			||||||
 | 
							if (server == old_server &&
 | 
				
			||||||
 | 
							    vnode->cb_expires == vnode->cb_expires_at) {
 | 
				
			||||||
 | 
								_leave(" [no change]");
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							spin_lock(&old_server->cb_lock);
 | 
				
			||||||
 | 
							if (vnode->cb_promised) {
 | 
				
			||||||
 | 
								_debug("delete");
 | 
				
			||||||
 | 
								rb_erase(&vnode->cb_promise, &old_server->cb_promises);
 | 
				
			||||||
 | 
								vnode->cb_promised = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							spin_unlock(&old_server->cb_lock);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (vnode->server != server)
 | 
				
			||||||
 | 
							afs_install_vnode(vnode, server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vnode->cb_expires_at = vnode->cb_expires;
 | 
				
			||||||
 | 
						_debug("PROMISE on %p {%lu}",
 | 
				
			||||||
 | 
						       vnode, (unsigned long) vnode->cb_expires_at);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* abuse an RB-tree to hold the expiration order (we may have multiple
 | 
				
			||||||
 | 
						 * items with the same expiration time) */
 | 
				
			||||||
 | 
						spin_lock(&server->cb_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parent = NULL;
 | 
				
			||||||
 | 
						p = &server->cb_promises.rb_node;
 | 
				
			||||||
 | 
						while (*p) {
 | 
				
			||||||
 | 
							parent = *p;
 | 
				
			||||||
 | 
							xvnode = rb_entry(parent, struct afs_vnode, cb_promise);
 | 
				
			||||||
 | 
							if (vnode->cb_expires_at < xvnode->cb_expires_at)
 | 
				
			||||||
 | 
								p = &(*p)->rb_left;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								p = &(*p)->rb_right;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rb_link_node(&vnode->cb_promise, parent, p);
 | 
				
			||||||
 | 
						rb_insert_color(&vnode->cb_promise, &server->cb_promises);
 | 
				
			||||||
 | 
						vnode->cb_promised = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * handle remote file deletion by discarding the callback promise
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct afs_server *server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_bit(AFS_VNODE_DELETED, &vnode->flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server = vnode->server;
 | 
				
			||||||
 | 
						if (vnode->cb_promised) {
 | 
				
			||||||
 | 
							spin_lock(&server->cb_lock);
 | 
				
			||||||
 | 
							if (vnode->cb_promised) {
 | 
				
			||||||
 | 
								rb_erase(&vnode->cb_promise, &server->cb_promises);
 | 
				
			||||||
 | 
								vnode->cb_promised = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							spin_unlock(&server->cb_lock);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						afs_put_server(server);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * finish off updating the recorded status of a file
 | 
					 * finish off updating the recorded status of a file
 | 
				
			||||||
 * - starts callback expiry timer
 | 
					 * - starts callback expiry timer
 | 
				
			||||||
| 
						 | 
					@ -94,43 +205,19 @@ static void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&vnode->lock);
 | 
						spin_lock(&vnode->lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vnode->flags &= ~AFS_VNODE_CHANGED;
 | 
						clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ret == 0) {
 | 
					 | 
				
			||||||
		/* adjust the callback timeout appropriately */
 | 
					 | 
				
			||||||
		afs_kafstimod_add_timer(&vnode->cb_timeout,
 | 
					 | 
				
			||||||
					vnode->cb_expiry * HZ);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		spin_lock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
		list_move_tail(&vnode->cb_hash_link,
 | 
					 | 
				
			||||||
			       &afs_cb_hash(server, &vnode->fid));
 | 
					 | 
				
			||||||
		spin_unlock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* swap ref to old callback server with that for new callback
 | 
					 | 
				
			||||||
		 * server */
 | 
					 | 
				
			||||||
		oldserver = xchg(&vnode->cb_server, server);
 | 
					 | 
				
			||||||
		if (oldserver != server) {
 | 
					 | 
				
			||||||
			if (oldserver) {
 | 
					 | 
				
			||||||
				spin_lock(&oldserver->cb_lock);
 | 
					 | 
				
			||||||
				list_del_init(&vnode->cb_link);
 | 
					 | 
				
			||||||
				spin_unlock(&oldserver->cb_lock);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			afs_get_server(server);
 | 
					 | 
				
			||||||
			spin_lock(&server->cb_lock);
 | 
					 | 
				
			||||||
			list_add_tail(&vnode->cb_link, &server->cb_promises);
 | 
					 | 
				
			||||||
			spin_unlock(&server->cb_lock);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			/* same server */
 | 
					 | 
				
			||||||
			oldserver = NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (ret == -ENOENT) {
 | 
					 | 
				
			||||||
		/* the file was deleted - clear the callback timeout */
 | 
					 | 
				
			||||||
		oldserver = xchg(&vnode->cb_server, NULL);
 | 
					 | 
				
			||||||
		afs_kafstimod_del_timer(&vnode->cb_timeout);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (ret) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							afs_vnode_note_promise(vnode, server);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							/* the file was deleted on the server */
 | 
				
			||||||
		_debug("got NOENT from server - marking file deleted");
 | 
							_debug("got NOENT from server - marking file deleted");
 | 
				
			||||||
		vnode->flags |= AFS_VNODE_DELETED;
 | 
							afs_vnode_deleted_remotely(vnode);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vnode->update_cnt--;
 | 
						vnode->update_cnt--;
 | 
				
			||||||
| 
						 | 
					@ -162,19 +249,21 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
 | 
				
			||||||
	       vnode->volume->vlocation->vldb.name,
 | 
						       vnode->volume->vlocation->vldb.name,
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
 | 
						       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) {
 | 
						if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
 | 
				
			||||||
 | 
						    vnode->cb_promised) {
 | 
				
			||||||
		_leave(" [unchanged]");
 | 
							_leave(" [unchanged]");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (vnode->flags & AFS_VNODE_DELETED) {
 | 
						if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
 | 
				
			||||||
		_leave(" [deleted]");
 | 
							_leave(" [deleted]");
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&vnode->lock);
 | 
						spin_lock(&vnode->lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(vnode->flags & AFS_VNODE_CHANGED)) {
 | 
						if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
 | 
				
			||||||
 | 
						    vnode->cb_promised) {
 | 
				
			||||||
		spin_unlock(&vnode->lock);
 | 
							spin_unlock(&vnode->lock);
 | 
				
			||||||
		_leave(" [unchanged]");
 | 
							_leave(" [unchanged]");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
| 
						 | 
					@ -183,17 +272,18 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
 | 
				
			||||||
	if (vnode->update_cnt > 0) {
 | 
						if (vnode->update_cnt > 0) {
 | 
				
			||||||
		/* someone else started a fetch */
 | 
							/* someone else started a fetch */
 | 
				
			||||||
		set_current_state(TASK_UNINTERRUPTIBLE);
 | 
							set_current_state(TASK_UNINTERRUPTIBLE);
 | 
				
			||||||
 | 
							ASSERT(myself.func != NULL);
 | 
				
			||||||
		add_wait_queue(&vnode->update_waitq, &myself);
 | 
							add_wait_queue(&vnode->update_waitq, &myself);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* wait for the status to be updated */
 | 
							/* wait for the status to be updated */
 | 
				
			||||||
		for (;;) {
 | 
							for (;;) {
 | 
				
			||||||
			if (!(vnode->flags & AFS_VNODE_CHANGED))
 | 
								if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			if (vnode->flags & AFS_VNODE_DELETED)
 | 
								if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* it got updated and invalidated all before we saw
 | 
								/* check to see if it got updated and invalidated all
 | 
				
			||||||
			 * it */
 | 
								 * before we saw it */
 | 
				
			||||||
			if (vnode->update_cnt == 0) {
 | 
								if (vnode->update_cnt == 0) {
 | 
				
			||||||
				remove_wait_queue(&vnode->update_waitq,
 | 
									remove_wait_queue(&vnode->update_waitq,
 | 
				
			||||||
						  &myself);
 | 
											  &myself);
 | 
				
			||||||
| 
						 | 
					@ -213,7 +303,8 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
 | 
				
			||||||
		spin_unlock(&vnode->lock);
 | 
							spin_unlock(&vnode->lock);
 | 
				
			||||||
		set_current_state(TASK_RUNNING);
 | 
							set_current_state(TASK_RUNNING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0;
 | 
							return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
 | 
				
			||||||
 | 
								-ENOENT : 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
get_anyway:
 | 
					get_anyway:
 | 
				
			||||||
| 
						 | 
					@ -226,15 +317,17 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
 | 
				
			||||||
	 * vnode */
 | 
						 * vnode */
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		/* pick a server to query */
 | 
							/* pick a server to query */
 | 
				
			||||||
		ret = afs_volume_pick_fileserver(vnode->volume, &server);
 | 
							server = afs_volume_pick_fileserver(vnode);
 | 
				
			||||||
		if (ret<0)
 | 
							if (IS_ERR(server))
 | 
				
			||||||
			return ret;
 | 
								return PTR_ERR(server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
 | 
							_debug("USING SERVER: %p{%08x}",
 | 
				
			||||||
 | 
							       server, ntohl(server->addr.s_addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = afs_rxfs_fetch_file_status(server, vnode, NULL);
 | 
							ret = afs_fs_fetch_file_status(server, vnode, NULL,
 | 
				
			||||||
 | 
										       &afs_sync_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} while (!afs_volume_release_fileserver(vnode->volume, server, ret));
 | 
						} while (!afs_volume_release_fileserver(vnode, server, ret));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* adjust the flags */
 | 
						/* adjust the flags */
 | 
				
			||||||
	afs_vnode_finalise_status_update(vnode, server, ret);
 | 
						afs_vnode_finalise_status_update(vnode, server, ret);
 | 
				
			||||||
| 
						 | 
					@ -247,8 +340,8 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode)
 | 
				
			||||||
 * fetch file data from the volume
 | 
					 * fetch file data from the volume
 | 
				
			||||||
 * - TODO implement caching and server failover
 | 
					 * - TODO implement caching and server failover
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_vnode_fetch_data(struct afs_vnode *vnode,
 | 
					int afs_vnode_fetch_data(struct afs_vnode *vnode, off_t offset, size_t length,
 | 
				
			||||||
			 struct afs_rxfs_fetch_descriptor *desc)
 | 
								 struct page *page)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_server *server;
 | 
						struct afs_server *server;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					@ -268,15 +361,16 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode,
 | 
				
			||||||
	 * vnode */
 | 
						 * vnode */
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		/* pick a server to query */
 | 
							/* pick a server to query */
 | 
				
			||||||
		ret = afs_volume_pick_fileserver(vnode->volume, &server);
 | 
							server = afs_volume_pick_fileserver(vnode);
 | 
				
			||||||
		if (ret < 0)
 | 
							if (IS_ERR(server))
 | 
				
			||||||
			return ret;
 | 
								return PTR_ERR(server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
 | 
							_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = afs_rxfs_fetch_file_data(server, vnode, desc, NULL);
 | 
							ret = afs_fs_fetch_data(server, vnode, offset, length, page,
 | 
				
			||||||
 | 
										NULL, &afs_sync_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} while (!afs_volume_release_fileserver(vnode->volume, server, ret));
 | 
						} while (!afs_volume_release_fileserver(vnode, server, ret));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* adjust the flags */
 | 
						/* adjust the flags */
 | 
				
			||||||
	afs_vnode_finalise_status_update(vnode, server, ret);
 | 
						afs_vnode_finalise_status_update(vnode, server, ret);
 | 
				
			||||||
| 
						 | 
					@ -284,99 +378,3 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode,
 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * break any outstanding callback on a vnode
 | 
					 | 
				
			||||||
 * - only relevent to server that issued it
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int afs_vnode_give_up_callback(struct afs_vnode *vnode)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_server *server;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("%s,{%u,%u,%u}",
 | 
					 | 
				
			||||||
	       vnode->volume->vlocation->vldb.name,
 | 
					 | 
				
			||||||
	       vnode->fid.vid,
 | 
					 | 
				
			||||||
	       vnode->fid.vnode,
 | 
					 | 
				
			||||||
	       vnode->fid.unique);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_lock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
	list_del_init(&vnode->cb_hash_link);
 | 
					 | 
				
			||||||
	spin_unlock(&afs_cb_hash_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* set the changed flag in the vnode and release the server */
 | 
					 | 
				
			||||||
	spin_lock(&vnode->lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_kafstimod_del_timer(&vnode->cb_timeout);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	server = xchg(&vnode->cb_server, NULL);
 | 
					 | 
				
			||||||
	if (server) {
 | 
					 | 
				
			||||||
		vnode->flags |= AFS_VNODE_CHANGED;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		spin_lock(&server->cb_lock);
 | 
					 | 
				
			||||||
		list_del_init(&vnode->cb_link);
 | 
					 | 
				
			||||||
		spin_unlock(&server->cb_lock);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_unlock(&vnode->lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = 0;
 | 
					 | 
				
			||||||
	if (server) {
 | 
					 | 
				
			||||||
		ret = afs_rxfs_give_up_callback(server, vnode);
 | 
					 | 
				
			||||||
		afs_put_server(server);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * match a vnode record stored in the cache
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static cachefs_match_val_t afs_vnode_cache_match(void *target,
 | 
					 | 
				
			||||||
						 const void *entry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const struct afs_cache_vnode *cvnode = entry;
 | 
					 | 
				
			||||||
	struct afs_vnode *vnode = target;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("{%x,%x,%Lx},{%x,%x,%Lx}",
 | 
					 | 
				
			||||||
	       vnode->fid.vnode,
 | 
					 | 
				
			||||||
	       vnode->fid.unique,
 | 
					 | 
				
			||||||
	       vnode->status.version,
 | 
					 | 
				
			||||||
	       cvnode->vnode_id,
 | 
					 | 
				
			||||||
	       cvnode->vnode_unique,
 | 
					 | 
				
			||||||
	       cvnode->data_version);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (vnode->fid.vnode != cvnode->vnode_id) {
 | 
					 | 
				
			||||||
		_leave(" = FAILED");
 | 
					 | 
				
			||||||
		return CACHEFS_MATCH_FAILED;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (vnode->fid.unique != cvnode->vnode_unique ||
 | 
					 | 
				
			||||||
	    vnode->status.version != cvnode->data_version) {
 | 
					 | 
				
			||||||
		_leave(" = DELETE");
 | 
					 | 
				
			||||||
		return CACHEFS_MATCH_SUCCESS_DELETE;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = SUCCESS");
 | 
					 | 
				
			||||||
	return CACHEFS_MATCH_SUCCESS;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * update a vnode record stored in the cache
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static void afs_vnode_cache_update(void *source, void *entry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_cache_vnode *cvnode = entry;
 | 
					 | 
				
			||||||
	struct afs_vnode *vnode = source;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cvnode->vnode_id	= vnode->fid.vnode;
 | 
					 | 
				
			||||||
	cvnode->vnode_unique	= vnode->fid.unique;
 | 
					 | 
				
			||||||
	cvnode->data_version	= vnode->status.version;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,84 +0,0 @@
 | 
				
			||||||
/* AFS vnode record
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef AFS_VNODE_H
 | 
					 | 
				
			||||||
#define AFS_VNODE_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <linux/fs.h>
 | 
					 | 
				
			||||||
#include "server.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include "cache.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct afs_rxfs_fetch_descriptor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * vnode catalogue entry
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_cache_vnode {
 | 
					 | 
				
			||||||
	afs_vnodeid_t		vnode_id;	/* vnode ID */
 | 
					 | 
				
			||||||
	unsigned		vnode_unique;	/* vnode ID uniquifier */
 | 
					 | 
				
			||||||
	afs_dataversion_t	data_version;	/* data version */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
extern struct cachefs_index_def afs_vnode_cache_index_def;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * AFS inode private data
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_vnode {
 | 
					 | 
				
			||||||
	struct inode		vfs_inode;	/* the VFS's inode record */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct afs_volume	*volume;	/* volume on which vnode resides */
 | 
					 | 
				
			||||||
	struct afs_fid		fid;		/* the file identifier for this inode */
 | 
					 | 
				
			||||||
	struct afs_file_status	status;		/* AFS status info for this file */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
	struct cachefs_cookie	*cache;		/* caching cookie */
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wait_queue_head_t	update_waitq;	/* status fetch waitqueue */
 | 
					 | 
				
			||||||
	unsigned		update_cnt;	/* number of outstanding ops that will update the
 | 
					 | 
				
			||||||
						 * status */
 | 
					 | 
				
			||||||
	spinlock_t		lock;		/* waitqueue/flags lock */
 | 
					 | 
				
			||||||
	unsigned		flags;
 | 
					 | 
				
			||||||
#define AFS_VNODE_CHANGED	0x00000001	/* set if vnode reported changed by callback */
 | 
					 | 
				
			||||||
#define AFS_VNODE_DELETED	0x00000002	/* set if vnode deleted on server */
 | 
					 | 
				
			||||||
#define AFS_VNODE_MOUNTPOINT	0x00000004	/* set if vnode is a mountpoint symlink */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* outstanding callback notification on this file */
 | 
					 | 
				
			||||||
	struct afs_server	*cb_server;	/* server that made the current promise */
 | 
					 | 
				
			||||||
	struct list_head	cb_link;	/* link in server's promises list */
 | 
					 | 
				
			||||||
	struct list_head	cb_hash_link;	/* link in master callback hash */
 | 
					 | 
				
			||||||
	struct afs_timer	cb_timeout;	/* timeout on promise */
 | 
					 | 
				
			||||||
	unsigned		cb_version;	/* callback version */
 | 
					 | 
				
			||||||
	unsigned		cb_expiry;	/* callback expiry time */
 | 
					 | 
				
			||||||
	afs_callback_type_t	cb_type;	/* type of callback */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline struct afs_vnode *AFS_FS_I(struct inode *inode)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return container_of(inode, struct afs_vnode, vfs_inode);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return &vnode->vfs_inode;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_vnode_fetch_status(struct afs_vnode *);
 | 
					 | 
				
			||||||
extern int afs_vnode_fetch_data(struct afs_vnode *,
 | 
					 | 
				
			||||||
				struct afs_rxfs_fetch_descriptor *);
 | 
					 | 
				
			||||||
extern int afs_vnode_give_up_callback(struct afs_vnode *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_VNODE_H */
 | 
					 | 
				
			||||||
							
								
								
									
										141
									
								
								fs/afs/volume.c
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								fs/afs/volume.c
									
									
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/* AFS volume management
 | 
					/* AFS volume management
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 * Written by David Howells (dhowells@redhat.com)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
| 
						 | 
					@ -15,33 +15,9 @@
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
#include <linux/pagemap.h>
 | 
					#include <linux/pagemap.h>
 | 
				
			||||||
#include "volume.h"
 | 
					 | 
				
			||||||
#include "vnode.h"
 | 
					 | 
				
			||||||
#include "cell.h"
 | 
					 | 
				
			||||||
#include "cache.h"
 | 
					 | 
				
			||||||
#include "cmservice.h"
 | 
					 | 
				
			||||||
#include "fsclient.h"
 | 
					 | 
				
			||||||
#include "vlclient.h"
 | 
					 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __KDEBUG
 | 
					 | 
				
			||||||
static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
 | 
					static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static cachefs_match_val_t afs_volume_cache_match(void *target,
 | 
					 | 
				
			||||||
						  const void *entry);
 | 
					 | 
				
			||||||
static void afs_volume_cache_update(void *source, void *entry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cachefs_index_def afs_volume_cache_index_def = {
 | 
					 | 
				
			||||||
	.name		= "volume",
 | 
					 | 
				
			||||||
	.data_size	= sizeof(struct afs_cache_vhash),
 | 
					 | 
				
			||||||
	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
 | 
					 | 
				
			||||||
	.keys[1]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
 | 
					 | 
				
			||||||
	.match		= afs_volume_cache_match,
 | 
					 | 
				
			||||||
	.update		= afs_volume_cache_update,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * lookup a volume by name
 | 
					 * lookup a volume by name
 | 
				
			||||||
| 
						 | 
					@ -65,11 +41,12 @@ struct cachefs_index_def afs_volume_cache_index_def = {
 | 
				
			||||||
 * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
 | 
					 * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
 | 
				
			||||||
 *           explicitly told otherwise
 | 
					 *           explicitly told otherwise
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
 | 
					struct afs_volume *afs_volume_lookup(const char *name, struct afs_cell *cell,
 | 
				
			||||||
		      struct afs_volume **_volume)
 | 
									     int rwpath)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vlocation *vlocation = NULL;
 | 
						struct afs_vlocation *vlocation = NULL;
 | 
				
			||||||
	struct afs_volume *volume = NULL;
 | 
						struct afs_volume *volume = NULL;
 | 
				
			||||||
 | 
						struct afs_server *server = NULL;
 | 
				
			||||||
	afs_voltype_t type;
 | 
						afs_voltype_t type;
 | 
				
			||||||
	const char *cellname, *volname, *suffix;
 | 
						const char *cellname, *volname, *suffix;
 | 
				
			||||||
	char srvtmask;
 | 
						char srvtmask;
 | 
				
			||||||
| 
						 | 
					@ -79,7 +56,7 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
 | 
						if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
 | 
				
			||||||
		printk("kAFS: unparsable volume name\n");
 | 
							printk("kAFS: unparsable volume name\n");
 | 
				
			||||||
		return -EINVAL;
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* determine the type of volume we're looking for */
 | 
						/* determine the type of volume we're looking for */
 | 
				
			||||||
| 
						 | 
					@ -128,8 +105,9 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* lookup the cell record */
 | 
						/* lookup the cell record */
 | 
				
			||||||
	if (cellname || !cell) {
 | 
						if (cellname || !cell) {
 | 
				
			||||||
		ret = afs_cell_lookup(cellname, cellnamesz, &cell);
 | 
							cell = afs_cell_lookup(cellname, cellnamesz);
 | 
				
			||||||
		if (ret<0) {
 | 
							if (IS_ERR(cell)) {
 | 
				
			||||||
 | 
								ret = PTR_ERR(cell);
 | 
				
			||||||
			printk("kAFS: unable to lookup cell '%s'\n",
 | 
								printk("kAFS: unable to lookup cell '%s'\n",
 | 
				
			||||||
			       cellname ?: "");
 | 
								       cellname ?: "");
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
| 
						 | 
					@ -139,9 +117,12 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* lookup the volume location record */
 | 
						/* lookup the volume location record */
 | 
				
			||||||
	ret = afs_vlocation_lookup(cell, volname, volnamesz, &vlocation);
 | 
						vlocation = afs_vlocation_lookup(cell, volname, volnamesz);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (IS_ERR(vlocation)) {
 | 
				
			||||||
 | 
							ret = PTR_ERR(vlocation);
 | 
				
			||||||
 | 
							vlocation = NULL;
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* make the final decision on the type we want */
 | 
						/* make the final decision on the type we want */
 | 
				
			||||||
	ret = -ENOMEDIUM;
 | 
						ret = -ENOMEDIUM;
 | 
				
			||||||
| 
						 | 
					@ -192,13 +173,14 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
 | 
				
			||||||
	/* look up all the applicable server records */
 | 
						/* look up all the applicable server records */
 | 
				
			||||||
	for (loop = 0; loop < 8; loop++) {
 | 
						for (loop = 0; loop < 8; loop++) {
 | 
				
			||||||
		if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
 | 
							if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
 | 
				
			||||||
			ret = afs_server_lookup(
 | 
								server = afs_lookup_server(
 | 
				
			||||||
				volume->cell,
 | 
								       volume->cell, &vlocation->vldb.servers[loop]);
 | 
				
			||||||
				&vlocation->vldb.servers[loop],
 | 
								if (IS_ERR(server)) {
 | 
				
			||||||
				&volume->servers[volume->nservers]);
 | 
									ret = PTR_ERR(server);
 | 
				
			||||||
			if (ret < 0)
 | 
					 | 
				
			||||||
				goto error_discard;
 | 
									goto error_discard;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								volume->servers[volume->nservers] = server;
 | 
				
			||||||
			volume->nservers++;
 | 
								volume->nservers++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -219,8 +201,11 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
 | 
				
			||||||
success:
 | 
					success:
 | 
				
			||||||
	_debug("kAFS selected %s volume %08x",
 | 
						_debug("kAFS selected %s volume %08x",
 | 
				
			||||||
	       afs_voltypes[volume->type], volume->vid);
 | 
						       afs_voltypes[volume->type], volume->vid);
 | 
				
			||||||
	*_volume = volume;
 | 
						up_write(&cell->vl_sem);
 | 
				
			||||||
	ret = 0;
 | 
						afs_put_vlocation(vlocation);
 | 
				
			||||||
 | 
						afs_put_cell(cell);
 | 
				
			||||||
 | 
						_leave(" = %p", volume);
 | 
				
			||||||
 | 
						return volume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* clean up */
 | 
						/* clean up */
 | 
				
			||||||
error_up:
 | 
					error_up:
 | 
				
			||||||
| 
						 | 
					@ -228,9 +213,8 @@ int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	afs_put_vlocation(vlocation);
 | 
						afs_put_vlocation(vlocation);
 | 
				
			||||||
	afs_put_cell(cell);
 | 
						afs_put_cell(cell);
 | 
				
			||||||
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	_leave(" = %d (%p)", ret, volume);
 | 
						return ERR_PTR(ret);
 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
error_discard:
 | 
					error_discard:
 | 
				
			||||||
	up_write(&cell->vl_sem);
 | 
						up_write(&cell->vl_sem);
 | 
				
			||||||
| 
						 | 
					@ -255,10 +239,9 @@ void afs_put_volume(struct afs_volume *volume)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%p", volume);
 | 
						_enter("%p", volume);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vlocation = volume->vlocation;
 | 
						ASSERTCMP(atomic_read(&volume->usage), >, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* sanity check */
 | 
						vlocation = volume->vlocation;
 | 
				
			||||||
	BUG_ON(atomic_read(&volume->usage) <= 0);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* to prevent a race, the decrement and the dequeue must be effectively
 | 
						/* to prevent a race, the decrement and the dequeue must be effectively
 | 
				
			||||||
	 * atomic */
 | 
						 * atomic */
 | 
				
			||||||
| 
						 | 
					@ -292,14 +275,21 @@ void afs_put_volume(struct afs_volume *volume)
 | 
				
			||||||
 * pick a server to use to try accessing this volume
 | 
					 * pick a server to use to try accessing this volume
 | 
				
			||||||
 * - returns with an elevated usage count on the server chosen
 | 
					 * - returns with an elevated usage count on the server chosen
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_volume_pick_fileserver(struct afs_volume *volume,
 | 
					struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
 | 
				
			||||||
			       struct afs_server **_server)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct afs_volume *volume = vnode->volume;
 | 
				
			||||||
	struct afs_server *server;
 | 
						struct afs_server *server;
 | 
				
			||||||
	int ret, state, loop;
 | 
						int ret, state, loop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%s", volume->vlocation->vldb.name);
 | 
						_enter("%s", volume->vlocation->vldb.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* stick with the server we're already using if we can */
 | 
				
			||||||
 | 
						if (vnode->server && vnode->server->fs_state == 0) {
 | 
				
			||||||
 | 
							afs_get_server(vnode->server);
 | 
				
			||||||
 | 
							_leave(" = %p [current]", vnode->server);
 | 
				
			||||||
 | 
							return vnode->server;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	down_read(&volume->server_sem);
 | 
						down_read(&volume->server_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* handle the no-server case */
 | 
						/* handle the no-server case */
 | 
				
			||||||
| 
						 | 
					@ -307,7 +297,7 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
 | 
				
			||||||
		ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
 | 
							ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
 | 
				
			||||||
		up_read(&volume->server_sem);
 | 
							up_read(&volume->server_sem);
 | 
				
			||||||
		_leave(" = %d [no servers]", ret);
 | 
							_leave(" = %d [no servers]", ret);
 | 
				
			||||||
		return ret;
 | 
							return ERR_PTR(ret);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* basically, just search the list for the first live server and use
 | 
						/* basically, just search the list for the first live server and use
 | 
				
			||||||
| 
						 | 
					@ -317,15 +307,16 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
 | 
				
			||||||
		server = volume->servers[loop];
 | 
							server = volume->servers[loop];
 | 
				
			||||||
		state = server->fs_state;
 | 
							state = server->fs_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_debug("consider %d [%d]", loop, state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch (state) {
 | 
							switch (state) {
 | 
				
			||||||
			/* found an apparently healthy server */
 | 
								/* found an apparently healthy server */
 | 
				
			||||||
		case 0:
 | 
							case 0:
 | 
				
			||||||
			afs_get_server(server);
 | 
								afs_get_server(server);
 | 
				
			||||||
			up_read(&volume->server_sem);
 | 
								up_read(&volume->server_sem);
 | 
				
			||||||
			*_server = server;
 | 
								_leave(" = %p (picked %08x)",
 | 
				
			||||||
			_leave(" = 0 (picked %08x)",
 | 
								       server, ntohl(server->addr.s_addr));
 | 
				
			||||||
			       ntohl(server->addr.s_addr));
 | 
								return server;
 | 
				
			||||||
			return 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case -ENETUNREACH:
 | 
							case -ENETUNREACH:
 | 
				
			||||||
			if (ret == 0)
 | 
								if (ret == 0)
 | 
				
			||||||
| 
						 | 
					@ -361,7 +352,7 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	up_read(&volume->server_sem);
 | 
						up_read(&volume->server_sem);
 | 
				
			||||||
	_leave(" = %d", ret);
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
	return ret;
 | 
						return ERR_PTR(ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -370,10 +361,11 @@ int afs_volume_pick_fileserver(struct afs_volume *volume,
 | 
				
			||||||
 * - records result of using a particular server to access a volume
 | 
					 * - records result of using a particular server to access a volume
 | 
				
			||||||
 * - return 0 to try again, 1 if okay or to issue error
 | 
					 * - return 0 to try again, 1 if okay or to issue error
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_volume_release_fileserver(struct afs_volume *volume,
 | 
					int afs_volume_release_fileserver(struct afs_vnode *vnode,
 | 
				
			||||||
				  struct afs_server *server,
 | 
									  struct afs_server *server,
 | 
				
			||||||
				  int result)
 | 
									  int result)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct afs_volume *volume = vnode->volume;
 | 
				
			||||||
	unsigned loop;
 | 
						unsigned loop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("%s,%08x,%d",
 | 
						_enter("%s,%08x,%d",
 | 
				
			||||||
| 
						 | 
					@ -384,6 +376,7 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
 | 
				
			||||||
		/* success */
 | 
							/* success */
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
		server->fs_act_jif = jiffies;
 | 
							server->fs_act_jif = jiffies;
 | 
				
			||||||
 | 
							server->fs_state = 0;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* the fileserver denied all knowledge of the volume */
 | 
							/* the fileserver denied all knowledge of the volume */
 | 
				
			||||||
| 
						 | 
					@ -391,7 +384,7 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
 | 
				
			||||||
		server->fs_act_jif = jiffies;
 | 
							server->fs_act_jif = jiffies;
 | 
				
			||||||
		down_write(&volume->server_sem);
 | 
							down_write(&volume->server_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* first, find where the server is in the active list (if it
 | 
							/* firstly, find where the server is in the active list (if it
 | 
				
			||||||
		 * is) */
 | 
							 * is) */
 | 
				
			||||||
		for (loop = 0; loop < volume->nservers; loop++)
 | 
							for (loop = 0; loop < volume->nservers; loop++)
 | 
				
			||||||
			if (volume->servers[loop] == server)
 | 
								if (volume->servers[loop] == server)
 | 
				
			||||||
| 
						 | 
					@ -429,6 +422,7 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
 | 
				
			||||||
	case -ENETUNREACH:
 | 
						case -ENETUNREACH:
 | 
				
			||||||
	case -EHOSTUNREACH:
 | 
						case -EHOSTUNREACH:
 | 
				
			||||||
	case -ECONNREFUSED:
 | 
						case -ECONNREFUSED:
 | 
				
			||||||
 | 
						case -ETIME:
 | 
				
			||||||
	case -ETIMEDOUT:
 | 
						case -ETIMEDOUT:
 | 
				
			||||||
	case -EREMOTEIO:
 | 
						case -EREMOTEIO:
 | 
				
			||||||
		/* mark the server as dead
 | 
							/* mark the server as dead
 | 
				
			||||||
| 
						 | 
					@ -464,40 +458,3 @@ int afs_volume_release_fileserver(struct afs_volume *volume,
 | 
				
			||||||
	_leave(" [try next server]");
 | 
						_leave(" [try next server]");
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * match a volume hash record stored in the cache
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static cachefs_match_val_t afs_volume_cache_match(void *target,
 | 
					 | 
				
			||||||
						  const void *entry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const struct afs_cache_vhash *vhash = entry;
 | 
					 | 
				
			||||||
	struct afs_volume *volume = target;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("{%u},{%u}", volume->type, vhash->vtype);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (volume->type == vhash->vtype) {
 | 
					 | 
				
			||||||
		_leave(" = SUCCESS");
 | 
					 | 
				
			||||||
		return CACHEFS_MATCH_SUCCESS;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = FAILED");
 | 
					 | 
				
			||||||
	return CACHEFS_MATCH_FAILED;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * update a volume hash record stored in the cache
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
static void afs_volume_cache_update(void *source, void *entry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct afs_cache_vhash *vhash = entry;
 | 
					 | 
				
			||||||
	struct afs_volume *volume = source;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vhash->vtype = volume->type;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										126
									
								
								fs/afs/volume.h
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								fs/afs/volume.h
									
									
									
									
									
								
							| 
						 | 
					@ -1,126 +0,0 @@
 | 
				
			||||||
/* AFS volume management
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 | 
					 | 
				
			||||||
 * Written by David Howells (dhowells@redhat.com)
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
 * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef AFS_VOLUME_H
 | 
					 | 
				
			||||||
#define AFS_VOLUME_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "types.h"
 | 
					 | 
				
			||||||
#include "fsclient.h"
 | 
					 | 
				
			||||||
#include "kafstimod.h"
 | 
					 | 
				
			||||||
#include "kafsasyncd.h"
 | 
					 | 
				
			||||||
#include "cache.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
	AFS_VLUPD_SLEEP,		/* sleeping waiting for update timer to fire */
 | 
					 | 
				
			||||||
	AFS_VLUPD_PENDING,		/* on pending queue */
 | 
					 | 
				
			||||||
	AFS_VLUPD_INPROGRESS,		/* op in progress */
 | 
					 | 
				
			||||||
	AFS_VLUPD_BUSYSLEEP,		/* sleeping because server returned EBUSY */
 | 
					 | 
				
			||||||
} __attribute__((packed)) afs_vlocation_upd_t;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * entry in the cached volume location catalogue
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_cache_vlocation {
 | 
					 | 
				
			||||||
	uint8_t			name[64];	/* volume name (lowercase, padded with NULs) */
 | 
					 | 
				
			||||||
	uint8_t			nservers;	/* number of entries used in servers[] */
 | 
					 | 
				
			||||||
	uint8_t			vidmask;	/* voltype mask for vid[] */
 | 
					 | 
				
			||||||
	uint8_t			srvtmask[8];	/* voltype masks for servers[] */
 | 
					 | 
				
			||||||
#define AFS_VOL_VTM_RW	0x01 /* R/W version of the volume is available (on this server) */
 | 
					 | 
				
			||||||
#define AFS_VOL_VTM_RO	0x02 /* R/O version of the volume is available (on this server) */
 | 
					 | 
				
			||||||
#define AFS_VOL_VTM_BAK	0x04 /* backup version of the volume is available (on this server) */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	afs_volid_t		vid[3];		/* volume IDs for R/W, R/O and Bak volumes */
 | 
					 | 
				
			||||||
	struct in_addr		servers[8];	/* fileserver addresses */
 | 
					 | 
				
			||||||
	time_t			rtime;		/* last retrieval time */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
extern struct cachefs_index_def afs_vlocation_cache_index_def;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * volume -> vnode hash table entry
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_cache_vhash {
 | 
					 | 
				
			||||||
	afs_voltype_t		vtype;		/* which volume variation */
 | 
					 | 
				
			||||||
	uint8_t			hash_bucket;	/* which hash bucket this represents */
 | 
					 | 
				
			||||||
} __attribute__((packed));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
extern struct cachefs_index_def afs_volume_cache_index_def;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * AFS volume location record
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_vlocation {
 | 
					 | 
				
			||||||
	atomic_t		usage;
 | 
					 | 
				
			||||||
	struct list_head	link;		/* link in cell volume location list */
 | 
					 | 
				
			||||||
	struct afs_timer	timeout;	/* decaching timer */
 | 
					 | 
				
			||||||
	struct afs_cell		*cell;		/* cell to which volume belongs */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
	struct cachefs_cookie	*cache;		/* caching cookie */
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	struct afs_cache_vlocation vldb;	/* volume information DB record */
 | 
					 | 
				
			||||||
	struct afs_volume	*vols[3];	/* volume access record pointer (index by type) */
 | 
					 | 
				
			||||||
	rwlock_t		lock;		/* access lock */
 | 
					 | 
				
			||||||
	unsigned long		read_jif;	/* time at which last read from vlserver */
 | 
					 | 
				
			||||||
	struct afs_timer	upd_timer;	/* update timer */
 | 
					 | 
				
			||||||
	struct afs_async_op	upd_op;		/* update operation */
 | 
					 | 
				
			||||||
	afs_vlocation_upd_t	upd_state;	/* update state */
 | 
					 | 
				
			||||||
	unsigned short		upd_first_svix;	/* first server index during update */
 | 
					 | 
				
			||||||
	unsigned short		upd_curr_svix;	/* current server index during update */
 | 
					 | 
				
			||||||
	unsigned short		upd_rej_cnt;	/* ENOMEDIUM count during update */
 | 
					 | 
				
			||||||
	unsigned short		upd_busy_cnt;	/* EBUSY count during update */
 | 
					 | 
				
			||||||
	unsigned short		valid;		/* T if valid */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_vlocation_lookup(struct afs_cell *, const char *, unsigned,
 | 
					 | 
				
			||||||
				struct afs_vlocation **);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define afs_get_vlocation(V) do { atomic_inc(&(V)->usage); } while(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern void afs_put_vlocation(struct afs_vlocation *);
 | 
					 | 
				
			||||||
extern void afs_vlocation_do_timeout(struct afs_vlocation *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * AFS volume access record
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct afs_volume {
 | 
					 | 
				
			||||||
	atomic_t		usage;
 | 
					 | 
				
			||||||
	struct afs_cell		*cell;		/* cell to which belongs (unrefd ptr) */
 | 
					 | 
				
			||||||
	struct afs_vlocation	*vlocation;	/* volume location */
 | 
					 | 
				
			||||||
#ifdef AFS_CACHING_SUPPORT
 | 
					 | 
				
			||||||
	struct cachefs_cookie	*cache;		/* caching cookie */
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	afs_volid_t		vid;		/* volume ID */
 | 
					 | 
				
			||||||
	afs_voltype_t		type;		/* type of volume */
 | 
					 | 
				
			||||||
	char			type_force;	/* force volume type (suppress R/O -> R/W) */
 | 
					 | 
				
			||||||
	unsigned short		nservers;	/* number of server slots filled */
 | 
					 | 
				
			||||||
	unsigned short		rjservers;	/* number of servers discarded due to -ENOMEDIUM */
 | 
					 | 
				
			||||||
	struct afs_server	*servers[8];	/* servers on which volume resides (ordered) */
 | 
					 | 
				
			||||||
	struct rw_semaphore	server_sem;	/* lock for accessing current server */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_volume_lookup(const char *, struct afs_cell *, int,
 | 
					 | 
				
			||||||
			     struct afs_volume **);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern void afs_put_volume(struct afs_volume *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_volume_pick_fileserver(struct afs_volume *,
 | 
					 | 
				
			||||||
				      struct afs_server **);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern int afs_volume_release_fileserver(struct afs_volume *,
 | 
					 | 
				
			||||||
					 struct afs_server *, int);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* AFS_VOLUME_H */
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in a new issue