blob: 21baa063d6c136f8f5e15d8fc4a06a8fec4d59e3 [file] [log] [blame]
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define DEBUG
14
15#include <linux/slab.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/string.h>
19#include <linux/errno.h>
20#include <linux/init.h>
21#include <linux/types.h>
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/sched.h>
25#include <linux/poll.h>
26#include <linux/wakelock.h>
27#include <linux/platform_device.h>
28#include <linux/uaccess.h>
29#include <linux/debugfs.h>
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -060030#include <linux/rwsem.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include <asm/uaccess.h>
33#include <asm/byteorder.h>
34
35#include <mach/smem_log.h>
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060036#include <mach/subsystem_notif.h>
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -060037#include <mach/msm_ipc_router.h>
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060038#include <mach/msm_ipc_logging.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
40#include "ipc_router.h"
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060041#include "modem_notifier.h"
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -060042#include "msm_ipc_router_security.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043
44enum {
45 SMEM_LOG = 1U << 0,
46 RTR_DBG = 1U << 1,
47 R2R_MSG = 1U << 2,
48 R2R_RAW = 1U << 3,
49 NTFY_MSG = 1U << 4,
50 R2R_RAW_HDR = 1U << 5,
51};
52
53static int msm_ipc_router_debug_mask;
54module_param_named(debug_mask, msm_ipc_router_debug_mask,
55 int, S_IRUGO | S_IWUSR | S_IWGRP);
56
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060057static void *ipc_rtr_log_ctxt;
58#define IPC_RTR_LOG_PAGES 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#define DIAG(x...) pr_info("[RR] ERROR " x)
60
61#if defined(DEBUG)
62#define D(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060063if (ipc_rtr_log_ctxt) \
64 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065if (msm_ipc_router_debug_mask & RTR_DBG) \
66 pr_info(x); \
67} while (0)
68
69#define RR(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060070if (ipc_rtr_log_ctxt) \
71 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072if (msm_ipc_router_debug_mask & R2R_MSG) \
73 pr_info("[RR] "x); \
74} while (0)
75
76#define RAW(x...) do { \
77if (msm_ipc_router_debug_mask & R2R_RAW) \
78 pr_info("[RAW] "x); \
79} while (0)
80
81#define NTFY(x...) do { \
82if (msm_ipc_router_debug_mask & NTFY_MSG) \
83 pr_info("[NOTIFY] "x); \
84} while (0)
85
86#define RAW_HDR(x...) do { \
87if (msm_ipc_router_debug_mask & R2R_RAW_HDR) \
88 pr_info("[HDR] "x); \
89} while (0)
90#else
91#define D(x...) do { } while (0)
92#define RR(x...) do { } while (0)
93#define RAW(x...) do { } while (0)
94#define RAW_HDR(x...) do { } while (0)
95#define NTFY(x...) do { } while (0)
96#endif
97
98#define IPC_ROUTER_LOG_EVENT_ERROR 0x10
99#define IPC_ROUTER_LOG_EVENT_TX 0x11
100#define IPC_ROUTER_LOG_EVENT_RX 0x12
101
102static LIST_HEAD(control_ports);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600103static DECLARE_RWSEM(control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104
105#define LP_HASH_SIZE 32
106static struct list_head local_ports[LP_HASH_SIZE];
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600107static DECLARE_RWSEM(local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600109/*
110 * Server info is organized as a hash table. The server's service ID is
111 * used to index into the hash table. The instance ID of most of the servers
112 * are 1 or 2. The service IDs are well distributed compared to the instance
113 * IDs and hence choosing service ID to index into this hash table optimizes
114 * the hash table operations like add, lookup, destroy.
115 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116#define SRV_HASH_SIZE 32
117static struct list_head server_list[SRV_HASH_SIZE];
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600118static DECLARE_RWSEM(server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119
120struct msm_ipc_server {
121 struct list_head list;
122 struct msm_ipc_port_name name;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600123 int synced_sec_rule;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600124 char pdev_name[32];
125 int next_pdev_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126 struct list_head server_port_list;
127};
128
129struct msm_ipc_server_port {
130 struct list_head list;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600131 struct platform_device pdev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 struct msm_ipc_port_addr server_addr;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600133 struct msm_ipc_router_xprt_info *xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134};
135
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530136struct msm_ipc_resume_tx_port {
137 struct list_head list;
138 uint32_t port_id;
139 uint32_t node_id;
140};
141
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142#define RP_HASH_SIZE 32
143struct msm_ipc_router_remote_port {
144 struct list_head list;
145 uint32_t node_id;
146 uint32_t port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147 uint32_t tx_quota_cnt;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600148 struct mutex quota_lock_lhb2;
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530149 struct list_head resume_tx_port_list;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600150 void *sec_rule;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600151 struct msm_ipc_server *server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152};
153
154struct msm_ipc_router_xprt_info {
155 struct list_head list;
156 struct msm_ipc_router_xprt *xprt;
157 uint32_t remote_node_id;
158 uint32_t initialized;
159 struct list_head pkt_list;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 struct wake_lock wakelock;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600161 struct mutex rx_lock_lhb2;
162 struct mutex tx_lock_lhb2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 uint32_t need_len;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600164 uint32_t abort_data_read;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165 struct work_struct read_data;
166 struct workqueue_struct *workqueue;
167};
168
169#define RT_HASH_SIZE 4
170struct msm_ipc_routing_table_entry {
171 struct list_head list;
172 uint32_t node_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600173 uint32_t neighbor_node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174 struct list_head remote_port_list[RP_HASH_SIZE];
175 struct msm_ipc_router_xprt_info *xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600176 struct rw_semaphore lock_lha4;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700177 unsigned long num_tx_bytes;
178 unsigned long num_rx_bytes;
179};
180
181static struct list_head routing_table[RT_HASH_SIZE];
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600182static DECLARE_RWSEM(routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183static int routing_table_inited;
184
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185static void do_read_data(struct work_struct *work);
186
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187static LIST_HEAD(xprt_info_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600188static DECLARE_RWSEM(xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -0600190static DECLARE_COMPLETION(msm_ipc_local_router_up);
191#define IPC_ROUTER_INIT_TIMEOUT (10 * HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192
193static uint32_t next_port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600194static DEFINE_MUTEX(next_port_id_lock_lha1);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600195static struct workqueue_struct *msm_ipc_router_workqueue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196
197enum {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 DOWN,
199 UP,
200};
201
202static void init_routing_table(void)
203{
204 int i;
205 for (i = 0; i < RT_HASH_SIZE; i++)
206 INIT_LIST_HEAD(&routing_table[i]);
207}
208
209static struct msm_ipc_routing_table_entry *alloc_routing_table_entry(
210 uint32_t node_id)
211{
212 int i;
213 struct msm_ipc_routing_table_entry *rt_entry;
214
215 rt_entry = kmalloc(sizeof(struct msm_ipc_routing_table_entry),
216 GFP_KERNEL);
217 if (!rt_entry) {
218 pr_err("%s: rt_entry allocation failed for %d\n",
219 __func__, node_id);
220 return NULL;
221 }
222
223 for (i = 0; i < RP_HASH_SIZE; i++)
224 INIT_LIST_HEAD(&rt_entry->remote_port_list[i]);
225
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600226 init_rwsem(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 rt_entry->node_id = node_id;
228 rt_entry->xprt_info = NULL;
229 return rt_entry;
230}
231
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600232/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233static int add_routing_table_entry(
234 struct msm_ipc_routing_table_entry *rt_entry)
235{
236 uint32_t key;
237
238 if (!rt_entry)
239 return -EINVAL;
240
241 key = (rt_entry->node_id % RT_HASH_SIZE);
242 list_add_tail(&rt_entry->list, &routing_table[key]);
243 return 0;
244}
245
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600246/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247static struct msm_ipc_routing_table_entry *lookup_routing_table(
248 uint32_t node_id)
249{
250 uint32_t key = (node_id % RT_HASH_SIZE);
251 struct msm_ipc_routing_table_entry *rt_entry;
252
253 list_for_each_entry(rt_entry, &routing_table[key], list) {
254 if (rt_entry->node_id == node_id)
255 return rt_entry;
256 }
257 return NULL;
258}
259
260struct rr_packet *rr_read(struct msm_ipc_router_xprt_info *xprt_info)
261{
262 struct rr_packet *temp_pkt;
263
264 if (!xprt_info)
265 return NULL;
266
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600267 mutex_lock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600268 if (xprt_info->abort_data_read) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600269 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -0600270 pr_err("%s detected SSR & exiting now\n",
271 xprt_info->xprt->name);
272 return NULL;
273 }
274
275 if (list_empty(&xprt_info->pkt_list)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600276 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600277 return NULL;
278 }
279
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 temp_pkt = list_first_entry(&xprt_info->pkt_list,
281 struct rr_packet, list);
282 list_del(&temp_pkt->list);
283 if (list_empty(&xprt_info->pkt_list))
284 wake_unlock(&xprt_info->wakelock);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600285 mutex_unlock(&xprt_info->rx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286 return temp_pkt;
287}
288
289struct rr_packet *clone_pkt(struct rr_packet *pkt)
290{
291 struct rr_packet *cloned_pkt;
292 struct sk_buff *temp_skb, *cloned_skb;
293 struct sk_buff_head *pkt_fragment_q;
294
295 cloned_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
296 if (!cloned_pkt) {
297 pr_err("%s: failure\n", __func__);
298 return NULL;
299 }
300
301 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
302 if (!pkt_fragment_q) {
303 pr_err("%s: pkt_frag_q alloc failure\n", __func__);
304 kfree(cloned_pkt);
305 return NULL;
306 }
307 skb_queue_head_init(pkt_fragment_q);
308
309 skb_queue_walk(pkt->pkt_fragment_q, temp_skb) {
310 cloned_skb = skb_clone(temp_skb, GFP_KERNEL);
311 if (!cloned_skb)
312 goto fail_clone;
313 skb_queue_tail(pkt_fragment_q, cloned_skb);
314 }
315 cloned_pkt->pkt_fragment_q = pkt_fragment_q;
316 cloned_pkt->length = pkt->length;
317 return cloned_pkt;
318
319fail_clone:
320 while (!skb_queue_empty(pkt_fragment_q)) {
321 temp_skb = skb_dequeue(pkt_fragment_q);
322 kfree_skb(temp_skb);
323 }
324 kfree(pkt_fragment_q);
325 kfree(cloned_pkt);
326 return NULL;
327}
328
329struct rr_packet *create_pkt(struct sk_buff_head *data)
330{
331 struct rr_packet *pkt;
332 struct sk_buff *temp_skb;
333
334 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
335 if (!pkt) {
336 pr_err("%s: failure\n", __func__);
337 return NULL;
338 }
339
340 pkt->pkt_fragment_q = data;
341 skb_queue_walk(pkt->pkt_fragment_q, temp_skb)
342 pkt->length += temp_skb->len;
343 return pkt;
344}
345
346void release_pkt(struct rr_packet *pkt)
347{
348 struct sk_buff *temp_skb;
349
350 if (!pkt)
351 return;
352
353 if (!pkt->pkt_fragment_q) {
354 kfree(pkt);
355 return;
356 }
357
358 while (!skb_queue_empty(pkt->pkt_fragment_q)) {
359 temp_skb = skb_dequeue(pkt->pkt_fragment_q);
360 kfree_skb(temp_skb);
361 }
362 kfree(pkt->pkt_fragment_q);
363 kfree(pkt);
364 return;
365}
366
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600367static struct sk_buff_head *msm_ipc_router_buf_to_skb(void *buf,
368 unsigned int buf_len)
369{
370 struct sk_buff_head *skb_head;
371 struct sk_buff *skb;
372 int first = 1, offset = 0;
373 int skb_size, data_size;
374 void *data;
375
376 skb_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
377 if (!skb_head) {
378 pr_err("%s: Couldnot allocate skb_head\n", __func__);
379 return NULL;
380 }
381 skb_queue_head_init(skb_head);
382
383 data_size = buf_len;
384 while (offset != buf_len) {
385 skb_size = data_size;
386 if (first)
387 skb_size += IPC_ROUTER_HDR_SIZE;
388
389 skb = alloc_skb(skb_size, GFP_KERNEL);
390 if (!skb) {
391 if (skb_size <= (PAGE_SIZE/2)) {
392 pr_err("%s: cannot allocate skb\n", __func__);
393 goto buf_to_skb_error;
394 }
395 data_size = data_size / 2;
396 continue;
397 }
398
399 if (first) {
400 skb_reserve(skb, IPC_ROUTER_HDR_SIZE);
401 first = 0;
402 }
403
404 data = skb_put(skb, data_size);
405 memcpy(skb->data, buf + offset, data_size);
406 skb_queue_tail(skb_head, skb);
407 offset += data_size;
408 data_size = buf_len - offset;
409 }
410 return skb_head;
411
412buf_to_skb_error:
413 while (!skb_queue_empty(skb_head)) {
414 skb = skb_dequeue(skb_head);
415 kfree_skb(skb);
416 }
417 kfree(skb_head);
418 return NULL;
419}
420
421static void *msm_ipc_router_skb_to_buf(struct sk_buff_head *skb_head,
422 unsigned int len)
423{
424 struct sk_buff *temp;
425 int offset = 0, buf_len = 0, copy_len;
426 void *buf;
427
428 if (!skb_head) {
429 pr_err("%s: NULL skb_head\n", __func__);
430 return NULL;
431 }
432
433 temp = skb_peek(skb_head);
434 buf_len = len;
435 buf = kmalloc(buf_len, GFP_KERNEL);
436 if (!buf) {
437 pr_err("%s: cannot allocate buf\n", __func__);
438 return NULL;
439 }
440 skb_queue_walk(skb_head, temp) {
441 copy_len = buf_len < temp->len ? buf_len : temp->len;
442 memcpy(buf + offset, temp->data, copy_len);
443 offset += copy_len;
444 buf_len -= copy_len;
445 }
446 return buf;
447}
448
449static void msm_ipc_router_free_skb(struct sk_buff_head *skb_head)
450{
451 struct sk_buff *temp_skb;
452
453 if (!skb_head)
454 return;
455
456 while (!skb_queue_empty(skb_head)) {
457 temp_skb = skb_dequeue(skb_head);
458 kfree_skb(temp_skb);
459 }
460 kfree(skb_head);
461}
462
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600463static int post_pkt_to_port(struct msm_ipc_port *port_ptr,
464 struct rr_packet *pkt, int clone)
465{
466 struct rr_packet *temp_pkt = pkt;
467
468 if (unlikely(!port_ptr || !pkt))
469 return -EINVAL;
470
471 if (clone) {
472 temp_pkt = clone_pkt(pkt);
473 if (!temp_pkt) {
474 pr_err("%s: Error cloning packet for port %08x:%08x\n",
475 __func__, port_ptr->this_port.node_id,
476 port_ptr->this_port.port_id);
477 return -ENOMEM;
478 }
479 }
480
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600481 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600482 wake_lock(&port_ptr->port_rx_wake_lock);
483 list_add_tail(&temp_pkt->list, &port_ptr->port_rx_q);
484 wake_up(&port_ptr->port_rx_wait_q);
485 if (port_ptr->notify)
486 port_ptr->notify(MSM_IPC_ROUTER_READ_CB, port_ptr->priv);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600487 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600488 return 0;
489}
490
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491static int post_control_ports(struct rr_packet *pkt)
492{
493 struct msm_ipc_port *port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494
495 if (!pkt)
496 return -EINVAL;
497
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600498 down_read(&control_ports_lock_lha5);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600499 list_for_each_entry(port_ptr, &control_ports, list)
500 post_pkt_to_port(port_ptr, pkt, 1);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600501 up_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502 return 0;
503}
504
505static uint32_t allocate_port_id(void)
506{
507 uint32_t port_id = 0, prev_port_id, key;
508 struct msm_ipc_port *port_ptr;
509
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600510 mutex_lock(&next_port_id_lock_lha1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700511 prev_port_id = next_port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600512 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 do {
514 next_port_id++;
515 if ((next_port_id & 0xFFFFFFFE) == 0xFFFFFFFE)
516 next_port_id = 1;
517
518 key = (next_port_id & (LP_HASH_SIZE - 1));
519 if (list_empty(&local_ports[key])) {
520 port_id = next_port_id;
521 break;
522 }
523 list_for_each_entry(port_ptr, &local_ports[key], list) {
524 if (port_ptr->this_port.port_id == next_port_id) {
525 port_id = next_port_id;
526 break;
527 }
528 }
529 if (!port_id) {
530 port_id = next_port_id;
531 break;
532 }
533 port_id = 0;
534 } while (next_port_id != prev_port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600535 up_read(&local_ports_lock_lha2);
536 mutex_unlock(&next_port_id_lock_lha1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537
538 return port_id;
539}
540
541void msm_ipc_router_add_local_port(struct msm_ipc_port *port_ptr)
542{
543 uint32_t key;
544
545 if (!port_ptr)
546 return;
547
548 key = (port_ptr->this_port.port_id & (LP_HASH_SIZE - 1));
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600549 down_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550 list_add_tail(&port_ptr->list, &local_ports[key]);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600551 up_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552}
553
554struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600555 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 void *priv)
557{
558 struct msm_ipc_port *port_ptr;
559
560 port_ptr = kzalloc(sizeof(struct msm_ipc_port), GFP_KERNEL);
561 if (!port_ptr)
562 return NULL;
563
564 port_ptr->this_port.node_id = IPC_ROUTER_NID_LOCAL;
565 port_ptr->this_port.port_id = allocate_port_id();
566 if (!port_ptr->this_port.port_id) {
567 pr_err("%s: All port ids are in use\n", __func__);
568 kfree(port_ptr);
569 return NULL;
570 }
571
572 spin_lock_init(&port_ptr->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573 INIT_LIST_HEAD(&port_ptr->port_rx_q);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600574 mutex_init(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575 init_waitqueue_head(&port_ptr->port_rx_wait_q);
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600576 snprintf(port_ptr->rx_wakelock_name, MAX_WAKELOCK_NAME_SZ,
Karthikeyan Ramasubramanian090486e2013-02-14 13:53:20 -0700577 "ipc%08x_%s",
578 port_ptr->this_port.port_id,
579 current->comm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580 wake_lock_init(&port_ptr->port_rx_wake_lock,
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600581 WAKE_LOCK_SUSPEND, port_ptr->rx_wakelock_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700582
583 port_ptr->endpoint = endpoint;
584 port_ptr->notify = notify;
585 port_ptr->priv = priv;
586
587 msm_ipc_router_add_local_port(port_ptr);
588 return port_ptr;
589}
590
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600591/* Must be called with local_ports_lock_lha2 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592static struct msm_ipc_port *msm_ipc_router_lookup_local_port(uint32_t port_id)
593{
594 int key = (port_id & (LP_HASH_SIZE - 1));
595 struct msm_ipc_port *port_ptr;
596
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597 list_for_each_entry(port_ptr, &local_ports[key], list) {
598 if (port_ptr->this_port.port_id == port_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599 return port_ptr;
600 }
601 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700602 return NULL;
603}
604
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600605/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606static struct msm_ipc_router_remote_port *msm_ipc_router_lookup_remote_port(
607 uint32_t node_id,
608 uint32_t port_id)
609{
610 struct msm_ipc_router_remote_port *rport_ptr;
611 struct msm_ipc_routing_table_entry *rt_entry;
612 int key = (port_id & (RP_HASH_SIZE - 1));
613
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700614 rt_entry = lookup_routing_table(node_id);
615 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700616 pr_err("%s: Node is not up\n", __func__);
617 return NULL;
618 }
619
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600620 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700621 list_for_each_entry(rport_ptr,
622 &rt_entry->remote_port_list[key], list) {
623 if (rport_ptr->port_id == port_id) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600624 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625 return rport_ptr;
626 }
627 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600628 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700629 return NULL;
630}
631
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600632/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633static struct msm_ipc_router_remote_port *msm_ipc_router_create_remote_port(
634 uint32_t node_id,
635 uint32_t port_id)
636{
637 struct msm_ipc_router_remote_port *rport_ptr;
638 struct msm_ipc_routing_table_entry *rt_entry;
639 int key = (port_id & (RP_HASH_SIZE - 1));
640
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700641 rt_entry = lookup_routing_table(node_id);
642 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700643 pr_err("%s: Node is not up\n", __func__);
644 return NULL;
645 }
646
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647 rport_ptr = kmalloc(sizeof(struct msm_ipc_router_remote_port),
648 GFP_KERNEL);
649 if (!rport_ptr) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700650 pr_err("%s: Remote port alloc failed\n", __func__);
651 return NULL;
652 }
653 rport_ptr->port_id = port_id;
654 rport_ptr->node_id = node_id;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600655 rport_ptr->sec_rule = NULL;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600656 rport_ptr->server = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657 rport_ptr->tx_quota_cnt = 0;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600658 mutex_init(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530659 INIT_LIST_HEAD(&rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600660 down_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700661 list_add_tail(&rport_ptr->list,
662 &rt_entry->remote_port_list[key]);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600663 up_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664 return rport_ptr;
665}
666
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530667/**
668 * msm_ipc_router_free_resume_tx_port() - Free the resume_tx ports
669 * @rport_ptr: Pointer to the remote port.
670 *
671 * This function deletes all the resume_tx ports associated with a remote port
672 * and frees the memory allocated to each resume_tx port.
673 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600674 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530675 */
676static void msm_ipc_router_free_resume_tx_port(
677 struct msm_ipc_router_remote_port *rport_ptr)
678{
679 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
680
681 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
682 &rport_ptr->resume_tx_port_list, list) {
683 list_del(&rtx_port->list);
684 kfree(rtx_port);
685 }
686}
687
688/**
689 * msm_ipc_router_lookup_resume_tx_port() - Lookup resume_tx port list
690 * @rport_ptr: Remote port whose resume_tx port list needs to be looked.
691 * @port_id: Port ID which needs to be looked from the list.
692 *
693 * return 1 if the port_id is found in the list, else 0.
694 *
695 * This function is used to lookup the existence of a local port in
696 * remote port's resume_tx list. This function is used to ensure that
697 * the same port is not added to the remote_port's resume_tx list repeatedly.
698 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600699 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530700 */
701static int msm_ipc_router_lookup_resume_tx_port(
702 struct msm_ipc_router_remote_port *rport_ptr, uint32_t port_id)
703{
704 struct msm_ipc_resume_tx_port *rtx_port;
705
706 list_for_each_entry(rtx_port, &rport_ptr->resume_tx_port_list, list) {
707 if (port_id == rtx_port->port_id)
708 return 1;
709 }
710 return 0;
711}
712
713/**
714 * post_resume_tx() - Post the resume_tx event
715 * @rport_ptr: Pointer to the remote port
716 * @pkt : The data packet that is received on a resume_tx event
717 *
718 * This function informs about the reception of the resume_tx message from a
719 * remote port pointed by rport_ptr to all the local ports that are in the
720 * resume_tx_ports_list of this remote port. On posting the information, this
721 * function sequentially deletes each entry in the resume_tx_port_list of the
722 * remote port.
723 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600724 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530725 */
726static void post_resume_tx(struct msm_ipc_router_remote_port *rport_ptr,
727 struct rr_packet *pkt)
728{
729 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
730 struct msm_ipc_port *local_port;
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530731
732 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
733 &rport_ptr->resume_tx_port_list, list) {
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530734 local_port =
735 msm_ipc_router_lookup_local_port(rtx_port->port_id);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600736 if (local_port)
737 post_pkt_to_port(local_port, pkt, 1);
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530738 list_del(&rtx_port->list);
739 kfree(rtx_port);
740 }
741}
742
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600743/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744static void msm_ipc_router_destroy_remote_port(
745 struct msm_ipc_router_remote_port *rport_ptr)
746{
747 uint32_t node_id;
748 struct msm_ipc_routing_table_entry *rt_entry;
749
750 if (!rport_ptr)
751 return;
752
753 node_id = rport_ptr->node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700754 rt_entry = lookup_routing_table(node_id);
755 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756 pr_err("%s: Node %d is not up\n", __func__, node_id);
757 return;
758 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600759 down_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700760 list_del(&rport_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600761 up_write(&rt_entry->lock_lha4);
762 mutex_lock(&rport_ptr->quota_lock_lhb2);
763 msm_ipc_router_free_resume_tx_port(rport_ptr);
764 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765 kfree(rport_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700766 return;
767}
768
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600769/**
770 * msm_ipc_router_lookup_server() - Lookup server information
771 * @service: Service ID of the server info to be looked up.
772 * @instance: Instance ID of the server info to be looked up.
773 * @node_id: Node/Processor ID in which the server is hosted.
774 * @port_id: Port ID within the node in which the server is hosted.
775 *
776 * @return: If found Pointer to server structure, else NULL.
777 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600778 * Note1: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600779 * Note2: If the <node_id:port_id> are <0:0>, then the lookup is restricted
780 * to <service:instance>. Used only when a client wants to send a
781 * message to any QMI server.
782 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700783static struct msm_ipc_server *msm_ipc_router_lookup_server(
784 uint32_t service,
785 uint32_t instance,
786 uint32_t node_id,
787 uint32_t port_id)
788{
789 struct msm_ipc_server *server;
790 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600791 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700792
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700793 list_for_each_entry(server, &server_list[key], list) {
794 if ((server->name.service != service) ||
795 (server->name.instance != instance))
796 continue;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600797 if ((node_id == 0) && (port_id == 0))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700798 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700799 list_for_each_entry(server_port, &server->server_port_list,
800 list) {
801 if ((server_port->server_addr.node_id == node_id) &&
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600802 (server_port->server_addr.port_id == port_id))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700804 }
805 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700806 return NULL;
807}
808
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600809static void dummy_release(struct device *dev)
810{
811}
812
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600813/**
814 * msm_ipc_router_create_server() - Add server info to hash table
815 * @service: Service ID of the server info to be created.
816 * @instance: Instance ID of the server info to be created.
817 * @node_id: Node/Processor ID in which the server is hosted.
818 * @port_id: Port ID within the node in which the server is hosted.
819 * @xprt_info: XPRT through which the node hosting the server is reached.
820 *
821 * @return: Pointer to server structure on success, else NULL.
822 *
823 * This function adds the server info to the hash table. If the same
824 * server(i.e. <service_id:instance_id>) is hosted in different nodes,
825 * they are maintained as list of "server_port" under "server" structure.
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600826 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600827 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700828static struct msm_ipc_server *msm_ipc_router_create_server(
829 uint32_t service,
830 uint32_t instance,
831 uint32_t node_id,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600832 uint32_t port_id,
833 struct msm_ipc_router_xprt_info *xprt_info)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700834{
835 struct msm_ipc_server *server = NULL;
836 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600837 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700839 list_for_each_entry(server, &server_list[key], list) {
840 if ((server->name.service == service) &&
841 (server->name.instance == instance))
842 goto create_srv_port;
843 }
844
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600845 server = kzalloc(sizeof(struct msm_ipc_server), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700846 if (!server) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700847 pr_err("%s: Server allocation failed\n", __func__);
848 return NULL;
849 }
850 server->name.service = service;
851 server->name.instance = instance;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600852 server->synced_sec_rule = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700853 INIT_LIST_HEAD(&server->server_port_list);
854 list_add_tail(&server->list, &server_list[key]);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600855 scnprintf(server->pdev_name, sizeof(server->pdev_name),
856 "QMI%08x:%08x", service, instance);
857 server->next_pdev_id = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700858
859create_srv_port:
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600860 server_port = kzalloc(sizeof(struct msm_ipc_server_port), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861 if (!server_port) {
862 if (list_empty(&server->server_port_list)) {
863 list_del(&server->list);
864 kfree(server);
865 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700866 pr_err("%s: Server Port allocation failed\n", __func__);
867 return NULL;
868 }
869 server_port->server_addr.node_id = node_id;
870 server_port->server_addr.port_id = port_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600871 server_port->xprt_info = xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 list_add_tail(&server_port->list, &server->server_port_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700873
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600874 server_port->pdev.name = server->pdev_name;
875 server_port->pdev.id = server->next_pdev_id++;
876 server_port->pdev.dev.release = dummy_release;
877 platform_device_register(&server_port->pdev);
878
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700879 return server;
880}
881
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600882/**
883 * msm_ipc_router_destroy_server() - Remove server info from hash table
884 * @server: Server info to be removed.
885 * @node_id: Node/Processor ID in which the server is hosted.
886 * @port_id: Port ID within the node in which the server is hosted.
887 *
888 * This function removes the server_port identified using <node_id:port_id>
889 * from the server structure. If the server_port list under server structure
890 * is empty after removal, then remove the server structure from the server
891 * hash table.
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600892 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600893 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700894static void msm_ipc_router_destroy_server(struct msm_ipc_server *server,
895 uint32_t node_id, uint32_t port_id)
896{
897 struct msm_ipc_server_port *server_port;
898
899 if (!server)
900 return;
901
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700902 list_for_each_entry(server_port, &server->server_port_list, list) {
903 if ((server_port->server_addr.node_id == node_id) &&
904 (server_port->server_addr.port_id == port_id))
905 break;
906 }
907 if (server_port) {
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600908 platform_device_unregister(&server_port->pdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700909 list_del(&server_port->list);
910 kfree(server_port);
911 }
912 if (list_empty(&server->server_port_list)) {
913 list_del(&server->list);
914 kfree(server);
915 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700916 return;
917}
918
919static int msm_ipc_router_send_control_msg(
920 struct msm_ipc_router_xprt_info *xprt_info,
921 union rr_control_msg *msg)
922{
923 struct rr_packet *pkt;
924 struct sk_buff *ipc_rtr_pkt;
925 struct rr_header *hdr;
926 int pkt_size;
927 void *data;
928 struct sk_buff_head *pkt_fragment_q;
929 int ret;
930
931 if (!xprt_info || ((msg->cmd != IPC_ROUTER_CTRL_CMD_HELLO) &&
932 !xprt_info->initialized)) {
933 pr_err("%s: xprt_info not initialized\n", __func__);
934 return -EINVAL;
935 }
936
937 if (xprt_info->remote_node_id == IPC_ROUTER_NID_LOCAL)
938 return 0;
939
940 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
941 if (!pkt) {
942 pr_err("%s: pkt alloc failed\n", __func__);
943 return -ENOMEM;
944 }
945
946 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
947 if (!pkt_fragment_q) {
948 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
949 kfree(pkt);
950 return -ENOMEM;
951 }
952 skb_queue_head_init(pkt_fragment_q);
953
954 pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg);
955 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
956 if (!ipc_rtr_pkt) {
957 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
958 kfree(pkt_fragment_q);
959 kfree(pkt);
960 return -ENOMEM;
961 }
962
963 skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
964 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
965 memcpy(data, msg, sizeof(*msg));
966 hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
967 if (!hdr) {
968 pr_err("%s: skb_push failed\n", __func__);
969 kfree_skb(ipc_rtr_pkt);
970 kfree(pkt_fragment_q);
971 kfree(pkt);
972 return -ENOMEM;
973 }
974
975 hdr->version = IPC_ROUTER_VERSION;
976 hdr->type = msg->cmd;
977 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
978 hdr->src_port_id = IPC_ROUTER_ADDRESS;
979 hdr->confirm_rx = 0;
980 hdr->size = sizeof(*msg);
981 hdr->dst_node_id = xprt_info->remote_node_id;
982 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
983 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
984 pkt->pkt_fragment_q = pkt_fragment_q;
985 pkt->length = pkt_size;
986
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600987 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -0700988 ret = xprt_info->xprt->write(pkt, pkt_size, xprt_info->xprt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600989 mutex_unlock(&xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700990
991 release_pkt(pkt);
992 return ret;
993}
994
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -0600995static int msm_ipc_router_send_server_list(uint32_t node_id,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700996 struct msm_ipc_router_xprt_info *xprt_info)
997{
998 union rr_control_msg ctl;
999 struct msm_ipc_server *server;
1000 struct msm_ipc_server_port *server_port;
1001 int i;
1002
1003 if (!xprt_info || !xprt_info->initialized) {
1004 pr_err("%s: Xprt info not initialized\n", __func__);
1005 return -EINVAL;
1006 }
1007
1008 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
1009
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010 for (i = 0; i < SRV_HASH_SIZE; i++) {
1011 list_for_each_entry(server, &server_list[i], list) {
1012 ctl.srv.service = server->name.service;
1013 ctl.srv.instance = server->name.instance;
1014 list_for_each_entry(server_port,
1015 &server->server_port_list, list) {
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001016 if (server_port->server_addr.node_id !=
1017 node_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001018 continue;
1019
1020 ctl.srv.node_id =
1021 server_port->server_addr.node_id;
1022 ctl.srv.port_id =
1023 server_port->server_addr.port_id;
1024 msm_ipc_router_send_control_msg(xprt_info,
1025 &ctl);
1026 }
1027 }
1028 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001029
1030 return 0;
1031}
1032
1033#if defined(DEBUG)
1034static char *type_to_str(int i)
1035{
1036 switch (i) {
1037 case IPC_ROUTER_CTRL_CMD_DATA:
1038 return "data ";
1039 case IPC_ROUTER_CTRL_CMD_HELLO:
1040 return "hello ";
1041 case IPC_ROUTER_CTRL_CMD_BYE:
1042 return "bye ";
1043 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
1044 return "new_srvr";
1045 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
1046 return "rmv_srvr";
1047 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
1048 return "rmv_clnt";
1049 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
1050 return "resum_tx";
1051 case IPC_ROUTER_CTRL_CMD_EXIT:
1052 return "cmd_exit";
1053 default:
1054 return "invalid";
1055 }
1056}
1057#endif
1058
1059static int broadcast_ctl_msg_locally(union rr_control_msg *msg)
1060{
1061 struct rr_packet *pkt;
1062 struct sk_buff *ipc_rtr_pkt;
1063 struct rr_header *hdr;
1064 int pkt_size;
1065 void *data;
1066 struct sk_buff_head *pkt_fragment_q;
1067 int ret;
1068
1069 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
1070 if (!pkt) {
1071 pr_err("%s: pkt alloc failed\n", __func__);
1072 return -ENOMEM;
1073 }
1074
1075 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
1076 if (!pkt_fragment_q) {
1077 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
1078 kfree(pkt);
1079 return -ENOMEM;
1080 }
1081 skb_queue_head_init(pkt_fragment_q);
1082
1083 pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg);
1084 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
1085 if (!ipc_rtr_pkt) {
1086 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
1087 kfree(pkt_fragment_q);
1088 kfree(pkt);
1089 return -ENOMEM;
1090 }
1091
1092 skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
1093 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
1094 memcpy(data, msg, sizeof(*msg));
1095 hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
1096 if (!hdr) {
1097 pr_err("%s: skb_push failed\n", __func__);
1098 kfree_skb(ipc_rtr_pkt);
1099 kfree(pkt_fragment_q);
1100 kfree(pkt);
1101 return -ENOMEM;
1102 }
1103 hdr->version = IPC_ROUTER_VERSION;
1104 hdr->type = msg->cmd;
1105 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
1106 hdr->src_port_id = IPC_ROUTER_ADDRESS;
1107 hdr->confirm_rx = 0;
1108 hdr->size = sizeof(*msg);
1109 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
1110 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
1111 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
1112 pkt->pkt_fragment_q = pkt_fragment_q;
1113 pkt->length = pkt_size;
1114
1115 ret = post_control_ports(pkt);
1116 release_pkt(pkt);
1117 return ret;
1118}
1119
1120static int broadcast_ctl_msg(union rr_control_msg *ctl)
1121{
1122 struct msm_ipc_router_xprt_info *xprt_info;
1123
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001124 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001125 list_for_each_entry(xprt_info, &xprt_info_list, list) {
1126 msm_ipc_router_send_control_msg(xprt_info, ctl);
1127 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001128 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001129
1130 return 0;
1131}
1132
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001133static int relay_ctl_msg(struct msm_ipc_router_xprt_info *xprt_info,
1134 union rr_control_msg *ctl)
1135{
1136 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1137
1138 if (!xprt_info || !ctl)
1139 return -EINVAL;
1140
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001141 down_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001142 list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
1143 if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
1144 msm_ipc_router_send_control_msg(fwd_xprt_info, ctl);
1145 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001146 up_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001147
1148 return 0;
1149}
1150
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001151static int relay_msg(struct msm_ipc_router_xprt_info *xprt_info,
1152 struct rr_packet *pkt)
1153{
1154 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1155
1156 if (!xprt_info || !pkt)
1157 return -EINVAL;
1158
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001159 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001160 list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001161 mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001162 if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07001163 fwd_xprt_info->xprt->write(pkt, pkt->length,
1164 fwd_xprt_info->xprt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001165 mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001166 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001167 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001168 return 0;
1169}
1170
1171static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info,
1172 struct rr_packet *pkt)
1173{
1174 uint32_t dst_node_id;
1175 struct sk_buff *head_pkt;
1176 struct rr_header *hdr;
1177 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1178 struct msm_ipc_routing_table_entry *rt_entry;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001179 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001180
1181 if (!xprt_info || !pkt)
1182 return -EINVAL;
1183
1184 head_pkt = skb_peek(pkt->pkt_fragment_q);
1185 if (!head_pkt)
1186 return -EINVAL;
1187
1188 hdr = (struct rr_header *)head_pkt->data;
1189 dst_node_id = hdr->dst_node_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001190 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001191 rt_entry = lookup_routing_table(dst_node_id);
1192 if (!(rt_entry) || !(rt_entry->xprt_info)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001193 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001194 pr_err("%s: Routing table not initialized\n", __func__);
1195 return -ENODEV;
1196 }
1197
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001198 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001199 fwd_xprt_info = rt_entry->xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001200 if (xprt_info->remote_node_id == fwd_xprt_info->remote_node_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001201 pr_err("%s: Discarding Command to route back\n", __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001202 ret = -EINVAL;
1203 goto fwd_msg_out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001204 }
1205
1206 if (xprt_info->xprt->link_id == fwd_xprt_info->xprt->link_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001207 pr_err("%s: DST in the same cluster\n", __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001208 goto fwd_msg_out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001209 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001210 mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07001211 fwd_xprt_info->xprt->write(pkt, pkt->length, fwd_xprt_info->xprt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001212 mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
1213fwd_msg_out:
1214 up_read(&rt_entry->lock_lha4);
1215 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001216
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001217 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001218}
1219
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001220static int msm_ipc_router_send_remove_client(struct comm_mode_info *mode_info,
1221 uint32_t node_id, uint32_t port_id)
1222{
1223 union rr_control_msg msg;
1224 struct msm_ipc_router_xprt_info *tmp_xprt_info;
1225 int mode;
1226 void *xprt_info;
1227 int rc = 0;
1228
1229 if (!mode_info) {
1230 pr_err("%s: NULL mode_info\n", __func__);
1231 return -EINVAL;
1232 }
1233 mode = mode_info->mode;
1234 xprt_info = mode_info->xprt_info;
1235
1236 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
1237 msg.cli.node_id = node_id;
1238 msg.cli.port_id = port_id;
1239
1240 if ((mode == SINGLE_LINK_MODE) && xprt_info) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001241 down_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001242 list_for_each_entry(tmp_xprt_info, &xprt_info_list, list) {
1243 if (tmp_xprt_info != xprt_info)
1244 continue;
1245 msm_ipc_router_send_control_msg(tmp_xprt_info, &msg);
1246 break;
1247 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001248 up_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001249 } else if ((mode == SINGLE_LINK_MODE) && !xprt_info) {
1250 broadcast_ctl_msg_locally(&msg);
1251 } else if (mode == MULTI_LINK_MODE) {
1252 broadcast_ctl_msg(&msg);
1253 broadcast_ctl_msg_locally(&msg);
1254 } else if (mode != NULL_MODE) {
1255 pr_err("%s: Invalid mode(%d) + xprt_inf(%p) for %08x:%08x\n",
1256 __func__, mode, xprt_info, node_id, port_id);
1257 rc = -EINVAL;
1258 }
1259 return rc;
1260}
1261
1262static void update_comm_mode_info(struct comm_mode_info *mode_info,
1263 struct msm_ipc_router_xprt_info *xprt_info)
1264{
1265 if (!mode_info) {
1266 pr_err("%s: NULL mode_info\n", __func__);
1267 return;
1268 }
1269
1270 if (mode_info->mode == NULL_MODE) {
1271 mode_info->xprt_info = xprt_info;
1272 mode_info->mode = SINGLE_LINK_MODE;
1273 } else if (mode_info->mode == SINGLE_LINK_MODE &&
1274 mode_info->xprt_info != xprt_info) {
1275 mode_info->mode = MULTI_LINK_MODE;
1276 }
1277
1278 return;
1279}
1280
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001281static void cleanup_rmt_server(struct msm_ipc_router_xprt_info *xprt_info,
1282 struct msm_ipc_router_remote_port *rport_ptr)
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001283{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001284 union rr_control_msg ctl;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001285 struct msm_ipc_server *server = rport_ptr->server;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001286
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001287 D("Remove server %08x:%08x - %08x:%08x",
1288 server->name.service, server->name.instance,
1289 rport_ptr->node_id, rport_ptr->port_id);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001290 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001291 ctl.srv.service = server->name.service;
1292 ctl.srv.instance = server->name.instance;
1293 ctl.srv.node_id = rport_ptr->node_id;
1294 ctl.srv.port_id = rport_ptr->port_id;
1295 relay_ctl_msg(xprt_info, &ctl);
1296 broadcast_ctl_msg_locally(&ctl);
1297 msm_ipc_router_destroy_server(server,
1298 rport_ptr->node_id, rport_ptr->port_id);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001299}
1300
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001301static void cleanup_rmt_ports(struct msm_ipc_router_xprt_info *xprt_info,
1302 struct msm_ipc_routing_table_entry *rt_entry)
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001303{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001304 struct msm_ipc_router_remote_port *rport_ptr, *tmp_rport_ptr;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001305 union rr_control_msg ctl;
1306 int j;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001307
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001308 for (j = 0; j < RP_HASH_SIZE; j++) {
1309 list_for_each_entry_safe(rport_ptr, tmp_rport_ptr,
1310 &rt_entry->remote_port_list[j], list) {
1311 list_del(&rport_ptr->list);
1312 mutex_lock(&rport_ptr->quota_lock_lhb2);
1313 msm_ipc_router_free_resume_tx_port(rport_ptr);
1314 mutex_unlock(&rport_ptr->quota_lock_lhb2);
1315
1316 if (rport_ptr->server)
1317 cleanup_rmt_server(xprt_info, rport_ptr);
1318
1319 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
1320 ctl.cli.node_id = rport_ptr->node_id;
1321 ctl.cli.port_id = rport_ptr->port_id;
1322 relay_ctl_msg(xprt_info, &ctl);
1323 broadcast_ctl_msg_locally(&ctl);
1324 kfree(rport_ptr);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001325 }
1326 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001327}
1328
1329static void msm_ipc_cleanup_routing_table(
1330 struct msm_ipc_router_xprt_info *xprt_info)
1331{
1332 int i;
1333 struct msm_ipc_routing_table_entry *rt_entry;
1334
1335 if (!xprt_info) {
1336 pr_err("%s: Invalid xprt_info\n", __func__);
1337 return;
1338 }
1339
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001340 down_write(&server_list_lock_lha2);
1341 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001342 for (i = 0; i < RT_HASH_SIZE; i++) {
1343 list_for_each_entry(rt_entry, &routing_table[i], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001344 down_write(&rt_entry->lock_lha4);
1345 if (rt_entry->xprt_info != xprt_info) {
1346 up_write(&rt_entry->lock_lha4);
1347 continue;
1348 }
1349 cleanup_rmt_ports(xprt_info, rt_entry);
1350 rt_entry->xprt_info = NULL;
1351 up_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001352 }
1353 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001354 up_write(&routing_table_lock_lha3);
1355 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001356}
1357
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001358/**
1359 * sync_sec_rule() - Synchrnoize the security rule into the server structure
1360 * @server: Server structure where the rule has to be synchronized.
1361 * @rule: Security tule to be synchronized.
1362 *
1363 * This function is used to update the server structure with the security
1364 * rule configured for the <service:instance> corresponding to that server.
1365 */
1366static void sync_sec_rule(struct msm_ipc_server *server, void *rule)
1367{
1368 struct msm_ipc_server_port *server_port;
1369 struct msm_ipc_router_remote_port *rport_ptr = NULL;
1370
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001371 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001372 list_for_each_entry(server_port, &server->server_port_list, list) {
1373 rport_ptr = msm_ipc_router_lookup_remote_port(
1374 server_port->server_addr.node_id,
1375 server_port->server_addr.port_id);
1376 if (!rport_ptr)
1377 continue;
1378 rport_ptr->sec_rule = rule;
1379 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001380 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001381 server->synced_sec_rule = 1;
1382}
1383
1384/**
1385 * msm_ipc_sync_sec_rule() - Sync the security rule to the service
1386 * @service: Service for which the rule has to be synchronized.
1387 * @instance: Instance for which the rule has to be synchronized.
1388 * @rule: Security rule to be synchronized.
1389 *
1390 * This function is used to syncrhonize the security rule with the server
1391 * hash table, if the user-space script configures the rule after the service
1392 * has come up. This function is used to synchronize the security rule to a
1393 * specific service and optionally a specific instance.
1394 */
1395void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule)
1396{
1397 int key = (service & (SRV_HASH_SIZE - 1));
1398 struct msm_ipc_server *server;
1399
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001400 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001401 list_for_each_entry(server, &server_list[key], list) {
1402 if (server->name.service != service)
1403 continue;
1404
1405 if (server->name.instance != instance &&
1406 instance != ALL_INSTANCE)
1407 continue;
1408
1409 /*
1410 * If the rule applies to all instances and if the specific
1411 * instance of a service has a rule synchronized already,
1412 * do not apply the rule for that specific instance.
1413 */
1414 if (instance == ALL_INSTANCE && server->synced_sec_rule)
1415 continue;
1416
1417 sync_sec_rule(server, rule);
1418 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001419 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001420}
1421
1422/**
1423 * msm_ipc_sync_default_sec_rule() - Default security rule to all services
1424 * @rule: Security rule to be synchronized.
1425 *
1426 * This function is used to syncrhonize the security rule with the server
1427 * hash table, if the user-space script configures the rule after the service
1428 * has come up. This function is used to synchronize the security rule that
1429 * applies to all services, if the concerned service do not have any rule
1430 * defined.
1431 */
1432void msm_ipc_sync_default_sec_rule(void *rule)
1433{
1434 int key;
1435 struct msm_ipc_server *server;
1436
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001437 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001438 for (key = 0; key < SRV_HASH_SIZE; key++) {
1439 list_for_each_entry(server, &server_list[key], list) {
1440 if (server->synced_sec_rule)
1441 continue;
1442
1443 sync_sec_rule(server, rule);
1444 }
1445 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001446 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001447}
1448
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001449static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
1450 struct rr_header *hdr)
1451{
1452 int i, rc = 0;
1453 union rr_control_msg ctl;
1454 struct msm_ipc_routing_table_entry *rt_entry;
1455
1456 if (!hdr)
1457 return -EINVAL;
1458
1459 RR("o HELLO NID %d\n", hdr->src_node_id);
1460
1461 xprt_info->remote_node_id = hdr->src_node_id;
1462 /*
1463 * Find the entry from Routing Table corresponding to Node ID.
1464 * Under SSR, an entry will be found. When the system boots up
1465 * for the 1st time, an entry will not be found and hence allocate
1466 * an entry. Update the entry with the Node ID that it corresponds
1467 * to and the XPRT through which it can be reached.
1468 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001469 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001470 rt_entry = lookup_routing_table(hdr->src_node_id);
1471 if (!rt_entry) {
1472 rt_entry = alloc_routing_table_entry(hdr->src_node_id);
1473 if (!rt_entry) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001474 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001475 pr_err("%s: rt_entry allocation failed\n", __func__);
1476 return -ENOMEM;
1477 }
1478 add_routing_table_entry(rt_entry);
1479 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001480 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001481 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1482 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001483 up_write(&rt_entry->lock_lha4);
1484 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001485
1486 /* Send a reply HELLO message */
1487 memset(&ctl, 0, sizeof(ctl));
1488 ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
1489 rc = msm_ipc_router_send_control_msg(xprt_info, &ctl);
1490 if (rc < 0) {
1491 pr_err("%s: Error sending reply HELLO message\n", __func__);
1492 return rc;
1493 }
1494 xprt_info->initialized = 1;
1495
1496 /*
1497 * Send list of servers from the local node and from nodes
1498 * outside the mesh network in which this XPRT is part of.
1499 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001500 down_read(&server_list_lock_lha2);
1501 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001502 for (i = 0; i < RT_HASH_SIZE; i++) {
1503 list_for_each_entry(rt_entry, &routing_table[i], list) {
1504 if ((rt_entry->node_id != IPC_ROUTER_NID_LOCAL) &&
Karthikeyan Ramasubramanian72ad5792013-01-30 14:17:57 -07001505 (!rt_entry->xprt_info ||
1506 (rt_entry->xprt_info->xprt->link_id ==
1507 xprt_info->xprt->link_id)))
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001508 continue;
1509 rc = msm_ipc_router_send_server_list(rt_entry->node_id,
1510 xprt_info);
1511 if (rc < 0) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001512 up_read(&routing_table_lock_lha3);
1513 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001514 return rc;
1515 }
1516 }
1517 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001518 up_read(&routing_table_lock_lha3);
1519 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001520 RR("HELLO message processed\n");
1521 return rc;
1522}
1523
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001524static int process_resume_tx_msg(union rr_control_msg *msg,
1525 struct rr_packet *pkt)
1526{
1527 struct msm_ipc_router_remote_port *rport_ptr;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001528 int ret = 0;
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001529
1530 RR("o RESUME_TX id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
1531
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001532 down_read(&local_ports_lock_lha2);
1533 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001534 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
1535 msg->cli.port_id);
1536 if (!rport_ptr) {
1537 pr_err("%s: Unable to resume client\n", __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001538 ret = -ENODEV;
1539 goto prtm_out;
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001540 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001541 mutex_lock(&rport_ptr->quota_lock_lhb2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001542 rport_ptr->tx_quota_cnt = 0;
1543 post_resume_tx(rport_ptr, pkt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001544 mutex_unlock(&rport_ptr->quota_lock_lhb2);
1545prtm_out:
1546 up_read(&routing_table_lock_lha3);
1547 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001548 return 0;
1549}
1550
1551static int process_new_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
1552 union rr_control_msg *msg, struct rr_packet *pkt)
1553{
1554 struct msm_ipc_routing_table_entry *rt_entry;
1555 struct msm_ipc_server *server;
1556 struct msm_ipc_router_remote_port *rport_ptr;
1557
1558 if (msg->srv.instance == 0) {
1559 pr_err("%s: Server %08x create rejected, version = 0\n",
1560 __func__, msg->srv.service);
1561 return -EINVAL;
1562 }
1563
1564 RR("o NEW_SERVER id=%d:%08x service=%08x:%08x\n", msg->srv.node_id,
1565 msg->srv.port_id, msg->srv.service, msg->srv.instance);
1566 /*
1567 * Find the entry from Routing Table corresponding to Node ID.
1568 * Under SSR, an entry will be found. When the subsystem hosting
1569 * service is not adjacent, an entry will not be found and hence
1570 * allocate an entry. Update the entry with the Node ID that it
1571 * corresponds to and the XPRT through which it can be reached.
1572 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001573 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001574 rt_entry = lookup_routing_table(msg->srv.node_id);
1575 if (!rt_entry) {
1576 rt_entry = alloc_routing_table_entry(msg->srv.node_id);
1577 if (!rt_entry) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001578 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001579 pr_err("%s: rt_entry allocation failed\n", __func__);
1580 return -ENOMEM;
1581 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001582 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001583 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1584 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001585 up_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001586 add_routing_table_entry(rt_entry);
1587 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001588 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001589
1590 /*
1591 * If the service does not exist already in the database, create and
1592 * store the service info. Create a remote port structure in which
1593 * the service is hosted and cache the security rule for the service
1594 * in that remote port structure.
1595 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001596 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001597 server = msm_ipc_router_lookup_server(msg->srv.service,
1598 msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
1599 if (!server) {
1600 server = msm_ipc_router_create_server(
1601 msg->srv.service, msg->srv.instance,
1602 msg->srv.node_id, msg->srv.port_id, xprt_info);
1603 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001604 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001605 pr_err("%s: Server Create failed\n", __func__);
1606 return -ENOMEM;
1607 }
1608
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001609 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001610 if (!msm_ipc_router_lookup_remote_port(
1611 msg->srv.node_id, msg->srv.port_id)) {
1612 rport_ptr = msm_ipc_router_create_remote_port(
1613 msg->srv.node_id, msg->srv.port_id);
1614 if (!rport_ptr) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001615 up_read(&routing_table_lock_lha3);
1616 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001617 return -ENOMEM;
1618 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001619 rport_ptr->server = server;
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001620 rport_ptr->sec_rule = msm_ipc_get_security_rule(
1621 msg->srv.service,
1622 msg->srv.instance);
1623 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001624 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001625 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001626 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001627
1628 /*
1629 * Relay the new server message to other subsystems that do not belong
1630 * to the cluster from which this message is received. Notify the
1631 * local clients waiting for this service.
1632 */
1633 relay_msg(xprt_info, pkt);
1634 post_control_ports(pkt);
1635 return 0;
1636}
1637
1638static int process_rmv_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
1639 union rr_control_msg *msg, struct rr_packet *pkt)
1640{
1641 struct msm_ipc_server *server;
1642
1643 RR("o REMOVE_SERVER service=%08x:%d\n",
1644 msg->srv.service, msg->srv.instance);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001645 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001646 server = msm_ipc_router_lookup_server(msg->srv.service,
1647 msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
1648 if (server) {
1649 msm_ipc_router_destroy_server(server, msg->srv.node_id,
1650 msg->srv.port_id);
1651 /*
1652 * Relay the new server message to other subsystems that do not
1653 * belong to the cluster from which this message is received.
1654 * Notify the local clients communicating with the service.
1655 */
1656 relay_msg(xprt_info, pkt);
1657 post_control_ports(pkt);
1658 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001659 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001660 return 0;
1661}
1662
1663static int process_rmv_client_msg(struct msm_ipc_router_xprt_info *xprt_info,
1664 union rr_control_msg *msg, struct rr_packet *pkt)
1665{
1666 struct msm_ipc_router_remote_port *rport_ptr;
1667
1668 RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001669 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001670 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
1671 msg->cli.port_id);
1672 if (rport_ptr)
1673 msm_ipc_router_destroy_remote_port(rport_ptr);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001674 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001675
1676 relay_msg(xprt_info, pkt);
1677 post_control_ports(pkt);
1678 return 0;
1679}
1680
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001681static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info,
1682 struct rr_packet *pkt)
1683{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001684 union rr_control_msg *msg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001685 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001686 struct sk_buff *temp_ptr;
1687 struct rr_header *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001688
1689 if (pkt->length != (IPC_ROUTER_HDR_SIZE + sizeof(*msg))) {
1690 pr_err("%s: r2r msg size %d != %d\n", __func__, pkt->length,
1691 (IPC_ROUTER_HDR_SIZE + sizeof(*msg)));
1692 return -EINVAL;
1693 }
1694
1695 temp_ptr = skb_peek(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001696 if (!temp_ptr) {
1697 pr_err("%s: pkt_fragment_q is empty\n", __func__);
1698 return -EINVAL;
1699 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001700 hdr = (struct rr_header *)temp_ptr->data;
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001701 if (!hdr) {
1702 pr_err("%s: No data inside the skb\n", __func__);
1703 return -EINVAL;
1704 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001705 msg = (union rr_control_msg *)((char *)hdr + IPC_ROUTER_HDR_SIZE);
1706
1707 switch (msg->cmd) {
1708 case IPC_ROUTER_CTRL_CMD_HELLO:
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001709 rc = process_hello_msg(xprt_info, hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001710 break;
1711 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001712 rc = process_resume_tx_msg(msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001713 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001714 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001715 rc = process_new_server_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001716 break;
1717 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001718 rc = process_rmv_server_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001719 break;
1720 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001721 rc = process_rmv_client_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001722 break;
1723 case IPC_ROUTER_CTRL_CMD_PING:
1724 /* No action needed for ping messages received */
1725 RR("o PING\n");
1726 break;
1727 default:
1728 RR("o UNKNOWN(%08x)\n", msg->cmd);
1729 rc = -ENOSYS;
1730 }
1731
1732 return rc;
1733}
1734
1735static void do_read_data(struct work_struct *work)
1736{
1737 struct rr_header *hdr;
1738 struct rr_packet *pkt = NULL;
1739 struct msm_ipc_port *port_ptr;
1740 struct sk_buff *head_skb;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001741 struct msm_ipc_router_remote_port *rport_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001742 uint32_t resume_tx, resume_tx_node_id, resume_tx_port_id;
1743
1744 struct msm_ipc_router_xprt_info *xprt_info =
1745 container_of(work,
1746 struct msm_ipc_router_xprt_info,
1747 read_data);
1748
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001749 while ((pkt = rr_read(xprt_info)) != NULL) {
1750 if (pkt->length < IPC_ROUTER_HDR_SIZE ||
1751 pkt->length > MAX_IPC_PKT_SIZE) {
1752 pr_err("%s: Invalid pkt length %d\n",
1753 __func__, pkt->length);
1754 goto fail_data;
1755 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001756
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001757 head_skb = skb_peek(pkt->pkt_fragment_q);
1758 if (!head_skb) {
1759 pr_err("%s: head_skb is invalid\n", __func__);
1760 goto fail_data;
1761 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001762
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001763 hdr = (struct rr_header *)(head_skb->data);
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06001764 RAW("ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
1765 hdr->version, hdr->type, hdr->src_node_id,
1766 hdr->src_port_id, hdr->confirm_rx, hdr->size,
1767 hdr->dst_node_id, hdr->dst_port_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001768
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001769 if (hdr->version != IPC_ROUTER_VERSION) {
1770 pr_err("version %d != %d\n",
1771 hdr->version, IPC_ROUTER_VERSION);
1772 goto fail_data;
1773 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001774
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001775 if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) &&
1776 ((hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX) ||
1777 (hdr->type == IPC_ROUTER_CTRL_CMD_DATA))) {
1778 forward_msg(xprt_info, pkt);
1779 release_pkt(pkt);
1780 continue;
1781 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001782
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001783 if ((hdr->dst_port_id == IPC_ROUTER_ADDRESS) ||
1784 (hdr->type == IPC_ROUTER_CTRL_CMD_HELLO)) {
1785 process_control_msg(xprt_info, pkt);
1786 release_pkt(pkt);
1787 continue;
1788 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001789#if defined(CONFIG_MSM_SMD_LOGGING)
1790#if defined(DEBUG)
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001791 if (msm_ipc_router_debug_mask & SMEM_LOG) {
1792 smem_log_event((SMEM_LOG_PROC_ID_APPS |
1793 SMEM_LOG_RPC_ROUTER_EVENT_BASE |
1794 IPC_ROUTER_LOG_EVENT_RX),
1795 (hdr->src_node_id << 24) |
1796 (hdr->src_port_id & 0xffffff),
1797 (hdr->dst_node_id << 24) |
1798 (hdr->dst_port_id & 0xffffff),
1799 (hdr->type << 24) | (hdr->confirm_rx << 16) |
1800 (hdr->size & 0xffff));
1801 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001802#endif
1803#endif
1804
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001805 resume_tx = hdr->confirm_rx;
1806 resume_tx_node_id = hdr->dst_node_id;
1807 resume_tx_port_id = hdr->dst_port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001808
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001809 down_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001810 port_ptr = msm_ipc_router_lookup_local_port(hdr->dst_port_id);
1811 if (!port_ptr) {
1812 pr_err("%s: No local port id %08x\n", __func__,
1813 hdr->dst_port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001814 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001815 release_pkt(pkt);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001816 goto process_done;
1817 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001818
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001819 down_read(&routing_table_lock_lha3);
1820 rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
1821 hdr->src_port_id);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001822 if (!rport_ptr) {
1823 rport_ptr = msm_ipc_router_create_remote_port(
1824 hdr->src_node_id,
1825 hdr->src_port_id);
1826 if (!rport_ptr) {
1827 pr_err("%s: Rmt Prt %08x:%08x create failed\n",
1828 __func__, hdr->src_node_id,
1829 hdr->src_port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001830 up_read(&routing_table_lock_lha3);
1831 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001832 goto process_done;
1833 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001834 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001835 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -06001836 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001837 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001838
1839process_done:
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001840 if (resume_tx) {
1841 union rr_control_msg msg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001842
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001843 msg.cmd = IPC_ROUTER_CTRL_CMD_RESUME_TX;
1844 msg.cli.node_id = resume_tx_node_id;
1845 msg.cli.port_id = resume_tx_port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001846
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001847 RR("x RESUME_TX id=%d:%08x\n",
1848 msg.cli.node_id, msg.cli.port_id);
1849 msm_ipc_router_send_control_msg(xprt_info, &msg);
1850 }
1851
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001852 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001853 return;
1854
1855fail_data:
1856 release_pkt(pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001857 pr_err("ipc_router has died\n");
1858}
1859
1860int msm_ipc_router_register_server(struct msm_ipc_port *port_ptr,
1861 struct msm_ipc_addr *name)
1862{
1863 struct msm_ipc_server *server;
1864 unsigned long flags;
1865 union rr_control_msg ctl;
1866
1867 if (!port_ptr || !name)
1868 return -EINVAL;
1869
1870 if (name->addrtype != MSM_IPC_ADDR_NAME)
1871 return -EINVAL;
1872
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001873 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001874 server = msm_ipc_router_lookup_server(name->addr.port_name.service,
1875 name->addr.port_name.instance,
1876 IPC_ROUTER_NID_LOCAL,
1877 port_ptr->this_port.port_id);
1878 if (server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001879 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001880 pr_err("%s: Server already present\n", __func__);
1881 return -EINVAL;
1882 }
1883
1884 server = msm_ipc_router_create_server(name->addr.port_name.service,
1885 name->addr.port_name.instance,
1886 IPC_ROUTER_NID_LOCAL,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001887 port_ptr->this_port.port_id,
1888 NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001889 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001890 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001891 pr_err("%s: Server Creation failed\n", __func__);
1892 return -EINVAL;
1893 }
1894
1895 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
1896 ctl.srv.service = server->name.service;
1897 ctl.srv.instance = server->name.instance;
1898 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
1899 ctl.srv.port_id = port_ptr->this_port.port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001900 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001901 broadcast_ctl_msg(&ctl);
1902 spin_lock_irqsave(&port_ptr->port_lock, flags);
1903 port_ptr->type = SERVER_PORT;
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001904 port_ptr->mode_info.mode = MULTI_LINK_MODE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001905 port_ptr->port_name.service = server->name.service;
1906 port_ptr->port_name.instance = server->name.instance;
1907 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
1908 return 0;
1909}
1910
1911int msm_ipc_router_unregister_server(struct msm_ipc_port *port_ptr)
1912{
1913 struct msm_ipc_server *server;
1914 unsigned long flags;
1915 union rr_control_msg ctl;
1916
1917 if (!port_ptr)
1918 return -EINVAL;
1919
1920 if (port_ptr->type != SERVER_PORT) {
1921 pr_err("%s: Trying to unregister a non-server port\n",
1922 __func__);
1923 return -EINVAL;
1924 }
1925
1926 if (port_ptr->this_port.node_id != IPC_ROUTER_NID_LOCAL) {
1927 pr_err("%s: Trying to unregister a remote server locally\n",
1928 __func__);
1929 return -EINVAL;
1930 }
1931
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001932 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001933 server = msm_ipc_router_lookup_server(port_ptr->port_name.service,
1934 port_ptr->port_name.instance,
1935 port_ptr->this_port.node_id,
1936 port_ptr->this_port.port_id);
1937 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001938 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001939 pr_err("%s: Server lookup failed\n", __func__);
1940 return -ENODEV;
1941 }
1942
1943 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
1944 ctl.srv.service = server->name.service;
1945 ctl.srv.instance = server->name.instance;
1946 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
1947 ctl.srv.port_id = port_ptr->this_port.port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001948 msm_ipc_router_destroy_server(server, port_ptr->this_port.node_id,
1949 port_ptr->this_port.port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001950 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001951 broadcast_ctl_msg(&ctl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001952 spin_lock_irqsave(&port_ptr->port_lock, flags);
1953 port_ptr->type = CLIENT_PORT;
1954 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
1955 return 0;
1956}
1957
1958static int loopback_data(struct msm_ipc_port *src,
1959 uint32_t port_id,
1960 struct sk_buff_head *data)
1961{
1962 struct sk_buff *head_skb;
1963 struct rr_header *hdr;
1964 struct msm_ipc_port *port_ptr;
1965 struct rr_packet *pkt;
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07001966 int ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001967
1968 if (!data) {
1969 pr_err("%s: Invalid pkt pointer\n", __func__);
1970 return -EINVAL;
1971 }
1972
1973 pkt = create_pkt(data);
1974 if (!pkt) {
1975 pr_err("%s: New pkt create failed\n", __func__);
1976 return -ENOMEM;
1977 }
1978
1979 head_skb = skb_peek(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001980 if (!head_skb) {
1981 pr_err("%s: pkt_fragment_q is empty\n", __func__);
Brent Hronik0e83d3b2013-05-01 16:25:00 -06001982 release_pkt(pkt);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001983 return -EINVAL;
1984 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001985 hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
1986 if (!hdr) {
1987 pr_err("%s: Prepend Header failed\n", __func__);
1988 release_pkt(pkt);
1989 return -ENOMEM;
1990 }
1991 hdr->version = IPC_ROUTER_VERSION;
1992 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
1993 hdr->src_node_id = src->this_port.node_id;
1994 hdr->src_port_id = src->this_port.port_id;
1995 hdr->size = pkt->length;
1996 hdr->confirm_rx = 0;
1997 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
1998 hdr->dst_port_id = port_id;
1999 pkt->length += IPC_ROUTER_HDR_SIZE;
2000
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002001 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002002 port_ptr = msm_ipc_router_lookup_local_port(port_id);
2003 if (!port_ptr) {
2004 pr_err("%s: Local port %d not present\n", __func__, port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002005 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002006 release_pkt(pkt);
2007 return -ENODEV;
2008 }
2009
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07002010 ret_len = pkt->length;
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -06002011 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002012 update_comm_mode_info(&src->mode_info, NULL);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002013 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002014
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07002015 return ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002016}
2017
2018static int msm_ipc_router_write_pkt(struct msm_ipc_port *src,
2019 struct msm_ipc_router_remote_port *rport_ptr,
2020 struct rr_packet *pkt)
2021{
2022 struct sk_buff *head_skb;
2023 struct rr_header *hdr;
2024 struct msm_ipc_router_xprt_info *xprt_info;
2025 struct msm_ipc_routing_table_entry *rt_entry;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302026 struct msm_ipc_resume_tx_port *resume_tx_port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002027 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002028
2029 if (!rport_ptr || !src || !pkt)
2030 return -EINVAL;
2031
2032 head_skb = skb_peek(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07002033 if (!head_skb) {
2034 pr_err("%s: pkt_fragment_q is empty\n", __func__);
2035 return -EINVAL;
2036 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002037 hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
2038 if (!hdr) {
2039 pr_err("%s: Prepend Header failed\n", __func__);
2040 return -ENOMEM;
2041 }
2042 hdr->version = IPC_ROUTER_VERSION;
2043 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2044 hdr->src_node_id = src->this_port.node_id;
2045 hdr->src_port_id = src->this_port.port_id;
2046 hdr->size = pkt->length;
2047 hdr->confirm_rx = 0;
2048 hdr->dst_node_id = rport_ptr->node_id;
2049 hdr->dst_port_id = rport_ptr->port_id;
2050 pkt->length += IPC_ROUTER_HDR_SIZE;
2051
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002052 mutex_lock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302053 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) {
2054 if (msm_ipc_router_lookup_resume_tx_port(
2055 rport_ptr, src->this_port.port_id)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002056 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302057 return -EAGAIN;
2058 }
2059 resume_tx_port =
2060 kzalloc(sizeof(struct msm_ipc_resume_tx_port),
2061 GFP_KERNEL);
2062 if (!resume_tx_port) {
2063 pr_err("%s: Resume_Tx port allocation failed\n",
2064 __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002065 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302066 return -ENOMEM;
2067 }
2068 INIT_LIST_HEAD(&resume_tx_port->list);
2069 resume_tx_port->port_id = src->this_port.port_id;
2070 resume_tx_port->node_id = src->this_port.node_id;
2071 list_add_tail(&resume_tx_port->list,
2072 &rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002073 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302074 return -EAGAIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002075 }
2076 rport_ptr->tx_quota_cnt++;
2077 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA)
2078 hdr->confirm_rx = 1;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002079 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002080
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002081 rt_entry = lookup_routing_table(hdr->dst_node_id);
2082 if (!rt_entry || !rt_entry->xprt_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002083 pr_err("%s: Remote node %d not up\n",
2084 __func__, hdr->dst_node_id);
2085 return -ENODEV;
2086 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002087 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002088 xprt_info = rt_entry->xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002089 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002090 ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002091 mutex_unlock(&xprt_info->tx_lock_lhb2);
2092 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002093
2094 if (ret < 0) {
2095 pr_err("%s: Write on XPRT failed\n", __func__);
2096 return ret;
2097 }
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002098 update_comm_mode_info(&src->mode_info, xprt_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002099
2100 RAW_HDR("[w rr_h] "
2101 "ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
2102 "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
2103 hdr->version, type_to_str(hdr->type),
2104 hdr->src_node_id, hdr->src_port_id,
2105 hdr->confirm_rx, hdr->size,
2106 hdr->dst_node_id, hdr->dst_port_id);
2107
2108#if defined(CONFIG_MSM_SMD_LOGGING)
2109#if defined(DEBUG)
2110 if (msm_ipc_router_debug_mask & SMEM_LOG) {
2111 smem_log_event((SMEM_LOG_PROC_ID_APPS |
2112 SMEM_LOG_RPC_ROUTER_EVENT_BASE |
2113 IPC_ROUTER_LOG_EVENT_TX),
2114 (hdr->src_node_id << 24) |
2115 (hdr->src_port_id & 0xffffff),
2116 (hdr->dst_node_id << 24) |
2117 (hdr->dst_port_id & 0xffffff),
2118 (hdr->type << 24) | (hdr->confirm_rx << 16) |
2119 (hdr->size & 0xffff));
2120 }
2121#endif
2122#endif
2123
2124 return pkt->length;
2125}
2126
2127int msm_ipc_router_send_to(struct msm_ipc_port *src,
2128 struct sk_buff_head *data,
2129 struct msm_ipc_addr *dest)
2130{
2131 uint32_t dst_node_id = 0, dst_port_id = 0;
2132 struct msm_ipc_server *server;
2133 struct msm_ipc_server_port *server_port;
2134 struct msm_ipc_router_remote_port *rport_ptr = NULL;
2135 struct rr_packet *pkt;
2136 int ret;
2137
2138 if (!src || !data || !dest) {
2139 pr_err("%s: Invalid Parameters\n", __func__);
2140 return -EINVAL;
2141 }
2142
2143 /* Resolve Address*/
2144 if (dest->addrtype == MSM_IPC_ADDR_ID) {
2145 dst_node_id = dest->addr.port_addr.node_id;
2146 dst_port_id = dest->addr.port_addr.port_id;
2147 } else if (dest->addrtype == MSM_IPC_ADDR_NAME) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002148 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002149 server = msm_ipc_router_lookup_server(
2150 dest->addr.port_name.service,
2151 dest->addr.port_name.instance,
2152 0, 0);
2153 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002154 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002155 pr_err("%s: Destination not reachable\n", __func__);
2156 return -ENODEV;
2157 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002158 server_port = list_first_entry(&server->server_port_list,
2159 struct msm_ipc_server_port,
2160 list);
2161 dst_node_id = server_port->server_addr.node_id;
2162 dst_port_id = server_port->server_addr.port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002163 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002164 }
2165 if (dst_node_id == IPC_ROUTER_NID_LOCAL) {
2166 ret = loopback_data(src, dst_port_id, data);
2167 return ret;
2168 }
2169
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002170 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002171 rport_ptr = msm_ipc_router_lookup_remote_port(dst_node_id,
2172 dst_port_id);
2173 if (!rport_ptr) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002174 up_read(&routing_table_lock_lha3);
Zaheerulla Meer2c515312013-05-10 15:51:28 +05302175 pr_err("%s: Remote port not found\n", __func__);
2176 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002177 }
2178
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002179 if (src->check_send_permissions) {
2180 ret = src->check_send_permissions(rport_ptr->sec_rule);
2181 if (ret <= 0) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002182 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002183 pr_err("%s: permission failure for %s\n",
2184 __func__, current->comm);
2185 return -EPERM;
2186 }
2187 }
2188
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002189 pkt = create_pkt(data);
2190 if (!pkt) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002191 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002192 pr_err("%s: Pkt creation failed\n", __func__);
2193 return -ENOMEM;
2194 }
2195
2196 ret = msm_ipc_router_write_pkt(src, rport_ptr, pkt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002197 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002198 release_pkt(pkt);
2199
2200 return ret;
2201}
2202
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002203int msm_ipc_router_send_msg(struct msm_ipc_port *src,
2204 struct msm_ipc_addr *dest,
2205 void *data, unsigned int data_len)
2206{
2207 struct sk_buff_head *out_skb_head;
2208 int ret;
2209
2210 out_skb_head = msm_ipc_router_buf_to_skb(data, data_len);
2211 if (!out_skb_head) {
2212 pr_err("%s: SKB conversion failed\n", __func__);
2213 return -EFAULT;
2214 }
2215
2216 ret = msm_ipc_router_send_to(src, out_skb_head, dest);
2217 if (ret < 0) {
2218 pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
2219 __func__, ret);
2220 msm_ipc_router_free_skb(out_skb_head);
2221 }
2222 return 0;
2223}
2224
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002225int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
2226 struct sk_buff_head **data,
2227 size_t buf_len)
2228{
2229 struct rr_packet *pkt;
2230 int ret;
2231
2232 if (!port_ptr || !data)
2233 return -EINVAL;
2234
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002235 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002236 if (list_empty(&port_ptr->port_rx_q)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002237 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002238 return -EAGAIN;
2239 }
2240
2241 pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list);
2242 if ((buf_len) && ((pkt->length - IPC_ROUTER_HDR_SIZE) > buf_len)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002243 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002244 return -ETOOSMALL;
2245 }
2246 list_del(&pkt->list);
2247 if (list_empty(&port_ptr->port_rx_q))
2248 wake_unlock(&port_ptr->port_rx_wake_lock);
2249 *data = pkt->pkt_fragment_q;
2250 ret = pkt->length;
2251 kfree(pkt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002252 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002253
2254 return ret;
2255}
2256
2257int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
2258 struct sk_buff_head **data,
2259 struct msm_ipc_addr *src,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002260 long timeout)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002261{
2262 int ret, data_len, align_size;
2263 struct sk_buff *temp_skb;
2264 struct rr_header *hdr = NULL;
2265
2266 if (!port_ptr || !data) {
2267 pr_err("%s: Invalid pointers being passed\n", __func__);
2268 return -EINVAL;
2269 }
2270
2271 *data = NULL;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002272 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002273 while (list_empty(&port_ptr->port_rx_q)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002274 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002275 if (timeout < 0) {
2276 ret = wait_event_interruptible(
2277 port_ptr->port_rx_wait_q,
2278 !list_empty(&port_ptr->port_rx_q));
2279 if (ret)
2280 return ret;
2281 } else if (timeout > 0) {
2282 timeout = wait_event_interruptible_timeout(
2283 port_ptr->port_rx_wait_q,
2284 !list_empty(&port_ptr->port_rx_q),
2285 timeout);
2286 if (timeout < 0)
2287 return -EFAULT;
2288 }
2289 if (timeout == 0)
2290 return -ETIMEDOUT;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002291 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002292 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002293 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002294
2295 ret = msm_ipc_router_read(port_ptr, data, 0);
2296 if (ret <= 0 || !(*data))
2297 return ret;
2298
2299 temp_skb = skb_peek(*data);
2300 hdr = (struct rr_header *)(temp_skb->data);
2301 if (src) {
2302 src->addrtype = MSM_IPC_ADDR_ID;
2303 src->addr.port_addr.node_id = hdr->src_node_id;
2304 src->addr.port_addr.port_id = hdr->src_port_id;
2305 }
2306
2307 data_len = hdr->size;
2308 skb_pull(temp_skb, IPC_ROUTER_HDR_SIZE);
2309 align_size = ALIGN_SIZE(data_len);
2310 if (align_size) {
2311 temp_skb = skb_peek_tail(*data);
2312 skb_trim(temp_skb, (temp_skb->len - align_size));
2313 }
2314 return data_len;
2315}
2316
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002317int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
2318 struct msm_ipc_addr *src,
2319 unsigned char **data,
2320 unsigned int *len)
2321{
2322 struct sk_buff_head *in_skb_head;
2323 int ret;
2324
2325 ret = msm_ipc_router_recv_from(port_ptr, &in_skb_head, src, -1);
2326 if (ret < 0) {
2327 pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
2328 __func__, ret);
2329 return ret;
2330 }
2331
2332 *data = msm_ipc_router_skb_to_buf(in_skb_head, ret);
2333 if (!(*data))
2334 pr_err("%s: Buf conversion failed\n", __func__);
2335
2336 *len = ret;
2337 msm_ipc_router_free_skb(in_skb_head);
2338 return 0;
2339}
2340
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002341struct msm_ipc_port *msm_ipc_router_create_port(
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002342 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002343 void *priv)
2344{
2345 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002346 int ret;
2347
2348 ret = wait_for_completion_interruptible(&msm_ipc_local_router_up);
2349 if (ret < 0) {
2350 pr_err("%s: Error waiting for local router\n", __func__);
2351 return NULL;
2352 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002353
2354 port_ptr = msm_ipc_router_create_raw_port(NULL, notify, priv);
2355 if (!port_ptr)
2356 pr_err("%s: port_ptr alloc failed\n", __func__);
2357
2358 return port_ptr;
2359}
2360
2361int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
2362{
2363 union rr_control_msg msg;
2364 struct rr_packet *pkt, *temp_pkt;
2365 struct msm_ipc_server *server;
2366
2367 if (!port_ptr)
2368 return -EINVAL;
2369
2370 if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002371 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002372 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002373 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002374
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002375 if (port_ptr->type == SERVER_PORT) {
2376 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
2377 msg.srv.service = port_ptr->port_name.service;
2378 msg.srv.instance = port_ptr->port_name.instance;
2379 msg.srv.node_id = port_ptr->this_port.node_id;
2380 msg.srv.port_id = port_ptr->this_port.port_id;
2381 RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
2382 msg.srv.service, msg.srv.instance,
2383 msg.srv.node_id, msg.srv.port_id);
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002384 broadcast_ctl_msg(&msg);
2385 broadcast_ctl_msg_locally(&msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002386 }
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002387
2388 /*
2389 * Server port could have been a client port earlier.
2390 * Send REMOVE_CLIENT message in either case.
2391 */
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002392 RR("x REMOVE_CLIENT id=%d:%08x\n",
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002393 port_ptr->this_port.node_id, port_ptr->this_port.port_id);
2394 msm_ipc_router_send_remove_client(&port_ptr->mode_info,
2395 port_ptr->this_port.node_id,
2396 port_ptr->this_port.port_id);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002397 } else if (port_ptr->type == CONTROL_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002398 down_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002399 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002400 up_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002401 } else if (port_ptr->type == IRSC_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002402 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002403 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002404 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002405 signal_irsc_completion();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002406 }
2407
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002408 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002409 list_for_each_entry_safe(pkt, temp_pkt, &port_ptr->port_rx_q, list) {
2410 list_del(&pkt->list);
2411 release_pkt(pkt);
2412 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002413 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002414
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002415 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002416 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002417 server = msm_ipc_router_lookup_server(
2418 port_ptr->port_name.service,
2419 port_ptr->port_name.instance,
2420 port_ptr->this_port.node_id,
2421 port_ptr->this_port.port_id);
2422 if (server)
2423 msm_ipc_router_destroy_server(server,
2424 port_ptr->this_port.node_id,
2425 port_ptr->this_port.port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002426 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002427 }
2428
Karthikeyan Ramasubramaniandd8c3b52011-11-30 16:26:12 -07002429 wake_lock_destroy(&port_ptr->port_rx_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002430 kfree(port_ptr);
2431 return 0;
2432}
2433
2434int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr)
2435{
2436 struct rr_packet *pkt;
2437 int rc = 0;
2438
2439 if (!port_ptr)
2440 return -EINVAL;
2441
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002442 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002443 if (!list_empty(&port_ptr->port_rx_q)) {
2444 pkt = list_first_entry(&port_ptr->port_rx_q,
2445 struct rr_packet, list);
2446 rc = pkt->length;
2447 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002448 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002449
2450 return rc;
2451}
2452
2453int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr)
2454{
2455 if (!port_ptr)
2456 return -EINVAL;
2457
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002458 down_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002459 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002460 up_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002461 port_ptr->type = CONTROL_PORT;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002462 down_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002463 list_add_tail(&port_ptr->list, &control_ports);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002464 up_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002465
2466 return 0;
2467}
2468
2469int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002470 struct msm_ipc_server_info *srv_info,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002471 int num_entries_in_array,
2472 uint32_t lookup_mask)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002473{
2474 struct msm_ipc_server *server;
2475 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002476 int key, i = 0; /*num_entries_found*/
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002477
2478 if (!srv_name) {
2479 pr_err("%s: Invalid srv_name\n", __func__);
2480 return -EINVAL;
2481 }
2482
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002483 if (num_entries_in_array && !srv_info) {
2484 pr_err("%s: srv_info NULL\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002485 return -EINVAL;
2486 }
2487
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002488 down_read(&server_list_lock_lha2);
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002489 if (!lookup_mask)
2490 lookup_mask = 0xFFFFFFFF;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002491 key = (srv_name->service & (SRV_HASH_SIZE - 1));
2492 list_for_each_entry(server, &server_list[key], list) {
2493 if ((server->name.service != srv_name->service) ||
2494 ((server->name.instance & lookup_mask) !=
2495 srv_name->instance))
2496 continue;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002497
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002498 list_for_each_entry(server_port,
2499 &server->server_port_list, list) {
2500 if (i < num_entries_in_array) {
2501 srv_info[i].node_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002502 server_port->server_addr.node_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002503 srv_info[i].port_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002504 server_port->server_addr.port_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002505 srv_info[i].service = server->name.service;
2506 srv_info[i].instance = server->name.instance;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002507 }
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002508 i++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002509 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002510 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002511 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002512
2513 return i;
2514}
2515
2516int msm_ipc_router_close(void)
2517{
2518 struct msm_ipc_router_xprt_info *xprt_info, *tmp_xprt_info;
2519
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002520 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002521 list_for_each_entry_safe(xprt_info, tmp_xprt_info,
2522 &xprt_info_list, list) {
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002523 xprt_info->xprt->close(xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002524 list_del(&xprt_info->list);
2525 kfree(xprt_info);
2526 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002527 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002528 return 0;
2529}
2530
2531#if defined(CONFIG_DEBUG_FS)
2532static int dump_routing_table(char *buf, int max)
2533{
2534 int i = 0, j;
2535 struct msm_ipc_routing_table_entry *rt_entry;
2536
2537 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002538 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002539 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002540 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002541 i += scnprintf(buf + i, max - i,
2542 "Node Id: 0x%08x\n", rt_entry->node_id);
Karthikeyan Ramasubramanianc1a4e3a2012-09-10 16:10:24 -06002543 if (rt_entry->node_id == IPC_ROUTER_NID_LOCAL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002544 i += scnprintf(buf + i, max - i,
2545 "XPRT Name: Loopback\n");
2546 i += scnprintf(buf + i, max - i,
2547 "Next Hop: %d\n", rt_entry->node_id);
2548 } else {
2549 i += scnprintf(buf + i, max - i,
2550 "XPRT Name: %s\n",
2551 rt_entry->xprt_info->xprt->name);
2552 i += scnprintf(buf + i, max - i,
2553 "Next Hop: 0x%08x\n",
2554 rt_entry->xprt_info->remote_node_id);
2555 }
2556 i += scnprintf(buf + i, max - i, "\n");
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002557 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002558 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002559 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002560 }
2561
2562 return i;
2563}
2564
2565static int dump_xprt_info(char *buf, int max)
2566{
2567 int i = 0;
2568 struct msm_ipc_router_xprt_info *xprt_info;
2569
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002570 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002571 list_for_each_entry(xprt_info, &xprt_info_list, list) {
2572 i += scnprintf(buf + i, max - i, "XPRT Name: %s\n",
2573 xprt_info->xprt->name);
2574 i += scnprintf(buf + i, max - i, "Link Id: %d\n",
2575 xprt_info->xprt->link_id);
2576 i += scnprintf(buf + i, max - i, "Initialized: %s\n",
2577 (xprt_info->initialized ? "Y" : "N"));
2578 i += scnprintf(buf + i, max - i, "Remote Node Id: 0x%08x\n",
2579 xprt_info->remote_node_id);
2580 i += scnprintf(buf + i, max - i, "\n");
2581 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002582 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002583
2584 return i;
2585}
2586
2587static int dump_servers(char *buf, int max)
2588{
2589 int i = 0, j;
2590 struct msm_ipc_server *server;
2591 struct msm_ipc_server_port *server_port;
2592
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002593 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002594 for (j = 0; j < SRV_HASH_SIZE; j++) {
2595 list_for_each_entry(server, &server_list[j], list) {
2596 list_for_each_entry(server_port,
2597 &server->server_port_list,
2598 list) {
2599 i += scnprintf(buf + i, max - i, "Service: "
2600 "0x%08x\n", server->name.service);
2601 i += scnprintf(buf + i, max - i, "Instance: "
2602 "0x%08x\n", server->name.instance);
2603 i += scnprintf(buf + i, max - i,
2604 "Node_id: 0x%08x\n",
2605 server_port->server_addr.node_id);
2606 i += scnprintf(buf + i, max - i,
2607 "Port_id: 0x%08x\n",
2608 server_port->server_addr.port_id);
2609 i += scnprintf(buf + i, max - i, "\n");
2610 }
2611 }
2612 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002613 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002614
2615 return i;
2616}
2617
2618static int dump_remote_ports(char *buf, int max)
2619{
2620 int i = 0, j, k;
2621 struct msm_ipc_router_remote_port *rport_ptr;
2622 struct msm_ipc_routing_table_entry *rt_entry;
2623
2624 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002625 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002626 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002627 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002628 for (k = 0; k < RP_HASH_SIZE; k++) {
2629 list_for_each_entry(rport_ptr,
2630 &rt_entry->remote_port_list[k],
2631 list) {
2632 i += scnprintf(buf + i, max - i,
2633 "Node_id: 0x%08x\n",
2634 rport_ptr->node_id);
2635 i += scnprintf(buf + i, max - i,
2636 "Port_id: 0x%08x\n",
2637 rport_ptr->port_id);
2638 i += scnprintf(buf + i, max - i,
2639 "Quota_cnt: %d\n",
2640 rport_ptr->tx_quota_cnt);
2641 i += scnprintf(buf + i, max - i, "\n");
2642 }
2643 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002644 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002645 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002646 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002647 }
2648
2649 return i;
2650}
2651
2652static int dump_control_ports(char *buf, int max)
2653{
2654 int i = 0;
2655 struct msm_ipc_port *port_ptr;
2656
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002657 down_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002658 list_for_each_entry(port_ptr, &control_ports, list) {
2659 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
2660 port_ptr->this_port.node_id);
2661 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
2662 port_ptr->this_port.port_id);
2663 i += scnprintf(buf + i, max - i, "\n");
2664 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002665 up_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002666
2667 return i;
2668}
2669
2670static int dump_local_ports(char *buf, int max)
2671{
2672 int i = 0, j;
2673 unsigned long flags;
2674 struct msm_ipc_port *port_ptr;
2675
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002676 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002677 for (j = 0; j < LP_HASH_SIZE; j++) {
2678 list_for_each_entry(port_ptr, &local_ports[j], list) {
2679 spin_lock_irqsave(&port_ptr->port_lock, flags);
2680 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
2681 port_ptr->this_port.node_id);
2682 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
2683 port_ptr->this_port.port_id);
2684 i += scnprintf(buf + i, max - i, "# pkts tx'd %d\n",
2685 port_ptr->num_tx);
2686 i += scnprintf(buf + i, max - i, "# pkts rx'd %d\n",
2687 port_ptr->num_rx);
2688 i += scnprintf(buf + i, max - i, "# bytes tx'd %ld\n",
2689 port_ptr->num_tx_bytes);
2690 i += scnprintf(buf + i, max - i, "# bytes rx'd %ld\n",
2691 port_ptr->num_rx_bytes);
2692 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
2693 i += scnprintf(buf + i, max - i, "\n");
2694 }
2695 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002696 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002697
2698 return i;
2699}
2700
2701#define DEBUG_BUFMAX 4096
2702static char debug_buffer[DEBUG_BUFMAX];
2703
2704static ssize_t debug_read(struct file *file, char __user *buf,
2705 size_t count, loff_t *ppos)
2706{
2707 int (*fill)(char *buf, int max) = file->private_data;
2708 int bsize = fill(debug_buffer, DEBUG_BUFMAX);
2709 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
2710}
2711
2712static int debug_open(struct inode *inode, struct file *file)
2713{
2714 file->private_data = inode->i_private;
2715 return 0;
2716}
2717
2718static const struct file_operations debug_ops = {
2719 .read = debug_read,
2720 .open = debug_open,
2721};
2722
2723static void debug_create(const char *name, mode_t mode,
2724 struct dentry *dent,
2725 int (*fill)(char *buf, int max))
2726{
2727 debugfs_create_file(name, mode, dent, fill, &debug_ops);
2728}
2729
2730static void debugfs_init(void)
2731{
2732 struct dentry *dent;
2733
2734 dent = debugfs_create_dir("msm_ipc_router", 0);
2735 if (IS_ERR(dent))
2736 return;
2737
2738 debug_create("dump_local_ports", 0444, dent,
2739 dump_local_ports);
2740 debug_create("dump_remote_ports", 0444, dent,
2741 dump_remote_ports);
2742 debug_create("dump_control_ports", 0444, dent,
2743 dump_control_ports);
2744 debug_create("dump_servers", 0444, dent,
2745 dump_servers);
2746 debug_create("dump_xprt_info", 0444, dent,
2747 dump_xprt_info);
2748 debug_create("dump_routing_table", 0444, dent,
2749 dump_routing_table);
2750}
2751
2752#else
2753static void debugfs_init(void) {}
2754#endif
2755
2756static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
2757{
2758 struct msm_ipc_router_xprt_info *xprt_info;
2759 struct msm_ipc_routing_table_entry *rt_entry;
2760
2761 xprt_info = kmalloc(sizeof(struct msm_ipc_router_xprt_info),
2762 GFP_KERNEL);
2763 if (!xprt_info)
2764 return -ENOMEM;
2765
2766 xprt_info->xprt = xprt;
2767 xprt_info->initialized = 0;
2768 xprt_info->remote_node_id = -1;
2769 INIT_LIST_HEAD(&xprt_info->pkt_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002770 mutex_init(&xprt_info->rx_lock_lhb2);
2771 mutex_init(&xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002772 wake_lock_init(&xprt_info->wakelock,
2773 WAKE_LOCK_SUSPEND, xprt->name);
2774 xprt_info->need_len = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002775 xprt_info->abort_data_read = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002776 INIT_WORK(&xprt_info->read_data, do_read_data);
2777 INIT_LIST_HEAD(&xprt_info->list);
2778
2779 xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
2780 if (!xprt_info->workqueue) {
2781 kfree(xprt_info);
2782 return -ENOMEM;
2783 }
2784
2785 if (!strcmp(xprt->name, "msm_ipc_router_loopback_xprt")) {
2786 xprt_info->remote_node_id = IPC_ROUTER_NID_LOCAL;
2787 xprt_info->initialized = 1;
2788 }
2789
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002790 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002791 list_add_tail(&xprt_info->list, &xprt_info_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002792 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002793
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002794 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002795 if (!routing_table_inited) {
2796 init_routing_table();
2797 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
2798 add_routing_table_entry(rt_entry);
2799 routing_table_inited = 1;
2800 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002801 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002802
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002803 xprt->priv = xprt_info;
2804
2805 return 0;
2806}
2807
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002808static void msm_ipc_router_remove_xprt(struct msm_ipc_router_xprt *xprt)
2809{
2810 struct msm_ipc_router_xprt_info *xprt_info;
2811
2812 if (xprt && xprt->priv) {
2813 xprt_info = xprt->priv;
2814
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002815 mutex_lock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002816 xprt_info->abort_data_read = 1;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002817 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002818
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002819 down_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002820 list_del(&xprt_info->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002821 up_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002822
2823 flush_workqueue(xprt_info->workqueue);
2824 destroy_workqueue(xprt_info->workqueue);
2825 wake_lock_destroy(&xprt_info->wakelock);
2826
2827 xprt->priv = 0;
2828 kfree(xprt_info);
2829 }
2830}
2831
2832
2833struct msm_ipc_router_xprt_work {
2834 struct msm_ipc_router_xprt *xprt;
2835 struct work_struct work;
2836};
2837
2838static void xprt_open_worker(struct work_struct *work)
2839{
2840 struct msm_ipc_router_xprt_work *xprt_work =
2841 container_of(work, struct msm_ipc_router_xprt_work, work);
2842
2843 msm_ipc_router_add_xprt(xprt_work->xprt);
2844 kfree(xprt_work);
2845}
2846
2847static void xprt_close_worker(struct work_struct *work)
2848{
2849 struct msm_ipc_router_xprt_work *xprt_work =
2850 container_of(work, struct msm_ipc_router_xprt_work, work);
2851
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002852 msm_ipc_cleanup_routing_table(xprt_work->xprt->priv);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002853 msm_ipc_router_remove_xprt(xprt_work->xprt);
Zaheerulla Meer35893a62013-06-19 16:54:44 +05302854 xprt_work->xprt->sft_close_done(xprt_work->xprt);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002855 kfree(xprt_work);
2856}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002857
2858void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
2859 unsigned event,
2860 void *data)
2861{
2862 struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002863 struct msm_ipc_router_xprt_work *xprt_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002864 struct rr_packet *pkt;
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06002865 unsigned long ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002866
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06002867 if (!msm_ipc_router_workqueue) {
2868 ret = wait_for_completion_timeout(&msm_ipc_local_router_up,
2869 IPC_ROUTER_INIT_TIMEOUT);
2870 if (!ret || !msm_ipc_router_workqueue) {
2871 pr_err("%s: IPC Router not initialized\n", __func__);
2872 return;
2873 }
2874 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002875
2876 switch (event) {
2877 case IPC_ROUTER_XPRT_EVENT_OPEN:
2878 D("open event for '%s'\n", xprt->name);
2879 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
2880 GFP_ATOMIC);
Karthikeyan Ramasubramanianc51456c2013-05-16 15:51:29 -06002881 if (xprt_work) {
2882 xprt_work->xprt = xprt;
2883 INIT_WORK(&xprt_work->work, xprt_open_worker);
2884 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
2885 } else {
2886 pr_err("%s: malloc failure - Couldn't notify OPEN event",
2887 __func__);
2888 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002889 break;
2890
2891 case IPC_ROUTER_XPRT_EVENT_CLOSE:
2892 D("close event for '%s'\n", xprt->name);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002893 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
2894 GFP_ATOMIC);
Karthikeyan Ramasubramanianc51456c2013-05-16 15:51:29 -06002895 if (xprt_work) {
2896 xprt_work->xprt = xprt;
2897 INIT_WORK(&xprt_work->work, xprt_close_worker);
2898 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
2899 } else {
2900 pr_err("%s: malloc failure - Couldn't notify CLOSE event",
2901 __func__);
2902 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002903 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002904 }
2905
2906 if (!data)
2907 return;
2908
2909 while (!xprt_info) {
2910 msleep(100);
2911 xprt_info = xprt->priv;
2912 }
2913
2914 pkt = clone_pkt((struct rr_packet *)data);
2915 if (!pkt)
2916 return;
2917
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002918 mutex_lock(&xprt_info->rx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002919 list_add_tail(&pkt->list, &xprt_info->pkt_list);
2920 wake_lock(&xprt_info->wakelock);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002921 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002922 queue_work(xprt_info->workqueue, &xprt_info->read_data);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002923}
2924
2925static int __init msm_ipc_router_init(void)
2926{
2927 int i, ret;
2928 struct msm_ipc_routing_table_entry *rt_entry;
2929
2930 msm_ipc_router_debug_mask |= SMEM_LOG;
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06002931 ipc_rtr_log_ctxt = ipc_log_context_create(IPC_RTR_LOG_PAGES,
2932 "ipc_router");
2933 if (!ipc_rtr_log_ctxt)
2934 pr_err("%s: Unable to create IPC logging for IPC RTR",
2935 __func__);
2936
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002937 msm_ipc_router_workqueue =
2938 create_singlethread_workqueue("msm_ipc_router");
2939 if (!msm_ipc_router_workqueue)
2940 return -ENOMEM;
2941
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002942 debugfs_init();
2943
2944 for (i = 0; i < SRV_HASH_SIZE; i++)
2945 INIT_LIST_HEAD(&server_list[i]);
2946
2947 for (i = 0; i < LP_HASH_SIZE; i++)
2948 INIT_LIST_HEAD(&local_ports[i]);
2949
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002950 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002951 if (!routing_table_inited) {
2952 init_routing_table();
2953 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
2954 add_routing_table_entry(rt_entry);
2955 routing_table_inited = 1;
2956 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002957 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002958
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002959 ret = msm_ipc_router_init_sockets();
2960 if (ret < 0)
2961 pr_err("%s: Init sockets failed\n", __func__);
2962
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002963 ret = msm_ipc_router_security_init();
2964 if (ret < 0)
2965 pr_err("%s: Security Init failed\n", __func__);
2966
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06002967 complete_all(&msm_ipc_local_router_up);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002968 return ret;
2969}
2970
2971module_init(msm_ipc_router_init);
2972MODULE_DESCRIPTION("MSM IPC Router");
2973MODULE_LICENSE("GPL v2");