Re: [PATCH RESEND] net: qrtr: support qrtr service and lookup route

From: Bjorn Andersson
Date: Thu Apr 09 2020 - 15:38:06 EST


On Wed 08 Apr 03:46 PDT 2020, Wang Wenhu wrote:

> QSR implements maintenance of qrtr services and lookups. It would
> be helpful for developers to work with QRTR without the none-opensource
> user-space implementation part of IPC Router.
>
> As we know, the extremely important point of IPC Router is the support
> of services form different nodes. But QRTR was pushed into mainline
> without route process support of services, and the router port process
> is implemented in user-space as none-opensource codes, which is an
> great unconvenience for developers.
>
> QSR also implements a interface via chardev and a set of sysfs class
> files for the communication and debugging in user-space. We can get
> service and lookup entries conveniently via sysfs file in /sys/class/qsr/.
> Currently add-server, del-server, add-lookup and del-lookup control
> packatets are processed and enhancements could be taken easily upon
> currently implementation.
>
> Signed-off-by: Wang Wenhu <wenhu.wang@xxxxxxxx>

Hi Wang,

Isn't this implementing the same thing that was recently landed upstream
as net/qrtr/ns.c?

Regards,
Bjorn

> ---
> Changelog:
> This is a resent, but the first normal version of QSR support.
> The former one sent out earlier contains only the patch of coding
> style modification of qsr.c.
>
> Please do not be confused and take this patch as the NORMAL commit of QSR.
> ---
> net/qrtr/Kconfig | 8 +
> net/qrtr/Makefile | 2 +
> net/qrtr/qrtr.c | 5 +
> net/qrtr/qrtr.h | 2 +
> net/qrtr/qsr.c | 622 ++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 639 insertions(+)
> create mode 100644 net/qrtr/qsr.c
>
> diff --git a/net/qrtr/Kconfig b/net/qrtr/Kconfig
> index 63f89cc6e82c..d2ce8fb57278 100644
> --- a/net/qrtr/Kconfig
> +++ b/net/qrtr/Kconfig
> @@ -29,4 +29,12 @@ config QRTR_TUN
> implement endpoints of QRTR, for purpose of tunneling data to other
> hosts or testing purposes.
>
> +config QSR
> + tristate "QRTR Service Router"
> + help
> + Say Y here to enable the kernel QRTR Service Router module.
> + QSR support route processes of QRTR services and lookups. It would be
> + helpful when develop with QRTR without user-space implementation of
> + IPC Router support.
> +
> endif # QRTR
> diff --git a/net/qrtr/Makefile b/net/qrtr/Makefile
> index 1c6d6c120fb7..3882beaead29 100644
> --- a/net/qrtr/Makefile
> +++ b/net/qrtr/Makefile
> @@ -5,3 +5,5 @@ obj-$(CONFIG_QRTR_SMD) += qrtr-smd.o
> qrtr-smd-y := smd.o
> obj-$(CONFIG_QRTR_TUN) += qrtr-tun.o
> qrtr-tun-y := tun.o
> +obj-$(CONFIG_QSR) += qrtr-svc-router.o
> +qrtr-svc-router-y := qsr.o
> diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c
> index 5a8e42ad1504..267f7d6c746f 100644
> --- a/net/qrtr/qrtr.c
> +++ b/net/qrtr/qrtr.c
> @@ -158,6 +158,11 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
> static struct qrtr_sock *qrtr_port_lookup(int port);
> static void qrtr_port_put(struct qrtr_sock *ipc);
>
> +unsigned int get_qrtr_local_nid(void)
> +{
> + return qrtr_local_nid;
> +}
> +
> /* Release node resources and free the node.
> *
> * Do not call directly, use qrtr_node_release. To be used with
> diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h
> index b81e6953c04b..872d98fd36c6 100644
> --- a/net/qrtr/qrtr.h
> +++ b/net/qrtr/qrtr.h
> @@ -29,4 +29,6 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep);
>
> int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len);
>
> +unsigned int get_qrtr_local_nid(void);
> +
> #endif
> diff --git a/net/qrtr/qsr.c b/net/qrtr/qsr.c
> new file mode 100644
> index 000000000000..906f5903ebad
> --- /dev/null
> +++ b/net/qrtr/qsr.c
> @@ -0,0 +1,622 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2020 Vivo Communication Technology Co. Ltd.
> + * Copyright (C) 2020 Wang Wenhu <wenhu.wang@xxxxxxxx>
> + *
> + * The QRTR Service Route module aims at providing maintenance
> + * and route processes for qrtr service and lookup requests in
> + * kernel. Also, it provides sysfs class interface to expose
> + * the status of qrtr services and lookups. More could be done
> + * through the character device /dev/qsr in user-space.
> + *
> + * Currently, only server add, server delete, lookup add and
> + * lookup delete requests are processed.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/skbuff.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/string.h>
> +#include <linux/kobject.h>
> +#include <linux/cdev.h>
> +#include <linux/qrtr.h>
> +#include <net/sock.h>
> +
> +#include "qrtr.h"
> +
> +#define QSR_NAME "qsr"
> +
> +/**
> + * struct qsr_info - qrtr service route request information
> + * @service: service identity
> + * @instance: service instance
> + * @server: server address
> + * @client: client address
> + * @node: qrtr node of server or client
> + * @port: qrtr port of server or client
> + *
> + * When a control packet of new server request is received, the server
> + * field should be reference for the server node address. For the opposite
> + * situation, the client field should be referenced within a lookup request.
> + */
> +struct qsr_info {
> + __le32 service;
> + __le32 instance;
> +
> + union {
> + struct {
> + __le32 node;
> + __le32 port;
> + } server;
> +
> + struct {
> + __le32 node;
> + __le32 port;
> + } client;
> + };
> +
> + struct list_head list;
> +};
> +
> +/**
> + * struct qsr - qrtr service route device structure
> + * @dev: character device for user-space communication
> + * @sk: socket to process messages
> + * @sq: socket address to be binded
> + * @ops: callbacks of different control package types
> + * @qsr_lock: data buffer lock
> + * @recv_buf: receive buffer
> + * @recv_buf_size: receive buffer size
> + * @wq: workqueue for message process worker
> + * @work: work route to process queued messages
> + * @lookups: pending lookup requests
> + * @services: servers list to provide different kind of services
> + */
> +struct qsr {
> + struct device dev;
> + struct socket *sk;
> + struct sockaddr_qrtr sq;
> + struct qsr_ops *ops;
> +
> + struct mutex qsr_lock;
> + void *recv_buf;
> + size_t recv_buf_size;
> +
> + struct workqueue_struct *wq;
> + struct work_struct work;
> +
> + struct list_head lookups;
> + struct list_head services;
> +};
> +
> +struct qsr_ops {
> + int (*new_server)(struct qsr_info *svc);
> + int (*new_lookup)(struct qsr_info *svc, u32 node, u32 port);
> +};
> +
> +static int qsr_major;
> +static struct cdev *qsr_cdev;
> +static struct qsr *qsr;
> +
> +static int qsr_new_server(struct qsr_info *new)
> +{
> + struct qsr_info *lookup;
> + struct qsr_ops *ops = qsr->ops;
> + int ret;
> +
> + if (!ops->new_lookup)
> + return 0;
> +
> + list_for_each_entry(lookup, &qsr->lookups, list) {
> + if (lookup->service == new->service &&
> + lookup->instance == new->instance) {
> + ret = ops->new_lookup(new,
> + lookup->client.node,
> + lookup->client.port);
> + if (ret < 0)
> + pr_err("Error to notice client of new server, %d\n", ret);
> + else
> + list_del(&lookup->list);
> + return 0;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int qsr_new_lookup(struct qsr_info *svc, u32 node, u32 port)
> +{
> + struct qrtr_ctrl_pkt pkt;
> + struct sockaddr_qrtr sq;
> + struct msghdr msg = { };
> + struct kvec iv = { &pkt, sizeof(pkt) };
> + int ret = 0;
> +
> + memset(&pkt, 0, sizeof(pkt));
> + pkt.cmd = cpu_to_le32(QRTR_TYPE_NEW_SERVER);
> + pkt.server.service = cpu_to_le32(svc->service);
> + pkt.server.instance = cpu_to_le32(svc->instance);
> + pkt.server.node = cpu_to_le32(svc->server.node);
> + pkt.server.port = cpu_to_le32(svc->server.port);
> +
> + sq.sq_family = AF_QIPCRTR;
> + sq.sq_node = node;
> + sq.sq_port = port;
> +
> + msg.msg_name = &sq;
> + msg.msg_namelen = sizeof(sq);
> +
> + mutex_lock(&qsr->qsr_lock);
> + if (qsr->sk) {
> + ret = kernel_sendmsg(qsr->sk, &msg, &iv, 1, sizeof(pkt));
> + if (ret < 0)
> + pr_err("Error to send server info to client, %d\n", ret);
> + }
> + mutex_unlock(&qsr->qsr_lock);
> +
> + return ret;
> +}
> +
> +static void qsr_recv_new_server(u32 service,
> + u32 instance,
> + u32 node,
> + u32 port)
> +{
> + struct qsr_ops *ops = qsr->ops;
> + struct qsr_info *svc, *temp;
> + int ret;
> +
> + if (!ops->new_server)
> + return;
> +
> + if (!node && !port)
> + return;
> +
> + list_for_each_entry(temp, &qsr->services, list) {
> + if (temp->service == service && temp->instance == instance) {
> + pr_err("Error server exists, service:0x%x instance:0x%x",
> + service, instance);
> + return;
> + }
> + }
> +
> + svc = kzalloc(sizeof(*svc), GFP_KERNEL);
> + if (!svc)
> + return;
> +
> + svc->service = service;
> + svc->instance = instance;
> + svc->server.node = node;
> + svc->server.port = port;
> +
> + ret = ops->new_server(svc);
> + if (ret < 0)
> + kfree(svc);
> + else
> + list_add(&svc->list, &qsr->services);
> +}
> +
> +static void qsr_recv_del_server(u32 service, u32 instance)
> +{
> + struct qsr_info *svc;
> +
> + list_for_each_entry(svc, &qsr->lookups, list) {
> + if (svc->service == service && svc->instance == instance) {
> + list_del(&svc->list);
> + return;
> + }
> + }
> +}
> +
> +static void qsr_recv_new_lookup(u32 service,
> + u32 instance,
> + u32 node,
> + u32 port)
> +{
> + struct qsr_ops *ops = qsr->ops;
> + struct qsr_info *svc, *temp;
> + int ret;
> +
> + if (!ops->new_lookup)
> + return;
> +
> + if (!node && !port)
> + return;
> +
> + list_for_each_entry(temp, &qsr->lookups, list) {
> + if (temp->service == service &&
> + temp->instance == instance &&
> + temp->client.node == node &&
> + temp->client.port == port) {
> + pr_err("Error lookup exists, service:0x%x instance:0x%x node:%d port:%d",
> + service, instance, node, port);
> + return;
> + }
> + }
> +
> + list_for_each_entry(svc, &qsr->services, list) {
> + if (svc->service == service && svc->instance == instance) {
> + ret = ops->new_lookup(svc, node, port);
> + if (ret < 0)
> + pr_err("Error to send server info to client, %d", ret);
> + return;
> + }
> + }
> +
> + /* Server does not exist.
> + * Record the lookup information and add it to the pending list.
> + */
> +
> + svc = kzalloc(sizeof(*svc), GFP_KERNEL);
> + if (!svc)
> + return;
> +
> + svc->service = service;
> + svc->instance = instance;
> + svc->client.node = node;
> + svc->client.port = port;
> +
> + list_add(&svc->list, &qsr->lookups);
> +}
> +
> +static void qsr_recv_del_lookup(u32 service,
> + u32 instance,
> + u32 node,
> + u32 port)
> +{
> + struct qsr_info *lookup;
> +
> + if (!node && !port)
> + return;
> +
> + list_for_each_entry(lookup, &qsr->lookups, list) {
> + if (lookup->service == service &&
> + lookup->instance == instance &&
> + lookup->client.node == node &&
> + lookup->client.port == port) {
> + list_del(&lookup->list);
> + return;
> + }
> + }
> +}
> +
> +static void qsr_recv_ctrl_pkt(struct sockaddr_qrtr *sq,
> + const void *buf,
> + size_t len)
> +{
> + const struct qrtr_ctrl_pkt *pkt = buf;
> +
> + if (len < sizeof(struct qrtr_ctrl_pkt)) {
> + pr_debug("ignoring short control packet\n");
> + return;
> + }
> +
> + switch (le32_to_cpu(pkt->cmd)) {
> + case QRTR_TYPE_NEW_SERVER:
> + qsr_recv_new_server(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_NEW_LOOKUP:
> + qsr_recv_new_lookup(le32_to_cpu(pkt->server.service),
> + le32_to_cpu(pkt->server.instance),
> + sq->sq_node,
> + sq->sq_port);
> + break;
> +
> + case QRTR_TYPE_DEL_SERVER:
> + qsr_recv_del_server(le32_to_cpu(pkt->server.service),
> + le32_to_cpu(pkt->server.instance));
> + break;
> +
> + case QRTR_TYPE_DEL_LOOKUP:
> + qsr_recv_del_lookup(le32_to_cpu(pkt->server.service),
> + le32_to_cpu(pkt->server.instance),
> + sq->sq_node,
> + sq->sq_port);
> + break;
> + }
> +}
> +
> +static void qsr_recv_work(struct work_struct *work)
> +{
> + struct sockaddr_qrtr sq;
> + struct msghdr msg = { .msg_name = &sq, .msg_namelen = sizeof(sq) };
> + struct kvec iv;
> + ssize_t msglen;
> +
> + for (;;) {
> + iv.iov_base = qsr->recv_buf;
> + iv.iov_len = qsr->recv_buf_size;
> +
> + mutex_lock(&qsr->qsr_lock);
> + if (qsr->sk)
> + msglen = kernel_recvmsg(qsr->sk, &msg, &iv, 1,
> + iv.iov_len, MSG_DONTWAIT);
> + else
> + msglen = -EPIPE;
> + mutex_unlock(&qsr->qsr_lock);
> +
> + if (msglen == -EAGAIN)
> + break;
> +
> + if (msglen < 0) {
> + pr_err("qmi recvmsg failed: %zd\n", msglen);
> + break;
> + }
> +
> + qsr_recv_ctrl_pkt(&sq, qsr->recv_buf, msglen);
> + }
> +}
> +
> +static void qsr_data_ready(struct sock *sk)
> +{
> + read_lock_bh(&sk->sk_callback_lock);
> + queue_work(qsr->wq, &qsr->work);
> + read_unlock_bh(&sk->sk_callback_lock);
> +}
> +
> +static ssize_t name_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int ret;
> +
> + mutex_lock(&qsr->qsr_lock);
> + ret = sprintf(buf, "%s\n", QSR_NAME);
> + mutex_unlock(&qsr->qsr_lock);
> +
> + return ret;
> +}
> +static DEVICE_ATTR_RO(name);
> +
> +static ssize_t lookups_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct qsr_info *lookup;
> + int ret = 0;
> +
> + mutex_lock(&qsr->qsr_lock);
> + list_for_each_entry(lookup, &qsr->lookups, list) {
> + ret += sprintf(buf, "service:0x%04x instance:0x%04x node:%04d port:%04d\n",
> + lookup->service,
> + lookup->instance,
> + lookup->server.node,
> + lookup->server.port);
> + }
> + mutex_unlock(&qsr->qsr_lock);
> +
> + return ret;
> +}
> +static DEVICE_ATTR_RO(lookups);
> +
> +static ssize_t services_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct qsr_info *svc;
> + int ret = 0;
> +
> + mutex_lock(&qsr->qsr_lock);
> + list_for_each_entry(svc, &qsr->services, list) {
> + ret += sprintf(buf, "service:0x%04x instance:0x%04x node:%04d port:%04d\n",
> + svc->service,
> + svc->instance,
> + svc->server.node,
> + svc->server.port);
> + }
> + mutex_unlock(&qsr->qsr_lock);
> +
> + return ret;
> +}
> +static DEVICE_ATTR_RO(services);
> +
> +static struct attribute *qsr_attrs[] = {
> + &dev_attr_name.attr,
> + &dev_attr_lookups.attr,
> + &dev_attr_services.attr,
> + NULL,
> +};
> +ATTRIBUTE_GROUPS(qsr);
> +
> +/* Interface class infrastructure. */
> +static struct class qsr_class = {
> + .name = QSR_NAME,
> + .dev_groups = qsr_groups,
> +};
> +
> +static int qsr_dev_create(void)
> +{
> + int ret = -ENOMEM;
> +
> + qsr = kzalloc(sizeof(*qsr), GFP_KERNEL);
> + if (!qsr)
> + goto out;
> +
> + INIT_LIST_HEAD(&qsr->lookups);
> + INIT_LIST_HEAD(&qsr->services);
> +
> + INIT_WORK(&qsr->work, qsr_recv_work);
> +
> + qsr->recv_buf_size = sizeof(struct qrtr_ctrl_pkt);
> + qsr->recv_buf = kzalloc(qsr->recv_buf_size, GFP_KERNEL);
> + if (!qsr->recv_buf)
> + goto out_qsr_free;
> +
> + qsr->wq = alloc_workqueue("qsr_wq", WQ_UNBOUND, 1);
> + if (!qsr->wq)
> + goto out_recv_buf_free;
> +
> + device_initialize(&qsr->dev);
> + qsr->dev.devt = MKDEV(qsr_major, 0);
> + qsr->dev.class = &qsr_class;
> + dev_set_drvdata(&qsr->dev, qsr);
> +
> + ret = dev_set_name(&qsr->dev, QSR_NAME);
> + if (ret) {
> + pr_err("device name %s set failed, %d", QSR_NAME, ret);
> + goto out_recv_buf_free;
> + }
> +
> + mutex_init(&qsr->qsr_lock);
> +
> + ret = device_add(&qsr->dev);
> + if (ret)
> + goto out_wq_destroy;
> +
> + return ret;
> +
> +out_wq_destroy:
> + destroy_workqueue(qsr->wq);
> +out_recv_buf_free:
> + kfree(qsr->recv_buf);
> +out_qsr_free:
> + kfree(qsr);
> +out:
> + return ret;
> +}
> +
> +struct qsr_ops qsr_handle_ops = {
> + .new_server = qsr_new_server,
> + .new_lookup = qsr_new_lookup,
> +};
> +
> +static struct socket *qsr_sock_create(void)
> +{
> + struct socket *sock;
> + struct sockaddr addr;
> + int ret;
> +
> + ret = sock_create_kern(&init_net, AF_QIPCRTR, SOCK_DGRAM,
> + PF_QIPCRTR, &sock);
> +
> + if (ret < 0)
> + return ERR_PTR(ret);
> +
> + qsr->sq.sq_family = AF_QIPCRTR;
> + qsr->sq.sq_node = get_qrtr_local_nid();
> + qsr->sq.sq_port = QRTR_PORT_CTRL;
> + qsr->ops = &qsr_handle_ops;
> +
> + if (sock->ops->bind) {
> + memcpy(&addr, &qsr->sq, sizeof(qsr->sq));
> + ret = sock->ops->bind(sock, &addr, sizeof(addr));
> + if (ret) {
> + pr_err("Failed to bind socket address node:0x%x port:0x%x.\n",
> + qsr->sq.sq_node, qsr->sq.sq_port);
> + goto err_bind;
> + }
> + pr_debug("qsr router port binded successfully.\n");
> + }
> +
> + sock->sk->sk_user_data = qsr;
> + sock->sk->sk_data_ready = qsr_data_ready;
> + sock->sk->sk_error_report = qsr_data_ready;
> + sock->sk->sk_sndtimeo = HZ * 10;
> +
> + return sock;
> +
> +err_bind:
> + sock_release(sock);
> + return NULL;
> +}
> +
> +static int qsr_release(void)
> +{
> + sock_release(qsr->sk);
> +
> + kfree(qsr->recv_buf);
> +
> + destroy_workqueue(qsr->wq);
> +
> + kfree(qsr);
> +
> + return 0;
> +}
> +
> +static const struct file_operations qsr_fops = {
> + .owner = THIS_MODULE,
> +};
> +
> +static int __init qsr_init(void)
> +{
> + int ret;
> + static const char name[] = QSR_NAME;
> + struct cdev *cdev = NULL;
> + dev_t rtdev;
> +
> + /* 1. Allocate character device region. */
> + ret = alloc_chrdev_region(&rtdev, 0, 1, name);
> + if (ret) {
> + pr_err("failed to alloc chardev region\n");
> + goto out;
> + }
> +
> + /* 2. Allocate, initiate and add cdev. */
> + ret = -ENOMEM;
> + cdev = cdev_alloc();
> + if (!cdev) {
> + pr_err("failed to alloc cdev\n");
> + goto out_unregister;
> + }
> +
> + cdev->owner = THIS_MODULE;
> + cdev->ops = &qsr_fops;
> + kobject_set_name(&cdev->kobj, "%s", name);
> +
> + ret = cdev_add(cdev, rtdev, 1);
> + if (ret)
> + goto out_put;
> +
> + qsr_major = MAJOR(rtdev);
> + qsr_cdev = cdev;
> +
> + /* 3. Register class. */
> + ret = class_register(&qsr_class);
> + if (ret) {
> + pr_err("class_register failed for qrtr route\n");
> + goto out_cdev_del;
> + }
> +
> + /* 4. Create a qsr device. */
> + if (qsr_dev_create())
> + goto out_unregister_class;
> +
> + /* 5. Create a qrtr socket and bind it to Router port. */
> + qsr->sk = qsr_sock_create();
> + if (!qsr->sk)
> + goto out_qsr_dev_del;
> +
> + return 0;
> +
> +out_qsr_dev_del:
> + kfree(qsr);
> +out_unregister_class:
> + class_unregister(&qsr_class);
> +out_cdev_del:
> + cdev_del(qsr_cdev);
> +out_put:
> + kobject_put(&cdev->kobj);
> +out_unregister:
> + unregister_chrdev_region(rtdev, 1);
> +out:
> + return ret;
> +}
> +subsys_initcall(qsr_init);
> +
> +static void __exit qsr_exit(void)
> +{
> + qsr_release();
> + unregister_chrdev_region(MKDEV(qsr_major, 0), 1);
> + cdev_del(qsr_cdev);
> + class_unregister(&qsr_class);
> +}
> +module_exit(qsr_exit);
> +
> +MODULE_AUTHOR("Wang Wenhu");
> +MODULE_ALIAS("QRTR:" QSR_NAME);
> +MODULE_DESCRIPTION("Qualcomm IPC Router Service Route Support");
> +MODULE_LICENSE("GPL v2");
> --
> 2.17.1
>