162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014 Christoph Hellwig.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/blkdev.h>
662306a36Sopenharmony_ci#include <linux/kmod.h>
762306a36Sopenharmony_ci#include <linux/file.h>
862306a36Sopenharmony_ci#include <linux/jhash.h>
962306a36Sopenharmony_ci#include <linux/sched.h>
1062306a36Sopenharmony_ci#include <linux/sunrpc/addr.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "pnfs.h"
1362306a36Sopenharmony_ci#include "netns.h"
1462306a36Sopenharmony_ci#include "trace.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define NFSDDBG_FACILITY                NFSDDBG_PNFS
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct nfs4_layout {
1962306a36Sopenharmony_ci	struct list_head		lo_perstate;
2062306a36Sopenharmony_ci	struct nfs4_layout_stateid	*lo_state;
2162306a36Sopenharmony_ci	struct nfsd4_layout_seg		lo_seg;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic struct kmem_cache *nfs4_layout_cache;
2562306a36Sopenharmony_cistatic struct kmem_cache *nfs4_layout_stateid_cache;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct nfsd4_callback_ops nfsd4_cb_layout_ops;
2862306a36Sopenharmony_cistatic const struct lock_manager_operations nfsd4_layouts_lm_ops;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciconst struct nfsd4_layout_ops *nfsd4_layout_ops[LAYOUT_TYPE_MAX] =  {
3162306a36Sopenharmony_ci#ifdef CONFIG_NFSD_FLEXFILELAYOUT
3262306a36Sopenharmony_ci	[LAYOUT_FLEX_FILES]	= &ff_layout_ops,
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci#ifdef CONFIG_NFSD_BLOCKLAYOUT
3562306a36Sopenharmony_ci	[LAYOUT_BLOCK_VOLUME]	= &bl_layout_ops,
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci#ifdef CONFIG_NFSD_SCSILAYOUT
3862306a36Sopenharmony_ci	[LAYOUT_SCSI]		= &scsi_layout_ops,
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* pNFS device ID to export fsid mapping */
4362306a36Sopenharmony_ci#define DEVID_HASH_BITS	8
4462306a36Sopenharmony_ci#define DEVID_HASH_SIZE	(1 << DEVID_HASH_BITS)
4562306a36Sopenharmony_ci#define DEVID_HASH_MASK	(DEVID_HASH_SIZE - 1)
4662306a36Sopenharmony_cistatic u64 nfsd_devid_seq = 1;
4762306a36Sopenharmony_cistatic struct list_head nfsd_devid_hash[DEVID_HASH_SIZE];
4862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nfsd_devid_lock);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic inline u32 devid_hashfn(u64 idx)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	return jhash_2words(idx, idx >> 32, 0) & DEVID_HASH_MASK;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void
5662306a36Sopenharmony_cinfsd4_alloc_devid_map(const struct svc_fh *fhp)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	const struct knfsd_fh *fh = &fhp->fh_handle;
5962306a36Sopenharmony_ci	size_t fsid_len = key_len(fh->fh_fsid_type);
6062306a36Sopenharmony_ci	struct nfsd4_deviceid_map *map, *old;
6162306a36Sopenharmony_ci	int i;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	map = kzalloc(sizeof(*map) + fsid_len, GFP_KERNEL);
6462306a36Sopenharmony_ci	if (!map)
6562306a36Sopenharmony_ci		return;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	map->fsid_type = fh->fh_fsid_type;
6862306a36Sopenharmony_ci	memcpy(&map->fsid, fh->fh_fsid, fsid_len);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	spin_lock(&nfsd_devid_lock);
7162306a36Sopenharmony_ci	if (fhp->fh_export->ex_devid_map)
7262306a36Sopenharmony_ci		goto out_unlock;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	for (i = 0; i < DEVID_HASH_SIZE; i++) {
7562306a36Sopenharmony_ci		list_for_each_entry(old, &nfsd_devid_hash[i], hash) {
7662306a36Sopenharmony_ci			if (old->fsid_type != fh->fh_fsid_type)
7762306a36Sopenharmony_ci				continue;
7862306a36Sopenharmony_ci			if (memcmp(old->fsid, fh->fh_fsid,
7962306a36Sopenharmony_ci					key_len(old->fsid_type)))
8062306a36Sopenharmony_ci				continue;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci			fhp->fh_export->ex_devid_map = old;
8362306a36Sopenharmony_ci			goto out_unlock;
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	map->idx = nfsd_devid_seq++;
8862306a36Sopenharmony_ci	list_add_tail_rcu(&map->hash, &nfsd_devid_hash[devid_hashfn(map->idx)]);
8962306a36Sopenharmony_ci	fhp->fh_export->ex_devid_map = map;
9062306a36Sopenharmony_ci	map = NULL;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciout_unlock:
9362306a36Sopenharmony_ci	spin_unlock(&nfsd_devid_lock);
9462306a36Sopenharmony_ci	kfree(map);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct nfsd4_deviceid_map *
9862306a36Sopenharmony_cinfsd4_find_devid_map(int idx)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct nfsd4_deviceid_map *map, *ret = NULL;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	rcu_read_lock();
10362306a36Sopenharmony_ci	list_for_each_entry_rcu(map, &nfsd_devid_hash[devid_hashfn(idx)], hash)
10462306a36Sopenharmony_ci		if (map->idx == idx)
10562306a36Sopenharmony_ci			ret = map;
10662306a36Sopenharmony_ci	rcu_read_unlock();
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return ret;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciint
11262306a36Sopenharmony_cinfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
11362306a36Sopenharmony_ci		u32 device_generation)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	if (!fhp->fh_export->ex_devid_map) {
11662306a36Sopenharmony_ci		nfsd4_alloc_devid_map(fhp);
11762306a36Sopenharmony_ci		if (!fhp->fh_export->ex_devid_map)
11862306a36Sopenharmony_ci			return -ENOMEM;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	id->fsid_idx = fhp->fh_export->ex_devid_map->idx;
12262306a36Sopenharmony_ci	id->generation = device_generation;
12362306a36Sopenharmony_ci	id->pad = 0;
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_civoid nfsd4_setup_layout_type(struct svc_export *exp)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci#if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
13062306a36Sopenharmony_ci	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
13162306a36Sopenharmony_ci#endif
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!(exp->ex_flags & NFSEXP_PNFS))
13462306a36Sopenharmony_ci		return;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#ifdef CONFIG_NFSD_FLEXFILELAYOUT
13762306a36Sopenharmony_ci	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
13862306a36Sopenharmony_ci#endif
13962306a36Sopenharmony_ci#ifdef CONFIG_NFSD_BLOCKLAYOUT
14062306a36Sopenharmony_ci	if (sb->s_export_op->get_uuid &&
14162306a36Sopenharmony_ci	    sb->s_export_op->map_blocks &&
14262306a36Sopenharmony_ci	    sb->s_export_op->commit_blocks)
14362306a36Sopenharmony_ci		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
14462306a36Sopenharmony_ci#endif
14562306a36Sopenharmony_ci#ifdef CONFIG_NFSD_SCSILAYOUT
14662306a36Sopenharmony_ci	if (sb->s_export_op->map_blocks &&
14762306a36Sopenharmony_ci	    sb->s_export_op->commit_blocks &&
14862306a36Sopenharmony_ci	    sb->s_bdev &&
14962306a36Sopenharmony_ci	    sb->s_bdev->bd_disk->fops->pr_ops &&
15062306a36Sopenharmony_ci	    sb->s_bdev->bd_disk->fops->get_unique_id)
15162306a36Sopenharmony_ci		exp->ex_layout_types |= 1 << LAYOUT_SCSI;
15262306a36Sopenharmony_ci#endif
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void
15662306a36Sopenharmony_cinfsd4_free_layout_stateid(struct nfs4_stid *stid)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls = layoutstateid(stid);
15962306a36Sopenharmony_ci	struct nfs4_client *clp = ls->ls_stid.sc_client;
16062306a36Sopenharmony_ci	struct nfs4_file *fp = ls->ls_stid.sc_file;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	trace_nfsd_layoutstate_free(&ls->ls_stid.sc_stateid);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
16562306a36Sopenharmony_ci	list_del_init(&ls->ls_perclnt);
16662306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	spin_lock(&fp->fi_lock);
16962306a36Sopenharmony_ci	list_del_init(&ls->ls_perfile);
17062306a36Sopenharmony_ci	spin_unlock(&fp->fi_lock);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (!nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
17362306a36Sopenharmony_ci		vfs_setlease(ls->ls_file->nf_file, F_UNLCK, NULL, (void **)&ls);
17462306a36Sopenharmony_ci	nfsd_file_put(ls->ls_file);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (ls->ls_recalled)
17762306a36Sopenharmony_ci		atomic_dec(&ls->ls_stid.sc_file->fi_lo_recalls);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	kmem_cache_free(nfs4_layout_stateid_cache, ls);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int
18362306a36Sopenharmony_cinfsd4_layout_setlease(struct nfs4_layout_stateid *ls)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct file_lock *fl;
18662306a36Sopenharmony_ci	int status;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
18962306a36Sopenharmony_ci		return 0;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	fl = locks_alloc_lock();
19262306a36Sopenharmony_ci	if (!fl)
19362306a36Sopenharmony_ci		return -ENOMEM;
19462306a36Sopenharmony_ci	locks_init_lock(fl);
19562306a36Sopenharmony_ci	fl->fl_lmops = &nfsd4_layouts_lm_ops;
19662306a36Sopenharmony_ci	fl->fl_flags = FL_LAYOUT;
19762306a36Sopenharmony_ci	fl->fl_type = F_RDLCK;
19862306a36Sopenharmony_ci	fl->fl_end = OFFSET_MAX;
19962306a36Sopenharmony_ci	fl->fl_owner = ls;
20062306a36Sopenharmony_ci	fl->fl_pid = current->tgid;
20162306a36Sopenharmony_ci	fl->fl_file = ls->ls_file->nf_file;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	status = vfs_setlease(fl->fl_file, fl->fl_type, &fl, NULL);
20462306a36Sopenharmony_ci	if (status) {
20562306a36Sopenharmony_ci		locks_free_lock(fl);
20662306a36Sopenharmony_ci		return status;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	BUG_ON(fl != NULL);
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic struct nfs4_layout_stateid *
21362306a36Sopenharmony_cinfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
21462306a36Sopenharmony_ci		struct nfs4_stid *parent, u32 layout_type)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct nfs4_client *clp = cstate->clp;
21762306a36Sopenharmony_ci	struct nfs4_file *fp = parent->sc_file;
21862306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls;
21962306a36Sopenharmony_ci	struct nfs4_stid *stp;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache,
22262306a36Sopenharmony_ci					nfsd4_free_layout_stateid);
22362306a36Sopenharmony_ci	if (!stp)
22462306a36Sopenharmony_ci		return NULL;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	get_nfs4_file(fp);
22762306a36Sopenharmony_ci	stp->sc_file = fp;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ls = layoutstateid(stp);
23062306a36Sopenharmony_ci	INIT_LIST_HEAD(&ls->ls_perclnt);
23162306a36Sopenharmony_ci	INIT_LIST_HEAD(&ls->ls_perfile);
23262306a36Sopenharmony_ci	spin_lock_init(&ls->ls_lock);
23362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ls->ls_layouts);
23462306a36Sopenharmony_ci	mutex_init(&ls->ls_mutex);
23562306a36Sopenharmony_ci	ls->ls_layout_type = layout_type;
23662306a36Sopenharmony_ci	nfsd4_init_cb(&ls->ls_recall, clp, &nfsd4_cb_layout_ops,
23762306a36Sopenharmony_ci			NFSPROC4_CLNT_CB_LAYOUT);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (parent->sc_type == NFS4_DELEG_STID)
24062306a36Sopenharmony_ci		ls->ls_file = nfsd_file_get(fp->fi_deleg_file);
24162306a36Sopenharmony_ci	else
24262306a36Sopenharmony_ci		ls->ls_file = find_any_file(fp);
24362306a36Sopenharmony_ci	BUG_ON(!ls->ls_file);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (nfsd4_layout_setlease(ls)) {
24662306a36Sopenharmony_ci		nfsd_file_put(ls->ls_file);
24762306a36Sopenharmony_ci		put_nfs4_file(fp);
24862306a36Sopenharmony_ci		kmem_cache_free(nfs4_layout_stateid_cache, ls);
24962306a36Sopenharmony_ci		return NULL;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
25362306a36Sopenharmony_ci	stp->sc_type = NFS4_LAYOUT_STID;
25462306a36Sopenharmony_ci	list_add(&ls->ls_perclnt, &clp->cl_lo_states);
25562306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	spin_lock(&fp->fi_lock);
25862306a36Sopenharmony_ci	list_add(&ls->ls_perfile, &fp->fi_lo_states);
25962306a36Sopenharmony_ci	spin_unlock(&fp->fi_lock);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	trace_nfsd_layoutstate_alloc(&ls->ls_stid.sc_stateid);
26262306a36Sopenharmony_ci	return ls;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci__be32
26662306a36Sopenharmony_cinfsd4_preprocess_layout_stateid(struct svc_rqst *rqstp,
26762306a36Sopenharmony_ci		struct nfsd4_compound_state *cstate, stateid_t *stateid,
26862306a36Sopenharmony_ci		bool create, u32 layout_type, struct nfs4_layout_stateid **lsp)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls;
27162306a36Sopenharmony_ci	struct nfs4_stid *stid;
27262306a36Sopenharmony_ci	unsigned char typemask = NFS4_LAYOUT_STID;
27362306a36Sopenharmony_ci	__be32 status;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (create)
27662306a36Sopenharmony_ci		typemask |= (NFS4_OPEN_STID | NFS4_LOCK_STID | NFS4_DELEG_STID);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	status = nfsd4_lookup_stateid(cstate, stateid, typemask, &stid,
27962306a36Sopenharmony_ci			net_generic(SVC_NET(rqstp), nfsd_net_id));
28062306a36Sopenharmony_ci	if (status)
28162306a36Sopenharmony_ci		goto out;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (!fh_match(&cstate->current_fh.fh_handle,
28462306a36Sopenharmony_ci		      &stid->sc_file->fi_fhandle)) {
28562306a36Sopenharmony_ci		status = nfserr_bad_stateid;
28662306a36Sopenharmony_ci		goto out_put_stid;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (stid->sc_type != NFS4_LAYOUT_STID) {
29062306a36Sopenharmony_ci		ls = nfsd4_alloc_layout_stateid(cstate, stid, layout_type);
29162306a36Sopenharmony_ci		nfs4_put_stid(stid);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		status = nfserr_jukebox;
29462306a36Sopenharmony_ci		if (!ls)
29562306a36Sopenharmony_ci			goto out;
29662306a36Sopenharmony_ci		mutex_lock(&ls->ls_mutex);
29762306a36Sopenharmony_ci	} else {
29862306a36Sopenharmony_ci		ls = container_of(stid, struct nfs4_layout_stateid, ls_stid);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		status = nfserr_bad_stateid;
30162306a36Sopenharmony_ci		mutex_lock(&ls->ls_mutex);
30262306a36Sopenharmony_ci		if (nfsd4_stateid_generation_after(stateid, &stid->sc_stateid))
30362306a36Sopenharmony_ci			goto out_unlock_stid;
30462306a36Sopenharmony_ci		if (layout_type != ls->ls_layout_type)
30562306a36Sopenharmony_ci			goto out_unlock_stid;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	*lsp = ls;
30962306a36Sopenharmony_ci	return 0;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ciout_unlock_stid:
31262306a36Sopenharmony_ci	mutex_unlock(&ls->ls_mutex);
31362306a36Sopenharmony_ciout_put_stid:
31462306a36Sopenharmony_ci	nfs4_put_stid(stid);
31562306a36Sopenharmony_ciout:
31662306a36Sopenharmony_ci	return status;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void
32062306a36Sopenharmony_cinfsd4_recall_file_layout(struct nfs4_layout_stateid *ls)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	spin_lock(&ls->ls_lock);
32362306a36Sopenharmony_ci	if (ls->ls_recalled)
32462306a36Sopenharmony_ci		goto out_unlock;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (list_empty(&ls->ls_layouts))
32762306a36Sopenharmony_ci		goto out_unlock;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	ls->ls_recalled = true;
33062306a36Sopenharmony_ci	atomic_inc(&ls->ls_stid.sc_file->fi_lo_recalls);
33162306a36Sopenharmony_ci	trace_nfsd_layout_recall(&ls->ls_stid.sc_stateid);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	refcount_inc(&ls->ls_stid.sc_count);
33462306a36Sopenharmony_ci	nfsd4_run_cb(&ls->ls_recall);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ciout_unlock:
33762306a36Sopenharmony_ci	spin_unlock(&ls->ls_lock);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic inline u64
34162306a36Sopenharmony_cilayout_end(struct nfsd4_layout_seg *seg)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	u64 end = seg->offset + seg->length;
34462306a36Sopenharmony_ci	return end >= seg->offset ? end : NFS4_MAX_UINT64;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void
34862306a36Sopenharmony_cilayout_update_len(struct nfsd4_layout_seg *lo, u64 end)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	if (end == NFS4_MAX_UINT64)
35162306a36Sopenharmony_ci		lo->length = NFS4_MAX_UINT64;
35262306a36Sopenharmony_ci	else
35362306a36Sopenharmony_ci		lo->length = end - lo->offset;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic bool
35762306a36Sopenharmony_cilayouts_overlapping(struct nfs4_layout *lo, struct nfsd4_layout_seg *s)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	if (s->iomode != IOMODE_ANY && s->iomode != lo->lo_seg.iomode)
36062306a36Sopenharmony_ci		return false;
36162306a36Sopenharmony_ci	if (layout_end(&lo->lo_seg) <= s->offset)
36262306a36Sopenharmony_ci		return false;
36362306a36Sopenharmony_ci	if (layout_end(s) <= lo->lo_seg.offset)
36462306a36Sopenharmony_ci		return false;
36562306a36Sopenharmony_ci	return true;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic bool
36962306a36Sopenharmony_cilayouts_try_merge(struct nfsd4_layout_seg *lo, struct nfsd4_layout_seg *new)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	if (lo->iomode != new->iomode)
37262306a36Sopenharmony_ci		return false;
37362306a36Sopenharmony_ci	if (layout_end(new) < lo->offset)
37462306a36Sopenharmony_ci		return false;
37562306a36Sopenharmony_ci	if (layout_end(lo) < new->offset)
37662306a36Sopenharmony_ci		return false;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	lo->offset = min(lo->offset, new->offset);
37962306a36Sopenharmony_ci	layout_update_len(lo, max(layout_end(lo), layout_end(new)));
38062306a36Sopenharmony_ci	return true;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic __be32
38462306a36Sopenharmony_cinfsd4_recall_conflict(struct nfs4_layout_stateid *ls)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct nfs4_file *fp = ls->ls_stid.sc_file;
38762306a36Sopenharmony_ci	struct nfs4_layout_stateid *l, *n;
38862306a36Sopenharmony_ci	__be32 nfserr = nfs_ok;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	assert_spin_locked(&fp->fi_lock);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	list_for_each_entry_safe(l, n, &fp->fi_lo_states, ls_perfile) {
39362306a36Sopenharmony_ci		if (l != ls) {
39462306a36Sopenharmony_ci			nfsd4_recall_file_layout(l);
39562306a36Sopenharmony_ci			nfserr = nfserr_recallconflict;
39662306a36Sopenharmony_ci		}
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return nfserr;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci__be32
40362306a36Sopenharmony_cinfsd4_insert_layout(struct nfsd4_layoutget *lgp, struct nfs4_layout_stateid *ls)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct nfsd4_layout_seg *seg = &lgp->lg_seg;
40662306a36Sopenharmony_ci	struct nfs4_file *fp = ls->ls_stid.sc_file;
40762306a36Sopenharmony_ci	struct nfs4_layout *lp, *new = NULL;
40862306a36Sopenharmony_ci	__be32 nfserr;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	spin_lock(&fp->fi_lock);
41162306a36Sopenharmony_ci	nfserr = nfsd4_recall_conflict(ls);
41262306a36Sopenharmony_ci	if (nfserr)
41362306a36Sopenharmony_ci		goto out;
41462306a36Sopenharmony_ci	spin_lock(&ls->ls_lock);
41562306a36Sopenharmony_ci	list_for_each_entry(lp, &ls->ls_layouts, lo_perstate) {
41662306a36Sopenharmony_ci		if (layouts_try_merge(&lp->lo_seg, seg))
41762306a36Sopenharmony_ci			goto done;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci	spin_unlock(&ls->ls_lock);
42062306a36Sopenharmony_ci	spin_unlock(&fp->fi_lock);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	new = kmem_cache_alloc(nfs4_layout_cache, GFP_KERNEL);
42362306a36Sopenharmony_ci	if (!new)
42462306a36Sopenharmony_ci		return nfserr_jukebox;
42562306a36Sopenharmony_ci	memcpy(&new->lo_seg, seg, sizeof(new->lo_seg));
42662306a36Sopenharmony_ci	new->lo_state = ls;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	spin_lock(&fp->fi_lock);
42962306a36Sopenharmony_ci	nfserr = nfsd4_recall_conflict(ls);
43062306a36Sopenharmony_ci	if (nfserr)
43162306a36Sopenharmony_ci		goto out;
43262306a36Sopenharmony_ci	spin_lock(&ls->ls_lock);
43362306a36Sopenharmony_ci	list_for_each_entry(lp, &ls->ls_layouts, lo_perstate) {
43462306a36Sopenharmony_ci		if (layouts_try_merge(&lp->lo_seg, seg))
43562306a36Sopenharmony_ci			goto done;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	refcount_inc(&ls->ls_stid.sc_count);
43962306a36Sopenharmony_ci	list_add_tail(&new->lo_perstate, &ls->ls_layouts);
44062306a36Sopenharmony_ci	new = NULL;
44162306a36Sopenharmony_cidone:
44262306a36Sopenharmony_ci	nfs4_inc_and_copy_stateid(&lgp->lg_sid, &ls->ls_stid);
44362306a36Sopenharmony_ci	spin_unlock(&ls->ls_lock);
44462306a36Sopenharmony_ciout:
44562306a36Sopenharmony_ci	spin_unlock(&fp->fi_lock);
44662306a36Sopenharmony_ci	if (new)
44762306a36Sopenharmony_ci		kmem_cache_free(nfs4_layout_cache, new);
44862306a36Sopenharmony_ci	return nfserr;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic void
45262306a36Sopenharmony_cinfsd4_free_layouts(struct list_head *reaplist)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	while (!list_empty(reaplist)) {
45562306a36Sopenharmony_ci		struct nfs4_layout *lp = list_first_entry(reaplist,
45662306a36Sopenharmony_ci				struct nfs4_layout, lo_perstate);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		list_del(&lp->lo_perstate);
45962306a36Sopenharmony_ci		nfs4_put_stid(&lp->lo_state->ls_stid);
46062306a36Sopenharmony_ci		kmem_cache_free(nfs4_layout_cache, lp);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic void
46562306a36Sopenharmony_cinfsd4_return_file_layout(struct nfs4_layout *lp, struct nfsd4_layout_seg *seg,
46662306a36Sopenharmony_ci		struct list_head *reaplist)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct nfsd4_layout_seg *lo = &lp->lo_seg;
46962306a36Sopenharmony_ci	u64 end = layout_end(lo);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (seg->offset <= lo->offset) {
47262306a36Sopenharmony_ci		if (layout_end(seg) >= end) {
47362306a36Sopenharmony_ci			list_move_tail(&lp->lo_perstate, reaplist);
47462306a36Sopenharmony_ci			return;
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci		lo->offset = layout_end(seg);
47762306a36Sopenharmony_ci	} else {
47862306a36Sopenharmony_ci		/* retain the whole layout segment on a split. */
47962306a36Sopenharmony_ci		if (layout_end(seg) < end) {
48062306a36Sopenharmony_ci			dprintk("%s: split not supported\n", __func__);
48162306a36Sopenharmony_ci			return;
48262306a36Sopenharmony_ci		}
48362306a36Sopenharmony_ci		end = seg->offset;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	layout_update_len(lo, end);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci__be32
49062306a36Sopenharmony_cinfsd4_return_file_layouts(struct svc_rqst *rqstp,
49162306a36Sopenharmony_ci		struct nfsd4_compound_state *cstate,
49262306a36Sopenharmony_ci		struct nfsd4_layoutreturn *lrp)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls;
49562306a36Sopenharmony_ci	struct nfs4_layout *lp, *n;
49662306a36Sopenharmony_ci	LIST_HEAD(reaplist);
49762306a36Sopenharmony_ci	__be32 nfserr;
49862306a36Sopenharmony_ci	int found = 0;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lrp->lr_sid,
50162306a36Sopenharmony_ci						false, lrp->lr_layout_type,
50262306a36Sopenharmony_ci						&ls);
50362306a36Sopenharmony_ci	if (nfserr) {
50462306a36Sopenharmony_ci		trace_nfsd_layout_return_lookup_fail(&lrp->lr_sid);
50562306a36Sopenharmony_ci		return nfserr;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	spin_lock(&ls->ls_lock);
50962306a36Sopenharmony_ci	list_for_each_entry_safe(lp, n, &ls->ls_layouts, lo_perstate) {
51062306a36Sopenharmony_ci		if (layouts_overlapping(lp, &lrp->lr_seg)) {
51162306a36Sopenharmony_ci			nfsd4_return_file_layout(lp, &lrp->lr_seg, &reaplist);
51262306a36Sopenharmony_ci			found++;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci	if (!list_empty(&ls->ls_layouts)) {
51662306a36Sopenharmony_ci		if (found)
51762306a36Sopenharmony_ci			nfs4_inc_and_copy_stateid(&lrp->lr_sid, &ls->ls_stid);
51862306a36Sopenharmony_ci		lrp->lrs_present = 1;
51962306a36Sopenharmony_ci	} else {
52062306a36Sopenharmony_ci		trace_nfsd_layoutstate_unhash(&ls->ls_stid.sc_stateid);
52162306a36Sopenharmony_ci		nfs4_unhash_stid(&ls->ls_stid);
52262306a36Sopenharmony_ci		lrp->lrs_present = 0;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	spin_unlock(&ls->ls_lock);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	mutex_unlock(&ls->ls_mutex);
52762306a36Sopenharmony_ci	nfs4_put_stid(&ls->ls_stid);
52862306a36Sopenharmony_ci	nfsd4_free_layouts(&reaplist);
52962306a36Sopenharmony_ci	return nfs_ok;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci__be32
53362306a36Sopenharmony_cinfsd4_return_client_layouts(struct svc_rqst *rqstp,
53462306a36Sopenharmony_ci		struct nfsd4_compound_state *cstate,
53562306a36Sopenharmony_ci		struct nfsd4_layoutreturn *lrp)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls, *n;
53862306a36Sopenharmony_ci	struct nfs4_client *clp = cstate->clp;
53962306a36Sopenharmony_ci	struct nfs4_layout *lp, *t;
54062306a36Sopenharmony_ci	LIST_HEAD(reaplist);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	lrp->lrs_present = 0;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
54562306a36Sopenharmony_ci	list_for_each_entry_safe(ls, n, &clp->cl_lo_states, ls_perclnt) {
54662306a36Sopenharmony_ci		if (ls->ls_layout_type != lrp->lr_layout_type)
54762306a36Sopenharmony_ci			continue;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		if (lrp->lr_return_type == RETURN_FSID &&
55062306a36Sopenharmony_ci		    !fh_fsid_match(&ls->ls_stid.sc_file->fi_fhandle,
55162306a36Sopenharmony_ci				   &cstate->current_fh.fh_handle))
55262306a36Sopenharmony_ci			continue;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		spin_lock(&ls->ls_lock);
55562306a36Sopenharmony_ci		list_for_each_entry_safe(lp, t, &ls->ls_layouts, lo_perstate) {
55662306a36Sopenharmony_ci			if (lrp->lr_seg.iomode == IOMODE_ANY ||
55762306a36Sopenharmony_ci			    lrp->lr_seg.iomode == lp->lo_seg.iomode)
55862306a36Sopenharmony_ci				list_move_tail(&lp->lo_perstate, &reaplist);
55962306a36Sopenharmony_ci		}
56062306a36Sopenharmony_ci		spin_unlock(&ls->ls_lock);
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	nfsd4_free_layouts(&reaplist);
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic void
56962306a36Sopenharmony_cinfsd4_return_all_layouts(struct nfs4_layout_stateid *ls,
57062306a36Sopenharmony_ci		struct list_head *reaplist)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	spin_lock(&ls->ls_lock);
57362306a36Sopenharmony_ci	list_splice_init(&ls->ls_layouts, reaplist);
57462306a36Sopenharmony_ci	spin_unlock(&ls->ls_lock);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_civoid
57862306a36Sopenharmony_cinfsd4_return_all_client_layouts(struct nfs4_client *clp)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls, *n;
58162306a36Sopenharmony_ci	LIST_HEAD(reaplist);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
58462306a36Sopenharmony_ci	list_for_each_entry_safe(ls, n, &clp->cl_lo_states, ls_perclnt)
58562306a36Sopenharmony_ci		nfsd4_return_all_layouts(ls, &reaplist);
58662306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	nfsd4_free_layouts(&reaplist);
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_civoid
59262306a36Sopenharmony_cinfsd4_return_all_file_layouts(struct nfs4_client *clp, struct nfs4_file *fp)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls, *n;
59562306a36Sopenharmony_ci	LIST_HEAD(reaplist);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	spin_lock(&fp->fi_lock);
59862306a36Sopenharmony_ci	list_for_each_entry_safe(ls, n, &fp->fi_lo_states, ls_perfile) {
59962306a36Sopenharmony_ci		if (ls->ls_stid.sc_client == clp)
60062306a36Sopenharmony_ci			nfsd4_return_all_layouts(ls, &reaplist);
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci	spin_unlock(&fp->fi_lock);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	nfsd4_free_layouts(&reaplist);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void
60862306a36Sopenharmony_cinfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct nfs4_client *clp = ls->ls_stid.sc_client;
61162306a36Sopenharmony_ci	char addr_str[INET6_ADDRSTRLEN];
61262306a36Sopenharmony_ci	static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed";
61362306a36Sopenharmony_ci	static char *envp[] = {
61462306a36Sopenharmony_ci		"HOME=/",
61562306a36Sopenharmony_ci		"TERM=linux",
61662306a36Sopenharmony_ci		"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
61762306a36Sopenharmony_ci		NULL
61862306a36Sopenharmony_ci	};
61962306a36Sopenharmony_ci	char *argv[8];
62062306a36Sopenharmony_ci	int error;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	rpc_ntop((struct sockaddr *)&clp->cl_addr, addr_str, sizeof(addr_str));
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	printk(KERN_WARNING
62562306a36Sopenharmony_ci		"nfsd: client %s failed to respond to layout recall. "
62662306a36Sopenharmony_ci		"  Fencing..\n", addr_str);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	argv[0] = (char *)nfsd_recall_failed;
62962306a36Sopenharmony_ci	argv[1] = addr_str;
63062306a36Sopenharmony_ci	argv[2] = ls->ls_file->nf_file->f_path.mnt->mnt_sb->s_id;
63162306a36Sopenharmony_ci	argv[3] = NULL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	error = call_usermodehelper(nfsd_recall_failed, argv, envp,
63462306a36Sopenharmony_ci				    UMH_WAIT_PROC);
63562306a36Sopenharmony_ci	if (error) {
63662306a36Sopenharmony_ci		printk(KERN_ERR "nfsd: fence failed for client %s: %d!\n",
63762306a36Sopenharmony_ci			addr_str, error);
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic void
64262306a36Sopenharmony_cinfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls =
64562306a36Sopenharmony_ci		container_of(cb, struct nfs4_layout_stateid, ls_recall);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	mutex_lock(&ls->ls_mutex);
64862306a36Sopenharmony_ci	nfs4_inc_and_copy_stateid(&ls->ls_recall_sid, &ls->ls_stid);
64962306a36Sopenharmony_ci	mutex_unlock(&ls->ls_mutex);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic int
65362306a36Sopenharmony_cinfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls =
65662306a36Sopenharmony_ci		container_of(cb, struct nfs4_layout_stateid, ls_recall);
65762306a36Sopenharmony_ci	struct nfsd_net *nn;
65862306a36Sopenharmony_ci	ktime_t now, cutoff;
65962306a36Sopenharmony_ci	const struct nfsd4_layout_ops *ops;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	trace_nfsd_cb_layout_done(&ls->ls_stid.sc_stateid, task);
66262306a36Sopenharmony_ci	switch (task->tk_status) {
66362306a36Sopenharmony_ci	case 0:
66462306a36Sopenharmony_ci	case -NFS4ERR_DELAY:
66562306a36Sopenharmony_ci		/*
66662306a36Sopenharmony_ci		 * Anything left? If not, then call it done. Note that we don't
66762306a36Sopenharmony_ci		 * take the spinlock since this is an optimization and nothing
66862306a36Sopenharmony_ci		 * should get added until the cb counter goes to zero.
66962306a36Sopenharmony_ci		 */
67062306a36Sopenharmony_ci		if (list_empty(&ls->ls_layouts))
67162306a36Sopenharmony_ci			return 1;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		/* Poll the client until it's done with the layout */
67462306a36Sopenharmony_ci		now = ktime_get();
67562306a36Sopenharmony_ci		nn = net_generic(ls->ls_stid.sc_client->net, nfsd_net_id);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		/* Client gets 2 lease periods to return it */
67862306a36Sopenharmony_ci		cutoff = ktime_add_ns(task->tk_start,
67962306a36Sopenharmony_ci					 (u64)nn->nfsd4_lease * NSEC_PER_SEC * 2);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		if (ktime_before(now, cutoff)) {
68262306a36Sopenharmony_ci			rpc_delay(task, HZ/100); /* 10 mili-seconds */
68362306a36Sopenharmony_ci			return 0;
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci		fallthrough;
68662306a36Sopenharmony_ci	default:
68762306a36Sopenharmony_ci		/*
68862306a36Sopenharmony_ci		 * Unknown error or non-responding client, we'll need to fence.
68962306a36Sopenharmony_ci		 */
69062306a36Sopenharmony_ci		trace_nfsd_layout_recall_fail(&ls->ls_stid.sc_stateid);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		ops = nfsd4_layout_ops[ls->ls_layout_type];
69362306a36Sopenharmony_ci		if (ops->fence_client)
69462306a36Sopenharmony_ci			ops->fence_client(ls);
69562306a36Sopenharmony_ci		else
69662306a36Sopenharmony_ci			nfsd4_cb_layout_fail(ls);
69762306a36Sopenharmony_ci		return 1;
69862306a36Sopenharmony_ci	case -NFS4ERR_NOMATCHING_LAYOUT:
69962306a36Sopenharmony_ci		trace_nfsd_layout_recall_done(&ls->ls_stid.sc_stateid);
70062306a36Sopenharmony_ci		task->tk_status = 0;
70162306a36Sopenharmony_ci		return 1;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void
70662306a36Sopenharmony_cinfsd4_cb_layout_release(struct nfsd4_callback *cb)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct nfs4_layout_stateid *ls =
70962306a36Sopenharmony_ci		container_of(cb, struct nfs4_layout_stateid, ls_recall);
71062306a36Sopenharmony_ci	LIST_HEAD(reaplist);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	trace_nfsd_layout_recall_release(&ls->ls_stid.sc_stateid);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	nfsd4_return_all_layouts(ls, &reaplist);
71562306a36Sopenharmony_ci	nfsd4_free_layouts(&reaplist);
71662306a36Sopenharmony_ci	nfs4_put_stid(&ls->ls_stid);
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic const struct nfsd4_callback_ops nfsd4_cb_layout_ops = {
72062306a36Sopenharmony_ci	.prepare	= nfsd4_cb_layout_prepare,
72162306a36Sopenharmony_ci	.done		= nfsd4_cb_layout_done,
72262306a36Sopenharmony_ci	.release	= nfsd4_cb_layout_release,
72362306a36Sopenharmony_ci};
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic bool
72662306a36Sopenharmony_cinfsd4_layout_lm_break(struct file_lock *fl)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	/*
72962306a36Sopenharmony_ci	 * We don't want the locks code to timeout the lease for us;
73062306a36Sopenharmony_ci	 * we'll remove it ourself if a layout isn't returned
73162306a36Sopenharmony_ci	 * in time:
73262306a36Sopenharmony_ci	 */
73362306a36Sopenharmony_ci	fl->fl_break_time = 0;
73462306a36Sopenharmony_ci	nfsd4_recall_file_layout(fl->fl_owner);
73562306a36Sopenharmony_ci	return false;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int
73962306a36Sopenharmony_cinfsd4_layout_lm_change(struct file_lock *onlist, int arg,
74062306a36Sopenharmony_ci		struct list_head *dispose)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	BUG_ON(!(arg & F_UNLCK));
74362306a36Sopenharmony_ci	return lease_modify(onlist, arg, dispose);
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic const struct lock_manager_operations nfsd4_layouts_lm_ops = {
74762306a36Sopenharmony_ci	.lm_break	= nfsd4_layout_lm_break,
74862306a36Sopenharmony_ci	.lm_change	= nfsd4_layout_lm_change,
74962306a36Sopenharmony_ci};
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ciint
75262306a36Sopenharmony_cinfsd4_init_pnfs(void)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	int i;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	for (i = 0; i < DEVID_HASH_SIZE; i++)
75762306a36Sopenharmony_ci		INIT_LIST_HEAD(&nfsd_devid_hash[i]);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	nfs4_layout_cache = kmem_cache_create("nfs4_layout",
76062306a36Sopenharmony_ci			sizeof(struct nfs4_layout), 0, 0, NULL);
76162306a36Sopenharmony_ci	if (!nfs4_layout_cache)
76262306a36Sopenharmony_ci		return -ENOMEM;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	nfs4_layout_stateid_cache = kmem_cache_create("nfs4_layout_stateid",
76562306a36Sopenharmony_ci			sizeof(struct nfs4_layout_stateid), 0, 0, NULL);
76662306a36Sopenharmony_ci	if (!nfs4_layout_stateid_cache) {
76762306a36Sopenharmony_ci		kmem_cache_destroy(nfs4_layout_cache);
76862306a36Sopenharmony_ci		return -ENOMEM;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci	return 0;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_civoid
77462306a36Sopenharmony_cinfsd4_exit_pnfs(void)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	int i;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	kmem_cache_destroy(nfs4_layout_cache);
77962306a36Sopenharmony_ci	kmem_cache_destroy(nfs4_layout_stateid_cache);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	for (i = 0; i < DEVID_HASH_SIZE; i++) {
78262306a36Sopenharmony_ci		struct nfsd4_deviceid_map *map, *n;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		list_for_each_entry_safe(map, n, &nfsd_devid_hash[i], hash)
78562306a36Sopenharmony_ci			kfree(map);
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci}
788