Commit 62175be2 authored by Trond Myklebust's avatar Trond Myklebust

Implement stateful open() for NFSv4 as per RFC3010-bis.

The resulting state is saved in the NFS-specific part of the
struct inode.

Initially we just start with 3 possible states:
  - open for read
  - open for write
  - open for read/write
parent ec059472
...@@ -140,6 +140,10 @@ nfs_clear_inode(struct inode *inode) ...@@ -140,6 +140,10 @@ nfs_clear_inode(struct inode *inode)
cred = nfsi->cache_access.cred; cred = nfsi->cache_access.cred;
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
/* Clean up the V4 state */
nfs4_put_shareowner(inode, nfsi->wo_owner);
nfs4_put_shareowner(inode, nfsi->ro_owner);
nfs4_put_shareowner(inode, nfsi->rw_owner);
} }
void void
...@@ -1492,9 +1496,18 @@ static struct file_system_type nfs4_fs_type = { ...@@ -1492,9 +1496,18 @@ static struct file_system_type nfs4_fs_type = {
.kill_sb = nfs_kill_super, .kill_sb = nfs_kill_super,
.fs_flags = FS_ODD_RENAME, .fs_flags = FS_ODD_RENAME,
}; };
#define nfs4_zero_state(nfsi) \
do { \
(nfsi)->wo_owner = NULL; \
(nfsi)->ro_owner = NULL; \
(nfsi)->rw_owner = NULL; \
} while(0)
#define register_nfs4fs() register_filesystem(&nfs4_fs_type) #define register_nfs4fs() register_filesystem(&nfs4_fs_type)
#define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type) #define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type)
#else #else
#define nfs4_zero_state(nfsi) \
do { } while (0)
#define register_nfs4fs() (0) #define register_nfs4fs() (0)
#define unregister_nfs4fs() #define unregister_nfs4fs()
#endif #endif
...@@ -1516,6 +1529,7 @@ static struct inode *nfs_alloc_inode(struct super_block *sb) ...@@ -1516,6 +1529,7 @@ static struct inode *nfs_alloc_inode(struct super_block *sb)
return NULL; return NULL;
nfsi->flags = 0; nfsi->flags = 0;
nfsi->mm_cred = NULL; nfsi->mm_cred = NULL;
nfs4_zero_state(nfsi);
return &nfsi->vfs_inode; return &nfsi->vfs_inode;
} }
......
This diff is collapsed.
...@@ -42,6 +42,16 @@ ...@@ -42,6 +42,16 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
/* This protects most of the client-side state. */
static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED;
nfs4_stateid zero_stateid =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
nfs4_stateid one_stateid =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/* /*
* nfs4_get_client(): returns an empty client structure * nfs4_get_client(): returns an empty client structure
* nfs4_put_client(): drops reference to client structure * nfs4_put_client(): drops reference to client structure
...@@ -52,26 +62,164 @@ ...@@ -52,26 +62,164 @@
struct nfs4_client * struct nfs4_client *
nfs4_get_client(void) nfs4_get_client(void)
{ {
struct nfs4_client *clp; struct nfs4_client *clp;
if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL))) { if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
atomic_set(&clp->cl_count, 1); memset(clp, 0, sizeof(nfs4_verifier));
clp->cl_clientid = 0; return clp;
INIT_LIST_HEAD(&clp->cl_lockowners);
}
return clp;
} }
void void
nfs4_put_client(struct nfs4_client *clp) nfs4_put_client(struct nfs4_client *clp)
{ {
BUG_ON(!clp); BUG_ON(!clp);
BUG_ON(!atomic_read(&clp->cl_count)); kfree(clp);
}
if (atomic_dec_and_test(&clp->cl_count)) {
BUG_ON(!list_empty(&clp->cl_lockowners)); static inline u32
kfree(clp); nfs4_alloc_lockowner_id(struct nfs4_client *clp)
{
u32 res;
spin_lock(&state_spinlock);
res = clp->cl_lockowner_id ++;
spin_unlock(&state_spinlock);
return res;
}
/*
* nfs4_get_shareowner(): this is called on the OPEN or CREATE path to
* obtain a new shareowner.
*
* There are three shareowners (open_owner4 in rfc3010) per inode,
* one for each possible combination of share lock access. Since
* Linux does not support the deny access type, there are
* three (not 9) referenced by the nfs_inode:
*
* O_WRONLY: inode->wo_owner
* O_RDONLY: inode->ro_owner
* O_RDWR: inode->rw_owner
*
* We create a new shareowner the first time a file is OPENed with
* one of the above shares. All other OPENs with a similar
* share use the single stateid associated with the inode.
*
*/
struct nfs4_shareowner *
nfs4_get_shareowner(struct inode *dir)
{
struct nfs4_client *clp;
struct nfs4_shareowner *sp;
sp = kmalloc(sizeof(*sp),GFP_KERNEL);
if (!sp)
return NULL;
clp = (NFS_SB(dir->i_sb))->nfs4_state;
BUG_ON(!clp);
init_MUTEX(&sp->so_sema);
sp->so_seqid = 0; /* arbitrary */
memset(sp->so_stateid, 0, sizeof(nfs4_stateid));
sp->so_id = nfs4_alloc_lockowner_id(clp);
return sp;
}
/*
* Called for each inode shareowner in nfs_clear_inode,
* or if nfs4_do_open fails.
*/
void
nfs4_put_shareowner(struct inode *inode, struct nfs4_shareowner *sp)
{
if (!sp)
return;
kfree(sp);
}
/*
* Called with sp->so_sema held.
*
* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
* failed with a seqid incrementing error -
* see comments nfs_fs.h:seqid_mutating_error()
*/
void
nfs4_increment_seqid(u32 status, struct nfs4_shareowner *sp)
{
if (status == NFS_OK || seqid_mutating_err(status))
sp->so_seqid++;
}
/*
* Called by nfs4_proc_open to set the appropriate stateid
*/
int
nfs4_set_inode_share(struct inode * inode, struct nfs4_shareowner *sp, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
if (!nfsi->ro_owner) {
nfsi->ro_owner = sp;
return 0;
}
break;
case O_WRONLY:
if (!nfsi->wo_owner) {
nfsi->wo_owner = sp;
return 0;
}
break;
case O_RDWR:
if (!nfsi->rw_owner) {
nfsi->rw_owner = sp;
return 0;
}
}
return -EBUSY;
}
/*
* Boolean test to determine if an OPEN call goes on the wire.
*
* Called by nfs4_proc_open.
*/
int
nfs4_test_shareowner(struct inode *inode, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
if(nfsi->ro_owner)
return 0;
break;
case O_WRONLY:
if(nfsi->wo_owner)
return 0;
break;
case O_RDWR:
if(nfsi->rw_owner)
return 0;
} }
return 1;
}
struct nfs4_shareowner *
nfs4_get_inode_share(struct inode * inode, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
return nfsi->ro_owner;
case O_WRONLY:
return nfsi->wo_owner;
case O_RDWR:
return nfsi->rw_owner;
}
/* Duh gcc warning if we don't... */
return NULL;
} }
/* /*
......
This diff is collapsed.
...@@ -206,6 +206,8 @@ enum { ...@@ -206,6 +206,8 @@ enum {
NFSPROC4_CLNT_READ, NFSPROC4_CLNT_READ,
NFSPROC4_CLNT_WRITE, NFSPROC4_CLNT_WRITE,
NFSPROC4_CLNT_COMMIT, NFSPROC4_CLNT_COMMIT,
NFSPROC4_CLNT_OPEN,
NFSPROC4_CLNT_OPEN_CONFIRM,
}; };
#endif #endif
......
...@@ -155,6 +155,13 @@ struct nfs_inode { ...@@ -155,6 +155,13 @@ struct nfs_inode {
wait_queue_head_t nfs_i_wait; wait_queue_head_t nfs_i_wait;
#ifdef CONFIG_NFS_V4
/* NFSv4 state */
struct nfs4_shareowner *ro_owner;
struct nfs4_shareowner *wo_owner;
struct nfs4_shareowner *rw_owner;
#endif /* CONFIG_NFS_V4*/
struct inode vfs_inode; struct inode vfs_inode;
}; };
...@@ -435,28 +442,74 @@ extern void * nfs_root_data(void); ...@@ -435,28 +442,74 @@ extern void * nfs_root_data(void);
#define NFS_JUKEBOX_RETRY_TIME (5 * HZ) #define NFS_JUKEBOX_RETRY_TIME (5 * HZ)
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
/*
* In a seqid-mutating op, this macro controls which error return
* values trigger incrementation of the seqid.
*
* from rfc 3010:
* The client MUST monotonically increment the sequence number for the
* CLOSE, LOCK, LOCKU, OPEN, OPEN_CONFIRM, and OPEN_DOWNGRADE
* operations. This is true even in the event that the previous
* operation that used the sequence number received an error. The only
* exception to this rule is if the previous operation received one of
* the following errors: NFSERR_STALE_CLIENTID, NFSERR_STALE_STATEID,
* NFSERR_BAD_STATEID, NFSERR_BAD_SEQID, NFSERR_BADXDR,
* NFSERR_RESOURCE, NFSERR_NOFILEHANDLE.
*
*/
#define seqid_mutating_err(err) \
(((err) != NFSERR_STALE_CLIENTID) && \
((err) != NFSERR_STALE_STATEID) && \
((err) != NFSERR_BAD_STATEID) && \
((err) != NFSERR_BAD_SEQID) && \
((err) != NFSERR_BAD_XDR) && \
((err) != NFSERR_RESOURCE) && \
((err) != NFSERR_NOFILEHANDLE))
struct nfs4_client { struct nfs4_client {
atomic_t cl_count; /* refcount */
u64 cl_clientid; /* constant */ u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm; nfs4_verifier cl_confirm;
/* u32 cl_lockowner_id;
* Starts a list of lockowners, linked through lo_list. };
*/
struct list_head cl_lockowners; /* protected by state_spinlock */ /*
* The ->so_sema is held during all shareowner seqid-mutating operations:
* OPEN, OPEN_DOWNGRADE, and CLOSE.
* Its purpose is to properly serialize so_seqid, as mandated by
* the protocol.
*/
struct nfs4_shareowner {
u32 so_id; /* 32-bit identifier, unique */
struct semaphore so_sema;
u32 so_seqid; /* protected by so_sema */
nfs4_stateid so_stateid; /* protected by so_sema */
unsigned int so_flags; /* protected by so_sema */
}; };
/* nfs4proc.c */ /* nfs4proc.c */
extern int nfs4_proc_renew(struct nfs_server *server); extern int nfs4_proc_renew(struct nfs_server *server);
/* nfs4renewd.c */ /* nfs4renewd.c */
extern int nfs4_init_renewd(struct nfs_server *server); extern int nfs4_init_renewd(struct nfs_server *server);
#endif /* CONFIG_NFS_V4 */
#ifdef CONFIG_NFS_V4
/* nfs4state.c */
extern struct nfs4_client *nfs4_get_client(void); extern struct nfs4_client *nfs4_get_client(void);
extern void nfs4_put_client(struct nfs4_client *clp); extern void nfs4_put_client(struct nfs4_client *clp);
extern struct nfs4_shareowner * nfs4_get_shareowner(struct inode *inode);
void nfs4_put_shareowner(struct inode *inode, struct nfs4_shareowner *sp);
extern int nfs4_set_inode_share(struct inode * inode,
struct nfs4_shareowner *sp, unsigned int flags);
extern void nfs4_increment_seqid(u32 status, struct nfs4_shareowner *sp);
extern int nfs4_test_shareowner(struct inode *inode, unsigned int open_flags);
struct nfs4_shareowner * nfs4_get_inode_share(struct inode * inode, unsigned int open_flags);
struct nfs4_mount_data; struct nfs4_mount_data;
static inline int static inline int
...@@ -481,6 +534,7 @@ destroy_nfsv4_state(struct nfs_server *server) ...@@ -481,6 +534,7 @@ destroy_nfsv4_state(struct nfs_server *server)
#else #else
#define create_nfsv4_state(server, data) 0 #define create_nfsv4_state(server, data) 0
#define destroy_nfsv4_state(server) do { } while (0) #define destroy_nfsv4_state(server) do { } while (0)
#define nfs4_put_shareowner(inode, owner) do { } while (0)
#endif #endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -87,6 +87,51 @@ struct nfs_pathconf { ...@@ -87,6 +87,51 @@ struct nfs_pathconf {
__u32 max_namelen; /* max name length */ __u32 max_namelen; /* max name length */
}; };
/*
* Arguments to the open call.
*/
struct nfs_openargs {
struct nfs_fh * fh;
__u32 seqid;
__u32 share_access;
__u64 clientid;
__u32 id;
__u32 opentype;
__u32 createmode;
union {
struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */
} u;
struct qstr * name;
struct nfs4_getattr * f_getattr;
struct nfs4_getattr * d_getattr;
};
struct nfs_openres {
__u32 status;
nfs4_stateid stateid;
struct nfs_fh fh;
struct nfs4_change_info * cinfo;
__u32 rflags;
struct nfs4_getattr * f_getattr;
struct nfs4_getattr * d_getattr;
};
/*
* Arguments to the open_confirm call.
*/
struct nfs_open_confirmargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u32 seqid;
};
struct nfs_open_confirmres {
__u32 status;
nfs4_stateid stateid;
};
/* /*
* Arguments to the read call. * Arguments to the read call.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment