📄 nfs4state.c
字号:
/* * fs/nfs/nfs4state.c * * Client-side XDR for NFSv4. * * Copyright (c) 2002 The Regents of the University of Michigan. * All rights reserved. * * Kendrick Smith <kmsmith@umich.edu> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Implementation of the NFSv4 state model. For the time being, * this is minimal, but will be made much more complex in a * subsequent patch. */#include <linux/kernel.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/nfs_fs.h>#include <linux/nfs_idmap.h>#include <linux/kthread.h>#include <linux/module.h>#include <linux/random.h>#include <linux/workqueue.h>#include <linux/bitops.h>#include "nfs4_fs.h"#include "callback.h"#include "delegation.h"#include "internal.h"#define OPENOWNER_POOL_SIZE 8const nfs4_stateid zero_stateid;static LIST_HEAD(nfs4_clientid_list);static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred){ int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport, cred); if (status == 0) status = nfs4_proc_setclientid_confirm(clp, cred); if (status == 0) nfs4_schedule_state_renewal(clp); return status;}struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp){ struct nfs4_state_owner *sp; struct rb_node *pos; struct rpc_cred *cred = NULL; for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); if (list_empty(&sp->so_states)) continue; cred = get_rpccred(sp->so_cred); break; } return cred;}static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp){ struct nfs4_state_owner *sp; struct rb_node *pos; pos = rb_first(&clp->cl_state_owners); if (pos != NULL) { sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); return get_rpccred(sp->so_cred); } return NULL;}static void nfs_alloc_unique_id(struct rb_root *root, struct nfs_unique_id *new, __u64 minval, int maxbits){ struct rb_node **p, *parent; struct nfs_unique_id *pos; __u64 mask = ~0ULL; if (maxbits < 64) mask = (1ULL << maxbits) - 1ULL; /* Ensure distribution is more or less flat */ get_random_bytes(&new->id, sizeof(new->id)); new->id &= mask; if (new->id < minval) new->id += minval;retry: p = &root->rb_node; parent = NULL; while (*p != NULL) { parent = *p; pos = rb_entry(parent, struct nfs_unique_id, rb_node); if (new->id < pos->id) p = &(*p)->rb_left; else if (new->id > pos->id) p = &(*p)->rb_right; else goto id_exists; } rb_link_node(&new->rb_node, parent, p); rb_insert_color(&new->rb_node, root); return;id_exists: for (;;) { new->id++; if (new->id < minval || (new->id & mask) != new->id) { new->id = minval; break; } parent = rb_next(parent); if (parent == NULL) break; pos = rb_entry(parent, struct nfs_unique_id, rb_node); if (new->id < pos->id) break; } goto retry;}static void nfs_free_unique_id(struct rb_root *root, struct nfs_unique_id *id){ rb_erase(&id->rb_node, root);}static struct nfs4_state_owner *nfs4_find_state_owner(struct nfs_server *server, struct rpc_cred *cred){ struct nfs_client *clp = server->nfs_client; struct rb_node **p = &clp->cl_state_owners.rb_node, *parent = NULL; struct nfs4_state_owner *sp, *res = NULL; while (*p != NULL) { parent = *p; sp = rb_entry(parent, struct nfs4_state_owner, so_client_node); if (server < sp->so_server) { p = &parent->rb_left; continue; } if (server > sp->so_server) { p = &parent->rb_right; continue; } if (cred < sp->so_cred) p = &parent->rb_left; else if (cred > sp->so_cred) p = &parent->rb_right; else { atomic_inc(&sp->so_count); res = sp; break; } } return res;}static struct nfs4_state_owner *nfs4_insert_state_owner(struct nfs_client *clp, struct nfs4_state_owner *new){ struct rb_node **p = &clp->cl_state_owners.rb_node, *parent = NULL; struct nfs4_state_owner *sp; while (*p != NULL) { parent = *p; sp = rb_entry(parent, struct nfs4_state_owner, so_client_node); if (new->so_server < sp->so_server) { p = &parent->rb_left; continue; } if (new->so_server > sp->so_server) { p = &parent->rb_right; continue; } if (new->so_cred < sp->so_cred) p = &parent->rb_left; else if (new->so_cred > sp->so_cred) p = &parent->rb_right; else { atomic_inc(&sp->so_count); return sp; } } nfs_alloc_unique_id(&clp->cl_openowner_id, &new->so_owner_id, 1, 64); rb_link_node(&new->so_client_node, parent, p); rb_insert_color(&new->so_client_node, &clp->cl_state_owners); return new;}static voidnfs4_remove_state_owner(struct nfs_client *clp, struct nfs4_state_owner *sp){ if (!RB_EMPTY_NODE(&sp->so_client_node)) rb_erase(&sp->so_client_node, &clp->cl_state_owners); nfs_free_unique_id(&clp->cl_openowner_id, &sp->so_owner_id);}/* * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to * create a new state_owner. * */static struct nfs4_state_owner *nfs4_alloc_state_owner(void){ struct nfs4_state_owner *sp; sp = kzalloc(sizeof(*sp),GFP_KERNEL); if (!sp) return NULL; spin_lock_init(&sp->so_lock); INIT_LIST_HEAD(&sp->so_states); INIT_LIST_HEAD(&sp->so_delegations); rpc_init_wait_queue(&sp->so_sequence.wait, "Seqid_waitqueue"); sp->so_seqid.sequence = &sp->so_sequence; spin_lock_init(&sp->so_sequence.lock); INIT_LIST_HEAD(&sp->so_sequence.list); atomic_set(&sp->so_count, 1); return sp;}voidnfs4_drop_state_owner(struct nfs4_state_owner *sp){ if (!RB_EMPTY_NODE(&sp->so_client_node)) { struct nfs_client *clp = sp->so_client; spin_lock(&clp->cl_lock); rb_erase(&sp->so_client_node, &clp->cl_state_owners); RB_CLEAR_NODE(&sp->so_client_node); spin_unlock(&clp->cl_lock); }}/* * Note: must be called with clp->cl_sem held in order to prevent races * with reboot recovery! */struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred){ struct nfs_client *clp = server->nfs_client; struct nfs4_state_owner *sp, *new; spin_lock(&clp->cl_lock); sp = nfs4_find_state_owner(server, cred); spin_unlock(&clp->cl_lock); if (sp != NULL) return sp; new = nfs4_alloc_state_owner(); if (new == NULL) return NULL; new->so_client = clp; new->so_server = server; new->so_cred = cred; spin_lock(&clp->cl_lock); sp = nfs4_insert_state_owner(clp, new); spin_unlock(&clp->cl_lock); if (sp == new) get_rpccred(cred); else kfree(new); return sp;}/* * Must be called with clp->cl_sem held in order to avoid races * with state recovery... */void nfs4_put_state_owner(struct nfs4_state_owner *sp){ struct nfs_client *clp = sp->so_client; struct rpc_cred *cred = sp->so_cred; if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) return; nfs4_remove_state_owner(clp, sp); spin_unlock(&clp->cl_lock); put_rpccred(cred); kfree(sp);}static struct nfs4_state *nfs4_alloc_open_state(void){ struct nfs4_state *state; state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return NULL; atomic_set(&state->count, 1); INIT_LIST_HEAD(&state->lock_states); spin_lock_init(&state->state_lock); seqlock_init(&state->seqlock); return state;}voidnfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode){ if (state->state == mode) return; /* NB! List reordering - see the reclaim code for why. */ if ((mode & FMODE_WRITE) != (state->state & FMODE_WRITE)) { if (mode & FMODE_WRITE) list_move(&state->open_states, &state->owner->so_states); else list_move_tail(&state->open_states, &state->owner->so_states); } state->state = mode;}static struct nfs4_state *__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner){ struct nfs_inode *nfsi = NFS_I(inode); struct nfs4_state *state; list_for_each_entry(state, &nfsi->open_states, inode_states) { if (state->owner != owner) continue; if (atomic_inc_not_zero(&state->count)) return state; } return NULL;}static voidnfs4_free_open_state(struct nfs4_state *state){ kfree(state);}struct nfs4_state *nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner){ struct nfs4_state *state, *new; struct nfs_inode *nfsi = NFS_I(inode); spin_lock(&inode->i_lock); state = __nfs4_find_state_byowner(inode, owner); spin_unlock(&inode->i_lock); if (state) goto out; new = nfs4_alloc_open_state(); spin_lock(&owner->so_lock); spin_lock(&inode->i_lock); state = __nfs4_find_state_byowner(inode, owner); if (state == NULL && new != NULL) { state = new; state->owner = owner; atomic_inc(&owner->so_count); list_add(&state->inode_states, &nfsi->open_states); state->inode = igrab(inode); spin_unlock(&inode->i_lock); /* Note: The reclaim code dictates that we add stateless * and read-only stateids to the end of the list */ list_add_tail(&state->open_states, &owner->so_states); spin_unlock(&owner->so_lock); } else { spin_unlock(&inode->i_lock); spin_unlock(&owner->so_lock); if (new) nfs4_free_open_state(new); }out: return state;}/* * Beware! Caller must be holding exactly one * reference to clp->cl_sem! */void nfs4_put_open_state(struct nfs4_state *state){ struct inode *inode = state->inode; struct nfs4_state_owner *owner = state->owner; if (!atomic_dec_and_lock(&state->count, &owner->so_lock)) return; spin_lock(&inode->i_lock); list_del(&state->inode_states); list_del(&state->open_states); spin_unlock(&inode->i_lock); spin_unlock(&owner->so_lock); iput(inode); nfs4_free_open_state(state); nfs4_put_state_owner(owner);}/* * Close the current file. */static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mode, int wait){ struct nfs4_state_owner *owner = state->owner; int call_close = 0; int newstate; atomic_inc(&owner->so_count); /* Protect against nfs4_find_state() */ spin_lock(&owner->so_lock); switch (mode & (FMODE_READ | FMODE_WRITE)) { case FMODE_READ: state->n_rdonly--; break; case FMODE_WRITE: state->n_wronly--; break; case FMODE_READ|FMODE_WRITE: state->n_rdwr--; } newstate = FMODE_READ|FMODE_WRITE; if (state->n_rdwr == 0) { if (state->n_rdonly == 0) { newstate &= ~FMODE_READ; call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags); call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); } if (state->n_wronly == 0) { newstate &= ~FMODE_WRITE; call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags); call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); } if (newstate == 0) clear_bit(NFS_DELEGATED_STATE, &state->flags); } nfs4_state_set_mode_locked(state, newstate); spin_unlock(&owner->so_lock); if (!call_close) { nfs4_put_open_state(state); nfs4_put_state_owner(owner); } else nfs4_do_close(path, state, wait);}void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode){ __nfs4_close(path, state, mode, 0);}void nfs4_close_sync(struct path *path, struct nfs4_state *state, mode_t mode){ __nfs4_close(path, state, mode, 1);}/* * Search the state->lock_states for an existing lock_owner * that is compatible with current->files */static struct nfs4_lock_state *__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner){ struct nfs4_lock_state *pos; list_for_each_entry(pos, &state->lock_states, ls_locks) { if (pos->ls_owner != fl_owner) continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -