162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015, Sony Mobile Communications Inc.
462306a36Sopenharmony_ci * Copyright (c) 2013, The Linux Foundation. All rights reserved.
562306a36Sopenharmony_ci * Copyright (c) 2020, Linaro Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/qrtr.h>
1062306a36Sopenharmony_ci#include <linux/workqueue.h>
1162306a36Sopenharmony_ci#include <net/sock.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "qrtr.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <trace/events/sock.h>
1662306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
1762306a36Sopenharmony_ci#include <trace/events/qrtr.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic DEFINE_XARRAY(nodes);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic struct {
2262306a36Sopenharmony_ci	struct socket *sock;
2362306a36Sopenharmony_ci	struct sockaddr_qrtr bcast_sq;
2462306a36Sopenharmony_ci	struct list_head lookups;
2562306a36Sopenharmony_ci	struct workqueue_struct *workqueue;
2662306a36Sopenharmony_ci	struct work_struct work;
2762306a36Sopenharmony_ci	int local_node;
2862306a36Sopenharmony_ci} qrtr_ns;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const char * const qrtr_ctrl_pkt_strings[] = {
3162306a36Sopenharmony_ci	[QRTR_TYPE_HELLO]	= "hello",
3262306a36Sopenharmony_ci	[QRTR_TYPE_BYE]		= "bye",
3362306a36Sopenharmony_ci	[QRTR_TYPE_NEW_SERVER]	= "new-server",
3462306a36Sopenharmony_ci	[QRTR_TYPE_DEL_SERVER]	= "del-server",
3562306a36Sopenharmony_ci	[QRTR_TYPE_DEL_CLIENT]	= "del-client",
3662306a36Sopenharmony_ci	[QRTR_TYPE_RESUME_TX]	= "resume-tx",
3762306a36Sopenharmony_ci	[QRTR_TYPE_EXIT]	= "exit",
3862306a36Sopenharmony_ci	[QRTR_TYPE_PING]	= "ping",
3962306a36Sopenharmony_ci	[QRTR_TYPE_NEW_LOOKUP]	= "new-lookup",
4062306a36Sopenharmony_ci	[QRTR_TYPE_DEL_LOOKUP]	= "del-lookup",
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct qrtr_server_filter {
4462306a36Sopenharmony_ci	unsigned int service;
4562306a36Sopenharmony_ci	unsigned int instance;
4662306a36Sopenharmony_ci	unsigned int ifilter;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct qrtr_lookup {
5062306a36Sopenharmony_ci	unsigned int service;
5162306a36Sopenharmony_ci	unsigned int instance;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	struct sockaddr_qrtr sq;
5462306a36Sopenharmony_ci	struct list_head li;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct qrtr_server {
5862306a36Sopenharmony_ci	unsigned int service;
5962306a36Sopenharmony_ci	unsigned int instance;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	unsigned int node;
6262306a36Sopenharmony_ci	unsigned int port;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	struct list_head qli;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistruct qrtr_node {
6862306a36Sopenharmony_ci	unsigned int id;
6962306a36Sopenharmony_ci	struct xarray servers;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic struct qrtr_node *node_get(unsigned int node_id)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct qrtr_node *node;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	node = xa_load(&nodes, node_id);
7762306a36Sopenharmony_ci	if (node)
7862306a36Sopenharmony_ci		return node;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* If node didn't exist, allocate and insert it to the tree */
8162306a36Sopenharmony_ci	node = kzalloc(sizeof(*node), GFP_KERNEL);
8262306a36Sopenharmony_ci	if (!node)
8362306a36Sopenharmony_ci		return NULL;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	node->id = node_id;
8662306a36Sopenharmony_ci	xa_init(&node->servers);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (xa_store(&nodes, node_id, node, GFP_KERNEL)) {
8962306a36Sopenharmony_ci		kfree(node);
9062306a36Sopenharmony_ci		return NULL;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return node;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int server_match(const struct qrtr_server *srv,
9762306a36Sopenharmony_ci			const struct qrtr_server_filter *f)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	unsigned int ifilter = f->ifilter;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (f->service != 0 && srv->service != f->service)
10262306a36Sopenharmony_ci		return 0;
10362306a36Sopenharmony_ci	if (!ifilter && f->instance)
10462306a36Sopenharmony_ci		ifilter = ~0;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return (srv->instance & ifilter) == f->instance;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int service_announce_new(struct sockaddr_qrtr *dest,
11062306a36Sopenharmony_ci				struct qrtr_server *srv)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct qrtr_ctrl_pkt pkt;
11362306a36Sopenharmony_ci	struct msghdr msg = { };
11462306a36Sopenharmony_ci	struct kvec iv;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	trace_qrtr_ns_service_announce_new(srv->service, srv->instance,
11762306a36Sopenharmony_ci					   srv->node, srv->port);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	iv.iov_base = &pkt;
12062306a36Sopenharmony_ci	iv.iov_len = sizeof(pkt);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
12362306a36Sopenharmony_ci	pkt.cmd = cpu_to_le32(QRTR_TYPE_NEW_SERVER);
12462306a36Sopenharmony_ci	pkt.server.service = cpu_to_le32(srv->service);
12562306a36Sopenharmony_ci	pkt.server.instance = cpu_to_le32(srv->instance);
12662306a36Sopenharmony_ci	pkt.server.node = cpu_to_le32(srv->node);
12762306a36Sopenharmony_ci	pkt.server.port = cpu_to_le32(srv->port);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	msg.msg_name = (struct sockaddr *)dest;
13062306a36Sopenharmony_ci	msg.msg_namelen = sizeof(*dest);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int service_announce_del(struct sockaddr_qrtr *dest,
13662306a36Sopenharmony_ci				struct qrtr_server *srv)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct qrtr_ctrl_pkt pkt;
13962306a36Sopenharmony_ci	struct msghdr msg = { };
14062306a36Sopenharmony_ci	struct kvec iv;
14162306a36Sopenharmony_ci	int ret;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	trace_qrtr_ns_service_announce_del(srv->service, srv->instance,
14462306a36Sopenharmony_ci					   srv->node, srv->port);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	iv.iov_base = &pkt;
14762306a36Sopenharmony_ci	iv.iov_len = sizeof(pkt);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
15062306a36Sopenharmony_ci	pkt.cmd = cpu_to_le32(QRTR_TYPE_DEL_SERVER);
15162306a36Sopenharmony_ci	pkt.server.service = cpu_to_le32(srv->service);
15262306a36Sopenharmony_ci	pkt.server.instance = cpu_to_le32(srv->instance);
15362306a36Sopenharmony_ci	pkt.server.node = cpu_to_le32(srv->node);
15462306a36Sopenharmony_ci	pkt.server.port = cpu_to_le32(srv->port);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	msg.msg_name = (struct sockaddr *)dest;
15762306a36Sopenharmony_ci	msg.msg_namelen = sizeof(*dest);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
16062306a36Sopenharmony_ci	if (ret < 0)
16162306a36Sopenharmony_ci		pr_err("failed to announce del service\n");
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return ret;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv,
16762306a36Sopenharmony_ci			  bool new)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct qrtr_ctrl_pkt pkt;
17062306a36Sopenharmony_ci	struct msghdr msg = { };
17162306a36Sopenharmony_ci	struct kvec iv;
17262306a36Sopenharmony_ci	int ret;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	iv.iov_base = &pkt;
17562306a36Sopenharmony_ci	iv.iov_len = sizeof(pkt);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
17862306a36Sopenharmony_ci	pkt.cmd = new ? cpu_to_le32(QRTR_TYPE_NEW_SERVER) :
17962306a36Sopenharmony_ci			cpu_to_le32(QRTR_TYPE_DEL_SERVER);
18062306a36Sopenharmony_ci	if (srv) {
18162306a36Sopenharmony_ci		pkt.server.service = cpu_to_le32(srv->service);
18262306a36Sopenharmony_ci		pkt.server.instance = cpu_to_le32(srv->instance);
18362306a36Sopenharmony_ci		pkt.server.node = cpu_to_le32(srv->node);
18462306a36Sopenharmony_ci		pkt.server.port = cpu_to_le32(srv->port);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	msg.msg_name = (struct sockaddr *)to;
18862306a36Sopenharmony_ci	msg.msg_namelen = sizeof(*to);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
19162306a36Sopenharmony_ci	if (ret < 0)
19262306a36Sopenharmony_ci		pr_err("failed to send lookup notification\n");
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int announce_servers(struct sockaddr_qrtr *sq)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct qrtr_server *srv;
19862306a36Sopenharmony_ci	struct qrtr_node *node;
19962306a36Sopenharmony_ci	unsigned long index;
20062306a36Sopenharmony_ci	int ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	node = node_get(qrtr_ns.local_node);
20362306a36Sopenharmony_ci	if (!node)
20462306a36Sopenharmony_ci		return 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Announce the list of servers registered in this node */
20762306a36Sopenharmony_ci	xa_for_each(&node->servers, index, srv) {
20862306a36Sopenharmony_ci		ret = service_announce_new(sq, srv);
20962306a36Sopenharmony_ci		if (ret < 0) {
21062306a36Sopenharmony_ci			pr_err("failed to announce new service\n");
21162306a36Sopenharmony_ci			return ret;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic struct qrtr_server *server_add(unsigned int service,
21862306a36Sopenharmony_ci				      unsigned int instance,
21962306a36Sopenharmony_ci				      unsigned int node_id,
22062306a36Sopenharmony_ci				      unsigned int port)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct qrtr_server *srv;
22362306a36Sopenharmony_ci	struct qrtr_server *old;
22462306a36Sopenharmony_ci	struct qrtr_node *node;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (!service || !port)
22762306a36Sopenharmony_ci		return NULL;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	srv = kzalloc(sizeof(*srv), GFP_KERNEL);
23062306a36Sopenharmony_ci	if (!srv)
23162306a36Sopenharmony_ci		return NULL;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	srv->service = service;
23462306a36Sopenharmony_ci	srv->instance = instance;
23562306a36Sopenharmony_ci	srv->node = node_id;
23662306a36Sopenharmony_ci	srv->port = port;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	node = node_get(node_id);
23962306a36Sopenharmony_ci	if (!node)
24062306a36Sopenharmony_ci		goto err;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* Delete the old server on the same port */
24362306a36Sopenharmony_ci	old = xa_store(&node->servers, port, srv, GFP_KERNEL);
24462306a36Sopenharmony_ci	if (old) {
24562306a36Sopenharmony_ci		if (xa_is_err(old)) {
24662306a36Sopenharmony_ci			pr_err("failed to add server [0x%x:0x%x] ret:%d\n",
24762306a36Sopenharmony_ci			       srv->service, srv->instance, xa_err(old));
24862306a36Sopenharmony_ci			goto err;
24962306a36Sopenharmony_ci		} else {
25062306a36Sopenharmony_ci			kfree(old);
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	trace_qrtr_ns_server_add(srv->service, srv->instance,
25562306a36Sopenharmony_ci				 srv->node, srv->port);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return srv;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cierr:
26062306a36Sopenharmony_ci	kfree(srv);
26162306a36Sopenharmony_ci	return NULL;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct qrtr_lookup *lookup;
26762306a36Sopenharmony_ci	struct qrtr_server *srv;
26862306a36Sopenharmony_ci	struct list_head *li;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	srv = xa_load(&node->servers, port);
27162306a36Sopenharmony_ci	if (!srv)
27262306a36Sopenharmony_ci		return -ENOENT;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	xa_erase(&node->servers, port);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Broadcast the removal of local servers */
27762306a36Sopenharmony_ci	if (srv->node == qrtr_ns.local_node && bcast)
27862306a36Sopenharmony_ci		service_announce_del(&qrtr_ns.bcast_sq, srv);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Announce the service's disappearance to observers */
28162306a36Sopenharmony_ci	list_for_each(li, &qrtr_ns.lookups) {
28262306a36Sopenharmony_ci		lookup = container_of(li, struct qrtr_lookup, li);
28362306a36Sopenharmony_ci		if (lookup->service && lookup->service != srv->service)
28462306a36Sopenharmony_ci			continue;
28562306a36Sopenharmony_ci		if (lookup->instance && lookup->instance != srv->instance)
28662306a36Sopenharmony_ci			continue;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		lookup_notify(&lookup->sq, srv, false);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	kfree(srv);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int say_hello(struct sockaddr_qrtr *dest)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct qrtr_ctrl_pkt pkt;
29962306a36Sopenharmony_ci	struct msghdr msg = { };
30062306a36Sopenharmony_ci	struct kvec iv;
30162306a36Sopenharmony_ci	int ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	iv.iov_base = &pkt;
30462306a36Sopenharmony_ci	iv.iov_len = sizeof(pkt);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
30762306a36Sopenharmony_ci	pkt.cmd = cpu_to_le32(QRTR_TYPE_HELLO);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	msg.msg_name = (struct sockaddr *)dest;
31062306a36Sopenharmony_ci	msg.msg_namelen = sizeof(*dest);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
31362306a36Sopenharmony_ci	if (ret < 0)
31462306a36Sopenharmony_ci		pr_err("failed to send hello msg\n");
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return ret;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/* Announce the list of servers registered on the local node */
32062306a36Sopenharmony_cistatic int ctrl_cmd_hello(struct sockaddr_qrtr *sq)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	int ret;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = say_hello(sq);
32562306a36Sopenharmony_ci	if (ret < 0)
32662306a36Sopenharmony_ci		return ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return announce_servers(sq);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int ctrl_cmd_bye(struct sockaddr_qrtr *from)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct qrtr_node *local_node;
33462306a36Sopenharmony_ci	struct qrtr_ctrl_pkt pkt;
33562306a36Sopenharmony_ci	struct qrtr_server *srv;
33662306a36Sopenharmony_ci	struct sockaddr_qrtr sq;
33762306a36Sopenharmony_ci	struct msghdr msg = { };
33862306a36Sopenharmony_ci	struct qrtr_node *node;
33962306a36Sopenharmony_ci	unsigned long index;
34062306a36Sopenharmony_ci	struct kvec iv;
34162306a36Sopenharmony_ci	int ret;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	iv.iov_base = &pkt;
34462306a36Sopenharmony_ci	iv.iov_len = sizeof(pkt);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	node = node_get(from->sq_node);
34762306a36Sopenharmony_ci	if (!node)
34862306a36Sopenharmony_ci		return 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Advertise removal of this client to all servers of remote node */
35162306a36Sopenharmony_ci	xa_for_each(&node->servers, index, srv)
35262306a36Sopenharmony_ci		server_del(node, srv->port, true);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* Advertise the removal of this client to all local servers */
35562306a36Sopenharmony_ci	local_node = node_get(qrtr_ns.local_node);
35662306a36Sopenharmony_ci	if (!local_node)
35762306a36Sopenharmony_ci		return 0;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
36062306a36Sopenharmony_ci	pkt.cmd = cpu_to_le32(QRTR_TYPE_BYE);
36162306a36Sopenharmony_ci	pkt.client.node = cpu_to_le32(from->sq_node);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	xa_for_each(&local_node->servers, index, srv) {
36462306a36Sopenharmony_ci		sq.sq_family = AF_QIPCRTR;
36562306a36Sopenharmony_ci		sq.sq_node = srv->node;
36662306a36Sopenharmony_ci		sq.sq_port = srv->port;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		msg.msg_name = (struct sockaddr *)&sq;
36962306a36Sopenharmony_ci		msg.msg_namelen = sizeof(sq);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
37262306a36Sopenharmony_ci		if (ret < 0) {
37362306a36Sopenharmony_ci			pr_err("failed to send bye cmd\n");
37462306a36Sopenharmony_ci			return ret;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
38162306a36Sopenharmony_ci			       unsigned int node_id, unsigned int port)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct qrtr_node *local_node;
38462306a36Sopenharmony_ci	struct qrtr_lookup *lookup;
38562306a36Sopenharmony_ci	struct qrtr_ctrl_pkt pkt;
38662306a36Sopenharmony_ci	struct msghdr msg = { };
38762306a36Sopenharmony_ci	struct qrtr_server *srv;
38862306a36Sopenharmony_ci	struct sockaddr_qrtr sq;
38962306a36Sopenharmony_ci	struct qrtr_node *node;
39062306a36Sopenharmony_ci	struct list_head *tmp;
39162306a36Sopenharmony_ci	struct list_head *li;
39262306a36Sopenharmony_ci	unsigned long index;
39362306a36Sopenharmony_ci	struct kvec iv;
39462306a36Sopenharmony_ci	int ret;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	iv.iov_base = &pkt;
39762306a36Sopenharmony_ci	iv.iov_len = sizeof(pkt);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* Don't accept spoofed messages */
40062306a36Sopenharmony_ci	if (from->sq_node != node_id)
40162306a36Sopenharmony_ci		return -EINVAL;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Local DEL_CLIENT messages comes from the port being closed */
40462306a36Sopenharmony_ci	if (from->sq_node == qrtr_ns.local_node && from->sq_port != port)
40562306a36Sopenharmony_ci		return -EINVAL;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* Remove any lookups by this client */
40862306a36Sopenharmony_ci	list_for_each_safe(li, tmp, &qrtr_ns.lookups) {
40962306a36Sopenharmony_ci		lookup = container_of(li, struct qrtr_lookup, li);
41062306a36Sopenharmony_ci		if (lookup->sq.sq_node != node_id)
41162306a36Sopenharmony_ci			continue;
41262306a36Sopenharmony_ci		if (lookup->sq.sq_port != port)
41362306a36Sopenharmony_ci			continue;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		list_del(&lookup->li);
41662306a36Sopenharmony_ci		kfree(lookup);
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* Remove the server belonging to this port but don't broadcast
42062306a36Sopenharmony_ci	 * DEL_SERVER. Neighbours would've already removed the server belonging
42162306a36Sopenharmony_ci	 * to this port due to the DEL_CLIENT broadcast from qrtr_port_remove().
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	node = node_get(node_id);
42462306a36Sopenharmony_ci	if (node)
42562306a36Sopenharmony_ci		server_del(node, port, false);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* Advertise the removal of this client to all local servers */
42862306a36Sopenharmony_ci	local_node = node_get(qrtr_ns.local_node);
42962306a36Sopenharmony_ci	if (!local_node)
43062306a36Sopenharmony_ci		return 0;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
43362306a36Sopenharmony_ci	pkt.cmd = cpu_to_le32(QRTR_TYPE_DEL_CLIENT);
43462306a36Sopenharmony_ci	pkt.client.node = cpu_to_le32(node_id);
43562306a36Sopenharmony_ci	pkt.client.port = cpu_to_le32(port);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	xa_for_each(&local_node->servers, index, srv) {
43862306a36Sopenharmony_ci		sq.sq_family = AF_QIPCRTR;
43962306a36Sopenharmony_ci		sq.sq_node = srv->node;
44062306a36Sopenharmony_ci		sq.sq_port = srv->port;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		msg.msg_name = (struct sockaddr *)&sq;
44362306a36Sopenharmony_ci		msg.msg_namelen = sizeof(sq);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
44662306a36Sopenharmony_ci		if (ret < 0) {
44762306a36Sopenharmony_ci			pr_err("failed to send del client cmd\n");
44862306a36Sopenharmony_ci			return ret;
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	return 0;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int ctrl_cmd_new_server(struct sockaddr_qrtr *from,
45562306a36Sopenharmony_ci			       unsigned int service, unsigned int instance,
45662306a36Sopenharmony_ci			       unsigned int node_id, unsigned int port)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct qrtr_lookup *lookup;
45962306a36Sopenharmony_ci	struct qrtr_server *srv;
46062306a36Sopenharmony_ci	struct list_head *li;
46162306a36Sopenharmony_ci	int ret = 0;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Ignore specified node and port for local servers */
46462306a36Sopenharmony_ci	if (from->sq_node == qrtr_ns.local_node) {
46562306a36Sopenharmony_ci		node_id = from->sq_node;
46662306a36Sopenharmony_ci		port = from->sq_port;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	srv = server_add(service, instance, node_id, port);
47062306a36Sopenharmony_ci	if (!srv)
47162306a36Sopenharmony_ci		return -EINVAL;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (srv->node == qrtr_ns.local_node) {
47462306a36Sopenharmony_ci		ret = service_announce_new(&qrtr_ns.bcast_sq, srv);
47562306a36Sopenharmony_ci		if (ret < 0) {
47662306a36Sopenharmony_ci			pr_err("failed to announce new service\n");
47762306a36Sopenharmony_ci			return ret;
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* Notify any potential lookups about the new server */
48262306a36Sopenharmony_ci	list_for_each(li, &qrtr_ns.lookups) {
48362306a36Sopenharmony_ci		lookup = container_of(li, struct qrtr_lookup, li);
48462306a36Sopenharmony_ci		if (lookup->service && lookup->service != service)
48562306a36Sopenharmony_ci			continue;
48662306a36Sopenharmony_ci		if (lookup->instance && lookup->instance != instance)
48762306a36Sopenharmony_ci			continue;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		lookup_notify(&lookup->sq, srv, true);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return ret;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int ctrl_cmd_del_server(struct sockaddr_qrtr *from,
49662306a36Sopenharmony_ci			       unsigned int service, unsigned int instance,
49762306a36Sopenharmony_ci			       unsigned int node_id, unsigned int port)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct qrtr_node *node;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* Ignore specified node and port for local servers*/
50262306a36Sopenharmony_ci	if (from->sq_node == qrtr_ns.local_node) {
50362306a36Sopenharmony_ci		node_id = from->sq_node;
50462306a36Sopenharmony_ci		port = from->sq_port;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/* Local servers may only unregister themselves */
50862306a36Sopenharmony_ci	if (from->sq_node == qrtr_ns.local_node && from->sq_port != port)
50962306a36Sopenharmony_ci		return -EINVAL;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	node = node_get(node_id);
51262306a36Sopenharmony_ci	if (!node)
51362306a36Sopenharmony_ci		return -ENOENT;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	server_del(node, port, true);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from,
52162306a36Sopenharmony_ci			       unsigned int service, unsigned int instance)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct qrtr_server_filter filter;
52462306a36Sopenharmony_ci	struct qrtr_lookup *lookup;
52562306a36Sopenharmony_ci	struct qrtr_server *srv;
52662306a36Sopenharmony_ci	struct qrtr_node *node;
52762306a36Sopenharmony_ci	unsigned long node_idx;
52862306a36Sopenharmony_ci	unsigned long srv_idx;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Accept only local observers */
53162306a36Sopenharmony_ci	if (from->sq_node != qrtr_ns.local_node)
53262306a36Sopenharmony_ci		return -EINVAL;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	lookup = kzalloc(sizeof(*lookup), GFP_KERNEL);
53562306a36Sopenharmony_ci	if (!lookup)
53662306a36Sopenharmony_ci		return -ENOMEM;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	lookup->sq = *from;
53962306a36Sopenharmony_ci	lookup->service = service;
54062306a36Sopenharmony_ci	lookup->instance = instance;
54162306a36Sopenharmony_ci	list_add_tail(&lookup->li, &qrtr_ns.lookups);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	memset(&filter, 0, sizeof(filter));
54462306a36Sopenharmony_ci	filter.service = service;
54562306a36Sopenharmony_ci	filter.instance = instance;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	xa_for_each(&nodes, node_idx, node) {
54862306a36Sopenharmony_ci		xa_for_each(&node->servers, srv_idx, srv) {
54962306a36Sopenharmony_ci			if (!server_match(srv, &filter))
55062306a36Sopenharmony_ci				continue;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci			lookup_notify(from, srv, true);
55362306a36Sopenharmony_ci		}
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Empty notification, to indicate end of listing */
55762306a36Sopenharmony_ci	lookup_notify(from, NULL, true);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return 0;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void ctrl_cmd_del_lookup(struct sockaddr_qrtr *from,
56362306a36Sopenharmony_ci				unsigned int service, unsigned int instance)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct qrtr_lookup *lookup;
56662306a36Sopenharmony_ci	struct list_head *tmp;
56762306a36Sopenharmony_ci	struct list_head *li;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	list_for_each_safe(li, tmp, &qrtr_ns.lookups) {
57062306a36Sopenharmony_ci		lookup = container_of(li, struct qrtr_lookup, li);
57162306a36Sopenharmony_ci		if (lookup->sq.sq_node != from->sq_node)
57262306a36Sopenharmony_ci			continue;
57362306a36Sopenharmony_ci		if (lookup->sq.sq_port != from->sq_port)
57462306a36Sopenharmony_ci			continue;
57562306a36Sopenharmony_ci		if (lookup->service != service)
57662306a36Sopenharmony_ci			continue;
57762306a36Sopenharmony_ci		if (lookup->instance && lookup->instance != instance)
57862306a36Sopenharmony_ci			continue;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		list_del(&lookup->li);
58162306a36Sopenharmony_ci		kfree(lookup);
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic void qrtr_ns_worker(struct work_struct *work)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	const struct qrtr_ctrl_pkt *pkt;
58862306a36Sopenharmony_ci	size_t recv_buf_size = 4096;
58962306a36Sopenharmony_ci	struct sockaddr_qrtr sq;
59062306a36Sopenharmony_ci	struct msghdr msg = { };
59162306a36Sopenharmony_ci	unsigned int cmd;
59262306a36Sopenharmony_ci	ssize_t msglen;
59362306a36Sopenharmony_ci	void *recv_buf;
59462306a36Sopenharmony_ci	struct kvec iv;
59562306a36Sopenharmony_ci	int ret;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	msg.msg_name = (struct sockaddr *)&sq;
59862306a36Sopenharmony_ci	msg.msg_namelen = sizeof(sq);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	recv_buf = kzalloc(recv_buf_size, GFP_KERNEL);
60162306a36Sopenharmony_ci	if (!recv_buf)
60262306a36Sopenharmony_ci		return;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	for (;;) {
60562306a36Sopenharmony_ci		iv.iov_base = recv_buf;
60662306a36Sopenharmony_ci		iv.iov_len = recv_buf_size;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		msglen = kernel_recvmsg(qrtr_ns.sock, &msg, &iv, 1,
60962306a36Sopenharmony_ci					iv.iov_len, MSG_DONTWAIT);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		if (msglen == -EAGAIN)
61262306a36Sopenharmony_ci			break;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci		if (msglen < 0) {
61562306a36Sopenharmony_ci			pr_err("error receiving packet: %zd\n", msglen);
61662306a36Sopenharmony_ci			break;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		pkt = recv_buf;
62062306a36Sopenharmony_ci		cmd = le32_to_cpu(pkt->cmd);
62162306a36Sopenharmony_ci		if (cmd < ARRAY_SIZE(qrtr_ctrl_pkt_strings) &&
62262306a36Sopenharmony_ci		    qrtr_ctrl_pkt_strings[cmd])
62362306a36Sopenharmony_ci			trace_qrtr_ns_message(qrtr_ctrl_pkt_strings[cmd],
62462306a36Sopenharmony_ci					      sq.sq_node, sq.sq_port);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		ret = 0;
62762306a36Sopenharmony_ci		switch (cmd) {
62862306a36Sopenharmony_ci		case QRTR_TYPE_HELLO:
62962306a36Sopenharmony_ci			ret = ctrl_cmd_hello(&sq);
63062306a36Sopenharmony_ci			break;
63162306a36Sopenharmony_ci		case QRTR_TYPE_BYE:
63262306a36Sopenharmony_ci			ret = ctrl_cmd_bye(&sq);
63362306a36Sopenharmony_ci			break;
63462306a36Sopenharmony_ci		case QRTR_TYPE_DEL_CLIENT:
63562306a36Sopenharmony_ci			ret = ctrl_cmd_del_client(&sq,
63662306a36Sopenharmony_ci					le32_to_cpu(pkt->client.node),
63762306a36Sopenharmony_ci					le32_to_cpu(pkt->client.port));
63862306a36Sopenharmony_ci			break;
63962306a36Sopenharmony_ci		case QRTR_TYPE_NEW_SERVER:
64062306a36Sopenharmony_ci			ret = ctrl_cmd_new_server(&sq,
64162306a36Sopenharmony_ci					le32_to_cpu(pkt->server.service),
64262306a36Sopenharmony_ci					le32_to_cpu(pkt->server.instance),
64362306a36Sopenharmony_ci					le32_to_cpu(pkt->server.node),
64462306a36Sopenharmony_ci					le32_to_cpu(pkt->server.port));
64562306a36Sopenharmony_ci			break;
64662306a36Sopenharmony_ci		case QRTR_TYPE_DEL_SERVER:
64762306a36Sopenharmony_ci			ret = ctrl_cmd_del_server(&sq,
64862306a36Sopenharmony_ci					 le32_to_cpu(pkt->server.service),
64962306a36Sopenharmony_ci					 le32_to_cpu(pkt->server.instance),
65062306a36Sopenharmony_ci					 le32_to_cpu(pkt->server.node),
65162306a36Sopenharmony_ci					 le32_to_cpu(pkt->server.port));
65262306a36Sopenharmony_ci			break;
65362306a36Sopenharmony_ci		case QRTR_TYPE_EXIT:
65462306a36Sopenharmony_ci		case QRTR_TYPE_PING:
65562306a36Sopenharmony_ci		case QRTR_TYPE_RESUME_TX:
65662306a36Sopenharmony_ci			break;
65762306a36Sopenharmony_ci		case QRTR_TYPE_NEW_LOOKUP:
65862306a36Sopenharmony_ci			ret = ctrl_cmd_new_lookup(&sq,
65962306a36Sopenharmony_ci					 le32_to_cpu(pkt->server.service),
66062306a36Sopenharmony_ci					 le32_to_cpu(pkt->server.instance));
66162306a36Sopenharmony_ci			break;
66262306a36Sopenharmony_ci		case QRTR_TYPE_DEL_LOOKUP:
66362306a36Sopenharmony_ci			ctrl_cmd_del_lookup(&sq,
66462306a36Sopenharmony_ci				    le32_to_cpu(pkt->server.service),
66562306a36Sopenharmony_ci				    le32_to_cpu(pkt->server.instance));
66662306a36Sopenharmony_ci			break;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		if (ret < 0)
67062306a36Sopenharmony_ci			pr_err("failed while handling packet from %d:%d",
67162306a36Sopenharmony_ci			       sq.sq_node, sq.sq_port);
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	kfree(recv_buf);
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void qrtr_ns_data_ready(struct sock *sk)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	trace_sk_data_ready(sk);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	queue_work(qrtr_ns.workqueue, &qrtr_ns.work);
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ciint qrtr_ns_init(void)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct sockaddr_qrtr sq;
68762306a36Sopenharmony_ci	int ret;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	INIT_LIST_HEAD(&qrtr_ns.lookups);
69062306a36Sopenharmony_ci	INIT_WORK(&qrtr_ns.work, qrtr_ns_worker);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	ret = sock_create_kern(&init_net, AF_QIPCRTR, SOCK_DGRAM,
69362306a36Sopenharmony_ci			       PF_QIPCRTR, &qrtr_ns.sock);
69462306a36Sopenharmony_ci	if (ret < 0)
69562306a36Sopenharmony_ci		return ret;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	ret = kernel_getsockname(qrtr_ns.sock, (struct sockaddr *)&sq);
69862306a36Sopenharmony_ci	if (ret < 0) {
69962306a36Sopenharmony_ci		pr_err("failed to get socket name\n");
70062306a36Sopenharmony_ci		goto err_sock;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	qrtr_ns.workqueue = alloc_ordered_workqueue("qrtr_ns_handler", 0);
70462306a36Sopenharmony_ci	if (!qrtr_ns.workqueue) {
70562306a36Sopenharmony_ci		ret = -ENOMEM;
70662306a36Sopenharmony_ci		goto err_sock;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	sq.sq_port = QRTR_PORT_CTRL;
71262306a36Sopenharmony_ci	qrtr_ns.local_node = sq.sq_node;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	ret = kernel_bind(qrtr_ns.sock, (struct sockaddr *)&sq, sizeof(sq));
71562306a36Sopenharmony_ci	if (ret < 0) {
71662306a36Sopenharmony_ci		pr_err("failed to bind to socket\n");
71762306a36Sopenharmony_ci		goto err_wq;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	qrtr_ns.bcast_sq.sq_family = AF_QIPCRTR;
72162306a36Sopenharmony_ci	qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST;
72262306a36Sopenharmony_ci	qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	ret = say_hello(&qrtr_ns.bcast_sq);
72562306a36Sopenharmony_ci	if (ret < 0)
72662306a36Sopenharmony_ci		goto err_wq;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return 0;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cierr_wq:
73162306a36Sopenharmony_ci	destroy_workqueue(qrtr_ns.workqueue);
73262306a36Sopenharmony_cierr_sock:
73362306a36Sopenharmony_ci	sock_release(qrtr_ns.sock);
73462306a36Sopenharmony_ci	return ret;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qrtr_ns_init);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_civoid qrtr_ns_remove(void)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	cancel_work_sync(&qrtr_ns.work);
74162306a36Sopenharmony_ci	destroy_workqueue(qrtr_ns.workqueue);
74262306a36Sopenharmony_ci	sock_release(qrtr_ns.sock);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qrtr_ns_remove);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ciMODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
74762306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm IPC Router Nameservice");
74862306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
749