Messages in this thread | | | Date | Thu, 9 Apr 2020 12:38:07 -0700 | From | Bjorn Andersson <> | Subject | Re: [PATCH RESEND] net: qrtr: support qrtr service and lookup route |
| |
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@vivo.com>
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@vivo.com> > + * > + * 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 >
| |