nfsd: make client_lock per net
[pandora-kernel.git] / fs / nfsd / nfs4state.c
index 996a8a5..0e7e174 100644 (file)
@@ -190,7 +190,7 @@ static struct list_head file_hashtbl[FILE_HASH_SIZE];
 
 static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag)
 {
-       BUG_ON(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR]));
+       WARN_ON_ONCE(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR]));
        atomic_inc(&fp->fi_access[oflag]);
 }
 
@@ -249,7 +249,7 @@ static inline int get_new_stid(struct nfs4_stid *stid)
         * preallocations that can exist at a time, but the state lock
         * prevents anyone from using ours before we get here:
         */
-       BUG_ON(error);
+       WARN_ON_ONCE(error);
        /*
         * It shouldn't be a problem to reuse an opaque stateid value.
         * I don't think it is for 4.1.  But with 4.0 I worry that, for
@@ -388,9 +388,6 @@ unhash_delegation(struct nfs4_delegation *dp)
  * SETCLIENTID state 
  */
 
-/* client_lock protects the client lru list and session hash table */
-static DEFINE_SPINLOCK(client_lock);
-
 static unsigned int clientid_hashval(u32 id)
 {
        return id & CLIENT_HASH_MASK;
@@ -494,7 +491,8 @@ static int nfs4_access_to_omode(u32 access)
        case NFS4_SHARE_ACCESS_BOTH:
                return O_RDWR;
        }
-       BUG();
+       WARN_ON_ONCE(1);
+       return O_RDONLY;
 }
 
 /* release all access and file references for a given stateid */
@@ -871,18 +869,23 @@ static void __free_session(struct nfsd4_session *ses)
 static void free_session(struct kref *kref)
 {
        struct nfsd4_session *ses;
+       struct nfsd_net *nn;
 
-       lockdep_assert_held(&client_lock);
        ses = container_of(kref, struct nfsd4_session, se_ref);
+       nn = net_generic(ses->se_client->net, nfsd_net_id);
+
+       lockdep_assert_held(&nn->client_lock);
        nfsd4_del_conns(ses);
        __free_session(ses);
 }
 
 void nfsd4_put_session(struct nfsd4_session *ses)
 {
-       spin_lock(&client_lock);
+       struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id);
+
+       spin_lock(&nn->client_lock);
        nfsd4_put_session_locked(ses);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
 }
 
 static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan)
@@ -926,12 +929,12 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
        new->se_cb_sec = cses->cb_sec;
        kref_init(&new->se_ref);
        idx = hash_sessionid(&new->se_sessionid);
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
        spin_lock(&clp->cl_lock);
        list_add(&new->se_perclnt, &clp->cl_sessions);
        spin_unlock(&clp->cl_lock);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
 
        if (cses->flags & SESSION4_BACK_CHAN) {
                struct sockaddr *sa = svc_addr(rqstp);
@@ -1004,9 +1007,11 @@ renew_client_locked(struct nfs4_client *clp)
 static inline void
 renew_client(struct nfs4_client *clp)
 {
-       spin_lock(&client_lock);
+       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+       spin_lock(&nn->client_lock);
        renew_client_locked(clp);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
 }
 
 /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
@@ -1044,7 +1049,9 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
 static inline void
 free_client(struct nfs4_client *clp)
 {
-       lockdep_assert_held(&client_lock);
+       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+       lockdep_assert_held(&nn->client_lock);
        while (!list_empty(&clp->cl_sessions)) {
                struct nfsd4_session *ses;
                ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
@@ -1061,15 +1068,16 @@ void
 release_session_client(struct nfsd4_session *session)
 {
        struct nfs4_client *clp = session->se_client;
+       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
 
-       if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock))
+       if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock))
                return;
        if (is_client_expired(clp)) {
                free_client(clp);
                session->se_client = NULL;
        } else
                renew_client_locked(clp);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
 }
 
 /* must be called under the client_lock */
@@ -1118,11 +1126,11 @@ destroy_client(struct nfs4_client *clp)
                rb_erase(&clp->cl_namenode, &nn->conf_name_tree);
        else
                rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        unhash_client_locked(clp);
        if (atomic_read(&clp->cl_refcount) == 0)
                free_client(clp);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
 }
 
 static void expire_client(struct nfs4_client *clp)
@@ -1273,6 +1281,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
        struct sockaddr *sa = svc_addr(rqstp);
        int ret;
        struct net *net = SVC_NET(rqstp);
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
        clp = alloc_client(name);
        if (clp == NULL)
