1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f54bcf2eSTom Haynes /* 3f54bcf2eSTom Haynes * Common NFS I/O operations for the pnfs file based 4f54bcf2eSTom Haynes * layout drivers. 5f54bcf2eSTom Haynes * 6f54bcf2eSTom Haynes * Copyright (c) 2014, Primary Data, Inc. All rights reserved. 7f54bcf2eSTom Haynes * 8f54bcf2eSTom Haynes * Tom Haynes <loghyr@primarydata.com> 9f54bcf2eSTom Haynes */ 10f54bcf2eSTom Haynes 11f54bcf2eSTom Haynes #include <linux/nfs_fs.h> 12f54bcf2eSTom Haynes #include <linux/nfs_page.h> 136b7f3cf9SPeng Tao #include <linux/sunrpc/addr.h> 145f01d953SPeng Tao #include <linux/module.h> 15f54bcf2eSTom Haynes 167405f9e1SPeng Tao #include "nfs4session.h" 17f54bcf2eSTom Haynes #include "internal.h" 18f54bcf2eSTom Haynes #include "pnfs.h" 19f54bcf2eSTom Haynes 20875ae069SPeng Tao #define NFSDBG_FACILITY NFSDBG_PNFS 21875ae069SPeng Tao 22f54bcf2eSTom Haynes void pnfs_generic_rw_release(void *data) 23f54bcf2eSTom Haynes { 24f54bcf2eSTom Haynes struct nfs_pgio_header *hdr = data; 25f54bcf2eSTom Haynes 26f54bcf2eSTom Haynes nfs_put_client(hdr->ds_clp); 27f54bcf2eSTom Haynes hdr->mds_ops->rpc_release(data); 28f54bcf2eSTom Haynes } 29f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_rw_release); 30f54bcf2eSTom Haynes 31f54bcf2eSTom Haynes /* Fake up some data that will cause nfs_commit_release to retry the writes. */ 32f54bcf2eSTom Haynes void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data) 33f54bcf2eSTom Haynes { 34221203ceSTrond Myklebust struct nfs_writeverf *verf = data->res.verf; 35f54bcf2eSTom Haynes 36f54bcf2eSTom Haynes data->task.tk_status = 0; 37221203ceSTrond Myklebust memset(&verf->verifier, 0, sizeof(verf->verifier)); 38221203ceSTrond Myklebust verf->committed = NFS_UNSTABLE; 39f54bcf2eSTom Haynes } 40f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes); 41f54bcf2eSTom Haynes 42f54bcf2eSTom Haynes void pnfs_generic_write_commit_done(struct rpc_task *task, void *data) 43f54bcf2eSTom Haynes { 44f54bcf2eSTom Haynes struct nfs_commit_data *wdata = data; 45f54bcf2eSTom Haynes 46f54bcf2eSTom Haynes /* Note this may cause RPC to be resent */ 47f54bcf2eSTom Haynes wdata->mds_ops->rpc_call_done(task, data); 48f54bcf2eSTom Haynes } 49f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done); 50f54bcf2eSTom Haynes 51f54bcf2eSTom Haynes void pnfs_generic_commit_release(void *calldata) 52f54bcf2eSTom Haynes { 53f54bcf2eSTom Haynes struct nfs_commit_data *data = calldata; 54f54bcf2eSTom Haynes 55f54bcf2eSTom Haynes data->completion_ops->completion(data); 56f54bcf2eSTom Haynes pnfs_put_lseg(data->lseg); 57f54bcf2eSTom Haynes nfs_put_client(data->ds_clp); 58f54bcf2eSTom Haynes nfs_commitdata_release(data); 59f54bcf2eSTom Haynes } 60f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_commit_release); 61f54bcf2eSTom Haynes 62c84bea59STrond Myklebust static struct pnfs_layout_segment * 63c84bea59STrond Myklebust pnfs_free_bucket_lseg(struct pnfs_commit_bucket *bucket) 64c84bea59STrond Myklebust { 65c84bea59STrond Myklebust if (list_empty(&bucket->committing) && list_empty(&bucket->written)) { 66c84bea59STrond Myklebust struct pnfs_layout_segment *freeme = bucket->lseg; 67c84bea59STrond Myklebust bucket->lseg = NULL; 68c84bea59STrond Myklebust return freeme; 69c84bea59STrond Myklebust } 70c84bea59STrond Myklebust return NULL; 71c84bea59STrond Myklebust } 72c84bea59STrond Myklebust 73f54bcf2eSTom Haynes /* The generic layer is about to remove the req from the commit list. 74f54bcf2eSTom Haynes * If this will make the bucket empty, it will need to put the lseg reference. 75d0fbb1d8STrond Myklebust * Note this must be called holding nfsi->commit_mutex 76f54bcf2eSTom Haynes */ 77f54bcf2eSTom Haynes void 78f54bcf2eSTom Haynes pnfs_generic_clear_request_commit(struct nfs_page *req, 79f54bcf2eSTom Haynes struct nfs_commit_info *cinfo) 80f54bcf2eSTom Haynes { 811757655dSTrond Myklebust struct pnfs_commit_bucket *bucket = NULL; 82f54bcf2eSTom Haynes 83f54bcf2eSTom Haynes if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags)) 84f54bcf2eSTom Haynes goto out; 85f54bcf2eSTom Haynes cinfo->ds->nwritten--; 861757655dSTrond Myklebust if (list_is_singular(&req->wb_list)) 87f54bcf2eSTom Haynes bucket = list_first_entry(&req->wb_list, 881757655dSTrond Myklebust struct pnfs_commit_bucket, written); 89f54bcf2eSTom Haynes out: 90f54bcf2eSTom Haynes nfs_request_remove_commit_list(req, cinfo); 911757655dSTrond Myklebust if (bucket) 921757655dSTrond Myklebust pnfs_put_lseg(pnfs_free_bucket_lseg(bucket)); 93f54bcf2eSTom Haynes } 94f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit); 95f54bcf2eSTom Haynes 96d7242c46STrond Myklebust struct pnfs_commit_array * 97d7242c46STrond Myklebust pnfs_alloc_commit_array(size_t n, gfp_t gfp_flags) 98d7242c46STrond Myklebust { 99d7242c46STrond Myklebust struct pnfs_commit_array *p; 100d7242c46STrond Myklebust struct pnfs_commit_bucket *b; 101d7242c46STrond Myklebust 102d7242c46STrond Myklebust p = kmalloc(struct_size(p, buckets, n), gfp_flags); 103d7242c46STrond Myklebust if (!p) 104d7242c46STrond Myklebust return NULL; 105d7242c46STrond Myklebust p->nbuckets = n; 106d7242c46STrond Myklebust INIT_LIST_HEAD(&p->cinfo_list); 107d7242c46STrond Myklebust INIT_LIST_HEAD(&p->lseg_list); 108d7242c46STrond Myklebust p->lseg = NULL; 109d7242c46STrond Myklebust for (b = &p->buckets[0]; n != 0; b++, n--) { 110d7242c46STrond Myklebust INIT_LIST_HEAD(&b->written); 111d7242c46STrond Myklebust INIT_LIST_HEAD(&b->committing); 112c84bea59STrond Myklebust b->lseg = NULL; 113d7242c46STrond Myklebust b->direct_verf.committed = NFS_INVALID_STABLE_HOW; 114d7242c46STrond Myklebust } 115d7242c46STrond Myklebust return p; 116d7242c46STrond Myklebust } 117d7242c46STrond Myklebust EXPORT_SYMBOL_GPL(pnfs_alloc_commit_array); 118d7242c46STrond Myklebust 119d7242c46STrond Myklebust void 120d7242c46STrond Myklebust pnfs_free_commit_array(struct pnfs_commit_array *p) 121d7242c46STrond Myklebust { 122d7242c46STrond Myklebust kfree_rcu(p, rcu); 123d7242c46STrond Myklebust } 124d7242c46STrond Myklebust EXPORT_SYMBOL_GPL(pnfs_free_commit_array); 125d7242c46STrond Myklebust 126ba827c9aSTrond Myklebust static struct pnfs_commit_array * 127ba827c9aSTrond Myklebust pnfs_find_commit_array_by_lseg(struct pnfs_ds_commit_info *fl_cinfo, 128ba827c9aSTrond Myklebust struct pnfs_layout_segment *lseg) 129ba827c9aSTrond Myklebust { 130ba827c9aSTrond Myklebust struct pnfs_commit_array *array; 131ba827c9aSTrond Myklebust 132ba827c9aSTrond Myklebust list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) { 133ba827c9aSTrond Myklebust if (array->lseg == lseg) 134ba827c9aSTrond Myklebust return array; 135ba827c9aSTrond Myklebust } 136ba827c9aSTrond Myklebust return NULL; 137ba827c9aSTrond Myklebust } 138ba827c9aSTrond Myklebust 139ba827c9aSTrond Myklebust struct pnfs_commit_array * 140ba827c9aSTrond Myklebust pnfs_add_commit_array(struct pnfs_ds_commit_info *fl_cinfo, 141ba827c9aSTrond Myklebust struct pnfs_commit_array *new, 142ba827c9aSTrond Myklebust struct pnfs_layout_segment *lseg) 143ba827c9aSTrond Myklebust { 144ba827c9aSTrond Myklebust struct pnfs_commit_array *array; 145ba827c9aSTrond Myklebust 146ba827c9aSTrond Myklebust array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg); 147ba827c9aSTrond Myklebust if (array) 148ba827c9aSTrond Myklebust return array; 149ba827c9aSTrond Myklebust new->lseg = lseg; 150ba827c9aSTrond Myklebust refcount_set(&new->refcount, 1); 151ba827c9aSTrond Myklebust list_add_rcu(&new->cinfo_list, &fl_cinfo->commits); 152ba827c9aSTrond Myklebust list_add(&new->lseg_list, &lseg->pls_commits); 153ba827c9aSTrond Myklebust return new; 154ba827c9aSTrond Myklebust } 155ba827c9aSTrond Myklebust EXPORT_SYMBOL_GPL(pnfs_add_commit_array); 156ba827c9aSTrond Myklebust 157ba827c9aSTrond Myklebust static struct pnfs_commit_array * 158ba827c9aSTrond Myklebust pnfs_lookup_commit_array(struct pnfs_ds_commit_info *fl_cinfo, 159ba827c9aSTrond Myklebust struct pnfs_layout_segment *lseg) 160ba827c9aSTrond Myklebust { 161ba827c9aSTrond Myklebust struct pnfs_commit_array *array; 162ba827c9aSTrond Myklebust 163ba827c9aSTrond Myklebust rcu_read_lock(); 164ba827c9aSTrond Myklebust array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg); 165ba827c9aSTrond Myklebust if (!array) { 166ba827c9aSTrond Myklebust rcu_read_unlock(); 1679c455a8cSTrond Myklebust fl_cinfo->ops->setup_ds_info(fl_cinfo, lseg); 168ba827c9aSTrond Myklebust rcu_read_lock(); 169ba827c9aSTrond Myklebust array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg); 170ba827c9aSTrond Myklebust } 171ba827c9aSTrond Myklebust rcu_read_unlock(); 172ba827c9aSTrond Myklebust return array; 173ba827c9aSTrond Myklebust } 174ba827c9aSTrond Myklebust 175a9901899STrond Myklebust static void 176a9901899STrond Myklebust pnfs_release_commit_array_locked(struct pnfs_commit_array *array) 177a9901899STrond Myklebust { 178a9901899STrond Myklebust list_del_rcu(&array->cinfo_list); 179a9901899STrond Myklebust list_del(&array->lseg_list); 180a9901899STrond Myklebust pnfs_free_commit_array(array); 181a9901899STrond Myklebust } 182a9901899STrond Myklebust 183a9901899STrond Myklebust static void 184a9901899STrond Myklebust pnfs_put_commit_array_locked(struct pnfs_commit_array *array) 185a9901899STrond Myklebust { 186a9901899STrond Myklebust if (refcount_dec_and_test(&array->refcount)) 187a9901899STrond Myklebust pnfs_release_commit_array_locked(array); 188a9901899STrond Myklebust } 189a9901899STrond Myklebust 190a9901899STrond Myklebust static void 191a9901899STrond Myklebust pnfs_put_commit_array(struct pnfs_commit_array *array, struct inode *inode) 192a9901899STrond Myklebust { 193a9901899STrond Myklebust if (refcount_dec_and_lock(&array->refcount, &inode->i_lock)) { 194a9901899STrond Myklebust pnfs_release_commit_array_locked(array); 195a9901899STrond Myklebust spin_unlock(&inode->i_lock); 196a9901899STrond Myklebust } 197a9901899STrond Myklebust } 198a9901899STrond Myklebust 199a9901899STrond Myklebust static struct pnfs_commit_array * 200a9901899STrond Myklebust pnfs_get_commit_array(struct pnfs_commit_array *array) 201a9901899STrond Myklebust { 202a9901899STrond Myklebust if (refcount_inc_not_zero(&array->refcount)) 203a9901899STrond Myklebust return array; 204a9901899STrond Myklebust return NULL; 205a9901899STrond Myklebust } 206a9901899STrond Myklebust 207a9901899STrond Myklebust static void 208a9901899STrond Myklebust pnfs_remove_and_free_commit_array(struct pnfs_commit_array *array) 209a9901899STrond Myklebust { 210a9901899STrond Myklebust array->lseg = NULL; 211a9901899STrond Myklebust list_del_init(&array->lseg_list); 212a9901899STrond Myklebust pnfs_put_commit_array_locked(array); 213a9901899STrond Myklebust } 214a9901899STrond Myklebust 215a9901899STrond Myklebust void 216a9901899STrond Myklebust pnfs_generic_ds_cinfo_release_lseg(struct pnfs_ds_commit_info *fl_cinfo, 217a9901899STrond Myklebust struct pnfs_layout_segment *lseg) 218a9901899STrond Myklebust { 219a9901899STrond Myklebust struct pnfs_commit_array *array, *tmp; 220a9901899STrond Myklebust 221a9901899STrond Myklebust list_for_each_entry_safe(array, tmp, &lseg->pls_commits, lseg_list) 222a9901899STrond Myklebust pnfs_remove_and_free_commit_array(array); 223a9901899STrond Myklebust } 224a9901899STrond Myklebust EXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_release_lseg); 225a9901899STrond Myklebust 226a9901899STrond Myklebust void 227a9901899STrond Myklebust pnfs_generic_ds_cinfo_destroy(struct pnfs_ds_commit_info *fl_cinfo) 228a9901899STrond Myklebust { 229a9901899STrond Myklebust struct pnfs_commit_array *array, *tmp; 230a9901899STrond Myklebust 231a9901899STrond Myklebust list_for_each_entry_safe(array, tmp, &fl_cinfo->commits, cinfo_list) 232a9901899STrond Myklebust pnfs_remove_and_free_commit_array(array); 233a9901899STrond Myklebust } 234a9901899STrond Myklebust EXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_destroy); 235a9901899STrond Myklebust 236a8e3765eSTrond Myklebust /* 237a8e3765eSTrond Myklebust * Locks the nfs_page requests for commit and moves them to 238a8e3765eSTrond Myklebust * @bucket->committing. 239a8e3765eSTrond Myklebust */ 240f54bcf2eSTom Haynes static int 241a8e3765eSTrond Myklebust pnfs_bucket_scan_ds_commit_list(struct pnfs_commit_bucket *bucket, 242f54bcf2eSTom Haynes struct nfs_commit_info *cinfo, 243f54bcf2eSTom Haynes int max) 244f54bcf2eSTom Haynes { 245f54bcf2eSTom Haynes struct list_head *src = &bucket->written; 246f54bcf2eSTom Haynes struct list_head *dst = &bucket->committing; 247f54bcf2eSTom Haynes int ret; 248f54bcf2eSTom Haynes 249e824f99aSTrond Myklebust lockdep_assert_held(&NFS_I(cinfo->inode)->commit_mutex); 2505d2a9d9dSTrond Myklebust ret = nfs_scan_commit_list(src, dst, cinfo, max); 251f54bcf2eSTom Haynes if (ret) { 252f54bcf2eSTom Haynes cinfo->ds->nwritten -= ret; 253f54bcf2eSTom Haynes cinfo->ds->ncommitting += ret; 254f54bcf2eSTom Haynes } 255f54bcf2eSTom Haynes return ret; 256f54bcf2eSTom Haynes } 257f54bcf2eSTom Haynes 258a8e3765eSTrond Myklebust static int pnfs_bucket_scan_array(struct nfs_commit_info *cinfo, 259a8e3765eSTrond Myklebust struct pnfs_commit_bucket *buckets, 260a8e3765eSTrond Myklebust unsigned int nbuckets, 261a8e3765eSTrond Myklebust int max) 262a8e3765eSTrond Myklebust { 263a8e3765eSTrond Myklebust unsigned int i; 264a8e3765eSTrond Myklebust int rv = 0, cnt; 265a8e3765eSTrond Myklebust 266a8e3765eSTrond Myklebust for (i = 0; i < nbuckets && max != 0; i++) { 267a8e3765eSTrond Myklebust cnt = pnfs_bucket_scan_ds_commit_list(&buckets[i], cinfo, max); 268a8e3765eSTrond Myklebust rv += cnt; 269a8e3765eSTrond Myklebust max -= cnt; 270a8e3765eSTrond Myklebust } 271a8e3765eSTrond Myklebust return rv; 272a8e3765eSTrond Myklebust } 273a8e3765eSTrond Myklebust 274085d1e33STom Haynes /* Move reqs from written to committing lists, returning count 275085d1e33STom Haynes * of number moved. 276f54bcf2eSTom Haynes */ 277a8e3765eSTrond Myklebust int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo, int max) 278f54bcf2eSTom Haynes { 279a8e3765eSTrond Myklebust struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds; 280a8e3765eSTrond Myklebust struct pnfs_commit_array *array; 281a8e3765eSTrond Myklebust int rv = 0, cnt; 282f54bcf2eSTom Haynes 283a9901899STrond Myklebust rcu_read_lock(); 284a9901899STrond Myklebust list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) { 285a9901899STrond Myklebust if (!array->lseg || !pnfs_get_commit_array(array)) 286a9901899STrond Myklebust continue; 287a9901899STrond Myklebust rcu_read_unlock(); 288a8e3765eSTrond Myklebust cnt = pnfs_bucket_scan_array(cinfo, array->buckets, 289a8e3765eSTrond Myklebust array->nbuckets, max); 290a9901899STrond Myklebust rcu_read_lock(); 291a9901899STrond Myklebust pnfs_put_commit_array(array, cinfo->inode); 292a8e3765eSTrond Myklebust rv += cnt; 293a8e3765eSTrond Myklebust max -= cnt; 294a8e3765eSTrond Myklebust if (!max) 295a8e3765eSTrond Myklebust break; 296f54bcf2eSTom Haynes } 297a9901899STrond Myklebust rcu_read_unlock(); 298f54bcf2eSTom Haynes return rv; 299f54bcf2eSTom Haynes } 300f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists); 301f54bcf2eSTom Haynes 302fce9ed03STrond Myklebust static unsigned int 303fce9ed03STrond Myklebust pnfs_bucket_recover_commit_reqs(struct list_head *dst, 304fce9ed03STrond Myklebust struct pnfs_commit_bucket *buckets, 305fce9ed03STrond Myklebust unsigned int nbuckets, 306f54bcf2eSTom Haynes struct nfs_commit_info *cinfo) 307f54bcf2eSTom Haynes { 308f54bcf2eSTom Haynes struct pnfs_commit_bucket *b; 309f54bcf2eSTom Haynes struct pnfs_layout_segment *freeme; 310fce9ed03STrond Myklebust unsigned int nwritten, ret = 0; 311fce9ed03STrond Myklebust unsigned int i; 312f54bcf2eSTom Haynes 313f54bcf2eSTom Haynes restart: 314fce9ed03STrond Myklebust for (i = 0, b = buckets; i < nbuckets; i++, b++) { 3155d2a9d9dSTrond Myklebust nwritten = nfs_scan_commit_list(&b->written, dst, cinfo, 0); 316e39928f9STrond Myklebust if (!nwritten) 317e39928f9STrond Myklebust continue; 318fce9ed03STrond Myklebust ret += nwritten; 319c84bea59STrond Myklebust freeme = pnfs_free_bucket_lseg(b); 320c84bea59STrond Myklebust if (freeme) { 321f54bcf2eSTom Haynes pnfs_put_lseg(freeme); 322f54bcf2eSTom Haynes goto restart; 323f54bcf2eSTom Haynes } 324f54bcf2eSTom Haynes } 325fce9ed03STrond Myklebust return ret; 326fce9ed03STrond Myklebust } 327fce9ed03STrond Myklebust 328fce9ed03STrond Myklebust /* Pull everything off the committing lists and dump into @dst. */ 329fce9ed03STrond Myklebust void pnfs_generic_recover_commit_reqs(struct list_head *dst, 330fce9ed03STrond Myklebust struct nfs_commit_info *cinfo) 331fce9ed03STrond Myklebust { 332fce9ed03STrond Myklebust struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds; 333fce9ed03STrond Myklebust struct pnfs_commit_array *array; 334fce9ed03STrond Myklebust unsigned int nwritten; 335fce9ed03STrond Myklebust 336fce9ed03STrond Myklebust lockdep_assert_held(&NFS_I(cinfo->inode)->commit_mutex); 337a9901899STrond Myklebust rcu_read_lock(); 338a9901899STrond Myklebust list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) { 339a9901899STrond Myklebust if (!array->lseg || !pnfs_get_commit_array(array)) 340a9901899STrond Myklebust continue; 341a9901899STrond Myklebust rcu_read_unlock(); 342fce9ed03STrond Myklebust nwritten = pnfs_bucket_recover_commit_reqs(dst, 343fce9ed03STrond Myklebust array->buckets, 344fce9ed03STrond Myklebust array->nbuckets, 345fce9ed03STrond Myklebust cinfo); 346a9901899STrond Myklebust rcu_read_lock(); 347a9901899STrond Myklebust pnfs_put_commit_array(array, cinfo->inode); 348fce9ed03STrond Myklebust fl_cinfo->nwritten -= nwritten; 349fce9ed03STrond Myklebust } 350a9901899STrond Myklebust rcu_read_unlock(); 351f54bcf2eSTom Haynes } 352f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs); 353f54bcf2eSTom Haynes 354fb6b53baSTrond Myklebust static struct nfs_page * 355fb6b53baSTrond Myklebust pnfs_bucket_search_commit_reqs(struct pnfs_commit_bucket *buckets, 356fb6b53baSTrond Myklebust unsigned int nbuckets, struct page *page) 357fb6b53baSTrond Myklebust { 358fb6b53baSTrond Myklebust struct nfs_page *req; 359fb6b53baSTrond Myklebust struct pnfs_commit_bucket *b; 360fb6b53baSTrond Myklebust unsigned int i; 361fb6b53baSTrond Myklebust 362fb6b53baSTrond Myklebust /* Linearly search the commit lists for each bucket until a matching 363fb6b53baSTrond Myklebust * request is found */ 364fb6b53baSTrond Myklebust for (i = 0, b = buckets; i < nbuckets; i++, b++) { 365fb6b53baSTrond Myklebust list_for_each_entry(req, &b->written, wb_list) { 366fb6b53baSTrond Myklebust if (req->wb_page == page) 367fb6b53baSTrond Myklebust return req->wb_head; 368fb6b53baSTrond Myklebust } 369fb6b53baSTrond Myklebust list_for_each_entry(req, &b->committing, wb_list) { 370fb6b53baSTrond Myklebust if (req->wb_page == page) 371fb6b53baSTrond Myklebust return req->wb_head; 372fb6b53baSTrond Myklebust } 373fb6b53baSTrond Myklebust } 374fb6b53baSTrond Myklebust return NULL; 375fb6b53baSTrond Myklebust } 376fb6b53baSTrond Myklebust 377fb6b53baSTrond Myklebust /* pnfs_generic_search_commit_reqs - Search lists in @cinfo for the head reqest 378fb6b53baSTrond Myklebust * for @page 379fb6b53baSTrond Myklebust * @cinfo - commit info for current inode 380fb6b53baSTrond Myklebust * @page - page to search for matching head request 381fb6b53baSTrond Myklebust * 382fb6b53baSTrond Myklebust * Returns a the head request if one is found, otherwise returns NULL. 383fb6b53baSTrond Myklebust */ 384fb6b53baSTrond Myklebust struct nfs_page * 385fb6b53baSTrond Myklebust pnfs_generic_search_commit_reqs(struct nfs_commit_info *cinfo, struct page *page) 386fb6b53baSTrond Myklebust { 387fb6b53baSTrond Myklebust struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds; 388fb6b53baSTrond Myklebust struct pnfs_commit_array *array; 389fb6b53baSTrond Myklebust struct nfs_page *req; 390fb6b53baSTrond Myklebust 391fb6b53baSTrond Myklebust list_for_each_entry(array, &fl_cinfo->commits, cinfo_list) { 392fb6b53baSTrond Myklebust req = pnfs_bucket_search_commit_reqs(array->buckets, 393fb6b53baSTrond Myklebust array->nbuckets, page); 394fb6b53baSTrond Myklebust if (req) 395fb6b53baSTrond Myklebust return req; 396fb6b53baSTrond Myklebust } 397fb6b53baSTrond Myklebust return NULL; 398fb6b53baSTrond Myklebust } 399fb6b53baSTrond Myklebust EXPORT_SYMBOL_GPL(pnfs_generic_search_commit_reqs); 400fb6b53baSTrond Myklebust 40119573c93STrond Myklebust static struct pnfs_layout_segment * 40219573c93STrond Myklebust pnfs_bucket_get_committing(struct list_head *head, 40319573c93STrond Myklebust struct pnfs_commit_bucket *bucket, 40419573c93STrond Myklebust struct nfs_commit_info *cinfo) 405f54bcf2eSTom Haynes { 40646c9ea1dSTrond Myklebust struct pnfs_layout_segment *lseg; 40741181886STrond Myklebust struct list_head *pos; 408f54bcf2eSTom Haynes 40941181886STrond Myklebust list_for_each(pos, &bucket->committing) 41041181886STrond Myklebust cinfo->ds->ncommitting--; 41119573c93STrond Myklebust list_splice_init(&bucket->committing, head); 41246c9ea1dSTrond Myklebust lseg = pnfs_free_bucket_lseg(bucket); 41346c9ea1dSTrond Myklebust if (!lseg) 41446c9ea1dSTrond Myklebust lseg = pnfs_get_lseg(bucket->lseg); 41546c9ea1dSTrond Myklebust return lseg; 416f54bcf2eSTom Haynes } 41719573c93STrond Myklebust 41819573c93STrond Myklebust static struct nfs_commit_data * 41919573c93STrond Myklebust pnfs_bucket_fetch_commitdata(struct pnfs_commit_bucket *bucket, 42019573c93STrond Myklebust struct nfs_commit_info *cinfo) 42119573c93STrond Myklebust { 422*515dcdcdSTrond Myklebust struct nfs_commit_data *data = nfs_commitdata_alloc(); 42319573c93STrond Myklebust 42419573c93STrond Myklebust if (!data) 42519573c93STrond Myklebust return NULL; 42619573c93STrond Myklebust data->lseg = pnfs_bucket_get_committing(&data->pages, bucket, cinfo); 42719573c93STrond Myklebust return data; 42819573c93STrond Myklebust } 42919573c93STrond Myklebust 43019573c93STrond Myklebust static void pnfs_generic_retry_commit(struct pnfs_commit_bucket *buckets, 43119573c93STrond Myklebust unsigned int nbuckets, 43219573c93STrond Myklebust struct nfs_commit_info *cinfo, 43319573c93STrond Myklebust unsigned int idx) 43419573c93STrond Myklebust { 43519573c93STrond Myklebust struct pnfs_commit_bucket *bucket; 43619573c93STrond Myklebust struct pnfs_layout_segment *freeme; 43719573c93STrond Myklebust LIST_HEAD(pages); 43819573c93STrond Myklebust 43919573c93STrond Myklebust for (bucket = buckets; idx < nbuckets; bucket++, idx++) { 44019573c93STrond Myklebust if (list_empty(&bucket->committing)) 44119573c93STrond Myklebust continue; 44219573c93STrond Myklebust mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); 44319573c93STrond Myklebust freeme = pnfs_bucket_get_committing(&pages, bucket, cinfo); 444d0fbb1d8STrond Myklebust mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 44519573c93STrond Myklebust nfs_retry_commit(&pages, freeme, cinfo, idx); 44619573c93STrond Myklebust pnfs_put_lseg(freeme); 44719573c93STrond Myklebust } 448f54bcf2eSTom Haynes } 449f54bcf2eSTom Haynes 450f54bcf2eSTom Haynes static unsigned int 45119573c93STrond Myklebust pnfs_bucket_alloc_ds_commits(struct list_head *list, 45219573c93STrond Myklebust struct pnfs_commit_bucket *buckets, 45319573c93STrond Myklebust unsigned int nbuckets, 45419573c93STrond Myklebust struct nfs_commit_info *cinfo) 455f54bcf2eSTom Haynes { 456f54bcf2eSTom Haynes struct pnfs_commit_bucket *bucket; 457f54bcf2eSTom Haynes struct nfs_commit_data *data; 45819573c93STrond Myklebust unsigned int i; 459f54bcf2eSTom Haynes unsigned int nreq = 0; 460f54bcf2eSTom Haynes 46119573c93STrond Myklebust for (i = 0, bucket = buckets; i < nbuckets; i++, bucket++) { 462f54bcf2eSTom Haynes if (list_empty(&bucket->committing)) 463f54bcf2eSTom Haynes continue; 46419573c93STrond Myklebust mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); 46519573c93STrond Myklebust if (!list_empty(&bucket->committing)) { 46619573c93STrond Myklebust data = pnfs_bucket_fetch_commitdata(bucket, cinfo); 467f54bcf2eSTom Haynes if (!data) 46819573c93STrond Myklebust goto out_error; 469f54bcf2eSTom Haynes data->ds_commit_index = i; 47019573c93STrond Myklebust list_add_tail(&data->list, list); 471f54bcf2eSTom Haynes nreq++; 472f54bcf2eSTom Haynes } 473d0fbb1d8STrond Myklebust mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 47427571297STrond Myklebust } 47519573c93STrond Myklebust return nreq; 47619573c93STrond Myklebust out_error: 47719573c93STrond Myklebust mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 47819573c93STrond Myklebust /* Clean up on error */ 47919573c93STrond Myklebust pnfs_generic_retry_commit(buckets, nbuckets, cinfo, i); 48019573c93STrond Myklebust return nreq; 481ade8febdSWeston Andros Adamson } 482ade8febdSWeston Andros Adamson 4830cb1f6dfSTrond Myklebust static unsigned int 4840cb1f6dfSTrond Myklebust pnfs_alloc_ds_commits_list(struct list_head *list, 4850cb1f6dfSTrond Myklebust struct pnfs_ds_commit_info *fl_cinfo, 4860cb1f6dfSTrond Myklebust struct nfs_commit_info *cinfo) 4870cb1f6dfSTrond Myklebust { 4880cb1f6dfSTrond Myklebust struct pnfs_commit_array *array; 4890cb1f6dfSTrond Myklebust unsigned int ret = 0; 4900cb1f6dfSTrond Myklebust 491a9901899STrond Myklebust rcu_read_lock(); 492a9901899STrond Myklebust list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) { 493a9901899STrond Myklebust if (!array->lseg || !pnfs_get_commit_array(array)) 494a9901899STrond Myklebust continue; 495a9901899STrond Myklebust rcu_read_unlock(); 4960cb1f6dfSTrond Myklebust ret += pnfs_bucket_alloc_ds_commits(list, array->buckets, 4970cb1f6dfSTrond Myklebust array->nbuckets, cinfo); 498a9901899STrond Myklebust rcu_read_lock(); 499a9901899STrond Myklebust pnfs_put_commit_array(array, cinfo->inode); 500a9901899STrond Myklebust } 50127d231c0STrond Myklebust rcu_read_unlock(); 5020cb1f6dfSTrond Myklebust return ret; 5030cb1f6dfSTrond Myklebust } 5040cb1f6dfSTrond Myklebust 505f54bcf2eSTom Haynes /* This follows nfs_commit_list pretty closely */ 506f54bcf2eSTom Haynes int 507f54bcf2eSTom Haynes pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages, 508f54bcf2eSTom Haynes int how, struct nfs_commit_info *cinfo, 509f54bcf2eSTom Haynes int (*initiate_commit)(struct nfs_commit_data *data, 510f54bcf2eSTom Haynes int how)) 511f54bcf2eSTom Haynes { 51219573c93STrond Myklebust struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds; 513f54bcf2eSTom Haynes struct nfs_commit_data *data, *tmp; 514f54bcf2eSTom Haynes LIST_HEAD(list); 515f54bcf2eSTom Haynes unsigned int nreq = 0; 516f54bcf2eSTom Haynes 517f54bcf2eSTom Haynes if (!list_empty(mds_pages)) { 518*515dcdcdSTrond Myklebust data = nfs_commitdata_alloc(); 519*515dcdcdSTrond Myklebust if (!data) { 520*515dcdcdSTrond Myklebust nfs_retry_commit(mds_pages, NULL, cinfo, -1); 521*515dcdcdSTrond Myklebust return -ENOMEM; 522*515dcdcdSTrond Myklebust } 52327571297STrond Myklebust data->ds_commit_index = -1; 52419573c93STrond Myklebust list_splice_init(mds_pages, &data->pages); 52519573c93STrond Myklebust list_add_tail(&data->list, &list); 526f54bcf2eSTom Haynes nreq++; 527f54bcf2eSTom Haynes } 528f54bcf2eSTom Haynes 5290cb1f6dfSTrond Myklebust nreq += pnfs_alloc_ds_commits_list(&list, fl_cinfo, cinfo); 530af7cf057STrond Myklebust if (nreq == 0) 531f54bcf2eSTom Haynes goto out; 532f54bcf2eSTom Haynes 53319573c93STrond Myklebust list_for_each_entry_safe(data, tmp, &list, list) { 53419573c93STrond Myklebust list_del(&data->list); 53527571297STrond Myklebust if (data->ds_commit_index < 0) { 53619573c93STrond Myklebust nfs_init_commit(data, NULL, NULL, cinfo); 537f54bcf2eSTom Haynes nfs_initiate_commit(NFS_CLIENT(inode), data, 538c36aae9aSPeng Tao NFS_PROTO(data->inode), 5394fa7ef69STrond Myklebust data->mds_ops, how, 5404fa7ef69STrond Myklebust RPC_TASK_CRED_NOREF); 541f54bcf2eSTom Haynes } else { 54219573c93STrond Myklebust nfs_init_commit(data, NULL, data->lseg, cinfo); 543f54bcf2eSTom Haynes initiate_commit(data, how); 544f54bcf2eSTom Haynes } 545f54bcf2eSTom Haynes } 546f54bcf2eSTom Haynes out: 547f54bcf2eSTom Haynes return PNFS_ATTEMPTED; 548f54bcf2eSTom Haynes } 549f54bcf2eSTom Haynes EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist); 550875ae069SPeng Tao 551875ae069SPeng Tao /* 552875ae069SPeng Tao * Data server cache 553875ae069SPeng Tao * 554875ae069SPeng Tao * Data servers can be mapped to different device ids. 555875ae069SPeng Tao * nfs4_pnfs_ds reference counting 556875ae069SPeng Tao * - set to 1 on allocation 557875ae069SPeng Tao * - incremented when a device id maps a data server already in the cache. 558875ae069SPeng Tao * - decremented when deviceid is removed from the cache. 559875ae069SPeng Tao */ 560875ae069SPeng Tao static DEFINE_SPINLOCK(nfs4_ds_cache_lock); 561875ae069SPeng Tao static LIST_HEAD(nfs4_data_server_cache); 562875ae069SPeng Tao 563875ae069SPeng Tao /* Debug routines */ 564875ae069SPeng Tao static void 565875ae069SPeng Tao print_ds(struct nfs4_pnfs_ds *ds) 566875ae069SPeng Tao { 567875ae069SPeng Tao if (ds == NULL) { 568875ae069SPeng Tao printk(KERN_WARNING "%s NULL device\n", __func__); 569875ae069SPeng Tao return; 570875ae069SPeng Tao } 571875ae069SPeng Tao printk(KERN_WARNING " ds %s\n" 572875ae069SPeng Tao " ref count %d\n" 573875ae069SPeng Tao " client %p\n" 574875ae069SPeng Tao " cl_exchange_flags %x\n", 575875ae069SPeng Tao ds->ds_remotestr, 576a2a5dea7SElena Reshetova refcount_read(&ds->ds_count), ds->ds_clp, 577875ae069SPeng Tao ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0); 578875ae069SPeng Tao } 579875ae069SPeng Tao 580875ae069SPeng Tao static bool 581875ae069SPeng Tao same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2) 582875ae069SPeng Tao { 583875ae069SPeng Tao struct sockaddr_in *a, *b; 584875ae069SPeng Tao struct sockaddr_in6 *a6, *b6; 585875ae069SPeng Tao 586875ae069SPeng Tao if (addr1->sa_family != addr2->sa_family) 587875ae069SPeng Tao return false; 588875ae069SPeng Tao 589875ae069SPeng Tao switch (addr1->sa_family) { 590875ae069SPeng Tao case AF_INET: 591875ae069SPeng Tao a = (struct sockaddr_in *)addr1; 592875ae069SPeng Tao b = (struct sockaddr_in *)addr2; 593875ae069SPeng Tao 594875ae069SPeng Tao if (a->sin_addr.s_addr == b->sin_addr.s_addr && 595875ae069SPeng Tao a->sin_port == b->sin_port) 596875ae069SPeng Tao return true; 597875ae069SPeng Tao break; 598875ae069SPeng Tao 599875ae069SPeng Tao case AF_INET6: 600875ae069SPeng Tao a6 = (struct sockaddr_in6 *)addr1; 601875ae069SPeng Tao b6 = (struct sockaddr_in6 *)addr2; 602875ae069SPeng Tao 603875ae069SPeng Tao /* LINKLOCAL addresses must have matching scope_id */ 604875ae069SPeng Tao if (ipv6_addr_src_scope(&a6->sin6_addr) == 605875ae069SPeng Tao IPV6_ADDR_SCOPE_LINKLOCAL && 606875ae069SPeng Tao a6->sin6_scope_id != b6->sin6_scope_id) 607875ae069SPeng Tao return false; 608875ae069SPeng Tao 609875ae069SPeng Tao if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) && 610875ae069SPeng Tao a6->sin6_port == b6->sin6_port) 611875ae069SPeng Tao return true; 612875ae069SPeng Tao break; 613875ae069SPeng Tao 614875ae069SPeng Tao default: 615875ae069SPeng Tao dprintk("%s: unhandled address family: %u\n", 616875ae069SPeng Tao __func__, addr1->sa_family); 617875ae069SPeng Tao return false; 618875ae069SPeng Tao } 619875ae069SPeng Tao 620875ae069SPeng Tao return false; 621875ae069SPeng Tao } 622875ae069SPeng Tao 6236f536936STrond Myklebust /* 6246f536936STrond Myklebust * Checks if 'dsaddrs1' contains a subset of 'dsaddrs2'. If it does, 6256f536936STrond Myklebust * declare a match. 6266f536936STrond Myklebust */ 627875ae069SPeng Tao static bool 628875ae069SPeng Tao _same_data_server_addrs_locked(const struct list_head *dsaddrs1, 629875ae069SPeng Tao const struct list_head *dsaddrs2) 630875ae069SPeng Tao { 631875ae069SPeng Tao struct nfs4_pnfs_ds_addr *da1, *da2; 6326f536936STrond Myklebust struct sockaddr *sa1, *sa2; 6336f536936STrond Myklebust bool match = false; 634875ae069SPeng Tao 6356f536936STrond Myklebust list_for_each_entry(da1, dsaddrs1, da_node) { 6366f536936STrond Myklebust sa1 = (struct sockaddr *)&da1->da_addr; 6376f536936STrond Myklebust match = false; 6386f536936STrond Myklebust list_for_each_entry(da2, dsaddrs2, da_node) { 6396f536936STrond Myklebust sa2 = (struct sockaddr *)&da2->da_addr; 6406f536936STrond Myklebust match = same_sockaddr(sa1, sa2); 6416f536936STrond Myklebust if (match) 6426f536936STrond Myklebust break; 643875ae069SPeng Tao } 6446f536936STrond Myklebust if (!match) 6456f536936STrond Myklebust break; 6466f536936STrond Myklebust } 6476f536936STrond Myklebust return match; 648875ae069SPeng Tao } 649875ae069SPeng Tao 650875ae069SPeng Tao /* 651875ae069SPeng Tao * Lookup DS by addresses. nfs4_ds_cache_lock is held 652875ae069SPeng Tao */ 653875ae069SPeng Tao static struct nfs4_pnfs_ds * 654875ae069SPeng Tao _data_server_lookup_locked(const struct list_head *dsaddrs) 655875ae069SPeng Tao { 656875ae069SPeng Tao struct nfs4_pnfs_ds *ds; 657875ae069SPeng Tao 658875ae069SPeng Tao list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) 659875ae069SPeng Tao if (_same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs)) 660875ae069SPeng Tao return ds; 661875ae069SPeng Tao return NULL; 662875ae069SPeng Tao } 663875ae069SPeng Tao 664190c75a3STrond Myklebust static struct nfs4_pnfs_ds_addr *nfs4_pnfs_ds_addr_alloc(gfp_t gfp_flags) 665190c75a3STrond Myklebust { 666190c75a3STrond Myklebust struct nfs4_pnfs_ds_addr *da = kzalloc(sizeof(*da), gfp_flags); 667190c75a3STrond Myklebust if (da) 668190c75a3STrond Myklebust INIT_LIST_HEAD(&da->da_node); 669190c75a3STrond Myklebust return da; 670190c75a3STrond Myklebust } 671190c75a3STrond Myklebust 672190c75a3STrond Myklebust static void nfs4_pnfs_ds_addr_free(struct nfs4_pnfs_ds_addr *da) 673190c75a3STrond Myklebust { 674190c75a3STrond Myklebust kfree(da->da_remotestr); 6754be78d26STrond Myklebust kfree(da->da_netid); 676190c75a3STrond Myklebust kfree(da); 677190c75a3STrond Myklebust } 678190c75a3STrond Myklebust 679875ae069SPeng Tao static void destroy_ds(struct nfs4_pnfs_ds *ds) 680875ae069SPeng Tao { 681875ae069SPeng Tao struct nfs4_pnfs_ds_addr *da; 682875ae069SPeng Tao 683875ae069SPeng Tao dprintk("--> %s\n", __func__); 684875ae069SPeng Tao ifdebug(FACILITY) 685875ae069SPeng Tao print_ds(ds); 686875ae069SPeng Tao 687875ae069SPeng Tao nfs_put_client(ds->ds_clp); 688875ae069SPeng Tao 689875ae069SPeng Tao while (!list_empty(&ds->ds_addrs)) { 690875ae069SPeng Tao da = list_first_entry(&ds->ds_addrs, 691875ae069SPeng Tao struct nfs4_pnfs_ds_addr, 692875ae069SPeng Tao da_node); 693875ae069SPeng Tao list_del_init(&da->da_node); 694190c75a3STrond Myklebust nfs4_pnfs_ds_addr_free(da); 695875ae069SPeng Tao } 696875ae069SPeng Tao 697875ae069SPeng Tao kfree(ds->ds_remotestr); 698875ae069SPeng Tao kfree(ds); 699875ae069SPeng Tao } 700875ae069SPeng Tao 701875ae069SPeng Tao void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds) 702875ae069SPeng Tao { 703a2a5dea7SElena Reshetova if (refcount_dec_and_lock(&ds->ds_count, 704875ae069SPeng Tao &nfs4_ds_cache_lock)) { 705875ae069SPeng Tao list_del_init(&ds->ds_node); 706875ae069SPeng Tao spin_unlock(&nfs4_ds_cache_lock); 707875ae069SPeng Tao destroy_ds(ds); 708875ae069SPeng Tao } 709875ae069SPeng Tao } 710875ae069SPeng Tao EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_put); 711875ae069SPeng Tao 712875ae069SPeng Tao /* 713875ae069SPeng Tao * Create a string with a human readable address and port to avoid 714875ae069SPeng Tao * complicated setup around many dprinks. 715875ae069SPeng Tao */ 716875ae069SPeng Tao static char * 717875ae069SPeng Tao nfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags) 718875ae069SPeng Tao { 719875ae069SPeng Tao struct nfs4_pnfs_ds_addr *da; 720875ae069SPeng Tao char *remotestr; 721875ae069SPeng Tao size_t len; 722875ae069SPeng Tao char *p; 723875ae069SPeng Tao 724875ae069SPeng Tao len = 3; /* '{', '}' and eol */ 725875ae069SPeng Tao list_for_each_entry(da, dsaddrs, da_node) { 726875ae069SPeng Tao len += strlen(da->da_remotestr) + 1; /* string plus comma */ 727875ae069SPeng Tao } 728875ae069SPeng Tao 729875ae069SPeng Tao remotestr = kzalloc(len, gfp_flags); 730875ae069SPeng Tao if (!remotestr) 731875ae069SPeng Tao return NULL; 732875ae069SPeng Tao 733875ae069SPeng Tao p = remotestr; 734875ae069SPeng Tao *(p++) = '{'; 735875ae069SPeng Tao len--; 736875ae069SPeng Tao list_for_each_entry(da, dsaddrs, da_node) { 737875ae069SPeng Tao size_t ll = strlen(da->da_remotestr); 738875ae069SPeng Tao 739875ae069SPeng Tao if (ll > len) 740875ae069SPeng Tao goto out_err; 741875ae069SPeng Tao 742875ae069SPeng Tao memcpy(p, da->da_remotestr, ll); 743875ae069SPeng Tao p += ll; 744875ae069SPeng Tao len -= ll; 745875ae069SPeng Tao 746875ae069SPeng Tao if (len < 1) 747875ae069SPeng Tao goto out_err; 748875ae069SPeng Tao (*p++) = ','; 749875ae069SPeng Tao len--; 750875ae069SPeng Tao } 751875ae069SPeng Tao if (len < 2) 752875ae069SPeng Tao goto out_err; 753875ae069SPeng Tao *(p++) = '}'; 754875ae069SPeng Tao *p = '\0'; 755875ae069SPeng Tao return remotestr; 756875ae069SPeng Tao out_err: 757875ae069SPeng Tao kfree(remotestr); 758875ae069SPeng Tao return NULL; 759875ae069SPeng Tao } 760875ae069SPeng Tao 761875ae069SPeng Tao /* 762875ae069SPeng Tao * Given a list of multipath struct nfs4_pnfs_ds_addr, add it to ds cache if 763875ae069SPeng Tao * uncached and return cached struct nfs4_pnfs_ds. 764875ae069SPeng Tao */ 765875ae069SPeng Tao struct nfs4_pnfs_ds * 766875ae069SPeng Tao nfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags) 767875ae069SPeng Tao { 768875ae069SPeng Tao struct nfs4_pnfs_ds *tmp_ds, *ds = NULL; 769875ae069SPeng Tao char *remotestr; 770875ae069SPeng Tao 771875ae069SPeng Tao if (list_empty(dsaddrs)) { 772875ae069SPeng Tao dprintk("%s: no addresses defined\n", __func__); 773875ae069SPeng Tao goto out; 774875ae069SPeng Tao } 775875ae069SPeng Tao 776875ae069SPeng Tao ds = kzalloc(sizeof(*ds), gfp_flags); 777875ae069SPeng Tao if (!ds) 778875ae069SPeng Tao goto out; 779875ae069SPeng Tao 780875ae069SPeng Tao /* this is only used for debugging, so it's ok if its NULL */ 781875ae069SPeng Tao remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags); 782875ae069SPeng Tao 783875ae069SPeng Tao spin_lock(&nfs4_ds_cache_lock); 784875ae069SPeng Tao tmp_ds = _data_server_lookup_locked(dsaddrs); 785875ae069SPeng Tao if (tmp_ds == NULL) { 786875ae069SPeng Tao INIT_LIST_HEAD(&ds->ds_addrs); 787875ae069SPeng Tao list_splice_init(dsaddrs, &ds->ds_addrs); 788875ae069SPeng Tao ds->ds_remotestr = remotestr; 789a2a5dea7SElena Reshetova refcount_set(&ds->ds_count, 1); 790875ae069SPeng Tao INIT_LIST_HEAD(&ds->ds_node); 791875ae069SPeng Tao ds->ds_clp = NULL; 792875ae069SPeng Tao list_add(&ds->ds_node, &nfs4_data_server_cache); 793875ae069SPeng Tao dprintk("%s add new data server %s\n", __func__, 794875ae069SPeng Tao ds->ds_remotestr); 795875ae069SPeng Tao } else { 796875ae069SPeng Tao kfree(remotestr); 797875ae069SPeng Tao kfree(ds); 798a2a5dea7SElena Reshetova refcount_inc(&tmp_ds->ds_count); 799875ae069SPeng Tao dprintk("%s data server %s found, inc'ed ds_count to %d\n", 800875ae069SPeng Tao __func__, tmp_ds->ds_remotestr, 801a2a5dea7SElena Reshetova refcount_read(&tmp_ds->ds_count)); 802875ae069SPeng Tao ds = tmp_ds; 803875ae069SPeng Tao } 804875ae069SPeng Tao spin_unlock(&nfs4_ds_cache_lock); 805875ae069SPeng Tao out: 806875ae069SPeng Tao return ds; 807875ae069SPeng Tao } 808875ae069SPeng Tao EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_add); 8096b7f3cf9SPeng Tao 810f46f8493STrond Myklebust static int nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds) 8117405f9e1SPeng Tao { 8127405f9e1SPeng Tao might_sleep(); 813f46f8493STrond Myklebust return wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, TASK_KILLABLE); 8147405f9e1SPeng Tao } 8157405f9e1SPeng Tao 8167405f9e1SPeng Tao static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds) 8177405f9e1SPeng Tao { 8187405f9e1SPeng Tao smp_mb__before_atomic(); 819f46f8493STrond Myklebust clear_and_wake_up_bit(NFS4DS_CONNECTING, &ds->ds_state); 8207405f9e1SPeng Tao } 8217405f9e1SPeng Tao 8225f01d953SPeng Tao static struct nfs_client *(*get_v3_ds_connect)( 823b224f7cbSTigran Mkrtchyan struct nfs_server *mds_srv, 8245f01d953SPeng Tao const struct sockaddr *ds_addr, 8255f01d953SPeng Tao int ds_addrlen, 8265f01d953SPeng Tao int ds_proto, 8275f01d953SPeng Tao unsigned int ds_timeo, 8287d38de3fSAnna Schumaker unsigned int ds_retrans); 8295f01d953SPeng Tao 8305f01d953SPeng Tao static bool load_v3_ds_connect(void) 8315f01d953SPeng Tao { 8325f01d953SPeng Tao if (!get_v3_ds_connect) { 8335f01d953SPeng Tao get_v3_ds_connect = symbol_request(nfs3_set_ds_client); 8345f01d953SPeng Tao WARN_ON_ONCE(!get_v3_ds_connect); 8355f01d953SPeng Tao } 8365f01d953SPeng Tao 8375f01d953SPeng Tao return(get_v3_ds_connect != NULL); 8385f01d953SPeng Tao } 8395f01d953SPeng Tao 840df137bc1SArnd Bergmann void nfs4_pnfs_v3_ds_connect_unload(void) 8415f01d953SPeng Tao { 8425f01d953SPeng Tao if (get_v3_ds_connect) { 8435f01d953SPeng Tao symbol_put(nfs3_set_ds_client); 8445f01d953SPeng Tao get_v3_ds_connect = NULL; 8455f01d953SPeng Tao } 8465f01d953SPeng Tao } 8475f01d953SPeng Tao 8485f01d953SPeng Tao static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv, 8495f01d953SPeng Tao struct nfs4_pnfs_ds *ds, 8505f01d953SPeng Tao unsigned int timeo, 8517d38de3fSAnna Schumaker unsigned int retrans) 8525f01d953SPeng Tao { 8535f01d953SPeng Tao struct nfs_client *clp = ERR_PTR(-EIO); 8545f01d953SPeng Tao struct nfs4_pnfs_ds_addr *da; 8555f01d953SPeng Tao int status = 0; 8565f01d953SPeng Tao 8577d38de3fSAnna Schumaker dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr); 8585f01d953SPeng Tao 8595f01d953SPeng Tao if (!load_v3_ds_connect()) 860dd5c153eSTrond Myklebust return -EPROTONOSUPPORT; 8615f01d953SPeng Tao 8625f01d953SPeng Tao list_for_each_entry(da, &ds->ds_addrs, da_node) { 8635f01d953SPeng Tao dprintk("%s: DS %s: trying address %s\n", 8645f01d953SPeng Tao __func__, ds->ds_remotestr, da->da_remotestr); 8655f01d953SPeng Tao 866fc821d59STrond Myklebust if (!IS_ERR(clp)) { 867fc821d59STrond Myklebust struct xprt_create xprt_args = { 8684be78d26STrond Myklebust .ident = da->da_transport, 869fc821d59STrond Myklebust .net = clp->cl_net, 870fc821d59STrond Myklebust .dstaddr = (struct sockaddr *)&da->da_addr, 871fc821d59STrond Myklebust .addrlen = da->da_addrlen, 872fc821d59STrond Myklebust .servername = clp->cl_hostname, 873fc821d59STrond Myklebust }; 874a12f996dSTrond Myklebust 8754be78d26STrond Myklebust if (da->da_transport != clp->cl_proto) 8764be78d26STrond Myklebust continue; 877a12f996dSTrond Myklebust if (da->da_addr.ss_family != clp->cl_addr.ss_family) 878a12f996dSTrond Myklebust continue; 879fc821d59STrond Myklebust /* Add this address as an alias */ 880fc821d59STrond Myklebust rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args, 881fc821d59STrond Myklebust rpc_clnt_test_and_add_xprt, NULL); 882bf2bf9b8STrond Myklebust continue; 883bf2bf9b8STrond Myklebust } 884b224f7cbSTigran Mkrtchyan clp = get_v3_ds_connect(mds_srv, 8855f01d953SPeng Tao (struct sockaddr *)&da->da_addr, 8864be78d26STrond Myklebust da->da_addrlen, da->da_transport, 8877d38de3fSAnna Schumaker timeo, retrans); 888bf2bf9b8STrond Myklebust if (IS_ERR(clp)) 889bf2bf9b8STrond Myklebust continue; 890bf2bf9b8STrond Myklebust clp->cl_rpcclient->cl_softerr = 0; 891bf2bf9b8STrond Myklebust clp->cl_rpcclient->cl_softrtry = 0; 8925f01d953SPeng Tao } 8935f01d953SPeng Tao 8945f01d953SPeng Tao if (IS_ERR(clp)) { 8955f01d953SPeng Tao status = PTR_ERR(clp); 8965f01d953SPeng Tao goto out; 8975f01d953SPeng Tao } 8985f01d953SPeng Tao 8995f01d953SPeng Tao smp_wmb(); 900a2915fa0SBaptiste Lepers WRITE_ONCE(ds->ds_clp, clp); 9015f01d953SPeng Tao dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr); 9025f01d953SPeng Tao out: 9035f01d953SPeng Tao return status; 9045f01d953SPeng Tao } 9055f01d953SPeng Tao 9065f01d953SPeng Tao static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv, 9077405f9e1SPeng Tao struct nfs4_pnfs_ds *ds, 9087405f9e1SPeng Tao unsigned int timeo, 909064172f3SPeng Tao unsigned int retrans, 9107d38de3fSAnna Schumaker u32 minor_version) 9117405f9e1SPeng Tao { 9127405f9e1SPeng Tao struct nfs_client *clp = ERR_PTR(-EIO); 9137405f9e1SPeng Tao struct nfs4_pnfs_ds_addr *da; 9147405f9e1SPeng Tao int status = 0; 9157405f9e1SPeng Tao 9167d38de3fSAnna Schumaker dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr); 9177405f9e1SPeng Tao 9187405f9e1SPeng Tao list_for_each_entry(da, &ds->ds_addrs, da_node) { 9197405f9e1SPeng Tao dprintk("%s: DS %s: trying address %s\n", 9207405f9e1SPeng Tao __func__, ds->ds_remotestr, da->da_remotestr); 9217405f9e1SPeng Tao 92204fa2c6bSAndy Adamson if (!IS_ERR(clp) && clp->cl_mvops->session_trunk) { 92304fa2c6bSAndy Adamson struct xprt_create xprt_args = { 9244be78d26STrond Myklebust .ident = da->da_transport, 92504fa2c6bSAndy Adamson .net = clp->cl_net, 92604fa2c6bSAndy Adamson .dstaddr = (struct sockaddr *)&da->da_addr, 92704fa2c6bSAndy Adamson .addrlen = da->da_addrlen, 92804fa2c6bSAndy Adamson .servername = clp->cl_hostname, 92904fa2c6bSAndy Adamson }; 93004fa2c6bSAndy Adamson struct nfs4_add_xprt_data xprtdata = { 93104fa2c6bSAndy Adamson .clp = clp, 93204fa2c6bSAndy Adamson }; 93304fa2c6bSAndy Adamson struct rpc_add_xprt_test rpcdata = { 93404fa2c6bSAndy Adamson .add_xprt_test = clp->cl_mvops->session_trunk, 93504fa2c6bSAndy Adamson .data = &xprtdata, 93604fa2c6bSAndy Adamson }; 93704fa2c6bSAndy Adamson 9384be78d26STrond Myklebust if (da->da_transport != clp->cl_proto) 9394be78d26STrond Myklebust continue; 940a12f996dSTrond Myklebust if (da->da_addr.ss_family != clp->cl_addr.ss_family) 941a12f996dSTrond Myklebust continue; 94204fa2c6bSAndy Adamson /** 94304fa2c6bSAndy Adamson * Test this address for session trunking and 94404fa2c6bSAndy Adamson * add as an alias 94504fa2c6bSAndy Adamson */ 946a12f996dSTrond Myklebust xprtdata.cred = nfs4_get_clid_cred(clp), 94704fa2c6bSAndy Adamson rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args, 94804fa2c6bSAndy Adamson rpc_clnt_setup_test_and_add_xprt, 94904fa2c6bSAndy Adamson &rpcdata); 95004fa2c6bSAndy Adamson if (xprtdata.cred) 951a52458b4SNeilBrown put_cred(xprtdata.cred); 95204fa2c6bSAndy Adamson } else { 9533fc75f12STigran Mkrtchyan clp = nfs4_set_ds_client(mds_srv, 9547405f9e1SPeng Tao (struct sockaddr *)&da->da_addr, 9554be78d26STrond Myklebust da->da_addrlen, 9564be78d26STrond Myklebust da->da_transport, timeo, 9574be78d26STrond Myklebust retrans, minor_version); 95804fa2c6bSAndy Adamson if (IS_ERR(clp)) 95904fa2c6bSAndy Adamson continue; 96004fa2c6bSAndy Adamson 96104fa2c6bSAndy Adamson status = nfs4_init_ds_session(clp, 96204fa2c6bSAndy Adamson mds_srv->nfs_client->cl_lease_time); 96304fa2c6bSAndy Adamson if (status) { 96404fa2c6bSAndy Adamson nfs_put_client(clp); 96504fa2c6bSAndy Adamson clp = ERR_PTR(-EIO); 96604fa2c6bSAndy Adamson continue; 96704fa2c6bSAndy Adamson } 96804fa2c6bSAndy Adamson 96904fa2c6bSAndy Adamson } 9707405f9e1SPeng Tao } 9717405f9e1SPeng Tao 9727405f9e1SPeng Tao if (IS_ERR(clp)) { 9737405f9e1SPeng Tao status = PTR_ERR(clp); 9747405f9e1SPeng Tao goto out; 9757405f9e1SPeng Tao } 9767405f9e1SPeng Tao 9777405f9e1SPeng Tao smp_wmb(); 978a2915fa0SBaptiste Lepers WRITE_ONCE(ds->ds_clp, clp); 9797405f9e1SPeng Tao dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr); 9807405f9e1SPeng Tao out: 9817405f9e1SPeng Tao return status; 9827405f9e1SPeng Tao } 9837405f9e1SPeng Tao 9847405f9e1SPeng Tao /* 9857405f9e1SPeng Tao * Create an rpc connection to the nfs4_pnfs_ds data server. 9867405f9e1SPeng Tao * Currently only supports IPv4 and IPv6 addresses. 987a33e4b03SWeston Andros Adamson * If connection fails, make devid unavailable and return a -errno. 9887405f9e1SPeng Tao */ 989a33e4b03SWeston Andros Adamson int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds, 9907405f9e1SPeng Tao struct nfs4_deviceid_node *devid, unsigned int timeo, 9917d38de3fSAnna Schumaker unsigned int retrans, u32 version, u32 minor_version) 9927405f9e1SPeng Tao { 993da066f3fSWeston Andros Adamson int err; 9947405f9e1SPeng Tao 995f46f8493STrond Myklebust do { 996f46f8493STrond Myklebust err = nfs4_wait_ds_connect(ds); 997f46f8493STrond Myklebust if (err || ds->ds_clp) 998f46f8493STrond Myklebust goto out; 999f46f8493STrond Myklebust if (nfs4_test_deviceid_unavailable(devid)) 1000f46f8493STrond Myklebust return -ENODEV; 1001f46f8493STrond Myklebust } while (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) != 0); 1002f46f8493STrond Myklebust 1003f46f8493STrond Myklebust if (ds->ds_clp) 1004f46f8493STrond Myklebust goto connect_done; 1005f46f8493STrond Myklebust 1006f46f8493STrond Myklebust switch (version) { 1007f46f8493STrond Myklebust case 3: 1008f46f8493STrond Myklebust err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo, retrans); 1009f46f8493STrond Myklebust break; 1010f46f8493STrond Myklebust case 4: 1011f46f8493STrond Myklebust err = _nfs4_pnfs_v4_ds_connect(mds_srv, ds, timeo, retrans, 1012f46f8493STrond Myklebust minor_version); 1013f46f8493STrond Myklebust break; 1014f46f8493STrond Myklebust default: 1015f46f8493STrond Myklebust dprintk("%s: unsupported DS version %d\n", __func__, version); 10165f01d953SPeng Tao err = -EPROTONOSUPPORT; 10175f01d953SPeng Tao } 10185f01d953SPeng Tao 1019f46f8493STrond Myklebust connect_done: 10207405f9e1SPeng Tao nfs4_clear_ds_conn_bit(ds); 1021f46f8493STrond Myklebust out: 1022a33e4b03SWeston Andros Adamson /* 1023a33e4b03SWeston Andros Adamson * At this point the ds->ds_clp should be ready, but it might have 1024a33e4b03SWeston Andros Adamson * hit an error. 1025a33e4b03SWeston Andros Adamson */ 1026da066f3fSWeston Andros Adamson if (!err) { 1027a33e4b03SWeston Andros Adamson if (!ds->ds_clp || !nfs_client_init_is_complete(ds->ds_clp)) { 1028da066f3fSWeston Andros Adamson WARN_ON_ONCE(ds->ds_clp || 1029da066f3fSWeston Andros Adamson !nfs4_test_deviceid_unavailable(devid)); 1030a33e4b03SWeston Andros Adamson return -EINVAL; 1031a33e4b03SWeston Andros Adamson } 1032da066f3fSWeston Andros Adamson err = nfs_client_init_status(ds->ds_clp); 1033da066f3fSWeston Andros Adamson } 1034a33e4b03SWeston Andros Adamson 1035da066f3fSWeston Andros Adamson return err; 10367405f9e1SPeng Tao } 10377405f9e1SPeng Tao EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_connect); 10387405f9e1SPeng Tao 10396b7f3cf9SPeng Tao /* 10406b7f3cf9SPeng Tao * Currently only supports ipv4, ipv6 and one multi-path address. 10416b7f3cf9SPeng Tao */ 10426b7f3cf9SPeng Tao struct nfs4_pnfs_ds_addr * 10436b7f3cf9SPeng Tao nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags) 10446b7f3cf9SPeng Tao { 10456b7f3cf9SPeng Tao struct nfs4_pnfs_ds_addr *da = NULL; 10466b7f3cf9SPeng Tao char *buf, *portstr; 10476b7f3cf9SPeng Tao __be16 port; 104898899813STrond Myklebust ssize_t nlen, rlen; 10496b7f3cf9SPeng Tao int tmp[2]; 10504be78d26STrond Myklebust char *netid; 10514be78d26STrond Myklebust size_t len; 10526b7f3cf9SPeng Tao char *startsep = ""; 10536b7f3cf9SPeng Tao char *endsep = ""; 10546b7f3cf9SPeng Tao 10556b7f3cf9SPeng Tao 10566b7f3cf9SPeng Tao /* r_netid */ 105798899813STrond Myklebust nlen = xdr_stream_decode_string_dup(xdr, &netid, XDR_MAX_NETOBJ, 105898899813STrond Myklebust gfp_flags); 105998899813STrond Myklebust if (unlikely(nlen < 0)) 10606b7f3cf9SPeng Tao goto out_err; 10616b7f3cf9SPeng Tao 10626b7f3cf9SPeng Tao /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */ 10636b7f3cf9SPeng Tao /* port is ".ABC.DEF", 8 chars max */ 106498899813STrond Myklebust rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN + 106598899813STrond Myklebust IPV6_SCOPE_ID_LEN + 8, gfp_flags); 106698899813STrond Myklebust if (unlikely(rlen < 0)) 10676b7f3cf9SPeng Tao goto out_free_netid; 10686b7f3cf9SPeng Tao 10696b7f3cf9SPeng Tao /* replace port '.' with '-' */ 10706b7f3cf9SPeng Tao portstr = strrchr(buf, '.'); 10716b7f3cf9SPeng Tao if (!portstr) { 10726b7f3cf9SPeng Tao dprintk("%s: Failed finding expected dot in port\n", 10736b7f3cf9SPeng Tao __func__); 10746b7f3cf9SPeng Tao goto out_free_buf; 10756b7f3cf9SPeng Tao } 10766b7f3cf9SPeng Tao *portstr = '-'; 10776b7f3cf9SPeng Tao 10786b7f3cf9SPeng Tao /* find '.' between address and port */ 10796b7f3cf9SPeng Tao portstr = strrchr(buf, '.'); 10806b7f3cf9SPeng Tao if (!portstr) { 10816b7f3cf9SPeng Tao dprintk("%s: Failed finding expected dot between address and " 10826b7f3cf9SPeng Tao "port\n", __func__); 10836b7f3cf9SPeng Tao goto out_free_buf; 10846b7f3cf9SPeng Tao } 10856b7f3cf9SPeng Tao *portstr = '\0'; 10866b7f3cf9SPeng Tao 1087190c75a3STrond Myklebust da = nfs4_pnfs_ds_addr_alloc(gfp_flags); 10886b7f3cf9SPeng Tao if (unlikely(!da)) 10896b7f3cf9SPeng Tao goto out_free_buf; 10906b7f3cf9SPeng Tao 10916b7f3cf9SPeng Tao if (!rpc_pton(net, buf, portstr-buf, (struct sockaddr *)&da->da_addr, 10926b7f3cf9SPeng Tao sizeof(da->da_addr))) { 10936b7f3cf9SPeng Tao dprintk("%s: error parsing address %s\n", __func__, buf); 10946b7f3cf9SPeng Tao goto out_free_da; 10956b7f3cf9SPeng Tao } 10966b7f3cf9SPeng Tao 10976b7f3cf9SPeng Tao portstr++; 10986b7f3cf9SPeng Tao sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]); 10996b7f3cf9SPeng Tao port = htons((tmp[0] << 8) | (tmp[1])); 11006b7f3cf9SPeng Tao 11016b7f3cf9SPeng Tao switch (da->da_addr.ss_family) { 11026b7f3cf9SPeng Tao case AF_INET: 11036b7f3cf9SPeng Tao ((struct sockaddr_in *)&da->da_addr)->sin_port = port; 11046b7f3cf9SPeng Tao da->da_addrlen = sizeof(struct sockaddr_in); 11056b7f3cf9SPeng Tao break; 11066b7f3cf9SPeng Tao 11076b7f3cf9SPeng Tao case AF_INET6: 11086b7f3cf9SPeng Tao ((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port; 11096b7f3cf9SPeng Tao da->da_addrlen = sizeof(struct sockaddr_in6); 11106b7f3cf9SPeng Tao startsep = "["; 11116b7f3cf9SPeng Tao endsep = "]"; 11126b7f3cf9SPeng Tao break; 11136b7f3cf9SPeng Tao 11146b7f3cf9SPeng Tao default: 11156b7f3cf9SPeng Tao dprintk("%s: unsupported address family: %u\n", 11166b7f3cf9SPeng Tao __func__, da->da_addr.ss_family); 11176b7f3cf9SPeng Tao goto out_free_da; 11186b7f3cf9SPeng Tao } 11196b7f3cf9SPeng Tao 11204be78d26STrond Myklebust da->da_transport = xprt_find_transport_ident(netid); 11214be78d26STrond Myklebust if (da->da_transport < 0) { 11224be78d26STrond Myklebust dprintk("%s: ERROR: unknown r_netid \"%s\"\n", 11234be78d26STrond Myklebust __func__, netid); 11246b7f3cf9SPeng Tao goto out_free_da; 11256b7f3cf9SPeng Tao } 11266b7f3cf9SPeng Tao 11274be78d26STrond Myklebust da->da_netid = netid; 11284be78d26STrond Myklebust 11296b7f3cf9SPeng Tao /* save human readable address */ 11306b7f3cf9SPeng Tao len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7; 11316b7f3cf9SPeng Tao da->da_remotestr = kzalloc(len, gfp_flags); 11326b7f3cf9SPeng Tao 11336b7f3cf9SPeng Tao /* NULL is ok, only used for dprintk */ 11346b7f3cf9SPeng Tao if (da->da_remotestr) 11356b7f3cf9SPeng Tao snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep, 11366b7f3cf9SPeng Tao buf, endsep, ntohs(port)); 11376b7f3cf9SPeng Tao 11386b7f3cf9SPeng Tao dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr); 11396b7f3cf9SPeng Tao kfree(buf); 11406b7f3cf9SPeng Tao return da; 11416b7f3cf9SPeng Tao 11426b7f3cf9SPeng Tao out_free_da: 11436b7f3cf9SPeng Tao kfree(da); 11446b7f3cf9SPeng Tao out_free_buf: 11456b7f3cf9SPeng Tao dprintk("%s: Error parsing DS addr: %s\n", __func__, buf); 11466b7f3cf9SPeng Tao kfree(buf); 11476b7f3cf9SPeng Tao out_free_netid: 11486b7f3cf9SPeng Tao kfree(netid); 11496b7f3cf9SPeng Tao out_err: 11506b7f3cf9SPeng Tao return NULL; 11516b7f3cf9SPeng Tao } 11526b7f3cf9SPeng Tao EXPORT_SYMBOL_GPL(nfs4_decode_mp_ds_addr); 1153338d00cfSTom Haynes 1154338d00cfSTom Haynes void 1155338d00cfSTom Haynes pnfs_layout_mark_request_commit(struct nfs_page *req, 1156338d00cfSTom Haynes struct pnfs_layout_segment *lseg, 1157338d00cfSTom Haynes struct nfs_commit_info *cinfo, 1158338d00cfSTom Haynes u32 ds_commit_idx) 1159338d00cfSTom Haynes { 1160338d00cfSTom Haynes struct list_head *list; 1161ba827c9aSTrond Myklebust struct pnfs_commit_array *array; 1162e18c18ebSTrond Myklebust struct pnfs_commit_bucket *bucket; 1163338d00cfSTom Haynes 1164e824f99aSTrond Myklebust mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); 1165ba827c9aSTrond Myklebust array = pnfs_lookup_commit_array(cinfo->ds, lseg); 1166e18c18ebSTrond Myklebust if (!array || !pnfs_is_valid_lseg(lseg)) 1167ba827c9aSTrond Myklebust goto out_resched; 1168e18c18ebSTrond Myklebust bucket = &array->buckets[ds_commit_idx]; 1169e18c18ebSTrond Myklebust list = &bucket->written; 1170338d00cfSTom Haynes /* Non-empty buckets hold a reference on the lseg. That ref 1171338d00cfSTom Haynes * is normally transferred to the COMMIT call and released 1172338d00cfSTom Haynes * there. It could also be released if the last req is pulled 1173338d00cfSTom Haynes * off due to a rewrite, in which case it will be done in 1174338d00cfSTom Haynes * pnfs_common_clear_request_commit 1175338d00cfSTom Haynes */ 1176e18c18ebSTrond Myklebust if (!bucket->lseg) 1177e18c18ebSTrond Myklebust bucket->lseg = pnfs_get_lseg(lseg); 1178338d00cfSTom Haynes set_bit(PG_COMMIT_TO_DS, &req->wb_flags); 1179338d00cfSTom Haynes cinfo->ds->nwritten++; 1180338d00cfSTom Haynes 118186d80f97STrond Myklebust nfs_request_add_commit_list_locked(req, list, cinfo); 1182e824f99aSTrond Myklebust mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 118386d80f97STrond Myklebust nfs_mark_page_unstable(req->wb_page, cinfo); 1184ba827c9aSTrond Myklebust return; 1185ba827c9aSTrond Myklebust out_resched: 1186ba827c9aSTrond Myklebust mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 1187ba827c9aSTrond Myklebust cinfo->completion_ops->resched_write(cinfo, req); 1188338d00cfSTom Haynes } 1189338d00cfSTom Haynes EXPORT_SYMBOL_GPL(pnfs_layout_mark_request_commit); 11905bb89b47STrond Myklebust 11915bb89b47STrond Myklebust int 11925bb89b47STrond Myklebust pnfs_nfs_generic_sync(struct inode *inode, bool datasync) 11935bb89b47STrond Myklebust { 11942e18d4d8STrond Myklebust int ret; 11952e18d4d8STrond Myklebust 11962e18d4d8STrond Myklebust if (!pnfs_layoutcommit_outstanding(inode)) 11972e18d4d8STrond Myklebust return 0; 11982e18d4d8STrond Myklebust ret = nfs_commit_inode(inode, FLUSH_SYNC); 11992e18d4d8STrond Myklebust if (ret < 0) 12002e18d4d8STrond Myklebust return ret; 12015bb89b47STrond Myklebust if (datasync) 12025bb89b47STrond Myklebust return 0; 12035bb89b47STrond Myklebust return pnfs_layoutcommit_inode(inode, true); 12045bb89b47STrond Myklebust } 12055bb89b47STrond Myklebust EXPORT_SYMBOL_GPL(pnfs_nfs_generic_sync); 12065bb89b47STrond Myklebust 1207