18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/nfs/nfs4session.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Trond Myklebust <Trond.Myklebust@netapp.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/string.h> 118c2ecf20Sopenharmony_ci#include <linux/printk.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/sunrpc/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/sunrpc/bc_xprt.h> 158c2ecf20Sopenharmony_ci#include <linux/nfs.h> 168c2ecf20Sopenharmony_ci#include <linux/nfs4.h> 178c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "nfs4_fs.h" 218c2ecf20Sopenharmony_ci#include "internal.h" 228c2ecf20Sopenharmony_ci#include "nfs4session.h" 238c2ecf20Sopenharmony_ci#include "callback.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_STATE 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void nfs4_init_slot_table(struct nfs4_slot_table *tbl, const char *queue) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci tbl->highest_used_slotid = NFS4_NO_SLOT; 308c2ecf20Sopenharmony_ci spin_lock_init(&tbl->slot_tbl_lock); 318c2ecf20Sopenharmony_ci rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, queue); 328c2ecf20Sopenharmony_ci init_waitqueue_head(&tbl->slot_waitq); 338c2ecf20Sopenharmony_ci init_completion(&tbl->complete); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * nfs4_shrink_slot_table - free retired slots from the slot table 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct nfs4_slot **p; 428c2ecf20Sopenharmony_ci if (newsize >= tbl->max_slots) 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci p = &tbl->slots; 468c2ecf20Sopenharmony_ci while (newsize--) 478c2ecf20Sopenharmony_ci p = &(*p)->next; 488c2ecf20Sopenharmony_ci while (*p) { 498c2ecf20Sopenharmony_ci struct nfs4_slot *slot = *p; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci *p = slot->next; 528c2ecf20Sopenharmony_ci kfree(slot); 538c2ecf20Sopenharmony_ci tbl->max_slots--; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * nfs4_slot_tbl_drain_complete - wake waiters when drain is complete 598c2ecf20Sopenharmony_ci * @tbl: controlling slot table 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_civoid nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci if (nfs4_slot_tbl_draining(tbl)) 658c2ecf20Sopenharmony_ci complete(&tbl->complete); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * nfs4_free_slot - free a slot and efficiently update slot table. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * freeing a slot is trivially done by clearing its respective bit 728c2ecf20Sopenharmony_ci * in the bitmap. 738c2ecf20Sopenharmony_ci * If the freed slotid equals highest_used_slotid we want to update it 748c2ecf20Sopenharmony_ci * so that the server would be able to size down the slot table if needed, 758c2ecf20Sopenharmony_ci * otherwise we know that the highest_used_slotid is still in use. 768c2ecf20Sopenharmony_ci * When updating highest_used_slotid there may be "holes" in the bitmap 778c2ecf20Sopenharmony_ci * so we need to scan down from highest_used_slotid to 0 looking for the now 788c2ecf20Sopenharmony_ci * highest slotid in use. 798c2ecf20Sopenharmony_ci * If none found, highest_used_slotid is set to NFS4_NO_SLOT. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Must be called while holding tbl->slot_tbl_lock 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_civoid nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci u32 slotid = slot->slot_nr; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* clear used bit in bitmap */ 888c2ecf20Sopenharmony_ci __clear_bit(slotid, tbl->used_slots); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* update highest_used_slotid when it is freed */ 918c2ecf20Sopenharmony_ci if (slotid == tbl->highest_used_slotid) { 928c2ecf20Sopenharmony_ci u32 new_max = find_last_bit(tbl->used_slots, slotid); 938c2ecf20Sopenharmony_ci if (new_max < slotid) 948c2ecf20Sopenharmony_ci tbl->highest_used_slotid = new_max; 958c2ecf20Sopenharmony_ci else { 968c2ecf20Sopenharmony_ci tbl->highest_used_slotid = NFS4_NO_SLOT; 978c2ecf20Sopenharmony_ci nfs4_slot_tbl_drain_complete(tbl); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci dprintk("%s: slotid %u highest_used_slotid %u\n", __func__, 1018c2ecf20Sopenharmony_ci slotid, tbl->highest_used_slotid); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct nfs4_slot *nfs4_new_slot(struct nfs4_slot_table *tbl, 1058c2ecf20Sopenharmony_ci u32 slotid, u32 seq_init, gfp_t gfp_mask) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct nfs4_slot *slot; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci slot = kzalloc(sizeof(*slot), gfp_mask); 1108c2ecf20Sopenharmony_ci if (slot) { 1118c2ecf20Sopenharmony_ci slot->table = tbl; 1128c2ecf20Sopenharmony_ci slot->slot_nr = slotid; 1138c2ecf20Sopenharmony_ci slot->seq_nr = seq_init; 1148c2ecf20Sopenharmony_ci slot->seq_nr_highest_sent = seq_init; 1158c2ecf20Sopenharmony_ci slot->seq_nr_last_acked = seq_init - 1; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci return slot; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table *tbl, 1218c2ecf20Sopenharmony_ci u32 slotid, u32 seq_init, gfp_t gfp_mask) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct nfs4_slot **p, *slot; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci p = &tbl->slots; 1268c2ecf20Sopenharmony_ci for (;;) { 1278c2ecf20Sopenharmony_ci if (*p == NULL) { 1288c2ecf20Sopenharmony_ci *p = nfs4_new_slot(tbl, tbl->max_slots, 1298c2ecf20Sopenharmony_ci seq_init, gfp_mask); 1308c2ecf20Sopenharmony_ci if (*p == NULL) 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci tbl->max_slots++; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci slot = *p; 1358c2ecf20Sopenharmony_ci if (slot->slot_nr == slotid) 1368c2ecf20Sopenharmony_ci return slot; 1378c2ecf20Sopenharmony_ci p = &slot->next; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void nfs4_lock_slot(struct nfs4_slot_table *tbl, 1438c2ecf20Sopenharmony_ci struct nfs4_slot *slot) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u32 slotid = slot->slot_nr; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci __set_bit(slotid, tbl->used_slots); 1488c2ecf20Sopenharmony_ci if (slotid > tbl->highest_used_slotid || 1498c2ecf20Sopenharmony_ci tbl->highest_used_slotid == NFS4_NO_SLOT) 1508c2ecf20Sopenharmony_ci tbl->highest_used_slotid = slotid; 1518c2ecf20Sopenharmony_ci slot->generation = tbl->generation; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * nfs4_try_to_lock_slot - Given a slot try to allocate it 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * Note: must be called with the slot_tbl_lock held. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cibool nfs4_try_to_lock_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci if (nfs4_test_locked_slot(tbl, slot->slot_nr)) 1628c2ecf20Sopenharmony_ci return false; 1638c2ecf20Sopenharmony_ci nfs4_lock_slot(tbl, slot); 1648c2ecf20Sopenharmony_ci return true; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * nfs4_lookup_slot - Find a slot but don't allocate it 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Note: must be called with the slot_tbl_lock held. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistruct nfs4_slot *nfs4_lookup_slot(struct nfs4_slot_table *tbl, u32 slotid) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci if (slotid <= tbl->max_slotid) 1758c2ecf20Sopenharmony_ci return nfs4_find_or_create_slot(tbl, slotid, 0, GFP_NOWAIT); 1768c2ecf20Sopenharmony_ci return ERR_PTR(-E2BIG); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int nfs4_slot_get_seqid(struct nfs4_slot_table *tbl, u32 slotid, 1808c2ecf20Sopenharmony_ci u32 *seq_nr) 1818c2ecf20Sopenharmony_ci __must_hold(&tbl->slot_tbl_lock) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct nfs4_slot *slot; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci slot = nfs4_lookup_slot(tbl, slotid); 1878c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(slot); 1888c2ecf20Sopenharmony_ci if (!ret) 1898c2ecf20Sopenharmony_ci *seq_nr = slot->seq_nr; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * nfs4_slot_seqid_in_use - test if a slot sequence id is still in use 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Given a slot table, slot id and sequence number, determine if the 1988c2ecf20Sopenharmony_ci * RPC call in question is still in flight. This function is mainly 1998c2ecf20Sopenharmony_ci * intended for use by the callback channel. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic bool nfs4_slot_seqid_in_use(struct nfs4_slot_table *tbl, 2028c2ecf20Sopenharmony_ci u32 slotid, u32 seq_nr) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci u32 cur_seq = 0; 2058c2ecf20Sopenharmony_ci bool ret = false; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci spin_lock(&tbl->slot_tbl_lock); 2088c2ecf20Sopenharmony_ci if (nfs4_slot_get_seqid(tbl, slotid, &cur_seq) == 0 && 2098c2ecf20Sopenharmony_ci cur_seq == seq_nr && test_bit(slotid, tbl->used_slots)) 2108c2ecf20Sopenharmony_ci ret = true; 2118c2ecf20Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 2128c2ecf20Sopenharmony_ci return ret; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * nfs4_slot_wait_on_seqid - wait until a slot sequence id is complete 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * Given a slot table, slot id and sequence number, wait until the 2198c2ecf20Sopenharmony_ci * corresponding RPC call completes. This function is mainly 2208c2ecf20Sopenharmony_ci * intended for use by the callback channel. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ciint nfs4_slot_wait_on_seqid(struct nfs4_slot_table *tbl, 2238c2ecf20Sopenharmony_ci u32 slotid, u32 seq_nr, 2248c2ecf20Sopenharmony_ci unsigned long timeout) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci if (wait_event_timeout(tbl->slot_waitq, 2278c2ecf20Sopenharmony_ci !nfs4_slot_seqid_in_use(tbl, slotid, seq_nr), 2288c2ecf20Sopenharmony_ci timeout) == 0) 2298c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * nfs4_alloc_slot - efficiently look for a free slot 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * nfs4_alloc_slot looks for an unset bit in the used_slots bitmap. 2378c2ecf20Sopenharmony_ci * If found, we mark the slot as used, update the highest_used_slotid, 2388c2ecf20Sopenharmony_ci * and respectively set up the sequence operation args. 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * Note: must be called with under the slot_tbl_lock. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistruct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct nfs4_slot *ret = ERR_PTR(-EBUSY); 2458c2ecf20Sopenharmony_ci u32 slotid; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", 2488c2ecf20Sopenharmony_ci __func__, tbl->used_slots[0], tbl->highest_used_slotid, 2498c2ecf20Sopenharmony_ci tbl->max_slotid + 1); 2508c2ecf20Sopenharmony_ci slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slotid + 1); 2518c2ecf20Sopenharmony_ci if (slotid <= tbl->max_slotid) { 2528c2ecf20Sopenharmony_ci ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT); 2538c2ecf20Sopenharmony_ci if (!IS_ERR(ret)) 2548c2ecf20Sopenharmony_ci nfs4_lock_slot(tbl, ret); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci dprintk("<-- %s used_slots=%04lx highest_used=%u slotid=%u\n", 2578c2ecf20Sopenharmony_ci __func__, tbl->used_slots[0], tbl->highest_used_slotid, 2588c2ecf20Sopenharmony_ci !IS_ERR(ret) ? ret->slot_nr : NFS4_NO_SLOT); 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int nfs4_grow_slot_table(struct nfs4_slot_table *tbl, 2638c2ecf20Sopenharmony_ci u32 max_reqs, u32 ivalue) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci if (max_reqs <= tbl->max_slots) 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci if (!IS_ERR(nfs4_find_or_create_slot(tbl, max_reqs - 1, ivalue, GFP_NOFS))) 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci return -ENOMEM; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void nfs4_reset_slot_table(struct nfs4_slot_table *tbl, 2738c2ecf20Sopenharmony_ci u32 server_highest_slotid, 2748c2ecf20Sopenharmony_ci u32 ivalue) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct nfs4_slot **p; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci nfs4_shrink_slot_table(tbl, server_highest_slotid + 1); 2798c2ecf20Sopenharmony_ci p = &tbl->slots; 2808c2ecf20Sopenharmony_ci while (*p) { 2818c2ecf20Sopenharmony_ci (*p)->seq_nr = ivalue; 2828c2ecf20Sopenharmony_ci (*p)->seq_nr_highest_sent = ivalue; 2838c2ecf20Sopenharmony_ci (*p)->seq_nr_last_acked = ivalue - 1; 2848c2ecf20Sopenharmony_ci p = &(*p)->next; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci tbl->highest_used_slotid = NFS4_NO_SLOT; 2878c2ecf20Sopenharmony_ci tbl->target_highest_slotid = server_highest_slotid; 2888c2ecf20Sopenharmony_ci tbl->server_highest_slotid = server_highest_slotid; 2898c2ecf20Sopenharmony_ci tbl->d_target_highest_slotid = 0; 2908c2ecf20Sopenharmony_ci tbl->d2_target_highest_slotid = 0; 2918c2ecf20Sopenharmony_ci tbl->max_slotid = server_highest_slotid; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * (re)Initialise a slot table 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, 2988c2ecf20Sopenharmony_ci u32 max_reqs, u32 ivalue) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci int ret; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci dprintk("--> %s: max_reqs=%u, tbl->max_slots %u\n", __func__, 3038c2ecf20Sopenharmony_ci max_reqs, tbl->max_slots); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (max_reqs > NFS4_MAX_SLOT_TABLE) 3068c2ecf20Sopenharmony_ci max_reqs = NFS4_MAX_SLOT_TABLE; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = nfs4_grow_slot_table(tbl, max_reqs, ivalue); 3098c2ecf20Sopenharmony_ci if (ret) 3108c2ecf20Sopenharmony_ci goto out; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci spin_lock(&tbl->slot_tbl_lock); 3138c2ecf20Sopenharmony_ci nfs4_reset_slot_table(tbl, max_reqs - 1, ivalue); 3148c2ecf20Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci dprintk("%s: tbl=%p slots=%p max_slots=%u\n", __func__, 3178c2ecf20Sopenharmony_ci tbl, tbl->slots, tbl->max_slots); 3188c2ecf20Sopenharmony_ciout: 3198c2ecf20Sopenharmony_ci dprintk("<-- %s: return %d\n", __func__, ret); 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* 3248c2ecf20Sopenharmony_ci * nfs4_release_slot_table - release all slot table entries 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_cistatic void nfs4_release_slot_table(struct nfs4_slot_table *tbl) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci nfs4_shrink_slot_table(tbl, 0); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/** 3328c2ecf20Sopenharmony_ci * nfs4_shutdown_slot_table - release resources attached to a slot table 3338c2ecf20Sopenharmony_ci * @tbl: slot table to shut down 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_civoid nfs4_shutdown_slot_table(struct nfs4_slot_table *tbl) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci nfs4_release_slot_table(tbl); 3398c2ecf20Sopenharmony_ci rpc_destroy_wait_queue(&tbl->slot_tbl_waitq); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/** 3438c2ecf20Sopenharmony_ci * nfs4_setup_slot_table - prepare a stand-alone slot table for use 3448c2ecf20Sopenharmony_ci * @tbl: slot table to set up 3458c2ecf20Sopenharmony_ci * @max_reqs: maximum number of requests allowed 3468c2ecf20Sopenharmony_ci * @queue: name to give RPC wait queue 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * Returns zero on success, or a negative errno. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ciint nfs4_setup_slot_table(struct nfs4_slot_table *tbl, unsigned int max_reqs, 3518c2ecf20Sopenharmony_ci const char *queue) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci nfs4_init_slot_table(tbl, queue); 3548c2ecf20Sopenharmony_ci return nfs4_realloc_slot_table(tbl, max_reqs, 0); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic bool nfs41_assign_slot(struct rpc_task *task, void *pslot) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct nfs4_sequence_args *args = task->tk_msg.rpc_argp; 3608c2ecf20Sopenharmony_ci struct nfs4_sequence_res *res = task->tk_msg.rpc_resp; 3618c2ecf20Sopenharmony_ci struct nfs4_slot *slot = pslot; 3628c2ecf20Sopenharmony_ci struct nfs4_slot_table *tbl = slot->table; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (nfs4_slot_tbl_draining(tbl) && !args->sa_privileged) 3658c2ecf20Sopenharmony_ci return false; 3668c2ecf20Sopenharmony_ci slot->generation = tbl->generation; 3678c2ecf20Sopenharmony_ci args->sa_slot = slot; 3688c2ecf20Sopenharmony_ci res->sr_timestamp = jiffies; 3698c2ecf20Sopenharmony_ci res->sr_slot = slot; 3708c2ecf20Sopenharmony_ci res->sr_status_flags = 0; 3718c2ecf20Sopenharmony_ci res->sr_status = 1; 3728c2ecf20Sopenharmony_ci return true; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic bool __nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl, 3768c2ecf20Sopenharmony_ci struct nfs4_slot *slot) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci if (rpc_wake_up_first(&tbl->slot_tbl_waitq, nfs41_assign_slot, slot)) 3798c2ecf20Sopenharmony_ci return true; 3808c2ecf20Sopenharmony_ci return false; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cibool nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl, 3848c2ecf20Sopenharmony_ci struct nfs4_slot *slot) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci if (slot->slot_nr > tbl->max_slotid) 3878c2ecf20Sopenharmony_ci return false; 3888c2ecf20Sopenharmony_ci return __nfs41_wake_and_assign_slot(tbl, slot); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic bool nfs41_try_wake_next_slot_table_entry(struct nfs4_slot_table *tbl) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct nfs4_slot *slot = nfs4_alloc_slot(tbl); 3948c2ecf20Sopenharmony_ci if (!IS_ERR(slot)) { 3958c2ecf20Sopenharmony_ci bool ret = __nfs41_wake_and_assign_slot(tbl, slot); 3968c2ecf20Sopenharmony_ci if (ret) 3978c2ecf20Sopenharmony_ci return ret; 3988c2ecf20Sopenharmony_ci nfs4_free_slot(tbl, slot); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci return false; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_civoid nfs41_wake_slot_table(struct nfs4_slot_table *tbl) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci for (;;) { 4068c2ecf20Sopenharmony_ci if (!nfs41_try_wake_next_slot_table_entry(tbl)) 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci#if defined(CONFIG_NFS_V4_1) 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic void nfs41_set_max_slotid_locked(struct nfs4_slot_table *tbl, 4148c2ecf20Sopenharmony_ci u32 target_highest_slotid) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci u32 max_slotid; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, target_highest_slotid); 4198c2ecf20Sopenharmony_ci if (max_slotid > tbl->server_highest_slotid) 4208c2ecf20Sopenharmony_ci max_slotid = tbl->server_highest_slotid; 4218c2ecf20Sopenharmony_ci if (max_slotid > tbl->target_highest_slotid) 4228c2ecf20Sopenharmony_ci max_slotid = tbl->target_highest_slotid; 4238c2ecf20Sopenharmony_ci tbl->max_slotid = max_slotid; 4248c2ecf20Sopenharmony_ci nfs41_wake_slot_table(tbl); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/* Update the client's idea of target_highest_slotid */ 4288c2ecf20Sopenharmony_cistatic void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, 4298c2ecf20Sopenharmony_ci u32 target_highest_slotid) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci if (tbl->target_highest_slotid == target_highest_slotid) 4328c2ecf20Sopenharmony_ci return; 4338c2ecf20Sopenharmony_ci tbl->target_highest_slotid = target_highest_slotid; 4348c2ecf20Sopenharmony_ci tbl->generation++; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_civoid nfs41_set_target_slotid(struct nfs4_slot_table *tbl, 4388c2ecf20Sopenharmony_ci u32 target_highest_slotid) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci spin_lock(&tbl->slot_tbl_lock); 4418c2ecf20Sopenharmony_ci nfs41_set_target_slotid_locked(tbl, target_highest_slotid); 4428c2ecf20Sopenharmony_ci tbl->d_target_highest_slotid = 0; 4438c2ecf20Sopenharmony_ci tbl->d2_target_highest_slotid = 0; 4448c2ecf20Sopenharmony_ci nfs41_set_max_slotid_locked(tbl, target_highest_slotid); 4458c2ecf20Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl, 4498c2ecf20Sopenharmony_ci u32 highest_slotid) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci if (tbl->server_highest_slotid == highest_slotid) 4528c2ecf20Sopenharmony_ci return; 4538c2ecf20Sopenharmony_ci if (tbl->highest_used_slotid > highest_slotid) 4548c2ecf20Sopenharmony_ci return; 4558c2ecf20Sopenharmony_ci /* Deallocate slots */ 4568c2ecf20Sopenharmony_ci nfs4_shrink_slot_table(tbl, highest_slotid + 1); 4578c2ecf20Sopenharmony_ci tbl->server_highest_slotid = highest_slotid; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic s32 nfs41_derivative_target_slotid(s32 s1, s32 s2) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci s1 -= s2; 4638c2ecf20Sopenharmony_ci if (s1 == 0) 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci if (s1 < 0) 4668c2ecf20Sopenharmony_ci return (s1 - 1) >> 1; 4678c2ecf20Sopenharmony_ci return (s1 + 1) >> 1; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int nfs41_sign_s32(s32 s1) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci if (s1 > 0) 4738c2ecf20Sopenharmony_ci return 1; 4748c2ecf20Sopenharmony_ci if (s1 < 0) 4758c2ecf20Sopenharmony_ci return -1; 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic bool nfs41_same_sign_or_zero_s32(s32 s1, s32 s2) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci if (!s1 || !s2) 4828c2ecf20Sopenharmony_ci return true; 4838c2ecf20Sopenharmony_ci return nfs41_sign_s32(s1) == nfs41_sign_s32(s2); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* Try to eliminate outliers by checking for sharp changes in the 4878c2ecf20Sopenharmony_ci * derivatives and second derivatives 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_cistatic bool nfs41_is_outlier_target_slotid(struct nfs4_slot_table *tbl, 4908c2ecf20Sopenharmony_ci u32 new_target) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci s32 d_target, d2_target; 4938c2ecf20Sopenharmony_ci bool ret = true; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci d_target = nfs41_derivative_target_slotid(new_target, 4968c2ecf20Sopenharmony_ci tbl->target_highest_slotid); 4978c2ecf20Sopenharmony_ci d2_target = nfs41_derivative_target_slotid(d_target, 4988c2ecf20Sopenharmony_ci tbl->d_target_highest_slotid); 4998c2ecf20Sopenharmony_ci /* Is first derivative same sign? */ 5008c2ecf20Sopenharmony_ci if (nfs41_same_sign_or_zero_s32(d_target, tbl->d_target_highest_slotid)) 5018c2ecf20Sopenharmony_ci ret = false; 5028c2ecf20Sopenharmony_ci /* Is second derivative same sign? */ 5038c2ecf20Sopenharmony_ci if (nfs41_same_sign_or_zero_s32(d2_target, tbl->d2_target_highest_slotid)) 5048c2ecf20Sopenharmony_ci ret = false; 5058c2ecf20Sopenharmony_ci tbl->d_target_highest_slotid = d_target; 5068c2ecf20Sopenharmony_ci tbl->d2_target_highest_slotid = d2_target; 5078c2ecf20Sopenharmony_ci return ret; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_civoid nfs41_update_target_slotid(struct nfs4_slot_table *tbl, 5118c2ecf20Sopenharmony_ci struct nfs4_slot *slot, 5128c2ecf20Sopenharmony_ci struct nfs4_sequence_res *res) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci spin_lock(&tbl->slot_tbl_lock); 5158c2ecf20Sopenharmony_ci if (!nfs41_is_outlier_target_slotid(tbl, res->sr_target_highest_slotid)) 5168c2ecf20Sopenharmony_ci nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); 5178c2ecf20Sopenharmony_ci if (tbl->generation == slot->generation) 5188c2ecf20Sopenharmony_ci nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid); 5198c2ecf20Sopenharmony_ci nfs41_set_max_slotid_locked(tbl, res->sr_target_highest_slotid); 5208c2ecf20Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic void nfs4_release_session_slot_tables(struct nfs4_session *session) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci nfs4_release_slot_table(&session->fc_slot_table); 5268c2ecf20Sopenharmony_ci nfs4_release_slot_table(&session->bc_slot_table); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/* 5308c2ecf20Sopenharmony_ci * Initialize or reset the forechannel and backchannel tables 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ciint nfs4_setup_session_slot_tables(struct nfs4_session *ses) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct nfs4_slot_table *tbl; 5358c2ecf20Sopenharmony_ci int status; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci dprintk("--> %s\n", __func__); 5388c2ecf20Sopenharmony_ci /* Fore channel */ 5398c2ecf20Sopenharmony_ci tbl = &ses->fc_slot_table; 5408c2ecf20Sopenharmony_ci tbl->session = ses; 5418c2ecf20Sopenharmony_ci status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1); 5428c2ecf20Sopenharmony_ci if (status || !(ses->flags & SESSION4_BACK_CHAN)) /* -ENOMEM */ 5438c2ecf20Sopenharmony_ci return status; 5448c2ecf20Sopenharmony_ci /* Back channel */ 5458c2ecf20Sopenharmony_ci tbl = &ses->bc_slot_table; 5468c2ecf20Sopenharmony_ci tbl->session = ses; 5478c2ecf20Sopenharmony_ci status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0); 5488c2ecf20Sopenharmony_ci if (status && tbl->slots == NULL) 5498c2ecf20Sopenharmony_ci /* Fore and back channel share a connection so get 5508c2ecf20Sopenharmony_ci * both slot tables or neither */ 5518c2ecf20Sopenharmony_ci nfs4_release_session_slot_tables(ses); 5528c2ecf20Sopenharmony_ci return status; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistruct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct nfs4_session *session; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS); 5608c2ecf20Sopenharmony_ci if (!session) 5618c2ecf20Sopenharmony_ci return NULL; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci nfs4_init_slot_table(&session->fc_slot_table, "ForeChannel Slot table"); 5648c2ecf20Sopenharmony_ci nfs4_init_slot_table(&session->bc_slot_table, "BackChannel Slot table"); 5658c2ecf20Sopenharmony_ci session->session_state = 1<<NFS4_SESSION_INITING; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci session->clp = clp; 5688c2ecf20Sopenharmony_ci return session; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void nfs4_destroy_session_slot_tables(struct nfs4_session *session) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci nfs4_shutdown_slot_table(&session->fc_slot_table); 5748c2ecf20Sopenharmony_ci nfs4_shutdown_slot_table(&session->bc_slot_table); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_civoid nfs4_destroy_session(struct nfs4_session *session) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct rpc_xprt *xprt; 5808c2ecf20Sopenharmony_ci const struct cred *cred; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci cred = nfs4_get_clid_cred(session->clp); 5838c2ecf20Sopenharmony_ci nfs4_proc_destroy_session(session, cred); 5848c2ecf20Sopenharmony_ci put_cred(cred); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci rcu_read_lock(); 5878c2ecf20Sopenharmony_ci xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt); 5888c2ecf20Sopenharmony_ci rcu_read_unlock(); 5898c2ecf20Sopenharmony_ci dprintk("%s Destroy backchannel for xprt %p\n", 5908c2ecf20Sopenharmony_ci __func__, xprt); 5918c2ecf20Sopenharmony_ci xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); 5928c2ecf20Sopenharmony_ci nfs4_destroy_session_slot_tables(session); 5938c2ecf20Sopenharmony_ci kfree(session); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/* 5978c2ecf20Sopenharmony_ci * With sessions, the client is not marked ready until after a 5988c2ecf20Sopenharmony_ci * successful EXCHANGE_ID and CREATE_SESSION. 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate 6018c2ecf20Sopenharmony_ci * other versions of NFS can be tried. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_cistatic int nfs41_check_session_ready(struct nfs_client *clp) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int ret; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (clp->cl_cons_state == NFS_CS_SESSION_INITING) { 6088c2ecf20Sopenharmony_ci ret = nfs4_client_recover_expired_lease(clp); 6098c2ecf20Sopenharmony_ci if (ret) 6108c2ecf20Sopenharmony_ci return ret; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci if (clp->cl_cons_state < NFS_CS_READY) 6138c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 6148c2ecf20Sopenharmony_ci smp_rmb(); 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ciint nfs4_init_session(struct nfs_client *clp) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci if (!nfs4_has_session(clp)) 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci clear_bit(NFS4_SESSION_INITING, &clp->cl_session->session_state); 6248c2ecf20Sopenharmony_ci return nfs41_check_session_ready(clp); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ciint nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct nfs4_session *session = clp->cl_session; 6308c2ecf20Sopenharmony_ci int ret; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 6338c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) { 6348c2ecf20Sopenharmony_ci /* 6358c2ecf20Sopenharmony_ci * Do not set NFS_CS_CHECK_LEASE_TIME instead set the 6368c2ecf20Sopenharmony_ci * DS lease to be equal to the MDS lease. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci clp->cl_lease_time = lease_time; 6398c2ecf20Sopenharmony_ci clp->cl_last_renewal = jiffies; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci ret = nfs41_check_session_ready(clp); 6448c2ecf20Sopenharmony_ci if (ret) 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci /* Test for the DS role */ 6478c2ecf20Sopenharmony_ci if (!is_ds_client(clp)) 6488c2ecf20Sopenharmony_ci return -ENODEV; 6498c2ecf20Sopenharmony_ci return 0; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_init_ds_session); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci#endif /* defined(CONFIG_NFS_V4_1) */ 654