162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Common NFS I/O  operations for the pnfs file based
462306a36Sopenharmony_ci * layout drivers.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2014, Primary Data, Inc. All rights reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Tom Haynes <loghyr@primarydata.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1262306a36Sopenharmony_ci#include <linux/nfs_page.h>
1362306a36Sopenharmony_ci#include <linux/sunrpc/addr.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "nfs4session.h"
1762306a36Sopenharmony_ci#include "internal.h"
1862306a36Sopenharmony_ci#include "pnfs.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_PNFS
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_civoid pnfs_generic_rw_release(void *data)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct nfs_pgio_header *hdr = data;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	nfs_put_client(hdr->ds_clp);
2762306a36Sopenharmony_ci	hdr->mds_ops->rpc_release(data);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_rw_release);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Fake up some data that will cause nfs_commit_release to retry the writes. */
3262306a36Sopenharmony_civoid pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct nfs_writeverf *verf = data->res.verf;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	data->task.tk_status = 0;
3762306a36Sopenharmony_ci	memset(&verf->verifier, 0, sizeof(verf->verifier));
3862306a36Sopenharmony_ci	verf->committed = NFS_UNSTABLE;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_civoid pnfs_generic_write_commit_done(struct rpc_task *task, void *data)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct nfs_commit_data *wdata = data;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* Note this may cause RPC to be resent */
4762306a36Sopenharmony_ci	wdata->mds_ops->rpc_call_done(task, data);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_civoid pnfs_generic_commit_release(void *calldata)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct nfs_commit_data *data = calldata;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	data->completion_ops->completion(data);
5662306a36Sopenharmony_ci	pnfs_put_lseg(data->lseg);
5762306a36Sopenharmony_ci	nfs_put_client(data->ds_clp);
5862306a36Sopenharmony_ci	nfs_commitdata_release(data);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_commit_release);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct pnfs_layout_segment *
6362306a36Sopenharmony_cipnfs_free_bucket_lseg(struct pnfs_commit_bucket *bucket)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	if (list_empty(&bucket->committing) && list_empty(&bucket->written)) {
6662306a36Sopenharmony_ci		struct pnfs_layout_segment *freeme = bucket->lseg;
6762306a36Sopenharmony_ci		bucket->lseg = NULL;
6862306a36Sopenharmony_ci		return freeme;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci	return NULL;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* The generic layer is about to remove the req from the commit list.
7462306a36Sopenharmony_ci * If this will make the bucket empty, it will need to put the lseg reference.
7562306a36Sopenharmony_ci * Note this must be called holding nfsi->commit_mutex
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_civoid
7862306a36Sopenharmony_cipnfs_generic_clear_request_commit(struct nfs_page *req,
7962306a36Sopenharmony_ci				  struct nfs_commit_info *cinfo)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct pnfs_commit_bucket *bucket = NULL;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
8462306a36Sopenharmony_ci		goto out;
8562306a36Sopenharmony_ci	cinfo->ds->nwritten--;
8662306a36Sopenharmony_ci	if (list_is_singular(&req->wb_list))
8762306a36Sopenharmony_ci		bucket = list_first_entry(&req->wb_list,
8862306a36Sopenharmony_ci					  struct pnfs_commit_bucket, written);
8962306a36Sopenharmony_ciout:
9062306a36Sopenharmony_ci	nfs_request_remove_commit_list(req, cinfo);
9162306a36Sopenharmony_ci	if (bucket)
9262306a36Sopenharmony_ci		pnfs_put_lseg(pnfs_free_bucket_lseg(bucket));
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistruct pnfs_commit_array *
9762306a36Sopenharmony_cipnfs_alloc_commit_array(size_t n, gfp_t gfp_flags)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct pnfs_commit_array *p;
10062306a36Sopenharmony_ci	struct pnfs_commit_bucket *b;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	p = kmalloc(struct_size(p, buckets, n), gfp_flags);
10362306a36Sopenharmony_ci	if (!p)
10462306a36Sopenharmony_ci		return NULL;
10562306a36Sopenharmony_ci	p->nbuckets = n;
10662306a36Sopenharmony_ci	INIT_LIST_HEAD(&p->cinfo_list);
10762306a36Sopenharmony_ci	INIT_LIST_HEAD(&p->lseg_list);
10862306a36Sopenharmony_ci	p->lseg = NULL;
10962306a36Sopenharmony_ci	for (b = &p->buckets[0]; n != 0; b++, n--) {
11062306a36Sopenharmony_ci		INIT_LIST_HEAD(&b->written);
11162306a36Sopenharmony_ci		INIT_LIST_HEAD(&b->committing);
11262306a36Sopenharmony_ci		b->lseg = NULL;
11362306a36Sopenharmony_ci		b->direct_verf.committed = NFS_INVALID_STABLE_HOW;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci	return p;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_alloc_commit_array);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_civoid
12062306a36Sopenharmony_cipnfs_free_commit_array(struct pnfs_commit_array *p)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	kfree_rcu(p, rcu);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_free_commit_array);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic struct pnfs_commit_array *
12762306a36Sopenharmony_cipnfs_find_commit_array_by_lseg(struct pnfs_ds_commit_info *fl_cinfo,
12862306a36Sopenharmony_ci		struct pnfs_layout_segment *lseg)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct pnfs_commit_array *array;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
13362306a36Sopenharmony_ci		if (array->lseg == lseg)
13462306a36Sopenharmony_ci			return array;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	return NULL;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistruct pnfs_commit_array *
14062306a36Sopenharmony_cipnfs_add_commit_array(struct pnfs_ds_commit_info *fl_cinfo,
14162306a36Sopenharmony_ci		struct pnfs_commit_array *new,
14262306a36Sopenharmony_ci		struct pnfs_layout_segment *lseg)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct pnfs_commit_array *array;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg);
14762306a36Sopenharmony_ci	if (array)
14862306a36Sopenharmony_ci		return array;
14962306a36Sopenharmony_ci	new->lseg = lseg;
15062306a36Sopenharmony_ci	refcount_set(&new->refcount, 1);
15162306a36Sopenharmony_ci	list_add_rcu(&new->cinfo_list, &fl_cinfo->commits);
15262306a36Sopenharmony_ci	list_add(&new->lseg_list, &lseg->pls_commits);
15362306a36Sopenharmony_ci	return new;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_add_commit_array);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic struct pnfs_commit_array *
15862306a36Sopenharmony_cipnfs_lookup_commit_array(struct pnfs_ds_commit_info *fl_cinfo,
15962306a36Sopenharmony_ci		struct pnfs_layout_segment *lseg)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct pnfs_commit_array *array;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	rcu_read_lock();
16462306a36Sopenharmony_ci	array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg);
16562306a36Sopenharmony_ci	if (!array) {
16662306a36Sopenharmony_ci		rcu_read_unlock();
16762306a36Sopenharmony_ci		fl_cinfo->ops->setup_ds_info(fl_cinfo, lseg);
16862306a36Sopenharmony_ci		rcu_read_lock();
16962306a36Sopenharmony_ci		array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	rcu_read_unlock();
17262306a36Sopenharmony_ci	return array;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void
17662306a36Sopenharmony_cipnfs_release_commit_array_locked(struct pnfs_commit_array *array)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	list_del_rcu(&array->cinfo_list);
17962306a36Sopenharmony_ci	list_del(&array->lseg_list);
18062306a36Sopenharmony_ci	pnfs_free_commit_array(array);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void
18462306a36Sopenharmony_cipnfs_put_commit_array_locked(struct pnfs_commit_array *array)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	if (refcount_dec_and_test(&array->refcount))
18762306a36Sopenharmony_ci		pnfs_release_commit_array_locked(array);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void
19162306a36Sopenharmony_cipnfs_put_commit_array(struct pnfs_commit_array *array, struct inode *inode)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	if (refcount_dec_and_lock(&array->refcount, &inode->i_lock)) {
19462306a36Sopenharmony_ci		pnfs_release_commit_array_locked(array);
19562306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic struct pnfs_commit_array *
20062306a36Sopenharmony_cipnfs_get_commit_array(struct pnfs_commit_array *array)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	if (refcount_inc_not_zero(&array->refcount))
20362306a36Sopenharmony_ci		return array;
20462306a36Sopenharmony_ci	return NULL;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void
20862306a36Sopenharmony_cipnfs_remove_and_free_commit_array(struct pnfs_commit_array *array)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	array->lseg = NULL;
21162306a36Sopenharmony_ci	list_del_init(&array->lseg_list);
21262306a36Sopenharmony_ci	pnfs_put_commit_array_locked(array);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_civoid
21662306a36Sopenharmony_cipnfs_generic_ds_cinfo_release_lseg(struct pnfs_ds_commit_info *fl_cinfo,
21762306a36Sopenharmony_ci		struct pnfs_layout_segment *lseg)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct pnfs_commit_array *array, *tmp;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	list_for_each_entry_safe(array, tmp, &lseg->pls_commits, lseg_list)
22262306a36Sopenharmony_ci		pnfs_remove_and_free_commit_array(array);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_release_lseg);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_civoid
22762306a36Sopenharmony_cipnfs_generic_ds_cinfo_destroy(struct pnfs_ds_commit_info *fl_cinfo)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct pnfs_commit_array *array, *tmp;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	list_for_each_entry_safe(array, tmp, &fl_cinfo->commits, cinfo_list)
23262306a36Sopenharmony_ci		pnfs_remove_and_free_commit_array(array);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_destroy);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * Locks the nfs_page requests for commit and moves them to
23862306a36Sopenharmony_ci * @bucket->committing.
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_cistatic int
24162306a36Sopenharmony_cipnfs_bucket_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
24262306a36Sopenharmony_ci				struct nfs_commit_info *cinfo,
24362306a36Sopenharmony_ci				int max)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct list_head *src = &bucket->written;
24662306a36Sopenharmony_ci	struct list_head *dst = &bucket->committing;
24762306a36Sopenharmony_ci	int ret;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	lockdep_assert_held(&NFS_I(cinfo->inode)->commit_mutex);
25062306a36Sopenharmony_ci	ret = nfs_scan_commit_list(src, dst, cinfo, max);
25162306a36Sopenharmony_ci	if (ret) {
25262306a36Sopenharmony_ci		cinfo->ds->nwritten -= ret;
25362306a36Sopenharmony_ci		cinfo->ds->ncommitting += ret;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	return ret;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int pnfs_bucket_scan_array(struct nfs_commit_info *cinfo,
25962306a36Sopenharmony_ci				  struct pnfs_commit_bucket *buckets,
26062306a36Sopenharmony_ci				  unsigned int nbuckets,
26162306a36Sopenharmony_ci				  int max)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	unsigned int i;
26462306a36Sopenharmony_ci	int rv = 0, cnt;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	for (i = 0; i < nbuckets && max != 0; i++) {
26762306a36Sopenharmony_ci		cnt = pnfs_bucket_scan_ds_commit_list(&buckets[i], cinfo, max);
26862306a36Sopenharmony_ci		rv += cnt;
26962306a36Sopenharmony_ci		max -= cnt;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci	return rv;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/* Move reqs from written to committing lists, returning count
27562306a36Sopenharmony_ci * of number moved.
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_ciint pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo, int max)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
28062306a36Sopenharmony_ci	struct pnfs_commit_array *array;
28162306a36Sopenharmony_ci	int rv = 0, cnt;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	rcu_read_lock();
28462306a36Sopenharmony_ci	list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
28562306a36Sopenharmony_ci		if (!array->lseg || !pnfs_get_commit_array(array))
28662306a36Sopenharmony_ci			continue;
28762306a36Sopenharmony_ci		rcu_read_unlock();
28862306a36Sopenharmony_ci		cnt = pnfs_bucket_scan_array(cinfo, array->buckets,
28962306a36Sopenharmony_ci				array->nbuckets, max);
29062306a36Sopenharmony_ci		rcu_read_lock();
29162306a36Sopenharmony_ci		pnfs_put_commit_array(array, cinfo->inode);
29262306a36Sopenharmony_ci		rv += cnt;
29362306a36Sopenharmony_ci		max -= cnt;
29462306a36Sopenharmony_ci		if (!max)
29562306a36Sopenharmony_ci			break;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	rcu_read_unlock();
29862306a36Sopenharmony_ci	return rv;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic unsigned int
30362306a36Sopenharmony_cipnfs_bucket_recover_commit_reqs(struct list_head *dst,
30462306a36Sopenharmony_ci			        struct pnfs_commit_bucket *buckets,
30562306a36Sopenharmony_ci				unsigned int nbuckets,
30662306a36Sopenharmony_ci				struct nfs_commit_info *cinfo)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct pnfs_commit_bucket *b;
30962306a36Sopenharmony_ci	struct pnfs_layout_segment *freeme;
31062306a36Sopenharmony_ci	unsigned int nwritten, ret = 0;
31162306a36Sopenharmony_ci	unsigned int i;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cirestart:
31462306a36Sopenharmony_ci	for (i = 0, b = buckets; i < nbuckets; i++, b++) {
31562306a36Sopenharmony_ci		nwritten = nfs_scan_commit_list(&b->written, dst, cinfo, 0);
31662306a36Sopenharmony_ci		if (!nwritten)
31762306a36Sopenharmony_ci			continue;
31862306a36Sopenharmony_ci		ret += nwritten;
31962306a36Sopenharmony_ci		freeme = pnfs_free_bucket_lseg(b);
32062306a36Sopenharmony_ci		if (freeme) {
32162306a36Sopenharmony_ci			pnfs_put_lseg(freeme);
32262306a36Sopenharmony_ci			goto restart;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	return ret;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/* Pull everything off the committing lists and dump into @dst.  */
32962306a36Sopenharmony_civoid pnfs_generic_recover_commit_reqs(struct list_head *dst,
33062306a36Sopenharmony_ci				      struct nfs_commit_info *cinfo)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
33362306a36Sopenharmony_ci	struct pnfs_commit_array *array;
33462306a36Sopenharmony_ci	unsigned int nwritten;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	lockdep_assert_held(&NFS_I(cinfo->inode)->commit_mutex);
33762306a36Sopenharmony_ci	rcu_read_lock();
33862306a36Sopenharmony_ci	list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
33962306a36Sopenharmony_ci		if (!array->lseg || !pnfs_get_commit_array(array))
34062306a36Sopenharmony_ci			continue;
34162306a36Sopenharmony_ci		rcu_read_unlock();
34262306a36Sopenharmony_ci		nwritten = pnfs_bucket_recover_commit_reqs(dst,
34362306a36Sopenharmony_ci							   array->buckets,
34462306a36Sopenharmony_ci							   array->nbuckets,
34562306a36Sopenharmony_ci							   cinfo);
34662306a36Sopenharmony_ci		rcu_read_lock();
34762306a36Sopenharmony_ci		pnfs_put_commit_array(array, cinfo->inode);
34862306a36Sopenharmony_ci		fl_cinfo->nwritten -= nwritten;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci	rcu_read_unlock();
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic struct nfs_page *
35562306a36Sopenharmony_cipnfs_bucket_search_commit_reqs(struct pnfs_commit_bucket *buckets,
35662306a36Sopenharmony_ci			       unsigned int nbuckets, struct folio *folio)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct nfs_page *req;
35962306a36Sopenharmony_ci	struct pnfs_commit_bucket *b;
36062306a36Sopenharmony_ci	unsigned int i;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* Linearly search the commit lists for each bucket until a matching
36362306a36Sopenharmony_ci	 * request is found */
36462306a36Sopenharmony_ci	for (i = 0, b = buckets; i < nbuckets; i++, b++) {
36562306a36Sopenharmony_ci		list_for_each_entry(req, &b->written, wb_list) {
36662306a36Sopenharmony_ci			if (nfs_page_to_folio(req) == folio)
36762306a36Sopenharmony_ci				return req->wb_head;
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci		list_for_each_entry(req, &b->committing, wb_list) {
37062306a36Sopenharmony_ci			if (nfs_page_to_folio(req) == folio)
37162306a36Sopenharmony_ci				return req->wb_head;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	return NULL;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/* pnfs_generic_search_commit_reqs - Search lists in @cinfo for the head request
37862306a36Sopenharmony_ci *				   for @folio
37962306a36Sopenharmony_ci * @cinfo - commit info for current inode
38062306a36Sopenharmony_ci * @folio - page to search for matching head request
38162306a36Sopenharmony_ci *
38262306a36Sopenharmony_ci * Return: the head request if one is found, otherwise %NULL.
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_cistruct nfs_page *pnfs_generic_search_commit_reqs(struct nfs_commit_info *cinfo,
38562306a36Sopenharmony_ci						 struct folio *folio)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
38862306a36Sopenharmony_ci	struct pnfs_commit_array *array;
38962306a36Sopenharmony_ci	struct nfs_page *req;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	list_for_each_entry(array, &fl_cinfo->commits, cinfo_list) {
39262306a36Sopenharmony_ci		req = pnfs_bucket_search_commit_reqs(array->buckets,
39362306a36Sopenharmony_ci						     array->nbuckets, folio);
39462306a36Sopenharmony_ci		if (req)
39562306a36Sopenharmony_ci			return req;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci	return NULL;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_search_commit_reqs);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic struct pnfs_layout_segment *
40262306a36Sopenharmony_cipnfs_bucket_get_committing(struct list_head *head,
40362306a36Sopenharmony_ci			   struct pnfs_commit_bucket *bucket,
40462306a36Sopenharmony_ci			   struct nfs_commit_info *cinfo)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct pnfs_layout_segment *lseg;
40762306a36Sopenharmony_ci	struct list_head *pos;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	list_for_each(pos, &bucket->committing)
41062306a36Sopenharmony_ci		cinfo->ds->ncommitting--;
41162306a36Sopenharmony_ci	list_splice_init(&bucket->committing, head);
41262306a36Sopenharmony_ci	lseg = pnfs_free_bucket_lseg(bucket);
41362306a36Sopenharmony_ci	if (!lseg)
41462306a36Sopenharmony_ci		lseg = pnfs_get_lseg(bucket->lseg);
41562306a36Sopenharmony_ci	return lseg;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic struct nfs_commit_data *
41962306a36Sopenharmony_cipnfs_bucket_fetch_commitdata(struct pnfs_commit_bucket *bucket,
42062306a36Sopenharmony_ci			     struct nfs_commit_info *cinfo)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct nfs_commit_data *data = nfs_commitdata_alloc();
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (!data)
42562306a36Sopenharmony_ci		return NULL;
42662306a36Sopenharmony_ci	data->lseg = pnfs_bucket_get_committing(&data->pages, bucket, cinfo);
42762306a36Sopenharmony_ci	return data;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic void pnfs_generic_retry_commit(struct pnfs_commit_bucket *buckets,
43162306a36Sopenharmony_ci				      unsigned int nbuckets,
43262306a36Sopenharmony_ci				      struct nfs_commit_info *cinfo,
43362306a36Sopenharmony_ci				      unsigned int idx)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct pnfs_commit_bucket *bucket;
43662306a36Sopenharmony_ci	struct pnfs_layout_segment *freeme;
43762306a36Sopenharmony_ci	LIST_HEAD(pages);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	for (bucket = buckets; idx < nbuckets; bucket++, idx++) {
44062306a36Sopenharmony_ci		if (list_empty(&bucket->committing))
44162306a36Sopenharmony_ci			continue;
44262306a36Sopenharmony_ci		mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
44362306a36Sopenharmony_ci		freeme = pnfs_bucket_get_committing(&pages, bucket, cinfo);
44462306a36Sopenharmony_ci		mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
44562306a36Sopenharmony_ci		nfs_retry_commit(&pages, freeme, cinfo, idx);
44662306a36Sopenharmony_ci		pnfs_put_lseg(freeme);
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic unsigned int
45162306a36Sopenharmony_cipnfs_bucket_alloc_ds_commits(struct list_head *list,
45262306a36Sopenharmony_ci			     struct pnfs_commit_bucket *buckets,
45362306a36Sopenharmony_ci			     unsigned int nbuckets,
45462306a36Sopenharmony_ci			     struct nfs_commit_info *cinfo)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct pnfs_commit_bucket *bucket;
45762306a36Sopenharmony_ci	struct nfs_commit_data *data;
45862306a36Sopenharmony_ci	unsigned int i;
45962306a36Sopenharmony_ci	unsigned int nreq = 0;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	for (i = 0, bucket = buckets; i < nbuckets; i++, bucket++) {
46262306a36Sopenharmony_ci		if (list_empty(&bucket->committing))
46362306a36Sopenharmony_ci			continue;
46462306a36Sopenharmony_ci		mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
46562306a36Sopenharmony_ci		if (!list_empty(&bucket->committing)) {
46662306a36Sopenharmony_ci			data = pnfs_bucket_fetch_commitdata(bucket, cinfo);
46762306a36Sopenharmony_ci			if (!data)
46862306a36Sopenharmony_ci				goto out_error;
46962306a36Sopenharmony_ci			data->ds_commit_index = i;
47062306a36Sopenharmony_ci			list_add_tail(&data->list, list);
47162306a36Sopenharmony_ci			nreq++;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci	return nreq;
47662306a36Sopenharmony_ciout_error:
47762306a36Sopenharmony_ci	mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
47862306a36Sopenharmony_ci	/* Clean up on error */
47962306a36Sopenharmony_ci	pnfs_generic_retry_commit(buckets, nbuckets, cinfo, i);
48062306a36Sopenharmony_ci	return nreq;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic unsigned int
48462306a36Sopenharmony_cipnfs_alloc_ds_commits_list(struct list_head *list,
48562306a36Sopenharmony_ci			   struct pnfs_ds_commit_info *fl_cinfo,
48662306a36Sopenharmony_ci			   struct nfs_commit_info *cinfo)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct pnfs_commit_array *array;
48962306a36Sopenharmony_ci	unsigned int ret = 0;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	rcu_read_lock();
49262306a36Sopenharmony_ci	list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
49362306a36Sopenharmony_ci		if (!array->lseg || !pnfs_get_commit_array(array))
49462306a36Sopenharmony_ci			continue;
49562306a36Sopenharmony_ci		rcu_read_unlock();
49662306a36Sopenharmony_ci		ret += pnfs_bucket_alloc_ds_commits(list, array->buckets,
49762306a36Sopenharmony_ci				array->nbuckets, cinfo);
49862306a36Sopenharmony_ci		rcu_read_lock();
49962306a36Sopenharmony_ci		pnfs_put_commit_array(array, cinfo->inode);
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci	rcu_read_unlock();
50262306a36Sopenharmony_ci	return ret;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/* This follows nfs_commit_list pretty closely */
50662306a36Sopenharmony_ciint
50762306a36Sopenharmony_cipnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
50862306a36Sopenharmony_ci			     int how, struct nfs_commit_info *cinfo,
50962306a36Sopenharmony_ci			     int (*initiate_commit)(struct nfs_commit_data *data,
51062306a36Sopenharmony_ci						    int how))
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
51362306a36Sopenharmony_ci	struct nfs_commit_data *data, *tmp;
51462306a36Sopenharmony_ci	LIST_HEAD(list);
51562306a36Sopenharmony_ci	unsigned int nreq = 0;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (!list_empty(mds_pages)) {
51862306a36Sopenharmony_ci		data = nfs_commitdata_alloc();
51962306a36Sopenharmony_ci		if (!data) {
52062306a36Sopenharmony_ci			nfs_retry_commit(mds_pages, NULL, cinfo, -1);
52162306a36Sopenharmony_ci			return -ENOMEM;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci		data->ds_commit_index = -1;
52462306a36Sopenharmony_ci		list_splice_init(mds_pages, &data->pages);
52562306a36Sopenharmony_ci		list_add_tail(&data->list, &list);
52662306a36Sopenharmony_ci		nreq++;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	nreq += pnfs_alloc_ds_commits_list(&list, fl_cinfo, cinfo);
53062306a36Sopenharmony_ci	if (nreq == 0)
53162306a36Sopenharmony_ci		goto out;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	list_for_each_entry_safe(data, tmp, &list, list) {
53462306a36Sopenharmony_ci		list_del(&data->list);
53562306a36Sopenharmony_ci		if (data->ds_commit_index < 0) {
53662306a36Sopenharmony_ci			nfs_init_commit(data, NULL, NULL, cinfo);
53762306a36Sopenharmony_ci			nfs_initiate_commit(NFS_CLIENT(inode), data,
53862306a36Sopenharmony_ci					    NFS_PROTO(data->inode),
53962306a36Sopenharmony_ci					    data->mds_ops, how,
54062306a36Sopenharmony_ci					    RPC_TASK_CRED_NOREF);
54162306a36Sopenharmony_ci		} else {
54262306a36Sopenharmony_ci			nfs_init_commit(data, NULL, data->lseg, cinfo);
54362306a36Sopenharmony_ci			initiate_commit(data, how);
54462306a36Sopenharmony_ci		}
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ciout:
54762306a36Sopenharmony_ci	return PNFS_ATTEMPTED;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci/*
55262306a36Sopenharmony_ci * Data server cache
55362306a36Sopenharmony_ci *
55462306a36Sopenharmony_ci * Data servers can be mapped to different device ids.
55562306a36Sopenharmony_ci * nfs4_pnfs_ds reference counting
55662306a36Sopenharmony_ci *   - set to 1 on allocation
55762306a36Sopenharmony_ci *   - incremented when a device id maps a data server already in the cache.
55862306a36Sopenharmony_ci *   - decremented when deviceid is removed from the cache.
55962306a36Sopenharmony_ci */
56062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nfs4_ds_cache_lock);
56162306a36Sopenharmony_cistatic LIST_HEAD(nfs4_data_server_cache);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* Debug routines */
56462306a36Sopenharmony_cistatic void
56562306a36Sopenharmony_ciprint_ds(struct nfs4_pnfs_ds *ds)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	if (ds == NULL) {
56862306a36Sopenharmony_ci		printk(KERN_WARNING "%s NULL device\n", __func__);
56962306a36Sopenharmony_ci		return;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci	printk(KERN_WARNING "        ds %s\n"
57262306a36Sopenharmony_ci		"        ref count %d\n"
57362306a36Sopenharmony_ci		"        client %p\n"
57462306a36Sopenharmony_ci		"        cl_exchange_flags %x\n",
57562306a36Sopenharmony_ci		ds->ds_remotestr,
57662306a36Sopenharmony_ci		refcount_read(&ds->ds_count), ds->ds_clp,
57762306a36Sopenharmony_ci		ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic bool
58162306a36Sopenharmony_cisame_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct sockaddr_in *a, *b;
58462306a36Sopenharmony_ci	struct sockaddr_in6 *a6, *b6;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (addr1->sa_family != addr2->sa_family)
58762306a36Sopenharmony_ci		return false;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	switch (addr1->sa_family) {
59062306a36Sopenharmony_ci	case AF_INET:
59162306a36Sopenharmony_ci		a = (struct sockaddr_in *)addr1;
59262306a36Sopenharmony_ci		b = (struct sockaddr_in *)addr2;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		if (a->sin_addr.s_addr == b->sin_addr.s_addr &&
59562306a36Sopenharmony_ci		    a->sin_port == b->sin_port)
59662306a36Sopenharmony_ci			return true;
59762306a36Sopenharmony_ci		break;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	case AF_INET6:
60062306a36Sopenharmony_ci		a6 = (struct sockaddr_in6 *)addr1;
60162306a36Sopenharmony_ci		b6 = (struct sockaddr_in6 *)addr2;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		/* LINKLOCAL addresses must have matching scope_id */
60462306a36Sopenharmony_ci		if (ipv6_addr_src_scope(&a6->sin6_addr) ==
60562306a36Sopenharmony_ci		    IPV6_ADDR_SCOPE_LINKLOCAL &&
60662306a36Sopenharmony_ci		    a6->sin6_scope_id != b6->sin6_scope_id)
60762306a36Sopenharmony_ci			return false;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) &&
61062306a36Sopenharmony_ci		    a6->sin6_port == b6->sin6_port)
61162306a36Sopenharmony_ci			return true;
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	default:
61562306a36Sopenharmony_ci		dprintk("%s: unhandled address family: %u\n",
61662306a36Sopenharmony_ci			__func__, addr1->sa_family);
61762306a36Sopenharmony_ci		return false;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return false;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci/*
62462306a36Sopenharmony_ci * Checks if 'dsaddrs1' contains a subset of 'dsaddrs2'. If it does,
62562306a36Sopenharmony_ci * declare a match.
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cistatic bool
62862306a36Sopenharmony_ci_same_data_server_addrs_locked(const struct list_head *dsaddrs1,
62962306a36Sopenharmony_ci			       const struct list_head *dsaddrs2)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct nfs4_pnfs_ds_addr *da1, *da2;
63262306a36Sopenharmony_ci	struct sockaddr *sa1, *sa2;
63362306a36Sopenharmony_ci	bool match = false;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	list_for_each_entry(da1, dsaddrs1, da_node) {
63662306a36Sopenharmony_ci		sa1 = (struct sockaddr *)&da1->da_addr;
63762306a36Sopenharmony_ci		match = false;
63862306a36Sopenharmony_ci		list_for_each_entry(da2, dsaddrs2, da_node) {
63962306a36Sopenharmony_ci			sa2 = (struct sockaddr *)&da2->da_addr;
64062306a36Sopenharmony_ci			match = same_sockaddr(sa1, sa2);
64162306a36Sopenharmony_ci			if (match)
64262306a36Sopenharmony_ci				break;
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci		if (!match)
64562306a36Sopenharmony_ci			break;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci	return match;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/*
65162306a36Sopenharmony_ci * Lookup DS by addresses.  nfs4_ds_cache_lock is held
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic struct nfs4_pnfs_ds *
65462306a36Sopenharmony_ci_data_server_lookup_locked(const struct list_head *dsaddrs)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct nfs4_pnfs_ds *ds;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	list_for_each_entry(ds, &nfs4_data_server_cache, ds_node)
65962306a36Sopenharmony_ci		if (_same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs))
66062306a36Sopenharmony_ci			return ds;
66162306a36Sopenharmony_ci	return NULL;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic struct nfs4_pnfs_ds_addr *nfs4_pnfs_ds_addr_alloc(gfp_t gfp_flags)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct nfs4_pnfs_ds_addr *da = kzalloc(sizeof(*da), gfp_flags);
66762306a36Sopenharmony_ci	if (da)
66862306a36Sopenharmony_ci		INIT_LIST_HEAD(&da->da_node);
66962306a36Sopenharmony_ci	return da;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic void nfs4_pnfs_ds_addr_free(struct nfs4_pnfs_ds_addr *da)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	kfree(da->da_remotestr);
67562306a36Sopenharmony_ci	kfree(da->da_netid);
67662306a36Sopenharmony_ci	kfree(da);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic void destroy_ds(struct nfs4_pnfs_ds *ds)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct nfs4_pnfs_ds_addr *da;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	dprintk("--> %s\n", __func__);
68462306a36Sopenharmony_ci	ifdebug(FACILITY)
68562306a36Sopenharmony_ci		print_ds(ds);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	nfs_put_client(ds->ds_clp);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	while (!list_empty(&ds->ds_addrs)) {
69062306a36Sopenharmony_ci		da = list_first_entry(&ds->ds_addrs,
69162306a36Sopenharmony_ci				      struct nfs4_pnfs_ds_addr,
69262306a36Sopenharmony_ci				      da_node);
69362306a36Sopenharmony_ci		list_del_init(&da->da_node);
69462306a36Sopenharmony_ci		nfs4_pnfs_ds_addr_free(da);
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	kfree(ds->ds_remotestr);
69862306a36Sopenharmony_ci	kfree(ds);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_civoid nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	if (refcount_dec_and_lock(&ds->ds_count,
70462306a36Sopenharmony_ci				&nfs4_ds_cache_lock)) {
70562306a36Sopenharmony_ci		list_del_init(&ds->ds_node);
70662306a36Sopenharmony_ci		spin_unlock(&nfs4_ds_cache_lock);
70762306a36Sopenharmony_ci		destroy_ds(ds);
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_pnfs_ds_put);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci/*
71362306a36Sopenharmony_ci * Create a string with a human readable address and port to avoid
71462306a36Sopenharmony_ci * complicated setup around many dprinks.
71562306a36Sopenharmony_ci */
71662306a36Sopenharmony_cistatic char *
71762306a36Sopenharmony_cinfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct nfs4_pnfs_ds_addr *da;
72062306a36Sopenharmony_ci	char *remotestr;
72162306a36Sopenharmony_ci	size_t len;
72262306a36Sopenharmony_ci	char *p;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	len = 3;        /* '{', '}' and eol */
72562306a36Sopenharmony_ci	list_for_each_entry(da, dsaddrs, da_node) {
72662306a36Sopenharmony_ci		len += strlen(da->da_remotestr) + 1;    /* string plus comma */
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	remotestr = kzalloc(len, gfp_flags);
73062306a36Sopenharmony_ci	if (!remotestr)
73162306a36Sopenharmony_ci		return NULL;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	p = remotestr;
73462306a36Sopenharmony_ci	*(p++) = '{';
73562306a36Sopenharmony_ci	len--;
73662306a36Sopenharmony_ci	list_for_each_entry(da, dsaddrs, da_node) {
73762306a36Sopenharmony_ci		size_t ll = strlen(da->da_remotestr);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		if (ll > len)
74062306a36Sopenharmony_ci			goto out_err;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		memcpy(p, da->da_remotestr, ll);
74362306a36Sopenharmony_ci		p += ll;
74462306a36Sopenharmony_ci		len -= ll;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		if (len < 1)
74762306a36Sopenharmony_ci			goto out_err;
74862306a36Sopenharmony_ci		(*p++) = ',';
74962306a36Sopenharmony_ci		len--;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci	if (len < 2)
75262306a36Sopenharmony_ci		goto out_err;
75362306a36Sopenharmony_ci	*(p++) = '}';
75462306a36Sopenharmony_ci	*p = '\0';
75562306a36Sopenharmony_ci	return remotestr;
75662306a36Sopenharmony_ciout_err:
75762306a36Sopenharmony_ci	kfree(remotestr);
75862306a36Sopenharmony_ci	return NULL;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci/*
76262306a36Sopenharmony_ci * Given a list of multipath struct nfs4_pnfs_ds_addr, add it to ds cache if
76362306a36Sopenharmony_ci * uncached and return cached struct nfs4_pnfs_ds.
76462306a36Sopenharmony_ci */
76562306a36Sopenharmony_cistruct nfs4_pnfs_ds *
76662306a36Sopenharmony_cinfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	struct nfs4_pnfs_ds *tmp_ds, *ds = NULL;
76962306a36Sopenharmony_ci	char *remotestr;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (list_empty(dsaddrs)) {
77262306a36Sopenharmony_ci		dprintk("%s: no addresses defined\n", __func__);
77362306a36Sopenharmony_ci		goto out;
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	ds = kzalloc(sizeof(*ds), gfp_flags);
77762306a36Sopenharmony_ci	if (!ds)
77862306a36Sopenharmony_ci		goto out;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* this is only used for debugging, so it's ok if its NULL */
78162306a36Sopenharmony_ci	remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	spin_lock(&nfs4_ds_cache_lock);
78462306a36Sopenharmony_ci	tmp_ds = _data_server_lookup_locked(dsaddrs);
78562306a36Sopenharmony_ci	if (tmp_ds == NULL) {
78662306a36Sopenharmony_ci		INIT_LIST_HEAD(&ds->ds_addrs);
78762306a36Sopenharmony_ci		list_splice_init(dsaddrs, &ds->ds_addrs);
78862306a36Sopenharmony_ci		ds->ds_remotestr = remotestr;
78962306a36Sopenharmony_ci		refcount_set(&ds->ds_count, 1);
79062306a36Sopenharmony_ci		INIT_LIST_HEAD(&ds->ds_node);
79162306a36Sopenharmony_ci		ds->ds_clp = NULL;
79262306a36Sopenharmony_ci		list_add(&ds->ds_node, &nfs4_data_server_cache);
79362306a36Sopenharmony_ci		dprintk("%s add new data server %s\n", __func__,
79462306a36Sopenharmony_ci			ds->ds_remotestr);
79562306a36Sopenharmony_ci	} else {
79662306a36Sopenharmony_ci		kfree(remotestr);
79762306a36Sopenharmony_ci		kfree(ds);
79862306a36Sopenharmony_ci		refcount_inc(&tmp_ds->ds_count);
79962306a36Sopenharmony_ci		dprintk("%s data server %s found, inc'ed ds_count to %d\n",
80062306a36Sopenharmony_ci			__func__, tmp_ds->ds_remotestr,
80162306a36Sopenharmony_ci			refcount_read(&tmp_ds->ds_count));
80262306a36Sopenharmony_ci		ds = tmp_ds;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci	spin_unlock(&nfs4_ds_cache_lock);
80562306a36Sopenharmony_ciout:
80662306a36Sopenharmony_ci	return ds;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_pnfs_ds_add);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic int nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	might_sleep();
81362306a36Sopenharmony_ci	return wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, TASK_KILLABLE);
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	smp_mb__before_atomic();
81962306a36Sopenharmony_ci	clear_and_wake_up_bit(NFS4DS_CONNECTING, &ds->ds_state);
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic struct nfs_client *(*get_v3_ds_connect)(
82362306a36Sopenharmony_ci			struct nfs_server *mds_srv,
82462306a36Sopenharmony_ci			const struct sockaddr_storage *ds_addr,
82562306a36Sopenharmony_ci			int ds_addrlen,
82662306a36Sopenharmony_ci			int ds_proto,
82762306a36Sopenharmony_ci			unsigned int ds_timeo,
82862306a36Sopenharmony_ci			unsigned int ds_retrans);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic bool load_v3_ds_connect(void)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	if (!get_v3_ds_connect) {
83362306a36Sopenharmony_ci		get_v3_ds_connect = symbol_request(nfs3_set_ds_client);
83462306a36Sopenharmony_ci		WARN_ON_ONCE(!get_v3_ds_connect);
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	return(get_v3_ds_connect != NULL);
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_civoid nfs4_pnfs_v3_ds_connect_unload(void)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	if (get_v3_ds_connect) {
84362306a36Sopenharmony_ci		symbol_put(nfs3_set_ds_client);
84462306a36Sopenharmony_ci		get_v3_ds_connect = NULL;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
84962306a36Sopenharmony_ci				 struct nfs4_pnfs_ds *ds,
85062306a36Sopenharmony_ci				 unsigned int timeo,
85162306a36Sopenharmony_ci				 unsigned int retrans)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct nfs_client *clp = ERR_PTR(-EIO);
85462306a36Sopenharmony_ci	struct nfs4_pnfs_ds_addr *da;
85562306a36Sopenharmony_ci	unsigned long connect_timeout = timeo * (retrans + 1) * HZ / 10;
85662306a36Sopenharmony_ci	int status = 0;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (!load_v3_ds_connect())
86162306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	list_for_each_entry(da, &ds->ds_addrs, da_node) {
86462306a36Sopenharmony_ci		dprintk("%s: DS %s: trying address %s\n",
86562306a36Sopenharmony_ci			__func__, ds->ds_remotestr, da->da_remotestr);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		if (!IS_ERR(clp)) {
86862306a36Sopenharmony_ci			struct xprt_create xprt_args = {
86962306a36Sopenharmony_ci				.ident = da->da_transport,
87062306a36Sopenharmony_ci				.net = clp->cl_net,
87162306a36Sopenharmony_ci				.dstaddr = (struct sockaddr *)&da->da_addr,
87262306a36Sopenharmony_ci				.addrlen = da->da_addrlen,
87362306a36Sopenharmony_ci				.servername = clp->cl_hostname,
87462306a36Sopenharmony_ci				.connect_timeout = connect_timeout,
87562306a36Sopenharmony_ci				.reconnect_timeout = connect_timeout,
87662306a36Sopenharmony_ci			};
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci			if (da->da_transport != clp->cl_proto)
87962306a36Sopenharmony_ci				continue;
88062306a36Sopenharmony_ci			if (da->da_addr.ss_family != clp->cl_addr.ss_family)
88162306a36Sopenharmony_ci				continue;
88262306a36Sopenharmony_ci			/* Add this address as an alias */
88362306a36Sopenharmony_ci			rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
88462306a36Sopenharmony_ci					rpc_clnt_test_and_add_xprt, NULL);
88562306a36Sopenharmony_ci			continue;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci		clp = get_v3_ds_connect(mds_srv,
88862306a36Sopenharmony_ci				&da->da_addr,
88962306a36Sopenharmony_ci				da->da_addrlen, da->da_transport,
89062306a36Sopenharmony_ci				timeo, retrans);
89162306a36Sopenharmony_ci		if (IS_ERR(clp))
89262306a36Sopenharmony_ci			continue;
89362306a36Sopenharmony_ci		clp->cl_rpcclient->cl_softerr = 0;
89462306a36Sopenharmony_ci		clp->cl_rpcclient->cl_softrtry = 0;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (IS_ERR(clp)) {
89862306a36Sopenharmony_ci		status = PTR_ERR(clp);
89962306a36Sopenharmony_ci		goto out;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	smp_wmb();
90362306a36Sopenharmony_ci	WRITE_ONCE(ds->ds_clp, clp);
90462306a36Sopenharmony_ci	dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
90562306a36Sopenharmony_ciout:
90662306a36Sopenharmony_ci	return status;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
91062306a36Sopenharmony_ci				 struct nfs4_pnfs_ds *ds,
91162306a36Sopenharmony_ci				 unsigned int timeo,
91262306a36Sopenharmony_ci				 unsigned int retrans,
91362306a36Sopenharmony_ci				 u32 minor_version)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct nfs_client *clp = ERR_PTR(-EIO);
91662306a36Sopenharmony_ci	struct nfs4_pnfs_ds_addr *da;
91762306a36Sopenharmony_ci	int status = 0;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	list_for_each_entry(da, &ds->ds_addrs, da_node) {
92262306a36Sopenharmony_ci		char servername[48];
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		dprintk("%s: DS %s: trying address %s\n",
92562306a36Sopenharmony_ci			__func__, ds->ds_remotestr, da->da_remotestr);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		if (!IS_ERR(clp) && clp->cl_mvops->session_trunk) {
92862306a36Sopenharmony_ci			struct xprt_create xprt_args = {
92962306a36Sopenharmony_ci				.ident = da->da_transport,
93062306a36Sopenharmony_ci				.net = clp->cl_net,
93162306a36Sopenharmony_ci				.dstaddr = (struct sockaddr *)&da->da_addr,
93262306a36Sopenharmony_ci				.addrlen = da->da_addrlen,
93362306a36Sopenharmony_ci				.servername = clp->cl_hostname,
93462306a36Sopenharmony_ci				.xprtsec = clp->cl_xprtsec,
93562306a36Sopenharmony_ci			};
93662306a36Sopenharmony_ci			struct nfs4_add_xprt_data xprtdata = {
93762306a36Sopenharmony_ci				.clp = clp,
93862306a36Sopenharmony_ci			};
93962306a36Sopenharmony_ci			struct rpc_add_xprt_test rpcdata = {
94062306a36Sopenharmony_ci				.add_xprt_test = clp->cl_mvops->session_trunk,
94162306a36Sopenharmony_ci				.data = &xprtdata,
94262306a36Sopenharmony_ci			};
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci			if (da->da_transport != clp->cl_proto &&
94562306a36Sopenharmony_ci					clp->cl_proto != XPRT_TRANSPORT_TCP_TLS)
94662306a36Sopenharmony_ci				continue;
94762306a36Sopenharmony_ci			if (da->da_transport == XPRT_TRANSPORT_TCP &&
94862306a36Sopenharmony_ci				mds_srv->nfs_client->cl_proto ==
94962306a36Sopenharmony_ci					XPRT_TRANSPORT_TCP_TLS) {
95062306a36Sopenharmony_ci				struct sockaddr *addr =
95162306a36Sopenharmony_ci					(struct sockaddr *)&da->da_addr;
95262306a36Sopenharmony_ci				struct sockaddr_in *sin =
95362306a36Sopenharmony_ci					(struct sockaddr_in *)&da->da_addr;
95462306a36Sopenharmony_ci				struct sockaddr_in6 *sin6 =
95562306a36Sopenharmony_ci					(struct sockaddr_in6 *)&da->da_addr;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci				/* for NFS with TLS we need to supply a correct
95862306a36Sopenharmony_ci				 * servername of the trunked transport, not the
95962306a36Sopenharmony_ci				 * servername of the main transport stored in
96062306a36Sopenharmony_ci				 * clp->cl_hostname. And set the protocol to
96162306a36Sopenharmony_ci				 * indicate to use TLS
96262306a36Sopenharmony_ci				 */
96362306a36Sopenharmony_ci				servername[0] = '\0';
96462306a36Sopenharmony_ci				switch(addr->sa_family) {
96562306a36Sopenharmony_ci				case AF_INET:
96662306a36Sopenharmony_ci					snprintf(servername, sizeof(servername),
96762306a36Sopenharmony_ci						"%pI4", &sin->sin_addr.s_addr);
96862306a36Sopenharmony_ci					break;
96962306a36Sopenharmony_ci				case AF_INET6:
97062306a36Sopenharmony_ci					snprintf(servername, sizeof(servername),
97162306a36Sopenharmony_ci						"%pI6", &sin6->sin6_addr);
97262306a36Sopenharmony_ci					break;
97362306a36Sopenharmony_ci				default:
97462306a36Sopenharmony_ci					/* do not consider this address */
97562306a36Sopenharmony_ci					continue;
97662306a36Sopenharmony_ci				}
97762306a36Sopenharmony_ci				xprt_args.ident = XPRT_TRANSPORT_TCP_TLS;
97862306a36Sopenharmony_ci				xprt_args.servername = servername;
97962306a36Sopenharmony_ci			}
98062306a36Sopenharmony_ci			if (da->da_addr.ss_family != clp->cl_addr.ss_family)
98162306a36Sopenharmony_ci				continue;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci			/**
98462306a36Sopenharmony_ci			* Test this address for session trunking and
98562306a36Sopenharmony_ci			* add as an alias
98662306a36Sopenharmony_ci			*/
98762306a36Sopenharmony_ci			xprtdata.cred = nfs4_get_clid_cred(clp);
98862306a36Sopenharmony_ci			rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
98962306a36Sopenharmony_ci					  rpc_clnt_setup_test_and_add_xprt,
99062306a36Sopenharmony_ci					  &rpcdata);
99162306a36Sopenharmony_ci			if (xprtdata.cred)
99262306a36Sopenharmony_ci				put_cred(xprtdata.cred);
99362306a36Sopenharmony_ci		} else {
99462306a36Sopenharmony_ci			if (da->da_transport == XPRT_TRANSPORT_TCP &&
99562306a36Sopenharmony_ci				mds_srv->nfs_client->cl_proto ==
99662306a36Sopenharmony_ci					XPRT_TRANSPORT_TCP_TLS)
99762306a36Sopenharmony_ci				da->da_transport = XPRT_TRANSPORT_TCP_TLS;
99862306a36Sopenharmony_ci			clp = nfs4_set_ds_client(mds_srv,
99962306a36Sopenharmony_ci						&da->da_addr,
100062306a36Sopenharmony_ci						da->da_addrlen,
100162306a36Sopenharmony_ci						da->da_transport, timeo,
100262306a36Sopenharmony_ci						retrans, minor_version);
100362306a36Sopenharmony_ci			if (IS_ERR(clp))
100462306a36Sopenharmony_ci				continue;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci			status = nfs4_init_ds_session(clp,
100762306a36Sopenharmony_ci					mds_srv->nfs_client->cl_lease_time);
100862306a36Sopenharmony_ci			if (status) {
100962306a36Sopenharmony_ci				nfs_put_client(clp);
101062306a36Sopenharmony_ci				clp = ERR_PTR(-EIO);
101162306a36Sopenharmony_ci				continue;
101262306a36Sopenharmony_ci			}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		}
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	if (IS_ERR(clp)) {
101862306a36Sopenharmony_ci		status = PTR_ERR(clp);
101962306a36Sopenharmony_ci		goto out;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	smp_wmb();
102362306a36Sopenharmony_ci	WRITE_ONCE(ds->ds_clp, clp);
102462306a36Sopenharmony_ci	dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
102562306a36Sopenharmony_ciout:
102662306a36Sopenharmony_ci	return status;
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci/*
103062306a36Sopenharmony_ci * Create an rpc connection to the nfs4_pnfs_ds data server.
103162306a36Sopenharmony_ci * Currently only supports IPv4 and IPv6 addresses.
103262306a36Sopenharmony_ci * If connection fails, make devid unavailable and return a -errno.
103362306a36Sopenharmony_ci */
103462306a36Sopenharmony_ciint nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
103562306a36Sopenharmony_ci			  struct nfs4_deviceid_node *devid, unsigned int timeo,
103662306a36Sopenharmony_ci			  unsigned int retrans, u32 version, u32 minor_version)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	int err;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	do {
104162306a36Sopenharmony_ci		err = nfs4_wait_ds_connect(ds);
104262306a36Sopenharmony_ci		if (err || ds->ds_clp)
104362306a36Sopenharmony_ci			goto out;
104462306a36Sopenharmony_ci		if (nfs4_test_deviceid_unavailable(devid))
104562306a36Sopenharmony_ci			return -ENODEV;
104662306a36Sopenharmony_ci	} while (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) != 0);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (ds->ds_clp)
104962306a36Sopenharmony_ci		goto connect_done;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	switch (version) {
105262306a36Sopenharmony_ci	case 3:
105362306a36Sopenharmony_ci		err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo, retrans);
105462306a36Sopenharmony_ci		break;
105562306a36Sopenharmony_ci	case 4:
105662306a36Sopenharmony_ci		err = _nfs4_pnfs_v4_ds_connect(mds_srv, ds, timeo, retrans,
105762306a36Sopenharmony_ci					       minor_version);
105862306a36Sopenharmony_ci		break;
105962306a36Sopenharmony_ci	default:
106062306a36Sopenharmony_ci		dprintk("%s: unsupported DS version %d\n", __func__, version);
106162306a36Sopenharmony_ci		err = -EPROTONOSUPPORT;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ciconnect_done:
106562306a36Sopenharmony_ci	nfs4_clear_ds_conn_bit(ds);
106662306a36Sopenharmony_ciout:
106762306a36Sopenharmony_ci	/*
106862306a36Sopenharmony_ci	 * At this point the ds->ds_clp should be ready, but it might have
106962306a36Sopenharmony_ci	 * hit an error.
107062306a36Sopenharmony_ci	 */
107162306a36Sopenharmony_ci	if (!err) {
107262306a36Sopenharmony_ci		if (!ds->ds_clp || !nfs_client_init_is_complete(ds->ds_clp)) {
107362306a36Sopenharmony_ci			WARN_ON_ONCE(ds->ds_clp ||
107462306a36Sopenharmony_ci				!nfs4_test_deviceid_unavailable(devid));
107562306a36Sopenharmony_ci			return -EINVAL;
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci		err = nfs_client_init_status(ds->ds_clp);
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return err;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_pnfs_ds_connect);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci/*
108562306a36Sopenharmony_ci * Currently only supports ipv4, ipv6 and one multi-path address.
108662306a36Sopenharmony_ci */
108762306a36Sopenharmony_cistruct nfs4_pnfs_ds_addr *
108862306a36Sopenharmony_cinfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	struct nfs4_pnfs_ds_addr *da = NULL;
109162306a36Sopenharmony_ci	char *buf, *portstr;
109262306a36Sopenharmony_ci	__be16 port;
109362306a36Sopenharmony_ci	ssize_t nlen, rlen;
109462306a36Sopenharmony_ci	int tmp[2];
109562306a36Sopenharmony_ci	char *netid;
109662306a36Sopenharmony_ci	size_t len;
109762306a36Sopenharmony_ci	char *startsep = "";
109862306a36Sopenharmony_ci	char *endsep = "";
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	/* r_netid */
110262306a36Sopenharmony_ci	nlen = xdr_stream_decode_string_dup(xdr, &netid, XDR_MAX_NETOBJ,
110362306a36Sopenharmony_ci					    gfp_flags);
110462306a36Sopenharmony_ci	if (unlikely(nlen < 0))
110562306a36Sopenharmony_ci		goto out_err;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */
110862306a36Sopenharmony_ci	/* port is ".ABC.DEF", 8 chars max */
110962306a36Sopenharmony_ci	rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN +
111062306a36Sopenharmony_ci					    IPV6_SCOPE_ID_LEN + 8, gfp_flags);
111162306a36Sopenharmony_ci	if (unlikely(rlen < 0))
111262306a36Sopenharmony_ci		goto out_free_netid;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	/* replace port '.' with '-' */
111562306a36Sopenharmony_ci	portstr = strrchr(buf, '.');
111662306a36Sopenharmony_ci	if (!portstr) {
111762306a36Sopenharmony_ci		dprintk("%s: Failed finding expected dot in port\n",
111862306a36Sopenharmony_ci			__func__);
111962306a36Sopenharmony_ci		goto out_free_buf;
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci	*portstr = '-';
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/* find '.' between address and port */
112462306a36Sopenharmony_ci	portstr = strrchr(buf, '.');
112562306a36Sopenharmony_ci	if (!portstr) {
112662306a36Sopenharmony_ci		dprintk("%s: Failed finding expected dot between address and "
112762306a36Sopenharmony_ci			"port\n", __func__);
112862306a36Sopenharmony_ci		goto out_free_buf;
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci	*portstr = '\0';
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	da = nfs4_pnfs_ds_addr_alloc(gfp_flags);
113362306a36Sopenharmony_ci	if (unlikely(!da))
113462306a36Sopenharmony_ci		goto out_free_buf;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (!rpc_pton(net, buf, portstr-buf, (struct sockaddr *)&da->da_addr,
113762306a36Sopenharmony_ci		      sizeof(da->da_addr))) {
113862306a36Sopenharmony_ci		dprintk("%s: error parsing address %s\n", __func__, buf);
113962306a36Sopenharmony_ci		goto out_free_da;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	portstr++;
114362306a36Sopenharmony_ci	sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]);
114462306a36Sopenharmony_ci	port = htons((tmp[0] << 8) | (tmp[1]));
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	switch (da->da_addr.ss_family) {
114762306a36Sopenharmony_ci	case AF_INET:
114862306a36Sopenharmony_ci		((struct sockaddr_in *)&da->da_addr)->sin_port = port;
114962306a36Sopenharmony_ci		da->da_addrlen = sizeof(struct sockaddr_in);
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	case AF_INET6:
115362306a36Sopenharmony_ci		((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port;
115462306a36Sopenharmony_ci		da->da_addrlen = sizeof(struct sockaddr_in6);
115562306a36Sopenharmony_ci		startsep = "[";
115662306a36Sopenharmony_ci		endsep = "]";
115762306a36Sopenharmony_ci		break;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	default:
116062306a36Sopenharmony_ci		dprintk("%s: unsupported address family: %u\n",
116162306a36Sopenharmony_ci			__func__, da->da_addr.ss_family);
116262306a36Sopenharmony_ci		goto out_free_da;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	da->da_transport = xprt_find_transport_ident(netid);
116662306a36Sopenharmony_ci	if (da->da_transport < 0) {
116762306a36Sopenharmony_ci		dprintk("%s: ERROR: unknown r_netid \"%s\"\n",
116862306a36Sopenharmony_ci			__func__, netid);
116962306a36Sopenharmony_ci		goto out_free_da;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	da->da_netid = netid;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* save human readable address */
117562306a36Sopenharmony_ci	len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7;
117662306a36Sopenharmony_ci	da->da_remotestr = kzalloc(len, gfp_flags);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* NULL is ok, only used for dprintk */
117962306a36Sopenharmony_ci	if (da->da_remotestr)
118062306a36Sopenharmony_ci		snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep,
118162306a36Sopenharmony_ci			 buf, endsep, ntohs(port));
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr);
118462306a36Sopenharmony_ci	kfree(buf);
118562306a36Sopenharmony_ci	return da;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ciout_free_da:
118862306a36Sopenharmony_ci	kfree(da);
118962306a36Sopenharmony_ciout_free_buf:
119062306a36Sopenharmony_ci	dprintk("%s: Error parsing DS addr: %s\n", __func__, buf);
119162306a36Sopenharmony_ci	kfree(buf);
119262306a36Sopenharmony_ciout_free_netid:
119362306a36Sopenharmony_ci	kfree(netid);
119462306a36Sopenharmony_ciout_err:
119562306a36Sopenharmony_ci	return NULL;
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_decode_mp_ds_addr);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_civoid
120062306a36Sopenharmony_cipnfs_layout_mark_request_commit(struct nfs_page *req,
120162306a36Sopenharmony_ci				struct pnfs_layout_segment *lseg,
120262306a36Sopenharmony_ci				struct nfs_commit_info *cinfo,
120362306a36Sopenharmony_ci				u32 ds_commit_idx)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	struct list_head *list;
120662306a36Sopenharmony_ci	struct pnfs_commit_array *array;
120762306a36Sopenharmony_ci	struct pnfs_commit_bucket *bucket;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
121062306a36Sopenharmony_ci	array = pnfs_lookup_commit_array(cinfo->ds, lseg);
121162306a36Sopenharmony_ci	if (!array || !pnfs_is_valid_lseg(lseg))
121262306a36Sopenharmony_ci		goto out_resched;
121362306a36Sopenharmony_ci	bucket = &array->buckets[ds_commit_idx];
121462306a36Sopenharmony_ci	list = &bucket->written;
121562306a36Sopenharmony_ci	/* Non-empty buckets hold a reference on the lseg.  That ref
121662306a36Sopenharmony_ci	 * is normally transferred to the COMMIT call and released
121762306a36Sopenharmony_ci	 * there.  It could also be released if the last req is pulled
121862306a36Sopenharmony_ci	 * off due to a rewrite, in which case it will be done in
121962306a36Sopenharmony_ci	 * pnfs_common_clear_request_commit
122062306a36Sopenharmony_ci	 */
122162306a36Sopenharmony_ci	if (!bucket->lseg)
122262306a36Sopenharmony_ci		bucket->lseg = pnfs_get_lseg(lseg);
122362306a36Sopenharmony_ci	set_bit(PG_COMMIT_TO_DS, &req->wb_flags);
122462306a36Sopenharmony_ci	cinfo->ds->nwritten++;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	nfs_request_add_commit_list_locked(req, list, cinfo);
122762306a36Sopenharmony_ci	mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
122862306a36Sopenharmony_ci	nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo);
122962306a36Sopenharmony_ci	return;
123062306a36Sopenharmony_ciout_resched:
123162306a36Sopenharmony_ci	mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
123262306a36Sopenharmony_ci	cinfo->completion_ops->resched_write(cinfo, req);
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_layout_mark_request_commit);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ciint
123762306a36Sopenharmony_cipnfs_nfs_generic_sync(struct inode *inode, bool datasync)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	int ret;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	if (!pnfs_layoutcommit_outstanding(inode))
124262306a36Sopenharmony_ci		return 0;
124362306a36Sopenharmony_ci	ret = nfs_commit_inode(inode, FLUSH_SYNC);
124462306a36Sopenharmony_ci	if (ret < 0)
124562306a36Sopenharmony_ci		return ret;
124662306a36Sopenharmony_ci	if (datasync)
124762306a36Sopenharmony_ci		return 0;
124862306a36Sopenharmony_ci	return pnfs_layoutcommit_inode(inode, true);
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pnfs_nfs_generic_sync);
125162306a36Sopenharmony_ci
1252