+ * pending lock request messages are kept in this queue which is
+ * kept sorted by transaction ID (xid).
+ */
+uint64_t nfs_lockxid = 0;
+LOCKD_MSG_QUEUE nfs_pendlockq;
+
+/*
+ * This structure is used to identify processes which have acquired NFS locks.
+ * Knowing which processes have ever acquired locks allows us to short-circuit
+ * unlock requests for processes that have never had an NFS file lock. Thus
+ * avoiding a costly and unnecessary lockd request.
+ */
+struct nfs_lock_pid {
+ TAILQ_ENTRY(nfs_lock_pid) lp_lru; /* LRU list */
+ LIST_ENTRY(nfs_lock_pid) lp_hash; /* hash chain */
+ int lp_valid; /* valid entry? */
+ int lp_time; /* last time seen valid */
+ pid_t lp_pid; /* The process ID. */
+ struct timeval lp_pid_start; /* Start time of process id */
+};
+
+#define NFS_LOCK_PID_HASH_SIZE 64 // XXX tune me
+#define NFS_LOCK_PID_HASH(pid) \
+ (&nfs_lock_pid_hash_tbl[(pid) & nfs_lock_pid_hash])
+LIST_HEAD(, nfs_lock_pid) *nfs_lock_pid_hash_tbl;
+TAILQ_HEAD(, nfs_lock_pid) nfs_lock_pid_lru;
+u_long nfs_lock_pid_hash;
+int nfs_lock_pid_lock;
+
+
+/*
+ * initialize global nfs lock state
+ */
+void
+nfs_lockinit(void)
+{
+ TAILQ_INIT(&nfs_pendlockq);
+ nfs_lock_pid_lock = 0;
+ nfs_lock_pid_hash_tbl = hashinit(NFS_LOCK_PID_HASH_SIZE,
+ M_TEMP, &nfs_lock_pid_hash);
+ TAILQ_INIT(&nfs_lock_pid_lru);
+}
+
+/*
+ * insert a lock request message into the pending queue
+ */
+static inline void
+nfs_lockdmsg_enqueue(LOCKD_MSG_REQUEST *msgreq)
+{
+ LOCKD_MSG_REQUEST *mr;
+
+ mr = TAILQ_LAST(&nfs_pendlockq, nfs_lock_msg_queue);
+ if (!mr || (msgreq->lmr_msg.lm_xid > mr->lmr_msg.lm_xid)) {
+ /* fast path: empty queue or new largest xid */
+ TAILQ_INSERT_TAIL(&nfs_pendlockq, msgreq, lmr_next);
+ return;
+ }
+ /* slow path: need to walk list to find insertion point */
+ while (mr && (msgreq->lmr_msg.lm_xid > mr->lmr_msg.lm_xid)) {
+ mr = TAILQ_PREV(mr, nfs_lock_msg_queue, lmr_next);
+ }
+ if (mr) {
+ TAILQ_INSERT_AFTER(&nfs_pendlockq, mr, msgreq, lmr_next);
+ } else {
+ TAILQ_INSERT_HEAD(&nfs_pendlockq, msgreq, lmr_next);
+ }
+}
+
+/*
+ * remove a lock request message from the pending queue
+ */
+static inline void
+nfs_lockdmsg_dequeue(LOCKD_MSG_REQUEST *msgreq)
+{
+ TAILQ_REMOVE(&nfs_pendlockq, msgreq, lmr_next);
+}
+
+/*
+ * find a pending lock request message by xid
+ *
+ * We search from the head of the list assuming that the message we're
+ * looking for is for an older request (because we have an answer to it).
+ * This assumes that lock request will be answered primarily in FIFO order.
+ * However, this may not be the case if there are blocked requests. We may
+ * want to move blocked requests to a separate queue (but that'll complicate
+ * duplicate xid checking).
+ */
+static inline LOCKD_MSG_REQUEST *
+nfs_lockdmsg_find_by_xid(uint64_t lockxid)
+{
+ LOCKD_MSG_REQUEST *mr;
+
+ TAILQ_FOREACH(mr, &nfs_pendlockq, lmr_next) {
+ if (mr->lmr_msg.lm_xid == lockxid)
+ return mr;
+ if (mr->lmr_msg.lm_xid > lockxid)
+ return NULL;
+ }
+ return mr;
+}
+
+/*
+ * Because we can't depend on nlm_granted messages containing the same
+ * cookie we sent with the original lock request, we need code test if
+ * an nlm_granted answer matches the lock request. We also need code
+ * that can find a lockd message based solely on the nlm_granted answer.
+ */
+
+/*
+ * compare lockd message to answer
+ *
+ * returns 0 on equality and 1 if different
+ */
+static inline int
+nfs_lockdmsg_compare_to_answer(LOCKD_MSG_REQUEST *msgreq, struct lockd_ans *ansp)
+{
+ if (!(ansp->la_flags & LOCKD_ANS_LOCK_INFO))
+ return 1;
+ if (msgreq->lmr_msg.lm_fl.l_pid != ansp->la_pid)
+ return 1;
+ if (msgreq->lmr_msg.lm_fl.l_start != ansp->la_start)
+ return 1;
+ if (msgreq->lmr_msg.lm_fl.l_len != ansp->la_len)
+ return 1;
+ if (msgreq->lmr_msg.lm_fh_len != ansp->la_fh_len)
+ return 1;
+ if (bcmp(msgreq->lmr_msg.lm_fh, ansp->la_fh, ansp->la_fh_len))
+ return 1;
+ return 0;
+}
+
+/*
+ * find a pending lock request message based on the lock info provided
+ * in the lockd_ans/nlm_granted data. We need this because we can't
+ * depend on nlm_granted messages containing the same cookie we sent
+ * with the original lock request.
+ *
+ * We search from the head of the list assuming that the message we're
+ * looking for is for an older request (because we have an answer to it).
+ * This assumes that lock request will be answered primarily in FIFO order.
+ * However, this may not be the case if there are blocked requests. We may
+ * want to move blocked requests to a separate queue (but that'll complicate
+ * duplicate xid checking).
+ */
+static inline LOCKD_MSG_REQUEST *
+nfs_lockdmsg_find_by_answer(struct lockd_ans *ansp)
+{
+ LOCKD_MSG_REQUEST *mr;
+
+ if (!(ansp->la_flags & LOCKD_ANS_LOCK_INFO))
+ return NULL;
+ TAILQ_FOREACH(mr, &nfs_pendlockq, lmr_next) {
+ if (!nfs_lockdmsg_compare_to_answer(mr, ansp))
+ break;
+ }
+ return mr;
+}
+
+/*
+ * return the next unique lock request transaction ID