2020-02-20 18:13:26 +03: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"
2020-04-21 10:40:54 +03:00
# define CREATE_TRACE_POINTS
# include <trace/events/qrtr.h>
2020-02-20 18:13:26 +03:00
static RADIX_TREE ( nodes , GFP_KERNEL ) ;
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 ;
struct radix_tree_root servers ;
} ;
static struct qrtr_node * node_get ( unsigned int node_id )
{
struct qrtr_node * node ;
node = radix_tree_lookup ( & nodes , node_id ) ;
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 17:51:53 +03:00
return NULL ;
2020-02-20 18:13:26 +03:00
node - > id = node_id ;
radix_tree_insert ( & nodes , node_id , node ) ;
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 10:40:54 +03:00
trace_qrtr_ns_service_announce_new ( srv - > service , srv - > instance ,
srv - > node , srv - > port ) ;
2020-02-20 18:13:26 +03: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 ) ) ;
}
static int service_announce_del ( struct sockaddr_qrtr * dest ,
struct qrtr_server * srv )
{
struct qrtr_ctrl_pkt pkt ;
struct msghdr msg = { } ;
struct kvec iv ;
int ret ;
2020-04-21 10:40:54 +03:00
trace_qrtr_ns_service_announce_del ( srv - > service , srv - > instance ,
srv - > node , srv - > port ) ;
2020-02-20 18:13:26 +03: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 ) ) ;
if ( ret < 0 )
2020-02-24 20:35:53 +03:00
pr_err ( " failed to announce del service \n " ) ;
2020-02-20 18:13:26 +03:00
return ret ;
}
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 ) ) ;
if ( ret < 0 )
pr_err ( " failed to send lookup notification \n " ) ;
}
static int announce_servers ( struct sockaddr_qrtr * sq )
{
struct radix_tree_iter iter ;
struct qrtr_server * srv ;
struct qrtr_node * node ;
void __rcu * * slot ;
2020-10-05 10:16:42 +03:00
int ret ;
2020-02-20 18:13:26 +03:00
node = node_get ( qrtr_ns . local_node ) ;
if ( ! node )
return 0 ;
2020-09-26 19:56:25 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
/* Announce the list of servers registered in this node */
radix_tree_for_each_slot ( slot , & node - > servers , & iter , 0 ) {
srv = radix_tree_deref_slot ( slot ) ;
2020-10-05 10:16:42 +03:00
if ( ! srv )
continue ;
if ( radix_tree_deref_retry ( srv ) ) {
slot = radix_tree_iter_retry ( & iter ) ;
continue ;
}
slot = radix_tree_iter_resume ( slot , & iter ) ;
rcu_read_unlock ( ) ;
2020-02-20 18:13:26 +03:00
ret = service_announce_new ( sq , srv ) ;
if ( ret < 0 ) {
pr_err ( " failed to announce new service \n " ) ;
2020-10-05 10:16:42 +03:00
return ret ;
2020-02-20 18:13:26 +03:00
}
2020-10-05 10:16:42 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
}
2020-09-26 19:56:25 +03:00
rcu_read_unlock ( ) ;
2020-10-05 10:16:42 +03:00
return 0 ;
2020-02-20 18:13:26 +03: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 17:51:53 +03:00
return NULL ;
2020-02-20 18:13:26 +03: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 */
old = radix_tree_lookup ( & node - > servers , port ) ;
if ( old ) {
radix_tree_delete ( & node - > servers , port ) ;
kfree ( old ) ;
}
radix_tree_insert ( & node - > servers , port , srv ) ;
2020-04-21 10:40:54 +03:00
trace_qrtr_ns_server_add ( srv - > service , srv - > instance ,
srv - > node , srv - > port ) ;
2020-02-20 18:13:26 +03:00
return srv ;
err :
kfree ( srv ) ;
return NULL ;
}
static int server_del ( struct qrtr_node * node , unsigned int port )
{
struct qrtr_lookup * lookup ;
struct qrtr_server * srv ;
struct list_head * li ;
srv = radix_tree_lookup ( & node - > servers , port ) ;
if ( ! srv )
return - ENOENT ;
radix_tree_delete ( & node - > servers , port ) ;
/* Broadcast the removal of local servers */
if ( srv - > node = = qrtr_ns . local_node )
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 10:03:04 +03: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 18:13:26 +03:00
/* Announce the list of servers registered on the local node */
static int ctrl_cmd_hello ( struct sockaddr_qrtr * sq )
{
2020-03-02 10:03:04 +03:00
int ret ;
ret = say_hello ( sq ) ;
if ( ret < 0 )
return ret ;
2020-02-20 18:13:26 +03:00
return announce_servers ( sq ) ;
}
static int ctrl_cmd_bye ( struct sockaddr_qrtr * from )
{
struct qrtr_node * local_node ;
struct radix_tree_iter iter ;
struct qrtr_ctrl_pkt pkt ;
struct qrtr_server * srv ;
struct sockaddr_qrtr sq ;
struct msghdr msg = { } ;
struct qrtr_node * node ;
void __rcu * * slot ;
struct kvec iv ;
2020-10-05 10:16:42 +03:00
int ret ;
2020-02-20 18:13:26 +03:00
iv . iov_base = & pkt ;
iv . iov_len = sizeof ( pkt ) ;
node = node_get ( from - > sq_node ) ;
if ( ! node )
return 0 ;
2020-09-26 19:56:25 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
/* Advertise removal of this client to all servers of remote node */
radix_tree_for_each_slot ( slot , & node - > servers , & iter , 0 ) {
srv = radix_tree_deref_slot ( slot ) ;
2020-10-05 10:16:42 +03:00
if ( ! srv )
continue ;
if ( radix_tree_deref_retry ( srv ) ) {
slot = radix_tree_iter_retry ( & iter ) ;
continue ;
}
slot = radix_tree_iter_resume ( slot , & iter ) ;
rcu_read_unlock ( ) ;
2020-02-20 18:13:26 +03:00
server_del ( node , srv - > port ) ;
2020-10-05 10:16:42 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
}
2020-09-26 19:56:25 +03:00
rcu_read_unlock ( ) ;
2020-02-20 18:13:26 +03: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 ) ;
2020-09-26 19:56:25 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
radix_tree_for_each_slot ( slot , & local_node - > servers , & iter , 0 ) {
srv = radix_tree_deref_slot ( slot ) ;
2020-10-05 10:16:42 +03:00
if ( ! srv )
continue ;
if ( radix_tree_deref_retry ( srv ) ) {
slot = radix_tree_iter_retry ( & iter ) ;
continue ;
}
slot = radix_tree_iter_resume ( slot , & iter ) ;
rcu_read_unlock ( ) ;
2020-02-20 18:13:26 +03: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 ) ) ;
if ( ret < 0 ) {
pr_err ( " failed to send bye cmd \n " ) ;
2020-10-05 10:16:42 +03:00
return ret ;
2020-02-20 18:13:26 +03:00
}
2020-10-05 10:16:42 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
}
2020-09-26 19:56:25 +03:00
rcu_read_unlock ( ) ;
2020-10-05 10:16:42 +03:00
return 0 ;
2020-02-20 18:13:26 +03:00
}
static int ctrl_cmd_del_client ( struct sockaddr_qrtr * from ,
unsigned int node_id , unsigned int port )
{
struct qrtr_node * local_node ;
struct radix_tree_iter iter ;
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 ;
void __rcu * * slot ;
struct kvec iv ;
2020-10-05 10:16:42 +03:00
int ret ;
2020-02-20 18:13:26 +03: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 ) ;
}
/* Remove the server belonging to this port */
node = node_get ( node_id ) ;
if ( node )
server_del ( node , port ) ;
/* 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 ) ;
2020-09-26 19:56:25 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
radix_tree_for_each_slot ( slot , & local_node - > servers , & iter , 0 ) {
srv = radix_tree_deref_slot ( slot ) ;
2020-10-05 10:16:42 +03:00
if ( ! srv )
continue ;
if ( radix_tree_deref_retry ( srv ) ) {
slot = radix_tree_iter_retry ( & iter ) ;
continue ;
}
slot = radix_tree_iter_resume ( slot , & iter ) ;
rcu_read_unlock ( ) ;
2020-02-20 18:13:26 +03: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 ) ) ;
if ( ret < 0 ) {
pr_err ( " failed to send del client cmd \n " ) ;
2020-10-05 10:16:42 +03:00
return ret ;
2020-02-20 18:13:26 +03:00
}
2020-10-05 10:16:42 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
}
2020-09-26 19:56:25 +03:00
rcu_read_unlock ( ) ;
2020-10-05 10:16:42 +03:00
return 0 ;
2020-02-20 18:13:26 +03: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 ;
return server_del ( node , port ) ;
}
static int ctrl_cmd_new_lookup ( struct sockaddr_qrtr * from ,
unsigned int service , unsigned int instance )
{
struct radix_tree_iter node_iter ;
struct qrtr_server_filter filter ;
struct radix_tree_iter srv_iter ;
struct qrtr_lookup * lookup ;
struct qrtr_node * node ;
void __rcu * * node_slot ;
void __rcu * * srv_slot ;
/* 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 ;
2020-09-26 19:56:25 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
radix_tree_for_each_slot ( node_slot , & nodes , & node_iter , 0 ) {
node = radix_tree_deref_slot ( node_slot ) ;
2020-10-05 10:16:42 +03:00
if ( ! node )
continue ;
if ( radix_tree_deref_retry ( node ) ) {
node_slot = radix_tree_iter_retry ( & node_iter ) ;
continue ;
}
node_slot = radix_tree_iter_resume ( node_slot , & node_iter ) ;
2020-02-20 18:13:26 +03:00
radix_tree_for_each_slot ( srv_slot , & node - > servers ,
& srv_iter , 0 ) {
struct qrtr_server * srv ;
srv = radix_tree_deref_slot ( srv_slot ) ;
2020-10-05 10:16:42 +03:00
if ( ! srv )
continue ;
if ( radix_tree_deref_retry ( srv ) ) {
srv_slot = radix_tree_iter_retry ( & srv_iter ) ;
continue ;
}
2020-02-20 18:13:26 +03:00
if ( ! server_match ( srv , & filter ) )
continue ;
2020-10-05 10:16:42 +03:00
srv_slot = radix_tree_iter_resume ( srv_slot , & srv_iter ) ;
rcu_read_unlock ( ) ;
2020-02-20 18:13:26 +03:00
lookup_notify ( from , srv , true ) ;
2020-10-05 10:16:42 +03:00
rcu_read_lock ( ) ;
2020-02-20 18:13:26 +03:00
}
}
2020-09-26 19:56:25 +03:00
rcu_read_unlock ( ) ;
2020-02-20 18:13:26 +03:00
/* 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 10:40:54 +03:00
trace_qrtr_ns_message ( qrtr_ctrl_pkt_strings [ cmd ] ,
sq . sq_node , sq . sq_port ) ;
2020-02-20 18:13:26 +03: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 )
{
queue_work ( qrtr_ns . workqueue , & qrtr_ns . work ) ;
}
2021-01-05 08:57:54 +03:00
int qrtr_ns_init ( void )
2020-02-20 18:13:26 +03: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 08:57:54 +03:00
return ret ;
2020-02-20 18:13:26 +03:00
ret = kernel_getsockname ( qrtr_ns . sock , ( struct sockaddr * ) & sq ) ;
if ( ret < 0 ) {
pr_err ( " failed to get socket name \n " ) ;
goto err_sock ;
}
2020-05-29 02:05:26 +03:00
qrtr_ns . workqueue = alloc_workqueue ( " qrtr_ns_handler " , WQ_UNBOUND , 1 ) ;
2021-05-19 18:58:52 +03:00
if ( ! qrtr_ns . workqueue ) {
ret = - ENOMEM ;
2020-05-29 02:05:26 +03:00
goto err_sock ;
2021-05-19 18:58:52 +03:00
}
2020-05-29 02:05:26 +03:00
2020-02-20 18:13:26 +03: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-29 02:05:26 +03:00
goto err_wq ;
2020-02-20 18:13:26 +03: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 10:03:04 +03:00
ret = say_hello ( & qrtr_ns . bcast_sq ) ;
2020-02-20 18:13:26 +03:00
if ( ret < 0 )
goto err_wq ;
2021-01-05 08:57:54 +03:00
return 0 ;
2020-02-20 18:13:26 +03:00
err_wq :
destroy_workqueue ( qrtr_ns . workqueue ) ;
err_sock :
sock_release ( qrtr_ns . sock ) ;
2021-01-05 08:57:54 +03:00
return ret ;
2020-02-20 18:13:26 +03:00
}
EXPORT_SYMBOL_GPL ( qrtr_ns_init ) ;
void qrtr_ns_remove ( void )
{
cancel_work_sync ( & qrtr_ns . work ) ;
destroy_workqueue ( qrtr_ns . workqueue ) ;
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 " ) ;