2020-02-20 15:13:26 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2015, Sony Mobile Communications Inc.
|
|
|
|
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
|
|
|
* Copyright (c) 2020, Linaro Ltd.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/qrtr.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <net/sock.h>
|
|
|
|
|
|
|
|
#include "qrtr.h"
|
|
|
|
|
2023-01-20 00:45:16 +00:00
|
|
|
#include <trace/events/sock.h>
|
2020-04-21 07:40:54 +00:00
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
|
#include <trace/events/qrtr.h>
|
|
|
|
|
2023-07-14 05:58:45 +00:00
|
|
|
static DEFINE_XARRAY(nodes);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
static struct {
|
|
|
|
struct socket *sock;
|
|
|
|
struct sockaddr_qrtr bcast_sq;
|
|
|
|
struct list_head lookups;
|
|
|
|
struct workqueue_struct *workqueue;
|
|
|
|
struct work_struct work;
|
|
|
|
int local_node;
|
|
|
|
} qrtr_ns;
|
|
|
|
|
|
|
|
static const char * const qrtr_ctrl_pkt_strings[] = {
|
|
|
|
[QRTR_TYPE_HELLO] = "hello",
|
|
|
|
[QRTR_TYPE_BYE] = "bye",
|
|
|
|
[QRTR_TYPE_NEW_SERVER] = "new-server",
|
|
|
|
[QRTR_TYPE_DEL_SERVER] = "del-server",
|
|
|
|
[QRTR_TYPE_DEL_CLIENT] = "del-client",
|
|
|
|
[QRTR_TYPE_RESUME_TX] = "resume-tx",
|
|
|
|
[QRTR_TYPE_EXIT] = "exit",
|
|
|
|
[QRTR_TYPE_PING] = "ping",
|
|
|
|
[QRTR_TYPE_NEW_LOOKUP] = "new-lookup",
|
|
|
|
[QRTR_TYPE_DEL_LOOKUP] = "del-lookup",
|
|
|
|
};
|
|
|
|
|
|
|
|
struct qrtr_server_filter {
|
|
|
|
unsigned int service;
|
|
|
|
unsigned int instance;
|
|
|
|
unsigned int ifilter;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct qrtr_lookup {
|
|
|
|
unsigned int service;
|
|
|
|
unsigned int instance;
|
|
|
|
|
|
|
|
struct sockaddr_qrtr sq;
|
|
|
|
struct list_head li;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct qrtr_server {
|
|
|
|
unsigned int service;
|
|
|
|
unsigned int instance;
|
|
|
|
|
|
|
|
unsigned int node;
|
|
|
|
unsigned int port;
|
|
|
|
|
|
|
|
struct list_head qli;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct qrtr_node {
|
|
|
|
unsigned int id;
|
2023-07-14 05:58:44 +00:00
|
|
|
struct xarray servers;
|
2020-02-20 15:13:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct qrtr_node *node_get(unsigned int node_id)
|
|
|
|
{
|
|
|
|
struct qrtr_node *node;
|
|
|
|
|
2023-07-14 05:58:45 +00:00
|
|
|
node = xa_load(&nodes, node_id);
|
2020-02-20 15:13:26 +00:00
|
|
|
if (node)
|
|
|
|
return node;
|
|
|
|
|
|
|
|
/* If node didn't exist, allocate and insert it to the tree */
|
|
|
|
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
|
|
|
if (!node)
|
2020-02-26 14:51:53 +00:00
|
|
|
return NULL;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
node->id = node_id;
|
2023-07-14 05:58:44 +00:00
|
|
|
xa_init(&node->servers);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
2023-07-14 05:58:45 +00:00
|
|
|
if (xa_store(&nodes, node_id, node, GFP_KERNEL)) {
|
2023-01-25 13:48:31 +00:00
|
|
|
kfree(node);
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int server_match(const struct qrtr_server *srv,
|
|
|
|
const struct qrtr_server_filter *f)
|
|
|
|
{
|
|
|
|
unsigned int ifilter = f->ifilter;
|
|
|
|
|
|
|
|
if (f->service != 0 && srv->service != f->service)
|
|
|
|
return 0;
|
|
|
|
if (!ifilter && f->instance)
|
|
|
|
ifilter = ~0;
|
|
|
|
|
|
|
|
return (srv->instance & ifilter) == f->instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int service_announce_new(struct sockaddr_qrtr *dest,
|
|
|
|
struct qrtr_server *srv)
|
|
|
|
{
|
|
|
|
struct qrtr_ctrl_pkt pkt;
|
|
|
|
struct msghdr msg = { };
|
|
|
|
struct kvec iv;
|
|
|
|
|
2020-04-21 07:40:54 +00:00
|
|
|
trace_qrtr_ns_service_announce_new(srv->service, srv->instance,
|
|
|
|
srv->node, srv->port);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
iv.iov_base = &pkt;
|
|
|
|
iv.iov_len = sizeof(pkt);
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
pkt.cmd = cpu_to_le32(QRTR_TYPE_NEW_SERVER);
|
|
|
|
pkt.server.service = cpu_to_le32(srv->service);
|
|
|
|
pkt.server.instance = cpu_to_le32(srv->instance);
|
|
|
|
pkt.server.node = cpu_to_le32(srv->node);
|
|
|
|
pkt.server.port = cpu_to_le32(srv->port);
|
|
|
|
|
|
|
|
msg.msg_name = (struct sockaddr *)dest;
|
|
|
|
msg.msg_namelen = sizeof(*dest);
|
|
|
|
|
|
|
|
return kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
|
|
|
}
|
|
|
|
|
2024-06-12 06:31:56 +00:00
|
|
|
static void service_announce_del(struct sockaddr_qrtr *dest,
|
|
|
|
struct qrtr_server *srv)
|
2020-02-20 15:13:26 +00:00
|
|
|
{
|
|
|
|
struct qrtr_ctrl_pkt pkt;
|
|
|
|
struct msghdr msg = { };
|
|
|
|
struct kvec iv;
|
|
|
|
int ret;
|
|
|
|
|
2020-04-21 07:40:54 +00:00
|
|
|
trace_qrtr_ns_service_announce_del(srv->service, srv->instance,
|
|
|
|
srv->node, srv->port);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
iv.iov_base = &pkt;
|
|
|
|
iv.iov_len = sizeof(pkt);
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
pkt.cmd = cpu_to_le32(QRTR_TYPE_DEL_SERVER);
|
|
|
|
pkt.server.service = cpu_to_le32(srv->service);
|
|
|
|
pkt.server.instance = cpu_to_le32(srv->instance);
|
|
|
|
pkt.server.node = cpu_to_le32(srv->node);
|
|
|
|
pkt.server.port = cpu_to_le32(srv->port);
|
|
|
|
|
|
|
|
msg.msg_name = (struct sockaddr *)dest;
|
|
|
|
msg.msg_namelen = sizeof(*dest);
|
|
|
|
|
|
|
|
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
2024-06-12 06:31:56 +00:00
|
|
|
if (ret < 0 && ret != -ENODEV)
|
2020-02-24 17:35:53 +00:00
|
|
|
pr_err("failed to announce del service\n");
|
2020-02-20 15:13:26 +00:00
|
|
|
|
2024-06-12 06:31:56 +00:00
|
|
|
return;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv,
|
|
|
|
bool new)
|
|
|
|
{
|
|
|
|
struct qrtr_ctrl_pkt pkt;
|
|
|
|
struct msghdr msg = { };
|
|
|
|
struct kvec iv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
iv.iov_base = &pkt;
|
|
|
|
iv.iov_len = sizeof(pkt);
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
pkt.cmd = new ? cpu_to_le32(QRTR_TYPE_NEW_SERVER) :
|
|
|
|
cpu_to_le32(QRTR_TYPE_DEL_SERVER);
|
|
|
|
if (srv) {
|
|
|
|
pkt.server.service = cpu_to_le32(srv->service);
|
|
|
|
pkt.server.instance = cpu_to_le32(srv->instance);
|
|
|
|
pkt.server.node = cpu_to_le32(srv->node);
|
|
|
|
pkt.server.port = cpu_to_le32(srv->port);
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.msg_name = (struct sockaddr *)to;
|
|
|
|
msg.msg_namelen = sizeof(*to);
|
|
|
|
|
|
|
|
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
2024-06-12 06:31:56 +00:00
|
|
|
if (ret < 0 && ret != -ENODEV)
|
2020-02-20 15:13:26 +00:00
|
|
|
pr_err("failed to send lookup notification\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int announce_servers(struct sockaddr_qrtr *sq)
|
|
|
|
{
|
|
|
|
struct qrtr_server *srv;
|
|
|
|
struct qrtr_node *node;
|
2023-07-14 05:58:44 +00:00
|
|
|
unsigned long index;
|
2020-10-05 07:16:42 +00:00
|
|
|
int ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
node = node_get(qrtr_ns.local_node);
|
|
|
|
if (!node)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Announce the list of servers registered in this node */
|
2023-07-14 05:58:44 +00:00
|
|
|
xa_for_each(&node->servers, index, srv) {
|
2020-02-20 15:13:26 +00:00
|
|
|
ret = service_announce_new(sq, srv);
|
|
|
|
if (ret < 0) {
|
2024-06-12 06:31:56 +00:00
|
|
|
if (ret == -ENODEV)
|
|
|
|
continue;
|
|
|
|
|
2020-02-20 15:13:26 +00:00
|
|
|
pr_err("failed to announce new service\n");
|
2020-10-05 07:16:42 +00:00
|
|
|
return ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-05 07:16:42 +00:00
|
|
|
return 0;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct qrtr_server *server_add(unsigned int service,
|
|
|
|
unsigned int instance,
|
|
|
|
unsigned int node_id,
|
|
|
|
unsigned int port)
|
|
|
|
{
|
|
|
|
struct qrtr_server *srv;
|
|
|
|
struct qrtr_server *old;
|
|
|
|
struct qrtr_node *node;
|
|
|
|
|
|
|
|
if (!service || !port)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
srv = kzalloc(sizeof(*srv), GFP_KERNEL);
|
|
|
|
if (!srv)
|
2020-02-26 14:51:53 +00:00
|
|
|
return NULL;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
srv->service = service;
|
|
|
|
srv->instance = instance;
|
|
|
|
srv->node = node_id;
|
|
|
|
srv->port = port;
|
|
|
|
|
|
|
|
node = node_get(node_id);
|
|
|
|
if (!node)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* Delete the old server on the same port */
|
2023-07-14 05:58:44 +00:00
|
|
|
old = xa_store(&node->servers, port, srv, GFP_KERNEL);
|
2020-02-20 15:13:26 +00:00
|
|
|
if (old) {
|
2023-07-14 05:58:44 +00:00
|
|
|
if (xa_is_err(old)) {
|
|
|
|
pr_err("failed to add server [0x%x:0x%x] ret:%d\n",
|
|
|
|
srv->service, srv->instance, xa_err(old));
|
|
|
|
goto err;
|
|
|
|
} else {
|
|
|
|
kfree(old);
|
|
|
|
}
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 07:40:54 +00:00
|
|
|
trace_qrtr_ns_server_add(srv->service, srv->instance,
|
|
|
|
srv->node, srv->port);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
return srv;
|
|
|
|
|
|
|
|
err:
|
|
|
|
kfree(srv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-04-03 06:58:51 +00:00
|
|
|
static int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
|
2020-02-20 15:13:26 +00:00
|
|
|
{
|
|
|
|
struct qrtr_lookup *lookup;
|
|
|
|
struct qrtr_server *srv;
|
|
|
|
struct list_head *li;
|
|
|
|
|
2023-07-14 05:58:44 +00:00
|
|
|
srv = xa_load(&node->servers, port);
|
2020-02-20 15:13:26 +00:00
|
|
|
if (!srv)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2023-07-14 05:58:44 +00:00
|
|
|
xa_erase(&node->servers, port);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
/* Broadcast the removal of local servers */
|
2023-04-03 06:58:51 +00:00
|
|
|
if (srv->node == qrtr_ns.local_node && bcast)
|
2020-02-20 15:13:26 +00:00
|
|
|
service_announce_del(&qrtr_ns.bcast_sq, srv);
|
|
|
|
|
|
|
|
/* Announce the service's disappearance to observers */
|
|
|
|
list_for_each(li, &qrtr_ns.lookups) {
|
|
|
|
lookup = container_of(li, struct qrtr_lookup, li);
|
|
|
|
if (lookup->service && lookup->service != srv->service)
|
|
|
|
continue;
|
|
|
|
if (lookup->instance && lookup->instance != srv->instance)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lookup_notify(&lookup->sq, srv, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(srv);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-02 07:03:04 +00:00
|
|
|
static int say_hello(struct sockaddr_qrtr *dest)
|
|
|
|
{
|
|
|
|
struct qrtr_ctrl_pkt pkt;
|
|
|
|
struct msghdr msg = { };
|
|
|
|
struct kvec iv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
iv.iov_base = &pkt;
|
|
|
|
iv.iov_len = sizeof(pkt);
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
pkt.cmd = cpu_to_le32(QRTR_TYPE_HELLO);
|
|
|
|
|
|
|
|
msg.msg_name = (struct sockaddr *)dest;
|
|
|
|
msg.msg_namelen = sizeof(*dest);
|
|
|
|
|
|
|
|
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
|
|
|
if (ret < 0)
|
|
|
|
pr_err("failed to send hello msg\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-02-20 15:13:26 +00:00
|
|
|
/* Announce the list of servers registered on the local node */
|
|
|
|
static int ctrl_cmd_hello(struct sockaddr_qrtr *sq)
|
|
|
|
{
|
2020-03-02 07:03:04 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = say_hello(sq);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2020-02-20 15:13:26 +00:00
|
|
|
return announce_servers(sq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
|
|
|
|
{
|
|
|
|
struct qrtr_node *local_node;
|
|
|
|
struct qrtr_ctrl_pkt pkt;
|
|
|
|
struct qrtr_server *srv;
|
|
|
|
struct sockaddr_qrtr sq;
|
|
|
|
struct msghdr msg = { };
|
|
|
|
struct qrtr_node *node;
|
2023-07-14 05:58:44 +00:00
|
|
|
unsigned long index;
|
2020-02-20 15:13:26 +00:00
|
|
|
struct kvec iv;
|
2020-10-05 07:16:42 +00:00
|
|
|
int ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
iv.iov_base = &pkt;
|
|
|
|
iv.iov_len = sizeof(pkt);
|
|
|
|
|
|
|
|
node = node_get(from->sq_node);
|
|
|
|
if (!node)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Advertise removal of this client to all servers of remote node */
|
2023-07-14 05:58:44 +00:00
|
|
|
xa_for_each(&node->servers, index, srv)
|
2023-04-03 06:58:51 +00:00
|
|
|
server_del(node, srv->port, true);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
/* Advertise the removal of this client to all local servers */
|
|
|
|
local_node = node_get(qrtr_ns.local_node);
|
|
|
|
if (!local_node)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
pkt.cmd = cpu_to_le32(QRTR_TYPE_BYE);
|
|
|
|
pkt.client.node = cpu_to_le32(from->sq_node);
|
|
|
|
|
2023-07-14 05:58:44 +00:00
|
|
|
xa_for_each(&local_node->servers, index, srv) {
|
2020-02-20 15:13:26 +00:00
|
|
|
sq.sq_family = AF_QIPCRTR;
|
|
|
|
sq.sq_node = srv->node;
|
|
|
|
sq.sq_port = srv->port;
|
|
|
|
|
|
|
|
msg.msg_name = (struct sockaddr *)&sq;
|
|
|
|
msg.msg_namelen = sizeof(sq);
|
|
|
|
|
|
|
|
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
2024-06-12 06:31:56 +00:00
|
|
|
if (ret < 0 && ret != -ENODEV) {
|
2020-02-20 15:13:26 +00:00
|
|
|
pr_err("failed to send bye cmd\n");
|
2020-10-05 07:16:42 +00:00
|
|
|
return ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-05 07:16:42 +00:00
|
|
|
return 0;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
|
|
|
|
unsigned int node_id, unsigned int port)
|
|
|
|
{
|
|
|
|
struct qrtr_node *local_node;
|
|
|
|
struct qrtr_lookup *lookup;
|
|
|
|
struct qrtr_ctrl_pkt pkt;
|
|
|
|
struct msghdr msg = { };
|
|
|
|
struct qrtr_server *srv;
|
|
|
|
struct sockaddr_qrtr sq;
|
|
|
|
struct qrtr_node *node;
|
|
|
|
struct list_head *tmp;
|
|
|
|
struct list_head *li;
|
2023-07-14 05:58:44 +00:00
|
|
|
unsigned long index;
|
2020-02-20 15:13:26 +00:00
|
|
|
struct kvec iv;
|
2020-10-05 07:16:42 +00:00
|
|
|
int ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
iv.iov_base = &pkt;
|
|
|
|
iv.iov_len = sizeof(pkt);
|
|
|
|
|
|
|
|
/* Don't accept spoofed messages */
|
|
|
|
if (from->sq_node != node_id)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Local DEL_CLIENT messages comes from the port being closed */
|
|
|
|
if (from->sq_node == qrtr_ns.local_node && from->sq_port != port)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Remove any lookups by this client */
|
|
|
|
list_for_each_safe(li, tmp, &qrtr_ns.lookups) {
|
|
|
|
lookup = container_of(li, struct qrtr_lookup, li);
|
|
|
|
if (lookup->sq.sq_node != node_id)
|
|
|
|
continue;
|
|
|
|
if (lookup->sq.sq_port != port)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
list_del(&lookup->li);
|
|
|
|
kfree(lookup);
|
|
|
|
}
|
|
|
|
|
2023-04-03 06:58:51 +00:00
|
|
|
/* Remove the server belonging to this port but don't broadcast
|
|
|
|
* DEL_SERVER. Neighbours would've already removed the server belonging
|
|
|
|
* to this port due to the DEL_CLIENT broadcast from qrtr_port_remove().
|
|
|
|
*/
|
2020-02-20 15:13:26 +00:00
|
|
|
node = node_get(node_id);
|
|
|
|
if (node)
|
2023-04-03 06:58:51 +00:00
|
|
|
server_del(node, port, false);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
/* Advertise the removal of this client to all local servers */
|
|
|
|
local_node = node_get(qrtr_ns.local_node);
|
|
|
|
if (!local_node)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
pkt.cmd = cpu_to_le32(QRTR_TYPE_DEL_CLIENT);
|
|
|
|
pkt.client.node = cpu_to_le32(node_id);
|
|
|
|
pkt.client.port = cpu_to_le32(port);
|
|
|
|
|
2023-07-14 05:58:44 +00:00
|
|
|
xa_for_each(&local_node->servers, index, srv) {
|
2020-02-20 15:13:26 +00:00
|
|
|
sq.sq_family = AF_QIPCRTR;
|
|
|
|
sq.sq_node = srv->node;
|
|
|
|
sq.sq_port = srv->port;
|
|
|
|
|
|
|
|
msg.msg_name = (struct sockaddr *)&sq;
|
|
|
|
msg.msg_namelen = sizeof(sq);
|
|
|
|
|
|
|
|
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
2024-06-12 06:31:56 +00:00
|
|
|
if (ret < 0 && ret != -ENODEV) {
|
2020-02-20 15:13:26 +00:00
|
|
|
pr_err("failed to send del client cmd\n");
|
2020-10-05 07:16:42 +00:00
|
|
|
return ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-05 07:16:42 +00:00
|
|
|
return 0;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ctrl_cmd_new_server(struct sockaddr_qrtr *from,
|
|
|
|
unsigned int service, unsigned int instance,
|
|
|
|
unsigned int node_id, unsigned int port)
|
|
|
|
{
|
|
|
|
struct qrtr_lookup *lookup;
|
|
|
|
struct qrtr_server *srv;
|
|
|
|
struct list_head *li;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Ignore specified node and port for local servers */
|
|
|
|
if (from->sq_node == qrtr_ns.local_node) {
|
|
|
|
node_id = from->sq_node;
|
|
|
|
port = from->sq_port;
|
|
|
|
}
|
|
|
|
|
|
|
|
srv = server_add(service, instance, node_id, port);
|
|
|
|
if (!srv)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (srv->node == qrtr_ns.local_node) {
|
|
|
|
ret = service_announce_new(&qrtr_ns.bcast_sq, srv);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("failed to announce new service\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Notify any potential lookups about the new server */
|
|
|
|
list_for_each(li, &qrtr_ns.lookups) {
|
|
|
|
lookup = container_of(li, struct qrtr_lookup, li);
|
|
|
|
if (lookup->service && lookup->service != service)
|
|
|
|
continue;
|
|
|
|
if (lookup->instance && lookup->instance != instance)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lookup_notify(&lookup->sq, srv, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ctrl_cmd_del_server(struct sockaddr_qrtr *from,
|
|
|
|
unsigned int service, unsigned int instance,
|
|
|
|
unsigned int node_id, unsigned int port)
|
|
|
|
{
|
|
|
|
struct qrtr_node *node;
|
|
|
|
|
|
|
|
/* Ignore specified node and port for local servers*/
|
|
|
|
if (from->sq_node == qrtr_ns.local_node) {
|
|
|
|
node_id = from->sq_node;
|
|
|
|
port = from->sq_port;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local servers may only unregister themselves */
|
|
|
|
if (from->sq_node == qrtr_ns.local_node && from->sq_port != port)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
node = node_get(node_id);
|
|
|
|
if (!node)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2023-12-21 10:06:51 +00:00
|
|
|
server_del(node, port, true);
|
|
|
|
|
|
|
|
return 0;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from,
|
|
|
|
unsigned int service, unsigned int instance)
|
|
|
|
{
|
|
|
|
struct qrtr_server_filter filter;
|
|
|
|
struct qrtr_lookup *lookup;
|
2023-07-14 05:58:44 +00:00
|
|
|
struct qrtr_server *srv;
|
2020-02-20 15:13:26 +00:00
|
|
|
struct qrtr_node *node;
|
2023-07-14 05:58:44 +00:00
|
|
|
unsigned long node_idx;
|
|
|
|
unsigned long srv_idx;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
/* Accept only local observers */
|
|
|
|
if (from->sq_node != qrtr_ns.local_node)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
lookup = kzalloc(sizeof(*lookup), GFP_KERNEL);
|
|
|
|
if (!lookup)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
lookup->sq = *from;
|
|
|
|
lookup->service = service;
|
|
|
|
lookup->instance = instance;
|
|
|
|
list_add_tail(&lookup->li, &qrtr_ns.lookups);
|
|
|
|
|
|
|
|
memset(&filter, 0, sizeof(filter));
|
|
|
|
filter.service = service;
|
|
|
|
filter.instance = instance;
|
|
|
|
|
2023-07-14 05:58:44 +00:00
|
|
|
xa_for_each(&nodes, node_idx, node) {
|
|
|
|
xa_for_each(&node->servers, srv_idx, srv) {
|
2020-02-20 15:13:26 +00:00
|
|
|
if (!server_match(srv, &filter))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lookup_notify(from, srv, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Empty notification, to indicate end of listing */
|
|
|
|
lookup_notify(from, NULL, true);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ctrl_cmd_del_lookup(struct sockaddr_qrtr *from,
|
|
|
|
unsigned int service, unsigned int instance)
|
|
|
|
{
|
|
|
|
struct qrtr_lookup *lookup;
|
|
|
|
struct list_head *tmp;
|
|
|
|
struct list_head *li;
|
|
|
|
|
|
|
|
list_for_each_safe(li, tmp, &qrtr_ns.lookups) {
|
|
|
|
lookup = container_of(li, struct qrtr_lookup, li);
|
|
|
|
if (lookup->sq.sq_node != from->sq_node)
|
|
|
|
continue;
|
|
|
|
if (lookup->sq.sq_port != from->sq_port)
|
|
|
|
continue;
|
|
|
|
if (lookup->service != service)
|
|
|
|
continue;
|
|
|
|
if (lookup->instance && lookup->instance != instance)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
list_del(&lookup->li);
|
|
|
|
kfree(lookup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qrtr_ns_worker(struct work_struct *work)
|
|
|
|
{
|
|
|
|
const struct qrtr_ctrl_pkt *pkt;
|
|
|
|
size_t recv_buf_size = 4096;
|
|
|
|
struct sockaddr_qrtr sq;
|
|
|
|
struct msghdr msg = { };
|
|
|
|
unsigned int cmd;
|
|
|
|
ssize_t msglen;
|
|
|
|
void *recv_buf;
|
|
|
|
struct kvec iv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
msg.msg_name = (struct sockaddr *)&sq;
|
|
|
|
msg.msg_namelen = sizeof(sq);
|
|
|
|
|
|
|
|
recv_buf = kzalloc(recv_buf_size, GFP_KERNEL);
|
|
|
|
if (!recv_buf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
iv.iov_base = recv_buf;
|
|
|
|
iv.iov_len = recv_buf_size;
|
|
|
|
|
|
|
|
msglen = kernel_recvmsg(qrtr_ns.sock, &msg, &iv, 1,
|
|
|
|
iv.iov_len, MSG_DONTWAIT);
|
|
|
|
|
|
|
|
if (msglen == -EAGAIN)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (msglen < 0) {
|
|
|
|
pr_err("error receiving packet: %zd\n", msglen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt = recv_buf;
|
|
|
|
cmd = le32_to_cpu(pkt->cmd);
|
|
|
|
if (cmd < ARRAY_SIZE(qrtr_ctrl_pkt_strings) &&
|
|
|
|
qrtr_ctrl_pkt_strings[cmd])
|
2020-04-21 07:40:54 +00:00
|
|
|
trace_qrtr_ns_message(qrtr_ctrl_pkt_strings[cmd],
|
|
|
|
sq.sq_node, sq.sq_port);
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
switch (cmd) {
|
|
|
|
case QRTR_TYPE_HELLO:
|
|
|
|
ret = ctrl_cmd_hello(&sq);
|
|
|
|
break;
|
|
|
|
case QRTR_TYPE_BYE:
|
|
|
|
ret = ctrl_cmd_bye(&sq);
|
|
|
|
break;
|
|
|
|
case QRTR_TYPE_DEL_CLIENT:
|
|
|
|
ret = ctrl_cmd_del_client(&sq,
|
|
|
|
le32_to_cpu(pkt->client.node),
|
|
|
|
le32_to_cpu(pkt->client.port));
|
|
|
|
break;
|
|
|
|
case QRTR_TYPE_NEW_SERVER:
|
|
|
|
ret = ctrl_cmd_new_server(&sq,
|
|
|
|
le32_to_cpu(pkt->server.service),
|
|
|
|
le32_to_cpu(pkt->server.instance),
|
|
|
|
le32_to_cpu(pkt->server.node),
|
|
|
|
le32_to_cpu(pkt->server.port));
|
|
|
|
break;
|
|
|
|
case QRTR_TYPE_DEL_SERVER:
|
|
|
|
ret = ctrl_cmd_del_server(&sq,
|
|
|
|
le32_to_cpu(pkt->server.service),
|
|
|
|
le32_to_cpu(pkt->server.instance),
|
|
|
|
le32_to_cpu(pkt->server.node),
|
|
|
|
le32_to_cpu(pkt->server.port));
|
|
|
|
break;
|
|
|
|
case QRTR_TYPE_EXIT:
|
|
|
|
case QRTR_TYPE_PING:
|
|
|
|
case QRTR_TYPE_RESUME_TX:
|
|
|
|
break;
|
|
|
|
case QRTR_TYPE_NEW_LOOKUP:
|
|
|
|
ret = ctrl_cmd_new_lookup(&sq,
|
|
|
|
le32_to_cpu(pkt->server.service),
|
|
|
|
le32_to_cpu(pkt->server.instance));
|
|
|
|
break;
|
|
|
|
case QRTR_TYPE_DEL_LOOKUP:
|
|
|
|
ctrl_cmd_del_lookup(&sq,
|
|
|
|
le32_to_cpu(pkt->server.service),
|
|
|
|
le32_to_cpu(pkt->server.instance));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
pr_err("failed while handling packet from %d:%d",
|
|
|
|
sq.sq_node, sq.sq_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(recv_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qrtr_ns_data_ready(struct sock *sk)
|
|
|
|
{
|
2023-01-20 00:45:16 +00:00
|
|
|
trace_sk_data_ready(sk);
|
|
|
|
|
2020-02-20 15:13:26 +00:00
|
|
|
queue_work(qrtr_ns.workqueue, &qrtr_ns.work);
|
|
|
|
}
|
|
|
|
|
2021-01-05 05:57:54 +00:00
|
|
|
int qrtr_ns_init(void)
|
2020-02-20 15:13:26 +00:00
|
|
|
{
|
|
|
|
struct sockaddr_qrtr sq;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&qrtr_ns.lookups);
|
|
|
|
INIT_WORK(&qrtr_ns.work, qrtr_ns_worker);
|
|
|
|
|
|
|
|
ret = sock_create_kern(&init_net, AF_QIPCRTR, SOCK_DGRAM,
|
|
|
|
PF_QIPCRTR, &qrtr_ns.sock);
|
|
|
|
if (ret < 0)
|
2021-01-05 05:57:54 +00:00
|
|
|
return ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
ret = kernel_getsockname(qrtr_ns.sock, (struct sockaddr *)&sq);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("failed to get socket name\n");
|
|
|
|
goto err_sock;
|
|
|
|
}
|
|
|
|
|
net: qrtr: Use alloc_ordered_workqueue() to create ordered workqueues
BACKGROUND
==========
When multiple work items are queued to a workqueue, their execution order
doesn't match the queueing order. They may get executed in any order and
simultaneously. When fully serialized execution - one by one in the queueing
order - is needed, an ordered workqueue should be used which can be created
with alloc_ordered_workqueue().
However, alloc_ordered_workqueue() was a later addition. Before it, an
ordered workqueue could be obtained by creating an UNBOUND workqueue with
@max_active==1. This originally was an implementation side-effect which was
broken by 4c16bd327c74 ("workqueue: restore WQ_UNBOUND/max_active==1 to be
ordered"). Because there were users that depended on the ordered execution,
5c0338c68706 ("workqueue: restore WQ_UNBOUND/max_active==1 to be ordered")
made workqueue allocation path to implicitly promote UNBOUND workqueues w/
@max_active==1 to ordered workqueues.
While this has worked okay, overloading the UNBOUND allocation interface
this way creates other issues. It's difficult to tell whether a given
workqueue actually needs to be ordered and users that legitimately want a
min concurrency level wq unexpectedly gets an ordered one instead. With
planned UNBOUND workqueue updates to improve execution locality and more
prevalence of chiplet designs which can benefit from such improvements, this
isn't a state we wanna be in forever.
This patch series audits all callsites that create an UNBOUND workqueue w/
@max_active==1 and converts them to alloc_ordered_workqueue() as necessary.
WHAT TO LOOK FOR
================
The conversions are from
alloc_workqueue(WQ_UNBOUND | flags, 1, args..)
to
alloc_ordered_workqueue(flags, args...)
which don't cause any functional changes. If you know that fully ordered
execution is not necessary, please let me know. I'll drop the conversion and
instead add a comment noting the fact to reduce confusion while conversion
is in progress.
If you aren't fully sure, it's completely fine to let the conversion
through. The behavior will stay exactly the same and we can always
reconsider later.
As there are follow-up workqueue core changes, I'd really appreciate if the
patch can be routed through the workqueue tree w/ your acks. Thanks.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Manivannan Sadhasivam <mani@kernel.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: netdev@vger.kernel.org
2023-05-25 22:15:33 +00:00
|
|
|
qrtr_ns.workqueue = alloc_ordered_workqueue("qrtr_ns_handler", 0);
|
2021-05-19 15:58:52 +00:00
|
|
|
if (!qrtr_ns.workqueue) {
|
|
|
|
ret = -ENOMEM;
|
2020-05-28 23:05:26 +00:00
|
|
|
goto err_sock;
|
2021-05-19 15:58:52 +00:00
|
|
|
}
|
2020-05-28 23:05:26 +00:00
|
|
|
|
2020-02-20 15:13:26 +00:00
|
|
|
qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
|
|
|
|
|
|
|
|
sq.sq_port = QRTR_PORT_CTRL;
|
|
|
|
qrtr_ns.local_node = sq.sq_node;
|
|
|
|
|
|
|
|
ret = kernel_bind(qrtr_ns.sock, (struct sockaddr *)&sq, sizeof(sq));
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("failed to bind to socket\n");
|
2020-05-28 23:05:26 +00:00
|
|
|
goto err_wq;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
qrtr_ns.bcast_sq.sq_family = AF_QIPCRTR;
|
|
|
|
qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST;
|
|
|
|
qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL;
|
|
|
|
|
2020-03-02 07:03:04 +00:00
|
|
|
ret = say_hello(&qrtr_ns.bcast_sq);
|
2020-02-20 15:13:26 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_wq;
|
|
|
|
|
2024-05-13 17:31:46 +00:00
|
|
|
/* As the qrtr ns socket owner and creator is the same module, we have
|
|
|
|
* to decrease the qrtr module reference count to guarantee that it
|
|
|
|
* remains zero after the ns socket is created, otherwise, executing
|
|
|
|
* "rmmod" command is unable to make the qrtr module deleted after the
|
|
|
|
* qrtr module is inserted successfully.
|
|
|
|
*
|
|
|
|
* However, the reference count is increased twice in
|
|
|
|
* sock_create_kern(): one is to increase the reference count of owner
|
|
|
|
* of qrtr socket's proto_ops struct; another is to increment the
|
|
|
|
* reference count of owner of qrtr proto struct. Therefore, we must
|
|
|
|
* decrement the module reference count twice to ensure that it keeps
|
|
|
|
* zero after server's listening socket is created. Of course, we
|
|
|
|
* must bump the module reference count twice as well before the socket
|
|
|
|
* is closed.
|
|
|
|
*/
|
|
|
|
module_put(qrtr_ns.sock->ops->owner);
|
|
|
|
module_put(qrtr_ns.sock->sk->sk_prot_creator->owner);
|
|
|
|
|
2021-01-05 05:57:54 +00:00
|
|
|
return 0;
|
2020-02-20 15:13:26 +00:00
|
|
|
|
|
|
|
err_wq:
|
|
|
|
destroy_workqueue(qrtr_ns.workqueue);
|
|
|
|
err_sock:
|
|
|
|
sock_release(qrtr_ns.sock);
|
2021-01-05 05:57:54 +00:00
|
|
|
return ret;
|
2020-02-20 15:13:26 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(qrtr_ns_init);
|
|
|
|
|
|
|
|
void qrtr_ns_remove(void)
|
|
|
|
{
|
|
|
|
cancel_work_sync(&qrtr_ns.work);
|
|
|
|
destroy_workqueue(qrtr_ns.workqueue);
|
2024-05-13 17:31:46 +00:00
|
|
|
|
|
|
|
/* sock_release() expects the two references that were put during
|
|
|
|
* qrtr_ns_init(). This function is only called during module remove,
|
|
|
|
* so try_stop_module() has already set the refcnt to 0. Use
|
|
|
|
* __module_get() instead of try_module_get() to successfully take two
|
|
|
|
* references.
|
|
|
|
*/
|
|
|
|
__module_get(qrtr_ns.sock->ops->owner);
|
|
|
|
__module_get(qrtr_ns.sock->sk->sk_prot_creator->owner);
|
2020-02-20 15:13:26 +00:00
|
|
|
sock_release(qrtr_ns.sock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(qrtr_ns_remove);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
|
|
|
|
MODULE_DESCRIPTION("Qualcomm IPC Router Nameservice");
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|