@@ -1281,9 +1290,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
        INIT_LIST_HEAD(&clp->cl_sessions);
        ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
        if (ret) {
-               spin_lock(&client_lock);
+               spin_lock(&nn->client_lock);
                free_client(clp);
-               spin_unlock(&client_lock);
+               spin_unlock(&nn->client_lock);
                return NULL;
        }
        idr_init(&clp->cl_stateids);
@@ -1605,10 +1614,9 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
        switch (exid->spa_how) {
        case SP4_NONE:
                break;
+       default:                                /* checked by xdr code */
+               WARN_ON_ONCE(1);
        case SP4_SSV:
-               return nfserr_serverfault;
-       default:
-               BUG();                          /* checked by xdr code */
        case SP4_MACH_CRED:
                return nfserr_serverfault;      /* no excuse :-/ */
        }
@@ -1873,11 +1881,12 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir)
 __be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc)
 {
        struct nfsd4_session *session = cstate->session;
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        session->se_cb_prog = bc->bc_cb_program;
        session->se_cb_sec = bc->bc_cb_sec;
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
 
        nfsd4_probe_callback(session->se_client);
 
@@ -1890,10 +1899,11 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
 {
        __be32 status;
        struct nfsd4_conn *conn;
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 
        if (!nfsd4_last_compound_op(rqstp))
                return nfserr_not_only_op;
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp));
        /* Sorta weird: we only need the refcnt'ing because new_conn acquires
         * client_lock iself: */
@@ -1901,7 +1911,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
                nfsd4_get_session(cstate->session);
                atomic_inc(&cstate->session->se_client->cl_refcount);
        }
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
        if (!cstate->session)
                return nfserr_badsession;
 
@@ -1929,6 +1939,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
 {
        struct nfsd4_session *ses;
        __be32 status = nfserr_badsession;
+       struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id);
 
        /* Notes:
         * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
@@ -1942,24 +1953,24 @@ nfsd4_destroy_session(struct svc_rqst *r,
                        return nfserr_not_only_op;
        }
        dump_sessionid(__func__, &sessionid->sessionid);
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r));
        if (!ses) {
-               spin_unlock(&client_lock);
+               spin_unlock(&nn->client_lock);
                goto out;
        }
 
        unhash_session(ses);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
 
        nfs4_lock_state();
        nfsd4_probe_callback_sync(ses->se_client);
        nfs4_unlock_state();
 
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        nfsd4_del_conns(ses);
        nfsd4_put_session_locked(ses);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
        status = nfs_ok;
 out:
        dprintk("%s returns %d\n", __func__, ntohl(status));
@@ -2025,6 +2036,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        struct nfsd4_slot *slot;
        struct nfsd4_conn *conn;
        __be32 status;
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 
        if (resp->opcnt != 1)
                return nfserr_sequence_pos;
@@ -2037,7 +2049,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        if (!conn)
                return nfserr_jukebox;
 
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        status = nfserr_badsession;
        session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp));
        if (!session)
@@ -2113,7 +2125,7 @@ out:
                }
        }
        kfree(conn);
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
        dprintk("%s: return %d\n", __func__, ntohl(status));
        return status;
 }
@@ -2912,7 +2924,7 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
                        open->op_why_no_deleg = WND4_CANCELLED;
                        break;
                case NFS4_SHARE_WANT_NO_DELEG:
-                       BUG();  /* not supposed to get here */
+                       WARN_ON_ONCE(1);
                }
        }
 }
@@ -3157,17 +3169,15 @@ out:
 }
 
 static void
