nfsd: introduce a generic nfsd4_cb
[firefly-linux-kernel-4.4.55.git] / fs / nfsd / state.h
index 0097d4771521747c164b1e9d4c3012abc0ccdfb3..5fb6ab17f386d25ccef98280a4c6f42c8c5b6263 100644 (file)
@@ -62,7 +62,6 @@ typedef struct {
        (s)->si_generation
 
 struct nfsd4_callback {
-       void *cb_op;
        struct nfs4_client *cb_clp;
        struct list_head cb_per_client;
        u32 cb_minorversion;
@@ -72,6 +71,11 @@ struct nfsd4_callback {
        bool cb_done;
 };
 
+/*
+ * A core object that represents a "common" stateid. These are generally
+ * embedded within the different (more specific) stateid objects and contain
+ * fields that are of general use to any stateid.
+ */
 struct nfs4_stid {
        atomic_t sc_count;
 #define NFS4_OPEN_STID 1
@@ -85,18 +89,39 @@ struct nfs4_stid {
        unsigned char sc_type;
        stateid_t sc_stateid;
        struct nfs4_client *sc_client;
+       struct nfs4_file *sc_file;
+       void (*sc_free)(struct nfs4_stid *);
 };
 
+/*
+ * Represents a delegation stateid. The nfs4_client holds references to these
+ * and they are put when it is being destroyed or when the delegation is
+ * returned by the client:
+ *
+ * o 1 reference as long as a delegation is still in force (taken when it's
+ *   alloc'd, put when it's returned or revoked)
+ *
+ * o 1 reference as long as a recall rpc is in progress (taken when the lease
+ *   is broken, put when the rpc exits)
+ *
+ * o 1 more ephemeral reference for each nfsd thread currently doing something
+ *   with that delegation without holding the cl_lock
+ *
+ * If the server attempts to recall a delegation and the client doesn't do so
+ * before a timeout, the server may also revoke the delegation. In that case,
+ * the object will either be destroyed (v4.0) or moved to a per-client list of
+ * revoked delegations (v4.1+).
+ *
+ * This object is a superset of the nfs4_stid.
+ */
 struct nfs4_delegation {
        struct nfs4_stid        dl_stid; /* must be first field */
        struct list_head        dl_perfile;
        struct list_head        dl_perclnt;
        struct list_head        dl_recall_lru;  /* delegation recalled */
-       struct nfs4_file        *dl_file;
        u32                     dl_type;
        time_t                  dl_time;
 /* For recall: */
-       struct knfsd_fh         dl_fh;
        int                     dl_retries;
        struct nfsd4_callback   dl_recall;
 };
@@ -195,6 +220,11 @@ struct nfsd4_conn {
        unsigned char cn_flags;
 };
 
+/*
+ * Representation of a v4.1+ session. These are refcounted in a similar fashion
+ * to the nfs4_client. References are only taken when the server is actively
+ * working on the object (primarily during the processing of compounds).
+ */
 struct nfsd4_session {
        atomic_t                se_ref;
        struct list_head        se_hash;        /* hash by sessionid */
@@ -224,17 +254,35 @@ struct nfsd4_sessionid {
 
 /*
  * struct nfs4_client - one per client.  Clientids live here.
- *     o Each nfs4_client is hashed by clientid.
  *
- *     o Each nfs4_clients is also hashed by name 
- *       (the opaque quantity initially sent by the client to identify itself).
+ * The initial object created by an NFS client using SETCLIENTID (for NFSv4.0)
+ * or EXCHANGE_ID (for NFSv4.1+). These objects are refcounted and timestamped.
+ * Each nfsd_net_ns object contains a set of these and they are tracked via
+ * short and long form clientid. They are hashed and searched for under the
+ * per-nfsd_net client_lock spinlock.
+ *
+ * References to it are only held during the processing of compounds, and in
+ * certain other operations. In their "resting state" they have a refcount of
+ * 0. If they are not renewed within a lease period, they become eligible for
+ * destruction by the laundromat.
+ *
+ * These objects can also be destroyed prematurely by the fault injection code,
+ * or if the client sends certain forms of SETCLIENTID or EXCHANGE_ID updates.
+ * Care is taken *not* to do this however when the objects have an elevated
+ * refcount.
+ *
+ * o Each nfs4_client is hashed by clientid
+ *
+ * o Each nfs4_clients is also hashed by name (the opaque quantity initially
+ *   sent by the client to identify itself).
  *       
- *     o cl_perclient list is used to ensure no dangling stateowner references
- *       when we expire the nfs4_client
+ * o cl_perclient list is used to ensure no dangling stateowner references
+ *   when we expire the nfs4_client
  */
 struct nfs4_client {
        struct list_head        cl_idhash;      /* hash by cl_clientid.id */
        struct rb_node          cl_namenode;    /* link into by-name trees */
+       struct list_head        *cl_ownerstr_hashtbl;
        struct list_head        cl_openowners;
        struct idr              cl_stateids;    /* stateid lookup */
        struct list_head        cl_delegations;
@@ -257,6 +305,7 @@ struct nfs4_client {
 #define NFSD4_CLIENT_STABLE            (2)     /* client on stable storage */
 #define NFSD4_CLIENT_RECLAIM_COMPLETE  (3)     /* reclaim_complete done */
 #define NFSD4_CLIENT_CONFIRMED         (4)     /* client is confirmed */
+#define NFSD4_CLIENT_UPCALL_LOCK       (5)     /* upcall serialization */
 #define NFSD4_CLIENT_CB_FLAG_MASK      (1 << NFSD4_CLIENT_CB_UPDATE | \
                                         1 << NFSD4_CLIENT_CB_KILL)
        unsigned long           cl_flags;
@@ -328,21 +377,43 @@ struct nfs4_replay {
        unsigned int            rp_buflen;
        char                    *rp_buf;
        struct knfsd_fh         rp_openfh;
+       struct mutex            rp_mutex;
        char                    rp_ibuf[NFSD4_REPLAY_ISIZE];
 };
 
+struct nfs4_stateowner;
+
+struct nfs4_stateowner_operations {
+       void (*so_unhash)(struct nfs4_stateowner *);
+       void (*so_free)(struct nfs4_stateowner *);
+};
+
+/*
+ * A core object that represents either an open or lock owner. The object and
+ * lock owner objects have one of these embedded within them. Refcounts and
+ * other fields common to both owner types are contained within these
+ * structures.
+ */
 struct nfs4_stateowner {
-       struct list_head        so_strhash;   /* hash by op_name */
-       struct list_head        so_stateids;
-       struct nfs4_client *    so_client;
-       /* after increment in ENCODE_SEQID_OP_TAIL, represents the next
+       struct list_head                        so_strhash;
+       struct list_head                        so_stateids;
+       struct nfs4_client                      *so_client;
+       const struct nfs4_stateowner_operations *so_ops;
+       /* after increment in nfsd4_bump_seqid, represents the next
         * sequence id expected from the client: */
-       u32                     so_seqid;
-       struct xdr_netobj       so_owner;     /* open owner name */
-       struct nfs4_replay      so_replay;
-       bool                    so_is_open_owner;
+       atomic_t                                so_count;
+       u32                                     so_seqid;
+       struct xdr_netobj                       so_owner; /* open owner name */
+       struct nfs4_replay                      so_replay;
+       bool                                    so_is_open_owner;
 };
 
+/*
+ * When a file is opened, the client provides an open state owner opaque string
+ * that indicates the "owner" of that open. These objects are refcounted.
+ * References to it are held by each open state associated with it. This object
+ * is a superset of the nfs4_stateowner struct.
+ */
 struct nfs4_openowner {
        struct nfs4_stateowner  oo_owner; /* must be first field */
        struct list_head        oo_perclient;
@@ -357,10 +428,15 @@ struct nfs4_openowner {
        struct nfs4_ol_stateid *oo_last_closed_stid;
        time_t                  oo_time; /* time of placement on so_close_lru */
 #define NFS4_OO_CONFIRMED   1
-#define NFS4_OO_NEW         4
        unsigned char           oo_flags;
 };
 
+/*
+ * Represents a generic "lockowner". Similar to an openowner. References to it
+ * are held by the lock stateids that are created on its behalf. This object is
+ * a superset of the nfs4_stateowner struct (or would be if it needed any extra
+ * fields).
+ */
 struct nfs4_lockowner {
        struct nfs4_stateowner  lo_owner; /* must be first element */
 };
@@ -375,7 +451,14 @@ static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so)
        return container_of(so, struct nfs4_lockowner, lo_owner);
 }
 
-/* nfs4_file: a file opened by some number of (open) nfs4_stateowners. */
+/*
+ * nfs4_file: a file opened by some number of (open) nfs4_stateowners.
+ *
+ * These objects are global. nfsd only keeps one instance of a nfs4_file per
+ * inode (though it may keep multiple file descriptors open per inode). These
+ * are tracked in the file_hashtbl which is protected by the state_lock
+ * spinlock.
+ */
 struct nfs4_file {
        atomic_t                fi_ref;
        spinlock_t              fi_lock;
@@ -400,14 +483,26 @@ struct nfs4_file {
        bool                    fi_had_conflict;
 };
 
-/* "ol" stands for "Open or Lock".  Better suggestions welcome. */
+/*
+ * A generic struct representing either a open or lock stateid. The nfs4_client
+ * holds a reference to each of these objects, and they in turn hold a
+ * reference to their respective stateowners. The client's reference is
+ * released in response to a close or unlock (depending on whether it's an open
+ * or lock stateid) or when the client is being destroyed.
+ *
+ * In the case of v4.0 open stateids, these objects are preserved for a little
+ * while after close in order to handle CLOSE replays. Those are eventually
+ * reclaimed via a LRU scheme by the laundromat.
+ *
+ * This object is a superset of the nfs4_stid. "ol" stands for "Open or Lock".
+ * Better suggestions welcome.
+ */
 struct nfs4_ol_stateid {
        struct nfs4_stid    st_stid; /* must be first field */
        struct list_head              st_perfile;
        struct list_head              st_perstateowner;
        struct list_head              st_locks;
        struct nfs4_stateowner      * st_stateowner;
-       struct nfs4_file            * st_file;
        unsigned char                 st_access_bmap;
        unsigned char                 st_deny_bmap;
        struct nfs4_ol_stateid         * st_openstp;
@@ -422,14 +517,20 @@ static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s)
 #define RD_STATE               0x00000010
 #define WR_STATE               0x00000020
 
+enum nfsd4_cb_op {
+       NFSPROC4_CLNT_CB_NULL = 0,
+       NFSPROC4_CLNT_CB_RECALL,
+       NFSPROC4_CLNT_CB_SEQUENCE,
+};
+
+
 struct nfsd4_compound_state;
 struct nfsd_net;
 
 extern __be32 nfs4_preprocess_stateid_op(struct net *net,
                struct nfsd4_compound_state *cstate,
                stateid_t *stateid, int flags, struct file **filp);
-extern void nfs4_lock_state(void);
-extern void nfs4_unlock_state(void);
+void nfs4_put_stid(struct nfs4_stid *s);
 void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *);
 extern void nfs4_release_reclaim(struct nfsd_net *);
 extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,
@@ -442,41 +543,49 @@ void nfsd4_run_cb_recall(struct work_struct *w);
 extern void nfsd4_probe_callback(struct nfs4_client *clp);
 extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
 extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
-extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
+extern void nfsd4_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
+               enum nfsd4_cb_op op);
 extern int nfsd4_create_callback_queue(void);
 extern void nfsd4_destroy_callback_queue(void);
 extern void nfsd4_shutdown_callback(struct nfs4_client *);
 extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
-extern void nfs4_put_delegation(struct nfs4_delegation *dp);
 extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
                                                        struct nfsd_net *nn);
 extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn);
 
+/* grace period management */
+void nfsd4_end_grace(struct nfsd_net *nn);
+
 /* nfs4recover operations */
 extern int nfsd4_client_tracking_init(struct net *net);
 extern void nfsd4_client_tracking_exit(struct net *net);
 extern void nfsd4_client_record_create(struct nfs4_client *clp);
 extern void nfsd4_client_record_remove(struct nfs4_client *clp);
 extern int nfsd4_client_record_check(struct nfs4_client *clp);
-extern void nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time);
+extern void nfsd4_record_grace_done(struct nfsd_net *nn);
 
 /* nfs fault injection functions */
 #ifdef CONFIG_NFSD_FAULT_INJECTION
 int nfsd_fault_inject_init(void);
 void nfsd_fault_inject_cleanup(void);
-u64 nfsd_for_n_state(u64, u64 (*)(struct nfs4_client *, u64));
-struct nfs4_client *nfsd_find_client(struct sockaddr_storage *, size_t);
-
-u64 nfsd_forget_client(struct nfs4_client *, u64);
-u64 nfsd_forget_client_locks(struct nfs4_client*, u64);
-u64 nfsd_forget_client_openowners(struct nfs4_client *, u64);
-u64 nfsd_forget_client_delegations(struct nfs4_client *, u64);
-u64 nfsd_recall_client_delegations(struct nfs4_client *, u64);
-
-u64 nfsd_print_client(struct nfs4_client *, u64);
-u64 nfsd_print_client_locks(struct nfs4_client *, u64);
-u64 nfsd_print_client_openowners(struct nfs4_client *, u64);
-u64 nfsd_print_client_delegations(struct nfs4_client *, u64);
+
+u64 nfsd_inject_print_clients(void);
+u64 nfsd_inject_forget_client(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_clients(u64);
+
+u64 nfsd_inject_print_locks(void);
+u64 nfsd_inject_forget_client_locks(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_locks(u64);
+
+u64 nfsd_inject_print_openowners(void);
+u64 nfsd_inject_forget_client_openowners(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_openowners(u64);
+
+u64 nfsd_inject_print_delegations(void);
+u64 nfsd_inject_forget_client_delegations(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_forget_delegations(u64);
+u64 nfsd_inject_recall_client_delegations(struct sockaddr_storage *, size_t);
+u64 nfsd_inject_recall_delegations(u64);
 #else /* CONFIG_NFSD_FAULT_INJECTION */
 static inline int nfsd_fault_inject_init(void) { return 0; }
 static inline void nfsd_fault_inject_cleanup(void) {}