diff options
Diffstat (limited to 'fs/nfsd/nfs4state.c')
| -rw-r--r-- | fs/nfsd/nfs4state.c | 351 |
1 files changed, 196 insertions, 155 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 48a1bad37334..d0237f872cc4 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
| @@ -758,7 +758,7 @@ static void nfsd4_put_drc_mem(int slotsize, int num) | |||
| 758 | spin_unlock(&nfsd_drc_lock); | 758 | spin_unlock(&nfsd_drc_lock); |
| 759 | } | 759 | } |
| 760 | 760 | ||
| 761 | static struct nfsd4_session *alloc_session(int slotsize, int numslots) | 761 | static struct nfsd4_session *__alloc_session(int slotsize, int numslots) |
| 762 | { | 762 | { |
| 763 | struct nfsd4_session *new; | 763 | struct nfsd4_session *new; |
| 764 | int mem, i; | 764 | int mem, i; |
| @@ -852,35 +852,28 @@ static int nfsd4_register_conn(struct nfsd4_conn *conn) | |||
| 852 | return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user); | 852 | return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user); |
| 853 | } | 853 | } |
| 854 | 854 | ||
| 855 | static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, u32 dir) | 855 | static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses) |
| 856 | { | 856 | { |
| 857 | struct nfsd4_conn *conn; | ||
| 858 | int ret; | 857 | int ret; |
| 859 | 858 | ||
| 860 | conn = alloc_conn(rqstp, dir); | ||
| 861 | if (!conn) | ||
| 862 | return nfserr_jukebox; | ||
| 863 | nfsd4_hash_conn(conn, ses); | 859 | nfsd4_hash_conn(conn, ses); |
| 864 | ret = nfsd4_register_conn(conn); | 860 | ret = nfsd4_register_conn(conn); |
| 865 | if (ret) | 861 | if (ret) |
| 866 | /* oops; xprt is already down: */ | 862 | /* oops; xprt is already down: */ |
| 867 | nfsd4_conn_lost(&conn->cn_xpt_user); | 863 | nfsd4_conn_lost(&conn->cn_xpt_user); |
| 868 | if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN && | 864 | if (conn->cn_flags & NFS4_CDFC4_BACK) { |
| 869 | dir & NFS4_CDFC4_BACK) { | ||
| 870 | /* callback channel may be back up */ | 865 | /* callback channel may be back up */ |
| 871 | nfsd4_probe_callback(ses->se_client); | 866 | nfsd4_probe_callback(ses->se_client); |
| 872 | } | 867 | } |
| 873 | return nfs_ok; | ||
| 874 | } | 868 | } |
| 875 | 869 | ||
| 876 | static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses) | 870 | static struct nfsd4_conn *alloc_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_create_session *cses) |
| 877 | { | 871 | { |
| 878 | u32 dir = NFS4_CDFC4_FORE; | 872 | u32 dir = NFS4_CDFC4_FORE; |
| 879 | 873 | ||
| 880 | if (ses->se_flags & SESSION4_BACK_CHAN) | 874 | if (cses->flags & SESSION4_BACK_CHAN) |
| 881 | dir |= NFS4_CDFC4_BACK; | 875 | dir |= NFS4_CDFC4_BACK; |
| 882 | 876 | return alloc_conn(rqstp, dir); | |
| 883 | return nfsd4_new_conn(rqstp, ses, dir); | ||
| 884 | } | 877 | } |
| 885 | 878 | ||
| 886 | /* must be called under client_lock */ | 879 | /* must be called under client_lock */ |
| @@ -903,20 +896,21 @@ static void nfsd4_del_conns(struct nfsd4_session *s) | |||
| 903 | spin_unlock(&clp->cl_lock); | 896 | spin_unlock(&clp->cl_lock); |
| 904 | } | 897 | } |
| 905 | 898 | ||
| 899 | static void __free_session(struct nfsd4_session *ses) | ||
| 900 | { | ||
| 901 | nfsd4_put_drc_mem(slot_bytes(&ses->se_fchannel), ses->se_fchannel.maxreqs); | ||
| 902 | free_session_slots(ses); | ||
| 903 | kfree(ses); | ||
| 904 | } | ||
| 905 | |||
| 906 | static void free_session(struct kref *kref) | 906 | static void free_session(struct kref *kref) |
| 907 | { | 907 | { |
| 908 | struct nfsd4_session *ses; | 908 | struct nfsd4_session *ses; |
| 909 | int mem; | ||
| 910 | 909 | ||
| 911 | lockdep_assert_held(&client_lock); | 910 | lockdep_assert_held(&client_lock); |
| 912 | ses = container_of(kref, struct nfsd4_session, se_ref); | 911 | ses = container_of(kref, struct nfsd4_session, se_ref); |
| 913 | nfsd4_del_conns(ses); | 912 | nfsd4_del_conns(ses); |
| 914 | spin_lock(&nfsd_drc_lock); | 913 | __free_session(ses); |
| 915 | mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel); | ||
| 916 | nfsd_drc_mem_used -= mem; | ||
| 917 | spin_unlock(&nfsd_drc_lock); | ||
| 918 | free_session_slots(ses); | ||
| 919 | kfree(ses); | ||
| 920 | } | 914 | } |
| 921 | 915 | ||
| 922 | void nfsd4_put_session(struct nfsd4_session *ses) | 916 | void nfsd4_put_session(struct nfsd4_session *ses) |
| @@ -926,14 +920,10 @@ void nfsd4_put_session(struct nfsd4_session *ses) | |||
| 926 | spin_unlock(&client_lock); | 920 | spin_unlock(&client_lock); |
| 927 | } | 921 | } |
| 928 | 922 | ||
| 929 | static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) | 923 | static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) |
| 930 | { | 924 | { |
| 931 | struct nfsd4_session *new; | 925 | struct nfsd4_session *new; |
| 932 | struct nfsd4_channel_attrs *fchan = &cses->fore_channel; | ||
| 933 | int numslots, slotsize; | 926 | int numslots, slotsize; |
| 934 | __be32 status; | ||
| 935 | int idx; | ||
| 936 | |||
| 937 | /* | 927 | /* |
| 938 | * Note decreasing slot size below client's request may | 928 | * Note decreasing slot size below client's request may |
| 939 | * make it difficult for client to function correctly, whereas | 929 | * make it difficult for client to function correctly, whereas |
| @@ -946,12 +936,18 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n | |||
| 946 | if (numslots < 1) | 936 | if (numslots < 1) |
| 947 | return NULL; | 937 | return NULL; |
| 948 | 938 | ||
| 949 | new = alloc_session(slotsize, numslots); | 939 | new = __alloc_session(slotsize, numslots); |
| 950 | if (!new) { | 940 | if (!new) { |
| 951 | nfsd4_put_drc_mem(slotsize, fchan->maxreqs); | 941 | nfsd4_put_drc_mem(slotsize, fchan->maxreqs); |
| 952 | return NULL; | 942 | return NULL; |
| 953 | } | 943 | } |
| 954 | init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); | 944 | init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); |
| 945 | return new; | ||
| 946 | } | ||
| 947 | |||
| 948 | static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) | ||
| 949 | { | ||
| 950 | int idx; | ||
| 955 | 951 | ||
| 956 | new->se_client = clp; | 952 | new->se_client = clp; |
| 957 | gen_sessionid(new); | 953 | gen_sessionid(new); |
| @@ -970,14 +966,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n | |||
| 970 | spin_unlock(&clp->cl_lock); | 966 | spin_unlock(&clp->cl_lock); |
| 971 | spin_unlock(&client_lock); | 967 | spin_unlock(&client_lock); |
| 972 | 968 | ||
| 973 | status = nfsd4_new_conn_from_crses(rqstp, new); | ||
| 974 | /* whoops: benny points out, status is ignored! (err, or bogus) */ | ||
| 975 | if (status) { | ||
| 976 | spin_lock(&client_lock); | ||
| 977 | free_session(&new->se_ref); | ||
| 978 | spin_unlock(&client_lock); | ||
| 979 | return NULL; | ||
| 980 | } | ||
| 981 | if (cses->flags & SESSION4_BACK_CHAN) { | 969 | if (cses->flags & SESSION4_BACK_CHAN) { |
| 982 | struct sockaddr *sa = svc_addr(rqstp); | 970 | struct sockaddr *sa = svc_addr(rqstp); |
| 983 | /* | 971 | /* |
| @@ -990,7 +978,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n | |||
| 990 | rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); | 978 | rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); |
| 991 | clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); | 979 | clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); |
| 992 | } | 980 | } |
| 993 | nfsd4_probe_callback(clp); | ||
| 994 | return new; | 981 | return new; |
| 995 | } | 982 | } |
| 996 | 983 | ||
| @@ -1131,7 +1118,7 @@ unhash_client_locked(struct nfs4_client *clp) | |||
| 1131 | } | 1118 | } |
| 1132 | 1119 | ||
| 1133 | static void | 1120 | static void |
| 1134 | expire_client(struct nfs4_client *clp) | 1121 | destroy_client(struct nfs4_client *clp) |
| 1135 | { | 1122 | { |
| 1136 | struct nfs4_openowner *oo; | 1123 | struct nfs4_openowner *oo; |
| 1137 | struct nfs4_delegation *dp; | 1124 | struct nfs4_delegation *dp; |
| @@ -1165,6 +1152,12 @@ expire_client(struct nfs4_client *clp) | |||
| 1165 | spin_unlock(&client_lock); | 1152 | spin_unlock(&client_lock); |
| 1166 | } | 1153 | } |
| 1167 | 1154 | ||
| 1155 | static void expire_client(struct nfs4_client *clp) | ||
| 1156 | { | ||
| 1157 | nfsd4_client_record_remove(clp); | ||
| 1158 | destroy_client(clp); | ||
| 1159 | } | ||
| 1160 | |||
| 1168 | static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) | 1161 | static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) |
| 1169 | { | 1162 | { |
| 1170 | memcpy(target->cl_verifier.data, source->data, | 1163 | memcpy(target->cl_verifier.data, source->data, |
| @@ -1223,10 +1216,26 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2) | |||
| 1223 | return true; | 1216 | return true; |
| 1224 | } | 1217 | } |
| 1225 | 1218 | ||
| 1219 | /* | ||
| 1220 | * RFC 3530 language requires clid_inuse be returned when the | ||
| 1221 | * "principal" associated with a requests differs from that previously | ||
| 1222 | * used. We use uid, gid's, and gss principal string as our best | ||
| 1223 | * approximation. We also don't want to allow non-gss use of a client | ||
| 1224 | * established using gss: in theory cr_principal should catch that | ||
| 1225 | * change, but in practice cr_principal can be null even in the gss case | ||
| 1226 | * since gssd doesn't always pass down a principal string. | ||
| 1227 | */ | ||
| 1228 | static bool is_gss_cred(struct svc_cred *cr) | ||
| 1229 | { | ||
| 1230 | /* Is cr_flavor one of the gss "pseudoflavors"?: */ | ||
| 1231 | return (cr->cr_flavor > RPC_AUTH_MAXFLAVOR); | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | |||
| 1226 | static bool | 1235 | static bool |
| 1227 | same_creds(struct svc_cred *cr1, struct svc_cred *cr2) | 1236 | same_creds(struct svc_cred *cr1, struct svc_cred *cr2) |
| 1228 | { | 1237 | { |
| 1229 | if ((cr1->cr_flavor != cr2->cr_flavor) | 1238 | if ((is_gss_cred(cr1) != is_gss_cred(cr2)) |
| 1230 | || (cr1->cr_uid != cr2->cr_uid) | 1239 | || (cr1->cr_uid != cr2->cr_uid) |
| 1231 | || (cr1->cr_gid != cr2->cr_gid) | 1240 | || (cr1->cr_gid != cr2->cr_gid) |
| 1232 | || !groups_equal(cr1->cr_group_info, cr2->cr_group_info)) | 1241 | || !groups_equal(cr1->cr_group_info, cr2->cr_group_info)) |
| @@ -1340,13 +1349,15 @@ move_to_confirmed(struct nfs4_client *clp) | |||
| 1340 | } | 1349 | } |
| 1341 | 1350 | ||
| 1342 | static struct nfs4_client * | 1351 | static struct nfs4_client * |
| 1343 | find_confirmed_client(clientid_t *clid) | 1352 | find_confirmed_client(clientid_t *clid, bool sessions) |
| 1344 | { | 1353 | { |
| 1345 | struct nfs4_client *clp; | 1354 | struct nfs4_client *clp; |
| 1346 | unsigned int idhashval = clientid_hashval(clid->cl_id); | 1355 | unsigned int idhashval = clientid_hashval(clid->cl_id); |
| 1347 | 1356 | ||
| 1348 | list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { | 1357 | list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { |
| 1349 | if (same_clid(&clp->cl_clientid, clid)) { | 1358 | if (same_clid(&clp->cl_clientid, clid)) { |
| 1359 | if ((bool)clp->cl_minorversion != sessions) | ||
| 1360 | return NULL; | ||
| 1350 | renew_client(clp); | 1361 | renew_client(clp); |
| 1351 | return clp; | 1362 | return clp; |
| 1352 | } | 1363 | } |
| @@ -1355,14 +1366,17 @@ find_confirmed_client(clientid_t *clid) | |||
| 1355 | } | 1366 | } |
| 1356 | 1367 | ||
| 1357 | static struct nfs4_client * | 1368 | static struct nfs4_client * |
| 1358 | find_unconfirmed_client(clientid_t *clid) | 1369 | find_unconfirmed_client(clientid_t *clid, bool sessions) |
| 1359 | { | 1370 | { |
| 1360 | struct nfs4_client *clp; | 1371 | struct nfs4_client *clp; |
| 1361 | unsigned int idhashval = clientid_hashval(clid->cl_id); | 1372 | unsigned int idhashval = clientid_hashval(clid->cl_id); |
| 1362 | 1373 | ||
| 1363 | list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { | 1374 | list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { |
| 1364 | if (same_clid(&clp->cl_clientid, clid)) | 1375 | if (same_clid(&clp->cl_clientid, clid)) { |
| 1376 | if ((bool)clp->cl_minorversion != sessions) | ||
| 1377 | return NULL; | ||
| 1365 | return clp; | 1378 | return clp; |
| 1379 | } | ||
| 1366 | } | 1380 | } |
| 1367 | return NULL; | 1381 | return NULL; |
| 1368 | } | 1382 | } |
| @@ -1651,6 +1665,7 @@ out_new: | |||
| 1651 | status = nfserr_jukebox; | 1665 | status = nfserr_jukebox; |
| 1652 | goto out; | 1666 | goto out; |
| 1653 | } | 1667 | } |
| 1668 | new->cl_minorversion = 1; | ||
| 1654 | 1669 | ||
| 1655 | gen_clid(new); | 1670 | gen_clid(new); |
| 1656 | add_to_unconfirmed(new, strhashval); | 1671 | add_to_unconfirmed(new, strhashval); |
| @@ -1743,67 +1758,71 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
| 1743 | struct sockaddr *sa = svc_addr(rqstp); | 1758 | struct sockaddr *sa = svc_addr(rqstp); |
| 1744 | struct nfs4_client *conf, *unconf; | 1759 | struct nfs4_client *conf, *unconf; |
| 1745 | struct nfsd4_session *new; | 1760 | struct nfsd4_session *new; |
| 1761 | struct nfsd4_conn *conn; | ||
| 1746 | struct nfsd4_clid_slot *cs_slot = NULL; | 1762 | struct nfsd4_clid_slot *cs_slot = NULL; |
| 1747 | bool confirm_me = false; | ||
| 1748 | __be32 status = 0; | 1763 | __be32 status = 0; |
| 1749 | 1764 | ||
| 1750 | if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) | 1765 | if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) |
| 1751 | return nfserr_inval; | 1766 | return nfserr_inval; |
| 1767 | if (check_forechannel_attrs(cr_ses->fore_channel)) | ||
| 1768 | return nfserr_toosmall; | ||
| 1769 | new = alloc_session(&cr_ses->fore_channel); | ||
| 1770 | if (!new) | ||
| 1771 | return nfserr_jukebox; | ||
| 1772 | status = nfserr_jukebox; | ||
| 1773 | conn = alloc_conn_from_crses(rqstp, cr_ses); | ||
| 1774 | if (!conn) | ||
| 1775 | goto out_free_session; | ||
| 1752 | 1776 | ||
| 1753 | nfs4_lock_state(); | 1777 | nfs4_lock_state(); |
| 1754 | unconf = find_unconfirmed_client(&cr_ses->clientid); | 1778 | unconf = find_unconfirmed_client(&cr_ses->clientid, true); |
| 1755 | conf = find_confirmed_client(&cr_ses->clientid); | 1779 | conf = find_confirmed_client(&cr_ses->clientid, true); |
| 1756 | 1780 | ||
| 1757 | if (conf) { | 1781 | if (conf) { |
| 1758 | cs_slot = &conf->cl_cs_slot; | 1782 | cs_slot = &conf->cl_cs_slot; |
| 1759 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); | 1783 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); |
| 1760 | if (status == nfserr_replay_cache) { | 1784 | if (status == nfserr_replay_cache) { |
| 1761 | status = nfsd4_replay_create_session(cr_ses, cs_slot); | 1785 | status = nfsd4_replay_create_session(cr_ses, cs_slot); |
| 1762 | goto out; | 1786 | goto out_free_conn; |
| 1763 | } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) { | 1787 | } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) { |
| 1764 | status = nfserr_seq_misordered; | 1788 | status = nfserr_seq_misordered; |
| 1765 | goto out; | 1789 | goto out_free_conn; |
| 1766 | } | 1790 | } |
| 1767 | } else if (unconf) { | 1791 | } else if (unconf) { |
| 1792 | unsigned int hash; | ||
| 1793 | struct nfs4_client *old; | ||
| 1768 | if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || | 1794 | if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || |
| 1769 | !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { | 1795 | !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { |
| 1770 | status = nfserr_clid_inuse; | 1796 | status = nfserr_clid_inuse; |
| 1771 | goto out; | 1797 | goto out_free_conn; |
| 1772 | } | 1798 | } |
| 1773 | cs_slot = &unconf->cl_cs_slot; | 1799 | cs_slot = &unconf->cl_cs_slot; |
| 1774 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); | 1800 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); |
| 1775 | if (status) { | 1801 | if (status) { |
| 1776 | /* an unconfirmed replay returns misordered */ | 1802 | /* an unconfirmed replay returns misordered */ |
| 1777 | status = nfserr_seq_misordered; | 1803 | status = nfserr_seq_misordered; |
| 1778 | goto out; | 1804 | goto out_free_conn; |
| 1779 | } | 1805 | } |
| 1780 | confirm_me = true; | 1806 | hash = clientstr_hashval(unconf->cl_recdir); |
| 1807 | old = find_confirmed_client_by_str(unconf->cl_recdir, hash); | ||
| 1808 | if (old) | ||
| 1809 | expire_client(old); | ||
| 1810 | move_to_confirmed(unconf); | ||
| 1781 | conf = unconf; | 1811 | conf = unconf; |
| 1782 | } else { | 1812 | } else { |
| 1783 | status = nfserr_stale_clientid; | 1813 | status = nfserr_stale_clientid; |
| 1784 | goto out; | 1814 | goto out_free_conn; |
| 1785 | } | 1815 | } |
| 1786 | 1816 | status = nfs_ok; | |
| 1787 | /* | ||
| 1788 | * XXX: we should probably set this at creation time, and check | ||
| 1789 | * for consistent minorversion use throughout: | ||
| 1790 | */ | ||
| 1791 | conf->cl_minorversion = 1; | ||
| 1792 | /* | 1817 | /* |
| 1793 | * We do not support RDMA or persistent sessions | 1818 | * We do not support RDMA or persistent sessions |
| 1794 | */ | 1819 | */ |
| 1795 | cr_ses->flags &= ~SESSION4_PERSIST; | 1820 | cr_ses->flags &= ~SESSION4_PERSIST; |
| 1796 | cr_ses->flags &= ~SESSION4_RDMA; | 1821 | cr_ses->flags &= ~SESSION4_RDMA; |
| 1797 | 1822 | ||
| 1798 | status = nfserr_toosmall; | 1823 | init_session(rqstp, new, conf, cr_ses); |
| 1799 | if (check_forechannel_attrs(cr_ses->fore_channel)) | 1824 | nfsd4_init_conn(rqstp, conn, new); |
| 1800 | goto out; | ||
| 1801 | 1825 | ||
| 1802 | status = nfserr_jukebox; | ||
| 1803 | new = alloc_init_session(rqstp, conf, cr_ses); | ||
| 1804 | if (!new) | ||
| 1805 | goto out; | ||
| 1806 | status = nfs_ok; | ||
| 1807 | memcpy(cr_ses->sessionid.data, new->se_sessionid.data, | 1826 | memcpy(cr_ses->sessionid.data, new->se_sessionid.data, |
| 1808 | NFS4_MAX_SESSIONID_LEN); | 1827 | NFS4_MAX_SESSIONID_LEN); |
| 1809 | memcpy(&cr_ses->fore_channel, &new->se_fchannel, | 1828 | memcpy(&cr_ses->fore_channel, &new->se_fchannel, |
| @@ -1813,18 +1832,15 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
| 1813 | 1832 | ||
| 1814 | /* cache solo and embedded create sessions under the state lock */ | 1833 | /* cache solo and embedded create sessions under the state lock */ |
| 1815 | nfsd4_cache_create_session(cr_ses, cs_slot, status); | 1834 | nfsd4_cache_create_session(cr_ses, cs_slot, status); |
| 1816 | if (confirm_me) { | ||
| 1817 | unsigned int hash = clientstr_hashval(unconf->cl_recdir); | ||
| 1818 | struct nfs4_client *old = | ||
| 1819 | find_confirmed_client_by_str(conf->cl_recdir, hash); | ||
| 1820 | if (old) | ||
| 1821 | expire_client(old); | ||
| 1822 | move_to_confirmed(conf); | ||
| 1823 | } | ||
| 1824 | out: | 1835 | out: |
| 1825 | nfs4_unlock_state(); | 1836 | nfs4_unlock_state(); |
| 1826 | dprintk("%s returns %d\n", __func__, ntohl(status)); | 1837 | dprintk("%s returns %d\n", __func__, ntohl(status)); |
| 1827 | return status; | 1838 | return status; |
| 1839 | out_free_conn: | ||
| 1840 | free_conn(conn); | ||
| 1841 | out_free_session: | ||
| 1842 | __free_session(new); | ||
| 1843 | goto out; | ||
| 1828 | } | 1844 | } |
| 1829 | 1845 | ||
| 1830 | static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) | 1846 | static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) |
| @@ -1854,6 +1870,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, | |||
| 1854 | struct nfsd4_bind_conn_to_session *bcts) | 1870 | struct nfsd4_bind_conn_to_session *bcts) |
| 1855 | { | 1871 | { |
| 1856 | __be32 status; | 1872 | __be32 status; |
| 1873 | struct nfsd4_conn *conn; | ||
| 1857 | 1874 | ||
| 1858 | if (!nfsd4_last_compound_op(rqstp)) | 1875 | if (!nfsd4_last_compound_op(rqstp)) |
| 1859 | return nfserr_not_only_op; | 1876 | return nfserr_not_only_op; |
| @@ -1870,9 +1887,13 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, | |||
| 1870 | return nfserr_badsession; | 1887 | return nfserr_badsession; |
| 1871 | 1888 | ||
| 1872 | status = nfsd4_map_bcts_dir(&bcts->dir); | 1889 | status = nfsd4_map_bcts_dir(&bcts->dir); |
| 1873 | if (!status) | 1890 | if (status) |
| 1874 | nfsd4_new_conn(rqstp, cstate->session, bcts->dir); | 1891 | return status; |
| 1875 | return status; | 1892 | conn = alloc_conn(rqstp, bcts->dir); |
| 1893 | if (!conn) | ||
| 1894 | return nfserr_jukebox; | ||
| 1895 | nfsd4_init_conn(rqstp, conn, cstate->session); | ||
| 1896 | return nfs_ok; | ||
| 1876 | } | 1897 | } |
| 1877 | 1898 | ||
| 1878 | static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) | 1899 | static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) |
| @@ -2085,8 +2106,8 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta | |||
| 2085 | __be32 status = 0; | 2106 | __be32 status = 0; |
| 2086 | 2107 | ||
| 2087 | nfs4_lock_state(); | 2108 | nfs4_lock_state(); |
| 2088 | unconf = find_unconfirmed_client(&dc->clientid); | 2109 | unconf = find_unconfirmed_client(&dc->clientid, true); |
| 2089 | conf = find_confirmed_client(&dc->clientid); | 2110 | conf = find_confirmed_client(&dc->clientid, true); |
| 2090 | 2111 | ||
| 2091 | if (conf) { | 2112 | if (conf) { |
| 2092 | clp = conf; | 2113 | clp = conf; |
| @@ -2200,10 +2221,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 2200 | copy_clid(new, conf); | 2221 | copy_clid(new, conf); |
| 2201 | else /* case 4 (new client) or cases 2, 3 (client reboot): */ | 2222 | else /* case 4 (new client) or cases 2, 3 (client reboot): */ |
| 2202 | gen_clid(new); | 2223 | gen_clid(new); |
| 2203 | /* | ||
| 2204 | * XXX: we should probably set this at creation time, and check | ||
| 2205 | * for consistent minorversion use throughout: | ||
| 2206 | */ | ||
| 2207 | new->cl_minorversion = 0; | 2224 | new->cl_minorversion = 0; |
| 2208 | gen_callback(new, setclid, rqstp); | 2225 | gen_callback(new, setclid, rqstp); |
| 2209 | add_to_unconfirmed(new, strhashval); | 2226 | add_to_unconfirmed(new, strhashval); |
| @@ -2232,8 +2249,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
| 2232 | return nfserr_stale_clientid; | 2249 | return nfserr_stale_clientid; |
| 2233 | nfs4_lock_state(); | 2250 | nfs4_lock_state(); |
| 2234 | 2251 | ||
| 2235 | conf = find_confirmed_client(clid); | 2252 | conf = find_confirmed_client(clid, false); |
| 2236 | unconf = find_unconfirmed_client(clid); | 2253 | unconf = find_unconfirmed_client(clid, false); |
| 2237 | /* | 2254 | /* |
| 2238 | * We try hard to give out unique clientid's, so if we get an | 2255 | * We try hard to give out unique clientid's, so if we get an |
| 2239 | * attempt to confirm the same clientid with a different cred, | 2256 | * attempt to confirm the same clientid with a different cred, |
| @@ -2262,10 +2279,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
| 2262 | unsigned int hash = clientstr_hashval(unconf->cl_recdir); | 2279 | unsigned int hash = clientstr_hashval(unconf->cl_recdir); |
| 2263 | 2280 | ||
| 2264 | conf = find_confirmed_client_by_str(unconf->cl_recdir, hash); | 2281 | conf = find_confirmed_client_by_str(unconf->cl_recdir, hash); |
| 2265 | if (conf) { | 2282 | if (conf) |
| 2266 | nfsd4_client_record_remove(conf); | ||
| 2267 | expire_client(conf); | 2283 | expire_client(conf); |
| 2268 | } | ||
| 2269 | move_to_confirmed(unconf); | 2284 | move_to_confirmed(unconf); |
| 2270 | nfsd4_probe_callback(unconf); | 2285 | nfsd4_probe_callback(unconf); |
| 2271 | } | 2286 | } |
| @@ -2447,16 +2462,20 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, | |||
| 2447 | } | 2462 | } |
| 2448 | 2463 | ||
| 2449 | static struct nfs4_openowner * | 2464 | static struct nfs4_openowner * |
| 2450 | find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open) | 2465 | find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, bool sessions) |
| 2451 | { | 2466 | { |
| 2452 | struct nfs4_stateowner *so; | 2467 | struct nfs4_stateowner *so; |
| 2453 | struct nfs4_openowner *oo; | 2468 | struct nfs4_openowner *oo; |
| 2469 | struct nfs4_client *clp; | ||
| 2454 | 2470 | ||
| 2455 | list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { | 2471 | list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { |
| 2456 | if (!so->so_is_open_owner) | 2472 | if (!so->so_is_open_owner) |
| 2457 | continue; | 2473 | continue; |
| 2458 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { | 2474 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { |
| 2459 | oo = openowner(so); | 2475 | oo = openowner(so); |
| 2476 | clp = oo->oo_owner.so_client; | ||
| 2477 | if ((bool)clp->cl_minorversion != sessions) | ||
| 2478 | return NULL; | ||
| 2460 | renew_client(oo->oo_owner.so_client); | 2479 | renew_client(oo->oo_owner.so_client); |
| 2461 | return oo; | 2480 | return oo; |
| 2462 | } | 2481 | } |
| @@ -2600,10 +2619,10 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, | |||
| 2600 | return nfserr_jukebox; | 2619 | return nfserr_jukebox; |
| 2601 | 2620 | ||
| 2602 | strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); | 2621 | strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); |
| 2603 | oo = find_openstateowner_str(strhashval, open); | 2622 | oo = find_openstateowner_str(strhashval, open, cstate->minorversion); |
| 2604 | open->op_openowner = oo; | 2623 | open->op_openowner = oo; |
| 2605 | if (!oo) { | 2624 | if (!oo) { |
| 2606 | clp = find_confirmed_client(clientid); | 2625 | clp = find_confirmed_client(clientid, cstate->minorversion); |
| 2607 | if (clp == NULL) | 2626 | if (clp == NULL) |
| 2608 | return nfserr_expired; | 2627 | return nfserr_expired; |
| 2609 | goto new_owner; | 2628 | goto new_owner; |
| @@ -2705,11 +2724,6 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st | |||
| 2705 | return nfs_ok; | 2724 | return nfs_ok; |
| 2706 | } | 2725 | } |
| 2707 | 2726 | ||
| 2708 | static void nfs4_free_stateid(struct nfs4_ol_stateid *s) | ||
| 2709 | { | ||
| 2710 | kmem_cache_free(stateid_slab, s); | ||
| 2711 | } | ||
| 2712 | |||
| 2713 | static inline int nfs4_access_to_access(u32 nfs4_access) | 2727 | static inline int nfs4_access_to_access(u32 nfs4_access) |
| 2714 | { | 2728 | { |
| 2715 | int flags = 0; | 2729 | int flags = 0; |
| @@ -3087,7 +3101,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) | |||
| 3087 | if (open->op_file) | 3101 | if (open->op_file) |
| 3088 | nfsd4_free_file(open->op_file); | 3102 | nfsd4_free_file(open->op_file); |
| 3089 | if (open->op_stp) | 3103 | if (open->op_stp) |
| 3090 | nfs4_free_stateid(open->op_stp); | 3104 | free_generic_stateid(open->op_stp); |
| 3091 | } | 3105 | } |
| 3092 | 3106 | ||
| 3093 | __be32 | 3107 | __be32 |
| @@ -3104,7 +3118,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 3104 | status = nfserr_stale_clientid; | 3118 | status = nfserr_stale_clientid; |
| 3105 | if (STALE_CLIENTID(clid, nn)) | 3119 | if (STALE_CLIENTID(clid, nn)) |
| 3106 | goto out; | 3120 | goto out; |
| 3107 | clp = find_confirmed_client(clid); | 3121 | clp = find_confirmed_client(clid, cstate->minorversion); |
| 3108 | status = nfserr_expired; | 3122 | status = nfserr_expired; |
| 3109 | if (clp == NULL) { | 3123 | if (clp == NULL) { |
| 3110 | /* We assume the client took too long to RENEW. */ | 3124 | /* We assume the client took too long to RENEW. */ |
| @@ -3180,7 +3194,6 @@ nfs4_laundromat(void) | |||
| 3180 | clp = list_entry(pos, struct nfs4_client, cl_lru); | 3194 | clp = list_entry(pos, struct nfs4_client, cl_lru); |
| 3181 | dprintk("NFSD: purging unused client (clientid %08x)\n", | 3195 | dprintk("NFSD: purging unused client (clientid %08x)\n", |
| 3182 | clp->cl_clientid.cl_id); | 3196 | clp->cl_clientid.cl_id); |
| 3183 | nfsd4_client_record_remove(clp); | ||
| 3184 | expire_client(clp); | 3197 | expire_client(clp); |
| 3185 | } | 3198 | } |
| 3186 | spin_lock(&recall_lock); | 3199 | spin_lock(&recall_lock); |
| @@ -3372,7 +3385,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) | |||
| 3372 | return nfs_ok; | 3385 | return nfs_ok; |
| 3373 | } | 3386 | } |
| 3374 | 3387 | ||
| 3375 | static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s) | 3388 | static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions) |
| 3376 | { | 3389 | { |
| 3377 | struct nfs4_client *cl; | 3390 | struct nfs4_client *cl; |
| 3378 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 3391 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); |
| @@ -3381,7 +3394,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s | |||
| 3381 | return nfserr_bad_stateid; | 3394 | return nfserr_bad_stateid; |
| 3382 | if (STALE_STATEID(stateid, nn)) | 3395 | if (STALE_STATEID(stateid, nn)) |
| 3383 | return nfserr_stale_stateid; | 3396 | return nfserr_stale_stateid; |
| 3384 | cl = find_confirmed_client(&stateid->si_opaque.so_clid); | 3397 | cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions); |
| 3385 | if (!cl) | 3398 | if (!cl) |
| 3386 | return nfserr_expired; | 3399 | return nfserr_expired; |
| 3387 | *s = find_stateid_by_type(cl, stateid, typemask); | 3400 | *s = find_stateid_by_type(cl, stateid, typemask); |
| @@ -3414,7 +3427,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, | |||
| 3414 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) | 3427 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) |
| 3415 | return check_special_stateids(net, current_fh, stateid, flags); | 3428 | return check_special_stateids(net, current_fh, stateid, flags); |
| 3416 | 3429 | ||
| 3417 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s); | 3430 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion); |
| 3418 | if (status) | 3431 | if (status) |
| 3419 | return status; | 3432 | return status; |
| 3420 | status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); | 3433 | status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); |
| @@ -3564,7 +3577,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, | |||
| 3564 | seqid, STATEID_VAL(stateid)); | 3577 | seqid, STATEID_VAL(stateid)); |
| 3565 | 3578 | ||
| 3566 | *stpp = NULL; | 3579 | *stpp = NULL; |
| 3567 | status = nfsd4_lookup_stateid(stateid, typemask, &s); | 3580 | status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion); |
| 3568 | if (status) | 3581 | if (status) |
| 3569 | return status; | 3582 | return status; |
| 3570 | *stpp = openlockstateid(s); | 3583 | *stpp = openlockstateid(s); |
| @@ -3765,6 +3778,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 3765 | memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); | 3778 | memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); |
| 3766 | 3779 | ||
| 3767 | nfsd4_close_open_stateid(stp); | 3780 | nfsd4_close_open_stateid(stp); |
| 3781 | release_last_closed_stateid(oo); | ||
| 3768 | oo->oo_last_closed_stid = stp; | 3782 | oo->oo_last_closed_stid = stp; |
| 3769 | 3783 | ||
| 3770 | if (list_empty(&oo->oo_owner.so_stateids)) { | 3784 | if (list_empty(&oo->oo_owner.so_stateids)) { |
| @@ -3801,7 +3815,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 3801 | inode = cstate->current_fh.fh_dentry->d_inode; | 3815 | inode = cstate->current_fh.fh_dentry->d_inode; |
| 3802 | 3816 | ||
| 3803 | nfs4_lock_state(); | 3817 | nfs4_lock_state(); |
| 3804 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s); | 3818 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion); |
| 3805 | if (status) | 3819 | if (status) |
| 3806 | goto out; | 3820 | goto out; |
| 3807 | dp = delegstateid(s); | 3821 | dp = delegstateid(s); |
| @@ -4045,8 +4059,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4045 | struct nfs4_lockowner *lock_sop = NULL; | 4059 | struct nfs4_lockowner *lock_sop = NULL; |
| 4046 | struct nfs4_ol_stateid *lock_stp; | 4060 | struct nfs4_ol_stateid *lock_stp; |
| 4047 | struct file *filp = NULL; | 4061 | struct file *filp = NULL; |
| 4048 | struct file_lock file_lock; | 4062 | struct file_lock *file_lock = NULL; |
| 4049 | struct file_lock conflock; | 4063 | struct file_lock *conflock = NULL; |
| 4050 | __be32 status = 0; | 4064 | __be32 status = 0; |
| 4051 | bool new_state = false; | 4065 | bool new_state = false; |
| 4052 | int lkflg; | 4066 | int lkflg; |
| @@ -4116,21 +4130,28 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4116 | if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim) | 4130 | if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim) |
| 4117 | goto out; | 4131 | goto out; |
| 4118 | 4132 | ||
| 4119 | locks_init_lock(&file_lock); | 4133 | file_lock = locks_alloc_lock(); |
| 4134 | if (!file_lock) { | ||
| 4135 | dprintk("NFSD: %s: unable to allocate lock!\n", __func__); | ||
| 4136 | status = nfserr_jukebox; | ||
| 4137 | goto out; | ||
| 4138 | } | ||
| 4139 | |||
| 4140 | locks_init_lock(file_lock); | ||
| 4120 | switch (lock->lk_type) { | 4141 | switch (lock->lk_type) { |
| 4121 | case NFS4_READ_LT: | 4142 | case NFS4_READ_LT: |
| 4122 | case NFS4_READW_LT: | 4143 | case NFS4_READW_LT: |
| 4123 | filp = find_readable_file(lock_stp->st_file); | 4144 | filp = find_readable_file(lock_stp->st_file); |
| 4124 | if (filp) | 4145 | if (filp) |
| 4125 | get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ); | 4146 | get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ); |
| 4126 | file_lock.fl_type = F_RDLCK; | 4147 | file_lock->fl_type = F_RDLCK; |
| 4127 | break; | 4148 | break; |
| 4128 | case NFS4_WRITE_LT: | 4149 | case NFS4_WRITE_LT: |
| 4129 | case NFS4_WRITEW_LT: | 4150 | case NFS4_WRITEW_LT: |
| 4130 | filp = find_writeable_file(lock_stp->st_file); | 4151 | filp = find_writeable_file(lock_stp->st_file); |
| 4131 | if (filp) | 4152 | if (filp) |
| 4132 | get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE); | 4153 | get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE); |
| 4133 | file_lock.fl_type = F_WRLCK; | 4154 | file_lock->fl_type = F_WRLCK; |
| 4134 | break; | 4155 | break; |
| 4135 | default: | 4156 | default: |
| 4136 | status = nfserr_inval; | 4157 | status = nfserr_inval; |
| @@ -4140,22 +4161,23 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4140 | status = nfserr_openmode; | 4161 | status = nfserr_openmode; |
| 4141 | goto out; | 4162 | goto out; |
| 4142 | } | 4163 | } |
| 4143 | file_lock.fl_owner = (fl_owner_t)lock_sop; | 4164 | file_lock->fl_owner = (fl_owner_t)lock_sop; |
| 4144 | file_lock.fl_pid = current->tgid; | 4165 | file_lock->fl_pid = current->tgid; |
| 4145 | file_lock.fl_file = filp; | 4166 | file_lock->fl_file = filp; |
| 4146 | file_lock.fl_flags = FL_POSIX; | 4167 | file_lock->fl_flags = FL_POSIX; |
| 4147 | file_lock.fl_lmops = &nfsd_posix_mng_ops; | 4168 | file_lock->fl_lmops = &nfsd_posix_mng_ops; |
| 4148 | 4169 | file_lock->fl_start = lock->lk_offset; | |
| 4149 | file_lock.fl_start = lock->lk_offset; | 4170 | file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length); |
| 4150 | file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length); | 4171 | nfs4_transform_lock_offset(file_lock); |
| 4151 | nfs4_transform_lock_offset(&file_lock); | 4172 | |
| 4152 | 4173 | conflock = locks_alloc_lock(); | |
| 4153 | /* | 4174 | if (!conflock) { |
| 4154 | * Try to lock the file in the VFS. | 4175 | dprintk("NFSD: %s: unable to allocate lock!\n", __func__); |
| 4155 | * Note: locks.c uses the BKL to protect the inode's lock list. | 4176 | status = nfserr_jukebox; |
| 4156 | */ | 4177 | goto out; |
| 4178 | } | ||
| 4157 | 4179 | ||
| 4158 | err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock); | 4180 | err = vfs_lock_file(filp, F_SETLK, file_lock, conflock); |
| 4159 | switch (-err) { | 4181 | switch (-err) { |
| 4160 | case 0: /* success! */ | 4182 | case 0: /* success! */ |
| 4161 | update_stateid(&lock_stp->st_stid.sc_stateid); | 4183 | update_stateid(&lock_stp->st_stid.sc_stateid); |
| @@ -4166,7 +4188,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4166 | case (EAGAIN): /* conflock holds conflicting lock */ | 4188 | case (EAGAIN): /* conflock holds conflicting lock */ |
| 4167 | status = nfserr_denied; | 4189 | status = nfserr_denied; |
| 4168 | dprintk("NFSD: nfsd4_lock: conflicting lock found!\n"); | 4190 | dprintk("NFSD: nfsd4_lock: conflicting lock found!\n"); |
| 4169 | nfs4_set_lock_denied(&conflock, &lock->lk_denied); | 4191 | nfs4_set_lock_denied(conflock, &lock->lk_denied); |
| 4170 | break; | 4192 | break; |
| 4171 | case (EDEADLK): | 4193 | case (EDEADLK): |
| 4172 | status = nfserr_deadlock; | 4194 | status = nfserr_deadlock; |
| @@ -4181,6 +4203,10 @@ out: | |||
| 4181 | release_lockowner(lock_sop); | 4203 | release_lockowner(lock_sop); |
| 4182 | if (!cstate->replay_owner) | 4204 | if (!cstate->replay_owner) |
| 4183 | nfs4_unlock_state(); | 4205 | nfs4_unlock_state(); |
| 4206 | if (file_lock) | ||
| 4207 | locks_free_lock(file_lock); | ||
| 4208 | if (conflock) | ||
| 4209 | locks_free_lock(conflock); | ||
| 4184 | return status; | 4210 | return status; |
| 4185 | } | 4211 | } |
| 4186 | 4212 | ||
| @@ -4209,7 +4235,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4209 | struct nfsd4_lockt *lockt) | 4235 | struct nfsd4_lockt *lockt) |
| 4210 | { | 4236 | { |
| 4211 | struct inode *inode; | 4237 | struct inode *inode; |
| 4212 | struct file_lock file_lock; | 4238 | struct file_lock *file_lock = NULL; |
| 4213 | struct nfs4_lockowner *lo; | 4239 | struct nfs4_lockowner *lo; |
| 4214 | __be32 status; | 4240 | __be32 status; |
| 4215 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 4241 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); |
| @@ -4230,15 +4256,21 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4230 | goto out; | 4256 | goto out; |
| 4231 | 4257 | ||
| 4232 | inode = cstate->current_fh.fh_dentry->d_inode; | 4258 | inode = cstate->current_fh.fh_dentry->d_inode; |
| 4233 | locks_init_lock(&file_lock); | 4259 | file_lock = locks_alloc_lock(); |
| 4260 | if (!file_lock) { | ||
| 4261 | dprintk("NFSD: %s: unable to allocate lock!\n", __func__); | ||
| 4262 | status = nfserr_jukebox; | ||
| 4263 | goto out; | ||
| 4264 | } | ||
| 4265 | locks_init_lock(file_lock); | ||
| 4234 | switch (lockt->lt_type) { | 4266 | switch (lockt->lt_type) { |
| 4235 | case NFS4_READ_LT: | 4267 | case NFS4_READ_LT: |
| 4236 | case NFS4_READW_LT: | 4268 | case NFS4_READW_LT: |
| 4237 | file_lock.fl_type = F_RDLCK; | 4269 | file_lock->fl_type = F_RDLCK; |
| 4238 | break; | 4270 | break; |
| 4239 | case NFS4_WRITE_LT: | 4271 | case NFS4_WRITE_LT: |
| 4240 | case NFS4_WRITEW_LT: | 4272 | case NFS4_WRITEW_LT: |
| 4241 | file_lock.fl_type = F_WRLCK; | 4273 | file_lock->fl_type = F_WRLCK; |
| 4242 | break; | 4274 | break; |
| 4243 | default: | 4275 | default: |
| 4244 | dprintk("NFSD: nfs4_lockt: bad lock type!\n"); | 4276 | dprintk("NFSD: nfs4_lockt: bad lock type!\n"); |
| @@ -4248,25 +4280,27 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4248 | 4280 | ||
| 4249 | lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); | 4281 | lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); |
| 4250 | if (lo) | 4282 | if (lo) |
| 4251 | file_lock.fl_owner = (fl_owner_t)lo; | 4283 | file_lock->fl_owner = (fl_owner_t)lo; |
| 4252 | file_lock.fl_pid = current->tgid; | 4284 | file_lock->fl_pid = current->tgid; |
| 4253 | file_lock.fl_flags = FL_POSIX; | 4285 | file_lock->fl_flags = FL_POSIX; |
| 4254 | 4286 | ||
| 4255 | file_lock.fl_start = lockt->lt_offset; | 4287 | file_lock->fl_start = lockt->lt_offset; |
| 4256 | file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length); | 4288 | file_lock->fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length); |
| 4257 | 4289 | ||
| 4258 | nfs4_transform_lock_offset(&file_lock); | 4290 | nfs4_transform_lock_offset(file_lock); |
| 4259 | 4291 | ||
| 4260 | status = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock); | 4292 | status = nfsd_test_lock(rqstp, &cstate->current_fh, file_lock); |
| 4261 | if (status) | 4293 | if (status) |
| 4262 | goto out; | 4294 | goto out; |
| 4263 | 4295 | ||
| 4264 | if (file_lock.fl_type != F_UNLCK) { | 4296 | if (file_lock->fl_type != F_UNLCK) { |
| 4265 | status = nfserr_denied; | 4297 | status = nfserr_denied; |
| 4266 | nfs4_set_lock_denied(&file_lock, &lockt->lt_denied); | 4298 | nfs4_set_lock_denied(file_lock, &lockt->lt_denied); |
| 4267 | } | 4299 | } |
| 4268 | out: | 4300 | out: |
| 4269 | nfs4_unlock_state(); | 4301 | nfs4_unlock_state(); |
| 4302 | if (file_lock) | ||
| 4303 | locks_free_lock(file_lock); | ||
| 4270 | return status; | 4304 | return status; |
| 4271 | } | 4305 | } |
| 4272 | 4306 | ||
| @@ -4276,7 +4310,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4276 | { | 4310 | { |
| 4277 | struct nfs4_ol_stateid *stp; | 4311 | struct nfs4_ol_stateid *stp; |
| 4278 | struct file *filp = NULL; | 4312 | struct file *filp = NULL; |
| 4279 | struct file_lock file_lock; | 4313 | struct file_lock *file_lock = NULL; |
| 4280 | __be32 status; | 4314 | __be32 status; |
| 4281 | int err; | 4315 | int err; |
| 4282 | 4316 | ||
| @@ -4298,23 +4332,29 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4298 | status = nfserr_lock_range; | 4332 | status = nfserr_lock_range; |
| 4299 | goto out; | 4333 | goto out; |
| 4300 | } | 4334 | } |
| 4301 | BUG_ON(!filp); | 4335 | file_lock = locks_alloc_lock(); |
| 4302 | locks_init_lock(&file_lock); | 4336 | if (!file_lock) { |
| 4303 | file_lock.fl_type = F_UNLCK; | 4337 | dprintk("NFSD: %s: unable to allocate lock!\n", __func__); |
| 4304 | file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); | 4338 | status = nfserr_jukebox; |
| 4305 | file_lock.fl_pid = current->tgid; | 4339 | goto out; |
| 4306 | file_lock.fl_file = filp; | 4340 | } |
| 4307 | file_lock.fl_flags = FL_POSIX; | 4341 | locks_init_lock(file_lock); |
| 4308 | file_lock.fl_lmops = &nfsd_posix_mng_ops; | 4342 | file_lock->fl_type = F_UNLCK; |
| 4309 | file_lock.fl_start = locku->lu_offset; | 4343 | file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); |
| 4310 | 4344 | file_lock->fl_pid = current->tgid; | |
| 4311 | file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length); | 4345 | file_lock->fl_file = filp; |
| 4312 | nfs4_transform_lock_offset(&file_lock); | 4346 | file_lock->fl_flags = FL_POSIX; |
| 4347 | file_lock->fl_lmops = &nfsd_posix_mng_ops; | ||
| 4348 | file_lock->fl_start = locku->lu_offset; | ||
| 4349 | |||
| 4350 | file_lock->fl_end = last_byte_offset(locku->lu_offset, | ||
| 4351 | locku->lu_length); | ||
| 4352 | nfs4_transform_lock_offset(file_lock); | ||
| 4313 | 4353 | ||
| 4314 | /* | 4354 | /* |
| 4315 | * Try to unlock the file in the VFS. | 4355 | * Try to unlock the file in the VFS. |
| 4316 | */ | 4356 | */ |
| 4317 | err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL); | 4357 | err = vfs_lock_file(filp, F_SETLK, file_lock, NULL); |
| 4318 | if (err) { | 4358 | if (err) { |
| 4319 | dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n"); | 4359 | dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n"); |
| 4320 | goto out_nfserr; | 4360 | goto out_nfserr; |
| @@ -4328,6 +4368,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
| 4328 | out: | 4368 | out: |
| 4329 | if (!cstate->replay_owner) | 4369 | if (!cstate->replay_owner) |
| 4330 | nfs4_unlock_state(); | 4370 | nfs4_unlock_state(); |
| 4371 | if (file_lock) | ||
| 4372 | locks_free_lock(file_lock); | ||
| 4331 | return status; | 4373 | return status; |
| 4332 | 4374 | ||
| 4333 | out_nfserr: | 4375 | out_nfserr: |
| @@ -4501,12 +4543,12 @@ nfsd4_find_reclaim_client(struct nfs4_client *clp) | |||
| 4501 | * Called from OPEN. Look for clientid in reclaim list. | 4543 | * Called from OPEN. Look for clientid in reclaim list. |
| 4502 | */ | 4544 | */ |
| 4503 | __be32 | 4545 | __be32 |
| 4504 | nfs4_check_open_reclaim(clientid_t *clid) | 4546 | nfs4_check_open_reclaim(clientid_t *clid, bool sessions) |
| 4505 | { | 4547 | { |
| 4506 | struct nfs4_client *clp; | 4548 | struct nfs4_client *clp; |
| 4507 | 4549 | ||
| 4508 | /* find clientid in conf_id_hashtbl */ | 4550 | /* find clientid in conf_id_hashtbl */ |
| 4509 | clp = find_confirmed_client(clid); | 4551 | clp = find_confirmed_client(clid, sessions); |
| 4510 | if (clp == NULL) | 4552 | if (clp == NULL) |
| 4511 | return nfserr_reclaim_bad; | 4553 | return nfserr_reclaim_bad; |
| 4512 | 4554 | ||
| @@ -4522,7 +4564,6 @@ void nfsd_forget_clients(u64 num) | |||
| 4522 | 4564 | ||
| 4523 | nfs4_lock_state(); | 4565 | nfs4_lock_state(); |
| 4524 | list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { | 4566 | list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { |
| 4525 | nfsd4_client_record_remove(clp); | ||
| 4526 | expire_client(clp); | 4567 | expire_client(clp); |
| 4527 | if (++count == num) | 4568 | if (++count == num) |
| 4528 | break; | 4569 | break; |
| @@ -4582,7 +4623,7 @@ void nfsd_forget_openowners(u64 num) | |||
| 4582 | printk(KERN_INFO "NFSD: Forgot %d open owners", count); | 4623 | printk(KERN_INFO "NFSD: Forgot %d open owners", count); |
| 4583 | } | 4624 | } |
| 4584 | 4625 | ||
| 4585 | int nfsd_process_n_delegations(u64 num, struct list_head *list) | 4626 | static int nfsd_process_n_delegations(u64 num, struct list_head *list) |
| 4586 | { | 4627 | { |
| 4587 | int i, count = 0; | 4628 | int i, count = 0; |
| 4588 | struct nfs4_file *fp, *fnext; | 4629 | struct nfs4_file *fp, *fnext; |
| @@ -4747,11 +4788,11 @@ __nfs4_state_shutdown(void) | |||
| 4747 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | 4788 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { |
| 4748 | while (!list_empty(&conf_id_hashtbl[i])) { | 4789 | while (!list_empty(&conf_id_hashtbl[i])) { |
| 4749 | clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); | 4790 | clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); |
| 4750 | expire_client(clp); | 4791 | destroy_client(clp); |
| 4751 | } | 4792 | } |
| 4752 | while (!list_empty(&unconf_str_hashtbl[i])) { | 4793 | while (!list_empty(&unconf_str_hashtbl[i])) { |
| 4753 | clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); | 4794 | clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); |
| 4754 | expire_client(clp); | 4795 | destroy_client(clp); |
| 4755 | } | 4796 | } |
| 4756 | } | 4797 | } |
| 4757 | INIT_LIST_HEAD(&reaplist); | 4798 | INIT_LIST_HEAD(&reaplist); |
