18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Server address list management 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/ctype.h> 108c2ecf20Sopenharmony_ci#include <linux/dns_resolver.h> 118c2ecf20Sopenharmony_ci#include <linux/inet.h> 128c2ecf20Sopenharmony_ci#include <keys/rxrpc-type.h> 138c2ecf20Sopenharmony_ci#include "internal.h" 148c2ecf20Sopenharmony_ci#include "afs_fs.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * Release an address list. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_civoid afs_put_addrlist(struct afs_addr_list *alist) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci if (alist && refcount_dec_and_test(&alist->usage)) 228c2ecf20Sopenharmony_ci kfree_rcu(alist, rcu); 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Allocate an address list. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cistruct afs_addr_list *afs_alloc_addrlist(unsigned int nr, 298c2ecf20Sopenharmony_ci unsigned short service, 308c2ecf20Sopenharmony_ci unsigned short port) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct afs_addr_list *alist; 338c2ecf20Sopenharmony_ci unsigned int i; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci _enter("%u,%u,%u", nr, service, port); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (nr > AFS_MAX_ADDRESSES) 388c2ecf20Sopenharmony_ci nr = AFS_MAX_ADDRESSES; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci alist = kzalloc(struct_size(alist, addrs, nr), GFP_KERNEL); 418c2ecf20Sopenharmony_ci if (!alist) 428c2ecf20Sopenharmony_ci return NULL; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci refcount_set(&alist->usage, 1); 458c2ecf20Sopenharmony_ci alist->max_addrs = nr; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci for (i = 0; i < nr; i++) { 488c2ecf20Sopenharmony_ci struct sockaddr_rxrpc *srx = &alist->addrs[i]; 498c2ecf20Sopenharmony_ci srx->srx_family = AF_RXRPC; 508c2ecf20Sopenharmony_ci srx->srx_service = service; 518c2ecf20Sopenharmony_ci srx->transport_type = SOCK_DGRAM; 528c2ecf20Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin6); 538c2ecf20Sopenharmony_ci srx->transport.sin6.sin6_family = AF_INET6; 548c2ecf20Sopenharmony_ci srx->transport.sin6.sin6_port = htons(port); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return alist; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Parse a text string consisting of delimited addresses. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistruct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *net, 648c2ecf20Sopenharmony_ci const char *text, size_t len, 658c2ecf20Sopenharmony_ci char delim, 668c2ecf20Sopenharmony_ci unsigned short service, 678c2ecf20Sopenharmony_ci unsigned short port) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct afs_vlserver_list *vllist; 708c2ecf20Sopenharmony_ci struct afs_addr_list *alist; 718c2ecf20Sopenharmony_ci const char *p, *end = text + len; 728c2ecf20Sopenharmony_ci const char *problem; 738c2ecf20Sopenharmony_ci unsigned int nr = 0; 748c2ecf20Sopenharmony_ci int ret = -ENOMEM; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci _enter("%*.*s,%c", (int)len, (int)len, text, delim); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (!len) { 798c2ecf20Sopenharmony_ci _leave(" = -EDESTADDRREQ [empty]"); 808c2ecf20Sopenharmony_ci return ERR_PTR(-EDESTADDRREQ); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len))) 848c2ecf20Sopenharmony_ci delim = ','; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Count the addresses */ 878c2ecf20Sopenharmony_ci p = text; 888c2ecf20Sopenharmony_ci do { 898c2ecf20Sopenharmony_ci if (!*p) { 908c2ecf20Sopenharmony_ci problem = "nul"; 918c2ecf20Sopenharmony_ci goto inval; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci if (*p == delim) 948c2ecf20Sopenharmony_ci continue; 958c2ecf20Sopenharmony_ci nr++; 968c2ecf20Sopenharmony_ci if (*p == '[') { 978c2ecf20Sopenharmony_ci p++; 988c2ecf20Sopenharmony_ci if (p == end) { 998c2ecf20Sopenharmony_ci problem = "brace1"; 1008c2ecf20Sopenharmony_ci goto inval; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci p = memchr(p, ']', end - p); 1038c2ecf20Sopenharmony_ci if (!p) { 1048c2ecf20Sopenharmony_ci problem = "brace2"; 1058c2ecf20Sopenharmony_ci goto inval; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci p++; 1088c2ecf20Sopenharmony_ci if (p >= end) 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci p = memchr(p, delim, end - p); 1138c2ecf20Sopenharmony_ci if (!p) 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci p++; 1168c2ecf20Sopenharmony_ci } while (p < end); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci _debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci vllist = afs_alloc_vlserver_list(1); 1218c2ecf20Sopenharmony_ci if (!vllist) 1228c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci vllist->nr_servers = 1; 1258c2ecf20Sopenharmony_ci vllist->servers[0].server = afs_alloc_vlserver("<dummy>", 7, AFS_VL_PORT); 1268c2ecf20Sopenharmony_ci if (!vllist->servers[0].server) 1278c2ecf20Sopenharmony_ci goto error_vl; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci alist = afs_alloc_addrlist(nr, service, AFS_VL_PORT); 1308c2ecf20Sopenharmony_ci if (!alist) 1318c2ecf20Sopenharmony_ci goto error; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Extract the addresses */ 1348c2ecf20Sopenharmony_ci p = text; 1358c2ecf20Sopenharmony_ci do { 1368c2ecf20Sopenharmony_ci const char *q, *stop; 1378c2ecf20Sopenharmony_ci unsigned int xport = port; 1388c2ecf20Sopenharmony_ci __be32 x[4]; 1398c2ecf20Sopenharmony_ci int family; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (*p == delim) { 1428c2ecf20Sopenharmony_ci p++; 1438c2ecf20Sopenharmony_ci continue; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (*p == '[') { 1478c2ecf20Sopenharmony_ci p++; 1488c2ecf20Sopenharmony_ci q = memchr(p, ']', end - p); 1498c2ecf20Sopenharmony_ci } else { 1508c2ecf20Sopenharmony_ci for (q = p; q < end; q++) 1518c2ecf20Sopenharmony_ci if (*q == '+' || *q == delim) 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop)) { 1568c2ecf20Sopenharmony_ci family = AF_INET; 1578c2ecf20Sopenharmony_ci } else if (in6_pton(p, q - p, (u8 *)x, -1, &stop)) { 1588c2ecf20Sopenharmony_ci family = AF_INET6; 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci problem = "family"; 1618c2ecf20Sopenharmony_ci goto bad_address; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci p = q; 1658c2ecf20Sopenharmony_ci if (stop != p) { 1668c2ecf20Sopenharmony_ci problem = "nostop"; 1678c2ecf20Sopenharmony_ci goto bad_address; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (q < end && *q == ']') 1718c2ecf20Sopenharmony_ci p++; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (p < end) { 1748c2ecf20Sopenharmony_ci if (*p == '+') { 1758c2ecf20Sopenharmony_ci /* Port number specification "+1234" */ 1768c2ecf20Sopenharmony_ci xport = 0; 1778c2ecf20Sopenharmony_ci p++; 1788c2ecf20Sopenharmony_ci if (p >= end || !isdigit(*p)) { 1798c2ecf20Sopenharmony_ci problem = "port"; 1808c2ecf20Sopenharmony_ci goto bad_address; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci do { 1838c2ecf20Sopenharmony_ci xport *= 10; 1848c2ecf20Sopenharmony_ci xport += *p - '0'; 1858c2ecf20Sopenharmony_ci if (xport > 65535) { 1868c2ecf20Sopenharmony_ci problem = "pval"; 1878c2ecf20Sopenharmony_ci goto bad_address; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci p++; 1908c2ecf20Sopenharmony_ci } while (p < end && isdigit(*p)); 1918c2ecf20Sopenharmony_ci } else if (*p == delim) { 1928c2ecf20Sopenharmony_ci p++; 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci problem = "weird"; 1958c2ecf20Sopenharmony_ci goto bad_address; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (family == AF_INET) 2008c2ecf20Sopenharmony_ci afs_merge_fs_addr4(alist, x[0], xport); 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci afs_merge_fs_addr6(alist, x, xport); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci } while (p < end); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci rcu_assign_pointer(vllist->servers[0].server->addresses, alist); 2078c2ecf20Sopenharmony_ci _leave(" = [nr %u]", alist->nr_addrs); 2088c2ecf20Sopenharmony_ci return vllist; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciinval: 2118c2ecf20Sopenharmony_ci _leave(" = -EINVAL [%s %zu %*.*s]", 2128c2ecf20Sopenharmony_ci problem, p - text, (int)len, (int)len, text); 2138c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2148c2ecf20Sopenharmony_cibad_address: 2158c2ecf20Sopenharmony_ci _leave(" = -EINVAL [%s %zu %*.*s]", 2168c2ecf20Sopenharmony_ci problem, p - text, (int)len, (int)len, text); 2178c2ecf20Sopenharmony_ci ret = -EINVAL; 2188c2ecf20Sopenharmony_cierror: 2198c2ecf20Sopenharmony_ci afs_put_addrlist(alist); 2208c2ecf20Sopenharmony_cierror_vl: 2218c2ecf20Sopenharmony_ci afs_put_vlserverlist(net, vllist); 2228c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* 2268c2ecf20Sopenharmony_ci * Compare old and new address lists to see if there's been any change. 2278c2ecf20Sopenharmony_ci * - How to do this in better than O(Nlog(N)) time? 2288c2ecf20Sopenharmony_ci * - We don't really want to sort the address list, but would rather take the 2298c2ecf20Sopenharmony_ci * list as we got it so as not to undo record rotation by the DNS server. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci#if 0 2328c2ecf20Sopenharmony_cistatic int afs_cmp_addr_list(const struct afs_addr_list *a1, 2338c2ecf20Sopenharmony_ci const struct afs_addr_list *a2) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci#endif 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * Perform a DNS query for VL servers and build a up an address list. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cistruct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct afs_vlserver_list *vllist; 2448c2ecf20Sopenharmony_ci char *result = NULL; 2458c2ecf20Sopenharmony_ci int ret; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci _enter("%s", cell->name); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ret = dns_query(cell->net->net, "afsdb", cell->name, cell->name_len, 2508c2ecf20Sopenharmony_ci "srv=1", &result, _expiry, true); 2518c2ecf20Sopenharmony_ci if (ret < 0) { 2528c2ecf20Sopenharmony_ci _leave(" = %d [dns]", ret); 2538c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (*_expiry == 0) 2578c2ecf20Sopenharmony_ci *_expiry = ktime_get_real_seconds() + 60; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (ret > 1 && result[0] == 0) 2608c2ecf20Sopenharmony_ci vllist = afs_extract_vlserver_list(cell, result, ret); 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci vllist = afs_parse_text_addrs(cell->net, result, ret, ',', 2638c2ecf20Sopenharmony_ci VL_SERVICE, AFS_VL_PORT); 2648c2ecf20Sopenharmony_ci kfree(result); 2658c2ecf20Sopenharmony_ci if (IS_ERR(vllist) && vllist != ERR_PTR(-ENOMEM)) 2668c2ecf20Sopenharmony_ci pr_err("Failed to parse DNS data %ld\n", PTR_ERR(vllist)); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return vllist; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* 2728c2ecf20Sopenharmony_ci * Merge an IPv4 entry into a fileserver address list. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_civoid afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct sockaddr_rxrpc *srx; 2778c2ecf20Sopenharmony_ci u32 addr = ntohl(xdr); 2788c2ecf20Sopenharmony_ci int i; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (alist->nr_addrs >= alist->max_addrs) 2818c2ecf20Sopenharmony_ci return; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci for (i = 0; i < alist->nr_ipv4; i++) { 2848c2ecf20Sopenharmony_ci struct sockaddr_in *a = &alist->addrs[i].transport.sin; 2858c2ecf20Sopenharmony_ci u32 a_addr = ntohl(a->sin_addr.s_addr); 2868c2ecf20Sopenharmony_ci u16 a_port = ntohs(a->sin_port); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (addr == a_addr && port == a_port) 2898c2ecf20Sopenharmony_ci return; 2908c2ecf20Sopenharmony_ci if (addr == a_addr && port < a_port) 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci if (addr < a_addr) 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (i < alist->nr_addrs) 2978c2ecf20Sopenharmony_ci memmove(alist->addrs + i + 1, 2988c2ecf20Sopenharmony_ci alist->addrs + i, 2998c2ecf20Sopenharmony_ci sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci srx = &alist->addrs[i]; 3028c2ecf20Sopenharmony_ci srx->srx_family = AF_RXRPC; 3038c2ecf20Sopenharmony_ci srx->transport_type = SOCK_DGRAM; 3048c2ecf20Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin); 3058c2ecf20Sopenharmony_ci srx->transport.sin.sin_family = AF_INET; 3068c2ecf20Sopenharmony_ci srx->transport.sin.sin_port = htons(port); 3078c2ecf20Sopenharmony_ci srx->transport.sin.sin_addr.s_addr = xdr; 3088c2ecf20Sopenharmony_ci alist->nr_ipv4++; 3098c2ecf20Sopenharmony_ci alist->nr_addrs++; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* 3138c2ecf20Sopenharmony_ci * Merge an IPv6 entry into a fileserver address list. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_civoid afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct sockaddr_rxrpc *srx; 3188c2ecf20Sopenharmony_ci int i, diff; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (alist->nr_addrs >= alist->max_addrs) 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { 3248c2ecf20Sopenharmony_ci struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6; 3258c2ecf20Sopenharmony_ci u16 a_port = ntohs(a->sin6_port); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci diff = memcmp(xdr, &a->sin6_addr, 16); 3288c2ecf20Sopenharmony_ci if (diff == 0 && port == a_port) 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci if (diff == 0 && port < a_port) 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci if (diff < 0) 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (i < alist->nr_addrs) 3378c2ecf20Sopenharmony_ci memmove(alist->addrs + i + 1, 3388c2ecf20Sopenharmony_ci alist->addrs + i, 3398c2ecf20Sopenharmony_ci sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci srx = &alist->addrs[i]; 3428c2ecf20Sopenharmony_ci srx->srx_family = AF_RXRPC; 3438c2ecf20Sopenharmony_ci srx->transport_type = SOCK_DGRAM; 3448c2ecf20Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin6); 3458c2ecf20Sopenharmony_ci srx->transport.sin6.sin6_family = AF_INET6; 3468c2ecf20Sopenharmony_ci srx->transport.sin6.sin6_port = htons(port); 3478c2ecf20Sopenharmony_ci memcpy(&srx->transport.sin6.sin6_addr, xdr, 16); 3488c2ecf20Sopenharmony_ci alist->nr_addrs++; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* 3528c2ecf20Sopenharmony_ci * Get an address to try. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_cibool afs_iterate_addresses(struct afs_addr_cursor *ac) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci unsigned long set, failed; 3578c2ecf20Sopenharmony_ci int index; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (!ac->alist) 3608c2ecf20Sopenharmony_ci return false; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci set = ac->alist->responded; 3638c2ecf20Sopenharmony_ci failed = ac->alist->failed; 3648c2ecf20Sopenharmony_ci _enter("%lx-%lx-%lx,%d", set, failed, ac->tried, ac->index); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ac->nr_iterations++; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci set &= ~(failed | ac->tried); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!set) 3718c2ecf20Sopenharmony_ci return false; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci index = READ_ONCE(ac->alist->preferred); 3748c2ecf20Sopenharmony_ci if (test_bit(index, &set)) 3758c2ecf20Sopenharmony_ci goto selected; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci index = __ffs(set); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciselected: 3808c2ecf20Sopenharmony_ci ac->index = index; 3818c2ecf20Sopenharmony_ci set_bit(index, &ac->tried); 3828c2ecf20Sopenharmony_ci ac->responded = false; 3838c2ecf20Sopenharmony_ci return true; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * Release an address list cursor. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ciint afs_end_cursor(struct afs_addr_cursor *ac) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct afs_addr_list *alist; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci alist = ac->alist; 3948c2ecf20Sopenharmony_ci if (alist) { 3958c2ecf20Sopenharmony_ci if (ac->responded && 3968c2ecf20Sopenharmony_ci ac->index != alist->preferred && 3978c2ecf20Sopenharmony_ci test_bit(ac->alist->preferred, &ac->tried)) 3988c2ecf20Sopenharmony_ci WRITE_ONCE(alist->preferred, ac->index); 3998c2ecf20Sopenharmony_ci afs_put_addrlist(alist); 4008c2ecf20Sopenharmony_ci ac->alist = NULL; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return ac->error; 4048c2ecf20Sopenharmony_ci} 405