* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
*/
#include <linux/kthread.h>
#include <linux/nfs_fs.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/clnt.h>
#include <net/netns/generic.h>
#include <linux/dns_resolver.h>
#include "../../../fs/nfs/nfs4_fs.h"
#include "../../../fs/nfs/netns.h"
#include "dns_internal.h"
#include "enfs_log.h"
#include "enfs_multipath.h"
#include "enfs_multipath_client.h"
#include "enfs_remount.h"
#include "enfs_config.h"
#include "exten_call.h"
static struct task_struct *dns_thread;
static struct workqueue_struct *dns_workq;
static LIST_HEAD(dns_cache_list);
static spinlock_t dns_cache_lock;
static char dns_sort_ip
[IP_ADDRESS_LEN_MAX];
struct name_list {
struct list_head next;
char name[MAX_DNS_NAME_LEN];
struct nfs_ip_list inet;
struct nfs_ip_list inet6;
struct nfs_ip_list inet_bc;
struct nfs_ip_list inet6_bc;
int ref;
};
static int sockaddr_ip_to_str(struct sockaddr *addr, char *buf, int len)
{
switch (addr->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
snprintf(buf, len, "%pI4", &sin->sin_addr);
return 0;
}
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
snprintf(buf, len, "%pI6", &sin6->sin6_addr);
return 0;
}
default:
break;
}
return 1;
}
void enfs_debug_print_name_list(void)
{
int i;
struct name_list *list;
char buf[128];
spin_lock(&dns_cache_lock);
list_for_each_entry(list, &dns_cache_list, next) {
enfs_log_info("domain name:%s\n", list->name);
for (i = 0; i < list->inet.count; i++) {
sockaddr_ip_to_str(
(struct sockaddr *)&list->inet.address[i], buf,
128);
enfs_log_info("%s\n", buf);
}
for (i = 0; i < list->inet6.count; i++) {
sockaddr_ip_to_str(
(struct sockaddr *)&list->inet6.address[i], buf,
128);
enfs_log_info("%s\n", buf);
}
}
spin_unlock(&dns_cache_lock);
}
void enfs_update_domain_name(char *name, struct nfs_ip_list *ip_list)
{
struct name_list *name_list;
struct sockaddr *addr;
int i;
spin_lock(&dns_cache_lock);
list_for_each_entry(name_list, &dns_cache_list, next) {
if (strcmp(name, name_list->name) != 0)
continue;
for (i = 0; i < ip_list->count; i++) {
addr = (struct sockaddr *)&ip_list->address[i];
switch (addr->sa_family) {
case AF_INET:
enfs_insert_ip_list(
&name_list->inet,
enfs_get_config_link_count_per_mount(),
&ip_list->address[i]);
break;
case AF_INET6:
enfs_insert_ip_list(
&name_list->inet6,
enfs_get_config_link_count_per_mount(),
&ip_list->address[i]);
break;
}
}
break;
}
spin_unlock(&dns_cache_lock);
}
* Exchange the IP address of the back end to the front end.
*/
void enfs_swap_name_cache(void)
{
struct name_list *name_list;
spin_lock(&dns_cache_lock);
list_for_each_entry(name_list, &dns_cache_list, next) {
if (name_list->inet_bc.count != 0) {
name_list->inet = name_list->inet_bc;
name_list->inet_bc.count = 0;
}
if (name_list->inet6_bc.count != 0) {
name_list->inet6 = name_list->inet6_bc;
name_list->inet6_bc.count = 0;
}
}
spin_unlock(&dns_cache_lock);
}
void enfs_domain_inc(char *name)
{
struct name_list *name_list;
spin_lock(&dns_cache_lock);
list_for_each_entry(name_list, &dns_cache_list, next) {
if (strcmp(name, name_list->name) == 0) {
name_list->ref++;
break;
}
}
if (&name_list->next == &dns_cache_list) {
name_list = kzalloc(sizeof(*name_list), GFP_KERNEL);
if (!name_list) {
spin_unlock(&dns_cache_lock);
enfs_log_error("alloc failed.\n");
return;
}
* The length must be verified in the mount|remount phase
* (enfs_valid_dns) .
*/
strscpy(name_list->name, name, MAX_DNS_NAME_LEN);
name_list->ref = 1;
list_add_tail(&name_list->next, &dns_cache_list);
}
spin_unlock(&dns_cache_lock);
}
bool enfs_ip_list_contain(struct nfs_ip_list *ip_list,
struct sockaddr_storage *addr)
{
int i;
for (i = 0; i < ip_list->count; i++) {
if (rpc_cmp_addr((struct sockaddr *)&ip_list->address[i],
(struct sockaddr *)addr)) {
return true;
}
}
return false;
}
bool enfs_insert_ip_list(struct nfs_ip_list *ip_list, int max,
struct sockaddr_storage *addr)
{
int i;
if (!ip_list || ip_list->count >= max)
return false;
for (i = 0; i < ip_list->count; i++) {
if (rpc_cmp_addr((struct sockaddr *)&ip_list->address[i],
(struct sockaddr *)addr)) {
return false;
}
}
if (i < max) {
ip_list->address[i] = *addr;
ip_list->count++;
return true;
}
return false;
}
void ip_list_append(struct nfs_ip_list *dst, struct nfs_ip_list *src,
int *tmp_slot)
{
int i;
struct sockaddr_storage *addr;
for (i = 0; i < src->count && *tmp_slot != 0; i++) {
addr = &src->address[i];
if (enfs_insert_ip_list(dst,
enfs_get_config_link_count_per_mount(),
addr)) {
(*tmp_slot)--;
}
}
}
static int dns_resolver_name_list(char *dns_result, int *tmp_slot,
struct nfs_ip_list *ip_list)
{
int error = 0;
ssize_t ip_len;
char *ip_str;
struct sockaddr_storage sa;
enfs_log_debug(" resolver name list\n");
ip_str = strsep(&dns_result, ",");
while (ip_str) {
if (ip_list->count == enfs_get_config_link_count_per_mount() ||
*tmp_slot == 0) {
error = 0;
break;
}
ip_len = rpc_pton(NULL, ip_str, strlen(ip_str),
(struct sockaddr *)&sa, sizeof(sa));
if (ip_len <= 0) {
enfs_log_error("pton name:%s failed.\n", ip_str);
error = -ESRCH;
break;
}
if (enfs_insert_ip_list(ip_list,
enfs_get_config_link_count_per_mount(),
&sa)) {
(*tmp_slot)--;
}
ip_str = strsep(&dns_result, ",");
}
return error;
}
* different ip.
* @slot: distributed when multiple domain names.
* @optsions: ipv4 or ipv6
*/
static int multi_query_dns(struct nfs_ip_list *ip_list, char *name, int slot,
int *tmp_slot, const char *options)
{
int error;
char *ip_addr = NULL;
int ip_len;
struct net *net;
net = current->nsproxy->net_ns;
enfs_log_debug("domain_name:%s option:%s\n", name, options);
ip_len = dns_query(net, NULL, name, strlen(name), options,
&ip_addr, NULL, true);
if (ip_len <= 0) {
enfs_log_debug("dns query:%s error.\n", ip_addr);
return -ESRCH;
}
* Note:
* Query domain name list, query only once.
* Now,the executable program in user space must return the IP list.
*/
error = dns_resolver_name_list(ip_addr, tmp_slot, ip_list);
kfree(ip_addr);
return error;
}
static int query_dns_cross_protocol(struct nfs_ip_list *ip_list, char *name,
int slot, unsigned short family)
{
int ret;
int tmp_slot = slot;
if (family == AF_INET6) {
ret = multi_query_dns(ip_list, name, slot, &tmp_slot,
"ipv6 list");
if (ret) {
enfs_log_debug("dns query name:%s type:ipv6 err:%d.\n",
name, ret);
}
ret = multi_query_dns(ip_list, name, slot, &tmp_slot,
"ipv4 list");
if (ret) {
enfs_log_debug("dns query name:%s type:ipv4 err:%d.\n",
name, ret);
}
return 0;
}
ret = multi_query_dns(ip_list, name, slot, &tmp_slot, "ipv4 list");
if (ret)
enfs_log_debug("dns query name:%s type:A err:%d.\n", name, ret);
return ret;
}
static bool query_domain_name_in_cache(struct nfs_ip_list *ip_list, char *name,
int slot, unsigned short family)
{
struct name_list *name_list;
int tmp_solt = slot;
bool ret = false;
spin_lock(&dns_cache_lock);
list_for_each_entry(name_list, &dns_cache_list, next) {
if (strcmp(name_list->name, name) != 0)
continue;
if (family == AF_INET6)
ip_list_append(ip_list, &name_list->inet6, &tmp_solt);
ip_list_append(ip_list, &name_list->inet, &tmp_solt);
if (tmp_solt != slot)
ret = true;
break;
}
spin_unlock(&dns_cache_lock);
return ret;
}
int enfs_quick_sort(int low, int high, struct enfs_dns_query_ip_info_single *dnsQueryIpInfo)
{
int i = low;
int j = high;
uint64_t key = dnsQueryIpInfo[i].lsId;
strscpy(dns_sort_ip, dnsQueryIpInfo[i].ipAddr, IP_ADDRESS_LEN_MAX);
while (i < j) {
while (i < j && dnsQueryIpInfo[j].lsId >= key)
j--;
dnsQueryIpInfo[i].lsId = dnsQueryIpInfo[j].lsId;
strscpy(dnsQueryIpInfo[i].ipAddr, dnsQueryIpInfo[j].ipAddr, IP_ADDRESS_LEN_MAX);
while (i < j && dnsQueryIpInfo[i].lsId <= key)
i++;
dnsQueryIpInfo[j].lsId = dnsQueryIpInfo[i].lsId;
strscpy(dnsQueryIpInfo[j].ipAddr, dnsQueryIpInfo[i].ipAddr, IP_ADDRESS_LEN_MAX);
}
dnsQueryIpInfo[i].lsId = key;
strscpy(dnsQueryIpInfo[i].ipAddr, dns_sort_ip, IP_ADDRESS_LEN_MAX);
if (i - 1 > low)
enfs_quick_sort(low, i - 1, dnsQueryIpInfo);
if (i + 1 < high)
enfs_quick_sort(i + 1, high, dnsQueryIpInfo);
memset(dns_sort_ip, 0, sizeof(dns_sort_ip));
return 0;
}
int enfs_dns_process_ip(struct enfs_dns_query_ip_info_single *dnsQueryIpInfo,
struct enfs_dns_query_lsid_rsp **dnsQueryLsidInfo, int *lsidCount,
int ipNumber)
{
int i;
int index = 0;
int count = 1;
struct enfs_dns_query_lsid_rsp *lsIdInfo = NULL;
enfs_quick_sort(0, ipNumber - 1, dnsQueryIpInfo);
for (i = 1; i < ipNumber; i++) {
if (dnsQueryIpInfo[i - 1].lsId != dnsQueryIpInfo[i].lsId)
count++;
}
*lsidCount = count;
lsIdInfo = kmalloc_array(count, sizeof(struct enfs_dns_query_lsid_rsp), GFP_KERNEL);
if (lsIdInfo == NULL)
return -ENOMEM;
for (i = 0; i < ipNumber; i++) {
if (i != 0 &&
dnsQueryIpInfo[i - 1].lsId == dnsQueryIpInfo[i].lsId) {
lsIdInfo[index].count++;
continue;
}
if (i != 0)
index++;
lsIdInfo[index].lsId = dnsQueryIpInfo[i].lsId;
lsIdInfo[index].offset = 0;
lsIdInfo[index].count = 0;
lsIdInfo[index].count++;
}
*dnsQueryLsidInfo = lsIdInfo;
return 0;
}
int enfs_server_query_dns(struct rpc_clnt *clnt, struct enfs_route_dns_info *dns_info,
struct nfs_ip_list *ipList, int slot,
uint32_t ip_type, uint32_t dnsNamecount,
char *dnsName)
{
int ret;
int i = 0;
int offset = 0;
int tmpSlot = slot;
struct enfs_dns_query_lsid_rsp *dnsQueryLsidInfo = NULL;
struct enfs_dns_query_ip_info_single *dnsQueryIpInfo = NULL;
int ipNumber;
int lsidCount;
ret = dorado_query_dns(clnt, &dnsQueryIpInfo, ip_type, dnsNamecount,
dnsName, &ipNumber);
if (ret)
return ret;
if (dnsQueryIpInfo == NULL)
return ret;
ret = enfs_dns_process_ip(dnsQueryIpInfo, &dnsQueryLsidInfo, &lsidCount,
ipNumber);
if (ret)
return ret;
i = 0;
while (ipList->count <
(enfs_get_config_link_count_per_mount() < ipNumber ?
enfs_get_config_link_count_per_mount() :
ipNumber)) {
if (dnsQueryLsidInfo[i].offset < dnsQueryLsidInfo[i].count) {
if (i != 0)
offset += dnsQueryLsidInfo[i - 1].count;
ret = dns_resolver_name_list(
dnsQueryIpInfo[offset +
dnsQueryLsidInfo[i].offset]
.ipAddr,
&tmpSlot, ipList);
if (ret)
goto out;
dnsQueryLsidInfo[i].offset++;
i++;
} else {
i++;
}
if (i == lsidCount) {
offset = 0;
i = 0;
}
}
out:
kfree(dnsQueryIpInfo);
kfree(dnsQueryLsidInfo);
return ret;
}
void query_dns_each_name(struct enfs_route_dns_info *dns_info, int slot,
struct nfs_ip_list *ipList, unsigned short family,
bool use_cache)
{
int ret;
int i;
char *dnsName = NULL;
for (i = 0; i < dns_info->dnsNameCount; i++) {
dnsName = dns_info->routeRemoteDnsList[i].dnsname;
enfs_log_debug("query DNS:%s\n", dnsName);
if (use_cache &&
query_domain_name_in_cache(ipList, dnsName, slot, family)) {
enfs_log_debug("cache name:%s.\n", dnsName);
continue;
}
ret = query_dns_cross_protocol(ipList, dnsName, slot, family);
if (ret != 0)
enfs_log_debug("dns multi query dns failed.\n");
else
enfs_update_domain_name(dnsName, ipList);
}
}
int multipath_query_dns(struct multipath_mount_options *opt,
unsigned short family, bool use_cache,
struct rpc_clnt *clnt)
{
int ret;
int i;
int slot = 0;
struct enfs_route_dns_info *dns_info;
char *dnsName = NULL;
struct nfs_ip_list *ip_list;
uint32_t ip_type = 0;
if (!opt->pRemoteDnsInfo || opt->pRemoteDnsInfo->dnsNameCount <= 0 ||
opt->pRemoteDnsInfo->dnsNameCount > MAX_DNS_SUPPORTED) {
return -EINVAL;
}
ip_list = kmalloc(sizeof(*ip_list), GFP_KERNEL);
if (!ip_list)
return -ENOMEM;
ip_list->count = 0;
dns_info = opt->pRemoteDnsInfo;
dnsName = kmalloc(dns_info->dnsNameCount * EXTEND_MAX_DNS_NAME_LEN, GFP_KERNEL);
if (!dnsName) {
kfree(ip_list);
return -ENOMEM;
}
if (clnt) {
if (family == AF_INET6)
ip_type = IP_TYPE_BOTH;
for (i = 0; i < dns_info->dnsNameCount; i++) {
sprintf(dnsName + i * EXTEND_MAX_DNS_NAME_LEN, "%s",
dns_info->routeRemoteDnsList[i].dnsname);
}
slot = enfs_get_config_link_count_per_mount() /
dns_info->dnsNameCount;
ret = enfs_server_query_dns(
clnt, dns_info, ip_list,
enfs_get_config_link_count_per_mount(), ip_type,
dns_info->dnsNameCount, dnsName);
if (ret != 0) {
query_dns_each_name(dns_info, slot, ip_list, family,
use_cache);
}
} else {
query_dns_each_name(dns_info, slot, ip_list, family, use_cache);
}
kfree(dnsName);
if (ip_list->count == 0) {
enfs_log_debug("query dns failed, no IP is found.\n");
kfree(ip_list);
return -ESRCH;
}
memcpy(opt->remote_ip_list, ip_list, sizeof(struct nfs_ip_list));
kfree(ip_list);
return 0;
}
typedef int (*enfs_iter_clnt)(struct nfs_client *clp, void *data);
int enfs_iter_nfs_clnt(enfs_iter_clnt fn, void *data)
{
struct net *net;
struct nfs_net *nn;
struct nfs_client *clp;
int ret = 0;
rcu_read_lock();
for_each_net_rcu(net) {
nn = net_generic(net, nfs_net_id);
if (nn == NULL)
continue;
if (list_empty(&nn->nfs_client_list))
continue;
spin_lock(&nn->nfs_client_lock);
list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
if (!clp->cl_multipath_data)
continue;
ret = fn(clp, data);
if (ret != 0)
break;
}
spin_unlock(&nn->nfs_client_lock);
break;
}
rcu_read_unlock();
return ret;
}
void enfs_add_domain_name(struct multipath_mount_options *opt)
{
int i;
if (!opt->pRemoteDnsInfo || opt->pRemoteDnsInfo->dnsNameCount == 0)
return;
for (i = 0;
i < MAX_DNS_SUPPORTED && i < opt->pRemoteDnsInfo->dnsNameCount;
i++) {
enfs_domain_inc(
opt->pRemoteDnsInfo->routeRemoteDnsList[i].dnsname);
}
}
static int collect_clnt_name(struct nfs_client *clp, void *data)
{
int i;
struct multipath_client_info *clp_info = clp->cl_multipath_data;
for (i = 0; i < MAX_DNS_SUPPORTED &&
i < clp_info->pRemoteDnsInfo->dnsNameCount;
i++) {
enfs_domain_inc(
clp_info->pRemoteDnsInfo->routeRemoteDnsList[i].dnsname);
}
return 0;
}
static int enfs_collect_all_domain_name(void)
{
struct name_list *ls;
struct name_list *ls_next;
spin_lock(&dns_cache_lock);
list_for_each_entry(ls, &dns_cache_list, next) {
ls->ref = 0;
}
spin_unlock(&dns_cache_lock);
enfs_iter_nfs_clnt(collect_clnt_name, NULL);
spin_lock(&dns_cache_lock);
list_for_each_entry_safe(ls, ls_next, &dns_cache_list, next) {
if (ls->ref == 0) {
list_del(&ls->next);
kfree(ls);
}
}
spin_unlock(&dns_cache_lock);
return 0;
}
void enfs_domain_for_each(int (*func)(struct name_list *, void *), void *data)
{
struct name_list *name_list;
struct name_list *next_list;
int ret;
spin_lock(&dns_cache_lock);
list_for_each_entry_safe(name_list, next_list, &dns_cache_list, next) {
ret = func(name_list, data);
if (ret)
break;
}
spin_unlock(&dns_cache_lock);
}
struct query_name_work {
struct work_struct work;
char name[MAX_DNS_NAME_LEN];
struct nfs_ip_list ip_list;
};
static void do_dns_update_new(struct work_struct *work)
{
int error;
struct multipath_mount_options opt;
struct enfs_route_dns_info dns_info;
struct query_name_work *query_work =
container_of(work, struct query_name_work, work);
dns_info.dnsNameCount = 1;
strscpy(dns_info.routeRemoteDnsList[0].dnsname, query_work->name, MAX_DNS_NAME_LEN);
opt.remote_ip_list = &query_work->ip_list;
opt.pRemoteDnsInfo = &dns_info;
error = multipath_query_dns(&opt, AF_INET, false, NULL);
if (error != 0)
enfs_log_debug("Scheduled update dns err:%d.\n", error);
else
enfs_update_domain_name(query_work->name, &query_work->ip_list);
error = multipath_query_dns(&opt, AF_INET6, false, NULL);
if (error != 0)
enfs_log_debug("Scheduled update dns err:%d.\n", error);
else
enfs_update_domain_name(query_work->name, &query_work->ip_list);
kfree(query_work);
}
static int domain_name_update(struct name_list *name_list, void *data)
{
bool ok;
struct query_name_work *query_work;
query_work = kmalloc(sizeof(*query_work), GFP_KERNEL);
if (!query_work) {
enfs_log_error("alloc failed.\n");
return 0;
}
INIT_WORK(&query_work->work, do_dns_update_new);
strscpy(query_work->name, name_list->name, MAX_DNS_NAME_LEN);
memset(&query_work->ip_list, 0, sizeof(struct nfs_ip_list));
ok = queue_work(dns_workq, &query_work->work);
if (!ok) {
kfree(query_work);
enfs_log_info("queue work failed\n");
}
return 0;
}
struct dns_work {
struct work_struct wk_work;
struct nfs_client *clp;
struct multipath_client_info *clp_info;
struct rpc_clnt *clRpcclient;
struct sockaddr_storage ss;
bool query_ok;
};
static int find_and_remount(struct nfs_client *clp, void *data)
{
int error;
struct dns_work *work = data;
struct multipath_mount_options opt;
struct multipath_client_info *clp_info = work->clp_info;
struct multipath_client_info *info = clp->cl_multipath_data;
if (clp != work->clp)
return 0;
if (!work->query_ok)
goto do_err;
opt.remote_ip_list = clp_info->remote_ip_list;
opt.local_ip_list = clp_info->local_ip_list;
opt.pRemoteDnsInfo = clp_info->pRemoteDnsInfo;
error = enfs_remount_iplist(clp, &opt);
if (error != 0)
enfs_log_info("Scheduled remount err:%d.\n", error);
do_err:
info->updating_domain = 0;
return 1;
}
static void do_dns_update(struct work_struct *work)
{
int error;
struct multipath_mount_options opt;
struct dns_work *wk = container_of(work, struct dns_work, wk_work);
struct multipath_client_info *clp_info = wk->clp_info;
opt.remote_ip_list = clp_info->remote_ip_list;
opt.local_ip_list = clp_info->local_ip_list;
opt.pRemoteDnsInfo = clp_info->pRemoteDnsInfo;
error = multipath_query_dns(&opt, wk->ss.ss_family, true,
wk->clRpcclient);
if (error != 0) {
enfs_log_info("Scheduled update dns err:%d.\n", error);
wk->query_ok = false;
}
find_and_remount(wk->clp, wk);
enfs_free_nfsclient_info(wk->clp_info);
nfs_put_client(wk->clp);
kfree(wk);
wk = NULL;
}
static int dns_update_work(struct nfs_client *clp, void *data)
{
bool ok;
int error;
struct dns_work *wk;
struct multipath_client_info *clp_info = clp->cl_multipath_data;
struct list_head *list = (struct list_head *)data;
struct clnt_release_item *item;
wk = kzalloc(sizeof(*wk), GFP_KERNEL);
if (!wk)
return -ENOMEM;
error = enfs_alloc_nfsclient_info(&wk->clp_info);
if (error) {
kfree(wk);
return -ENOMEM;
}
item = kzalloc(sizeof(*item), GFP_KERNEL);
if (!item) {
enfs_free_nfsclient_info(wk->clp_info);
kfree(wk);
return -ENOMEM;
}
clp_info->updating_domain = 1;
*wk->clp_info->remote_ip_list = *clp_info->remote_ip_list;
*wk->clp_info->local_ip_list = *clp_info->local_ip_list;
*wk->clp_info->pRemoteDnsInfo = *clp_info->pRemoteDnsInfo;
if (!refcount_inc_not_zero(&clp->cl_rpcclient->cl_count)) {
enfs_free_nfsclient_info(wk->clp_info);
kfree(wk);
kfree(item);
}
nfsclient_refinc(&clp->cl_count);
wk->clp = clp;
wk->clRpcclient = clp->cl_rpcclient;
INIT_WORK(&wk->wk_work, do_dns_update);
rpc_peeraddr(clp->cl_rpcclient, (struct sockaddr *)&wk->ss,
sizeof(struct sockaddr_storage));
wk->query_ok = true;
ok = queue_work(dns_workq, &wk->wk_work);
if (!ok) {
clp_info->updating_domain = 0;
enfs_free_nfsclient_info(wk->clp_info);
item->clnt = wk->clRpcclient;
item->client = wk->clp;
list_add_tail(&item->node, list);
kfree(wk);
return -1;
}
kfree(item);
return 0;
}
static int requery_clnt_dns(struct nfs_client *clp, void *data)
{
int error;
struct multipath_client_info *clp_info = clp->cl_multipath_data;
if (kthread_should_stop())
return 1;
if (!clp_info || !clp->cl_rpcclient || clp_info->fill_local ||
clp_info->updating_domain) {
return 0;
}
if (clp->cl_cons_state > NFS_CS_READY) {
enfs_log_info("client not ready.\n");
return 0;
}
if (!clp_info->pRemoteDnsInfo ||
clp_info->pRemoteDnsInfo->dnsNameCount == 0) {
return 0;
}
error = dns_update_work(clp, data);
if (error)
enfs_log_info("dns update queue err:%d\n", error);
return 0;
}
static int dns_update_loop(void *data)
{
int32_t interval_ms;
ktime_t start = ktime_get();
const int query_times = 5;
int times = 0;
LIST_HEAD(free_list);
while (!kthread_should_stop()) {
* Ensure the domain name is queried more
* than 5 times before being remount.
*/
interval_ms =
enfs_get_config_dns_update_interval() * 60 * 1000 / 5;
if (interval_ms != 0 && enfs_timeout_ms(&start, interval_ms) &&
enfs_get_config_multipath_state() ==
ENFS_MULTIPATH_ENABLE) {
start = ktime_get();
enfs_collect_all_domain_name();
enfs_domain_for_each(domain_name_update, NULL);
if (times == query_times) {
enfs_swap_name_cache();
enfs_iter_nfs_clnt(requery_clnt_dns,
&free_list);
enfs_destroy_clnt_list(&free_list);
enfs_log_debug("update DNS.");
times = 0;
}
times++;
}
enfs_msleep(1000);
}
return 0;
}
int enfs_dns_init(void)
{
spin_lock_init(&dns_cache_lock);
dns_workq = create_workqueue("enfs_dns_workqueue");
if (!dns_workq) {
enfs_log_error("create workqueue failed.\n");
return -ENOMEM;
}
dns_thread = kthread_run(dns_update_loop, NULL, "enfs_dns_update");
if (IS_ERR(dns_thread)) {
enfs_log_error("Failed to create thread enfs_dns_update.\n");
return PTR_ERR(dns_thread);
}
return 0;
}
void enfs_dns_exit(void)
{
if (dns_thread)
kthread_stop(dns_thread);
if (dns_workq)
destroy_workqueue(dns_workq);
}