-nfsd4_end_grace(struct net *net)
+nfsd4_end_grace(struct nfsd_net *nn)
 {
-       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
-
        /* do nothing if grace period already ended */
        if (nn->grace_ended)
                return;
 
        dprintk("NFSD: end of grace period\n");
        nn->grace_ended = true;
-       nfsd4_record_grace_done(net, nn->boot_time);
+       nfsd4_record_grace_done(nn, nn->boot_time);
        locks_end_grace(&nn->nfsd4_manager);
        /*
         * Now that every NFSv4 client has had the chance to recover and
@@ -3178,7 +3188,7 @@ nfsd4_end_grace(struct net *net)
 }
 
 static time_t
-nfs4_laundromat(void)
+nfs4_laundromat(struct nfsd_net *nn)
 {
        struct nfs4_client *clp;
        struct nfs4_openowner *oo;
@@ -3187,14 +3197,13 @@ nfs4_laundromat(void)
        time_t cutoff = get_seconds() - nfsd4_lease;
        time_t t, clientid_val = nfsd4_lease;
        time_t u, test_val = nfsd4_lease;
-       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
 
        nfs4_lock_state();
 
        dprintk("NFSD: laundromat service - starting\n");
-       nfsd4_end_grace(&init_net);
+       nfsd4_end_grace(nn);
        INIT_LIST_HEAD(&reaplist);
-       spin_lock(&client_lock);
+       spin_lock(&nn->client_lock);
        list_for_each_safe(pos, next, &nn->client_lru) {
                clp = list_entry(pos, struct nfs4_client, cl_lru);
                if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
@@ -3211,7 +3220,7 @@ nfs4_laundromat(void)
                unhash_client_locked(clp);
                list_add(&clp->cl_lru, &reaplist);
        }
-       spin_unlock(&client_lock);
+       spin_unlock(&nn->client_lock);
        list_for_each_safe(pos, next, &reaplist) {
                clp = list_entry(pos, struct nfs4_client, cl_lru);
                dprintk("NFSD: purging unused client (clientid %08x)\n",
@@ -3253,16 +3262,19 @@ nfs4_laundromat(void)
 
 static struct workqueue_struct *laundry_wq;
 static void laundromat_main(struct work_struct *);
-static DECLARE_DELAYED_WORK(laundromat_work, laundromat_main);
 
 static void
-laundromat_main(struct work_struct *not_used)
+laundromat_main(struct work_struct *laundry)
 {
        time_t t;
+       struct delayed_work *dwork = container_of(laundry, struct delayed_work,
+                                                 work);
+       struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
+                                          laundromat_work);
 
-       t = nfs4_laundromat();
+       t = nfs4_laundromat(nn);
        dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t);
-       queue_delayed_work(laundry_wq, &laundromat_work, t*HZ);
+       queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ);
 }
 
 static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp)
@@ -3466,7 +3478,11 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
                        goto out;
                if (filpp) {
                        *filpp = dp->dl_file->fi_deleg_file;
-                       BUG_ON(!*filpp);
+                       if (!*filpp) {
+                               WARN_ON_ONCE(1);
+                               status = nfserr_serverfault;
+                               goto out;
+                       }
                }
                break;
        case NFS4_OPEN_STID:
@@ -3693,7 +3709,7 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac
        case NFS4_SHARE_ACCESS_BOTH:
                break;
        default:
-               BUG();
+               WARN_ON_ONCE(1);
        }
 }
 
@@ -3882,7 +3898,7 @@ last_byte_offset(u64 start, u64 len)
 {
        u64 end;
 
-       BUG_ON(!len);
+       WARN_ON_ONCE(!len);
        end = start + len;
        return end > start ? end - 1: NFS4_MAX_UINT64;
 }
@@ -4552,7 +4568,7 @@ nfs4_release_reclaim(struct nfsd_net *nn)
                        nfs4_remove_reclaim_record(crp, nn);
                }
        }
-       BUG_ON(nn->reclaim_str_hashtbl_size);
+       WARN_ON_ONCE(nn->reclaim_str_hashtbl_size);
 }
 
 /*
@@ -4792,6 +4808,9 @@ static int nfs4_state_start_net(struct net *net)
        nn->unconf_name_tree = RB_ROOT;
        INIT_LIST_HEAD(&nn->client_lru);
        INIT_LIST_HEAD(&nn->close_lru);
+       spin_lock_init(&nn->client_lock);
+
+       INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
 
        return 0;
 
@@ -4877,7 +4896,8 @@ nfs4_state_start(void)
        ret = nfsd4_create_callback_queue();
        if (ret)
                goto out_free_laundry;
-       queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ);
+
+       queue_delayed_work(laundry_wq, &nn->laundromat_work, nfsd4_grace * HZ);
        set_max_delegations();
        return 0;
 out_free_laundry:
@@ -4920,12 +4940,10 @@ nfs4_state_shutdown(void)
        struct net *net = &init_net;
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
-       cancel_delayed_work_sync(&laundromat_work);
+       cancel_delayed_work_sync(&nn->laundromat_work);
        destroy_workqueue(laundry_wq);
        locks_end_grace(&nn->nfsd4_manager);
-       nfs4_lock_state();
        __nfs4_state_shutdown(net);
-       nfs4_unlock_state();
        nfsd4_destroy_callback_queue();
 }