| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- /* SPDX-License-Identifier: GPL-2.0 */
- #include <linux/nfs4.h>
- #include <linux/nfs.h>
- #include <linux/sunrpc/sched.h>
- #include <linux/nfs_fs.h>
- #include "internal.h"
- #include "nfs4_fs.h"
- #include "nfs40.h"
- #include "nfs4session.h"
- #include "nfs4trace.h"
- static void nfs40_call_sync_prepare(struct rpc_task *task, void *calldata)
- {
- struct nfs4_call_sync_data *data = calldata;
- nfs4_setup_sequence(data->seq_server->nfs_client,
- data->seq_args, data->seq_res, task);
- }
- static void nfs40_call_sync_done(struct rpc_task *task, void *calldata)
- {
- struct nfs4_call_sync_data *data = calldata;
- nfs4_sequence_done(task, data->seq_res);
- }
- static void nfs40_sequence_free_slot(struct nfs4_sequence_res *res)
- {
- struct nfs4_slot *slot = res->sr_slot;
- struct nfs4_slot_table *tbl;
- tbl = slot->table;
- spin_lock(&tbl->slot_tbl_lock);
- if (!nfs41_wake_and_assign_slot(tbl, slot))
- nfs4_free_slot(tbl, slot);
- spin_unlock(&tbl->slot_tbl_lock);
- res->sr_slot = NULL;
- }
- static int nfs40_sequence_done(struct rpc_task *task,
- struct nfs4_sequence_res *res)
- {
- if (res->sr_slot != NULL)
- nfs40_sequence_free_slot(res);
- return 1;
- }
- static void nfs40_clear_delegation_stateid(struct nfs4_state *state)
- {
- if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL)
- nfs_finish_clear_delegation_stateid(state, NULL);
- }
- static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
- {
- /* NFSv4.0 doesn't allow for delegation recovery on open expire */
- nfs40_clear_delegation_stateid(state);
- nfs_state_clear_open_state_flags(state);
- return nfs4_open_expired(sp, state);
- }
- struct nfs4_renewdata {
- struct nfs_client *client;
- unsigned long timestamp;
- };
- /*
- * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special
- * standalone procedure for queueing an asynchronous RENEW.
- */
- static void nfs4_renew_release(void *calldata)
- {
- struct nfs4_renewdata *data = calldata;
- struct nfs_client *clp = data->client;
- if (refcount_read(&clp->cl_count) > 1)
- nfs4_schedule_state_renewal(clp);
- nfs_put_client(clp);
- kfree(data);
- }
- static void nfs4_renew_done(struct rpc_task *task, void *calldata)
- {
- struct nfs4_renewdata *data = calldata;
- struct nfs_client *clp = data->client;
- unsigned long timestamp = data->timestamp;
- trace_nfs4_renew_async(clp, task->tk_status);
- switch (task->tk_status) {
- case 0:
- break;
- case -NFS4ERR_LEASE_MOVED:
- nfs4_schedule_lease_moved_recovery(clp);
- break;
- default:
- /* Unless we're shutting down, schedule state recovery! */
- if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0)
- return;
- if (task->tk_status != NFS4ERR_CB_PATH_DOWN) {
- nfs4_schedule_lease_recovery(clp);
- return;
- }
- nfs4_schedule_path_down_recovery(clp);
- }
- do_renew_lease(clp, timestamp);
- }
- static const struct rpc_call_ops nfs4_renew_ops = {
- .rpc_call_done = nfs4_renew_done,
- .rpc_release = nfs4_renew_release,
- };
- static int nfs4_proc_async_renew(struct nfs_client *clp, const struct cred *cred, unsigned renew_flags)
- {
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
- .rpc_argp = clp,
- .rpc_cred = cred,
- };
- struct nfs4_renewdata *data;
- if (renew_flags == 0)
- return 0;
- if (!refcount_inc_not_zero(&clp->cl_count))
- return -EIO;
- data = kmalloc_obj(*data, GFP_NOFS);
- if (data == NULL) {
- nfs_put_client(clp);
- return -ENOMEM;
- }
- data->client = clp;
- data->timestamp = jiffies;
- return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT,
- &nfs4_renew_ops, data);
- }
- static int nfs4_proc_renew(struct nfs_client *clp, const struct cred *cred)
- {
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
- .rpc_argp = clp,
- .rpc_cred = cred,
- };
- unsigned long now = jiffies;
- int status;
- status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
- if (status < 0)
- return status;
- do_renew_lease(clp, now);
- return 0;
- }
- static int nfs40_test_and_free_expired_stateid(struct nfs_server *server,
- nfs4_stateid *stateid,
- const struct cred *cred)
- {
- return -NFS4ERR_BAD_STATEID;
- }
- /*
- * This operation also signals the server that this client is
- * performing migration recovery. The server can stop returning
- * NFS4ERR_LEASE_MOVED to this client. A RENEW operation is
- * appended to this compound to identify the client ID which is
- * performing recovery.
- */
- static int _nfs40_proc_get_locations(struct nfs_server *server,
- struct nfs_fh *fhandle,
- struct nfs4_fs_locations *locations,
- struct page *page, const struct cred *cred)
- {
- struct rpc_clnt *clnt = server->client;
- struct nfs_client *clp = server->nfs_client;
- u32 bitmask[2] = {
- [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
- };
- struct nfs4_fs_locations_arg args = {
- .clientid = clp->cl_clientid,
- .fh = fhandle,
- .page = page,
- .bitmask = bitmask,
- .migration = 1, /* skip LOOKUP */
- .renew = 1, /* append RENEW */
- };
- struct nfs4_fs_locations_res res = {
- .fs_locations = locations,
- .migration = 1,
- .renew = 1,
- };
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
- .rpc_argp = &args,
- .rpc_resp = &res,
- .rpc_cred = cred,
- };
- unsigned long now = jiffies;
- int status;
- nfs_fattr_init(locations->fattr);
- locations->server = server;
- locations->nlocations = 0;
- nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 1);
- status = nfs4_call_sync_sequence(clnt, server, &msg,
- &args.seq_args, &res.seq_res);
- if (status)
- return status;
- renew_lease(server, now);
- return 0;
- }
- /*
- * This operation also signals the server that this client is
- * performing "lease moved" recovery. The server can stop
- * returning NFS4ERR_LEASE_MOVED to this client. A RENEW operation
- * is appended to this compound to identify the client ID which is
- * performing recovery.
- */
- static int _nfs40_proc_fsid_present(struct inode *inode, const struct cred *cred)
- {
- struct nfs_server *server = NFS_SERVER(inode);
- struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
- struct rpc_clnt *clnt = server->client;
- struct nfs4_fsid_present_arg args = {
- .fh = NFS_FH(inode),
- .clientid = clp->cl_clientid,
- .renew = 1, /* append RENEW */
- };
- struct nfs4_fsid_present_res res = {
- .renew = 1,
- };
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT],
- .rpc_argp = &args,
- .rpc_resp = &res,
- .rpc_cred = cred,
- };
- unsigned long now = jiffies;
- int status;
- res.fh = nfs_alloc_fhandle();
- if (res.fh == NULL)
- return -ENOMEM;
- nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 1);
- status = nfs4_call_sync_sequence(clnt, server, &msg,
- &args.seq_args, &res.seq_res);
- nfs_free_fhandle(res.fh);
- if (status)
- return status;
- do_renew_lease(clp, now);
- return 0;
- }
- struct nfs_release_lockowner_data {
- struct nfs4_lock_state *lsp;
- struct nfs_server *server;
- struct nfs_release_lockowner_args args;
- struct nfs_release_lockowner_res res;
- unsigned long timestamp;
- };
- static void nfs4_release_lockowner_prepare(struct rpc_task *task, void *calldata)
- {
- struct nfs_release_lockowner_data *data = calldata;
- struct nfs_server *server = data->server;
- nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
- &data->res.seq_res, task);
- data->args.lock_owner.clientid = server->nfs_client->cl_clientid;
- data->timestamp = jiffies;
- }
- static void nfs4_release_lockowner_done(struct rpc_task *task, void *calldata)
- {
- struct nfs_release_lockowner_data *data = calldata;
- struct nfs_server *server = data->server;
- nfs40_sequence_done(task, &data->res.seq_res);
- switch (task->tk_status) {
- case 0:
- renew_lease(server, data->timestamp);
- break;
- case -NFS4ERR_STALE_CLIENTID:
- case -NFS4ERR_EXPIRED:
- nfs4_schedule_lease_recovery(server->nfs_client);
- break;
- case -NFS4ERR_LEASE_MOVED:
- case -NFS4ERR_DELAY:
- if (nfs4_async_handle_error(task, server,
- NULL, NULL) == -EAGAIN)
- rpc_restart_call_prepare(task);
- }
- }
- static void nfs4_release_lockowner_release(void *calldata)
- {
- struct nfs_release_lockowner_data *data = calldata;
- nfs4_free_lock_state(data->server, data->lsp);
- kfree(calldata);
- }
- static const struct rpc_call_ops nfs4_release_lockowner_ops = {
- .rpc_call_prepare = nfs4_release_lockowner_prepare,
- .rpc_call_done = nfs4_release_lockowner_done,
- .rpc_release = nfs4_release_lockowner_release,
- };
- static void
- nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)
- {
- struct nfs_release_lockowner_data *data;
- struct nfs_client *clp = server->nfs_client;
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
- };
- if (clp->cl_mvops->minor_version != 0)
- return;
- data = kmalloc_obj(*data);
- if (!data)
- return;
- data->lsp = lsp;
- data->server = server;
- data->args.lock_owner.clientid = clp->cl_clientid;
- data->args.lock_owner.id = lsp->ls_seqid.owner_id;
- data->args.lock_owner.s_dev = server->s_dev;
- msg.rpc_argp = &data->args;
- msg.rpc_resp = &data->res;
- nfs4_init_sequence(clp, &data->args.seq_args, &data->res.seq_res, 0, 0);
- rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);
- }
- static const struct rpc_call_ops nfs40_call_sync_ops = {
- .rpc_call_prepare = nfs40_call_sync_prepare,
- .rpc_call_done = nfs40_call_sync_done,
- };
- static const struct nfs4_sequence_slot_ops nfs40_sequence_slot_ops = {
- .process = nfs40_sequence_done,
- .done = nfs40_sequence_done,
- .free_slot = nfs40_sequence_free_slot,
- };
- static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
- .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
- .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
- .recover_open = nfs4_open_reclaim,
- .recover_lock = nfs4_lock_reclaim,
- .establish_clid = nfs4_init_clientid,
- .detect_trunking = nfs40_discover_server_trunking,
- };
- static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
- .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
- .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
- .recover_open = nfs40_open_expired,
- .recover_lock = nfs4_lock_expired,
- .establish_clid = nfs4_init_clientid,
- };
- static const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = {
- .sched_state_renewal = nfs4_proc_async_renew,
- .get_state_renewal_cred = nfs4_get_renew_cred,
- .renew_lease = nfs4_proc_renew,
- };
- static const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = {
- .get_locations = _nfs40_proc_get_locations,
- .fsid_present = _nfs40_proc_fsid_present,
- };
- const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
- .minor_version = 0,
- .init_caps = NFS_CAP_READDIRPLUS
- | NFS_CAP_ATOMIC_OPEN
- | NFS_CAP_POSIX_LOCK,
- .init_client = nfs40_init_client,
- .shutdown_client = nfs40_shutdown_client,
- .match_stateid = nfs4_match_stateid,
- .find_root_sec = nfs4_find_root_sec,
- .free_lock_state = nfs4_release_lockowner,
- .test_and_free_expired = nfs40_test_and_free_expired_stateid,
- .alloc_seqid = nfs_alloc_seqid,
- .call_sync_ops = &nfs40_call_sync_ops,
- .sequence_slot_ops = &nfs40_sequence_slot_ops,
- .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
- .nograce_recovery_ops = &nfs40_nograce_recovery_ops,
- .state_renewal_ops = &nfs40_state_renewal_ops,
- .mig_recovery_ops = &nfs40_mig_recovery_ops,
- };
|