blob: 38f8d19f5f124a91c9d76181fe91e331cf75b763 [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>
30
31#include <asm/uaccess.h>
32#include <asm/byteorder.h>
33
34#include <mach/smem_log.h>
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060035#include <mach/subsystem_notif.h>
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -060036#include <mach/msm_ipc_router.h>
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060037#include <mach/msm_ipc_logging.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
39#include "ipc_router.h"
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060040#include "modem_notifier.h"
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -060041#include "msm_ipc_router_security.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042
43enum {
44 SMEM_LOG = 1U << 0,
45 RTR_DBG = 1U << 1,
46 R2R_MSG = 1U << 2,
47 R2R_RAW = 1U << 3,
48 NTFY_MSG = 1U << 4,
49 R2R_RAW_HDR = 1U << 5,
50};
51
52static int msm_ipc_router_debug_mask;
53module_param_named(debug_mask, msm_ipc_router_debug_mask,
54 int, S_IRUGO | S_IWUSR | S_IWGRP);
55
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060056static void *ipc_rtr_log_ctxt;
57#define IPC_RTR_LOG_PAGES 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058#define DIAG(x...) pr_info("[RR] ERROR " x)
59
60#if defined(DEBUG)
61#define D(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060062if (ipc_rtr_log_ctxt) \
63 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064if (msm_ipc_router_debug_mask & RTR_DBG) \
65 pr_info(x); \
66} while (0)
67
68#define RR(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060069if (ipc_rtr_log_ctxt) \
70 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071if (msm_ipc_router_debug_mask & R2R_MSG) \
72 pr_info("[RR] "x); \
73} while (0)
74
75#define RAW(x...) do { \
76if (msm_ipc_router_debug_mask & R2R_RAW) \
77 pr_info("[RAW] "x); \
78} while (0)
79
80#define NTFY(x...) do { \
81if (msm_ipc_router_debug_mask & NTFY_MSG) \
82 pr_info("[NOTIFY] "x); \
83} while (0)
84
85#define RAW_HDR(x...) do { \
86if (msm_ipc_router_debug_mask & R2R_RAW_HDR) \
87 pr_info("[HDR] "x); \
88} while (0)
89#else
90#define D(x...) do { } while (0)
91#define RR(x...) do { } while (0)
92#define RAW(x...) do { } while (0)
93#define RAW_HDR(x...) do { } while (0)
94#define NTFY(x...) do { } while (0)
95#endif
96
97#define IPC_ROUTER_LOG_EVENT_ERROR 0x10
98#define IPC_ROUTER_LOG_EVENT_TX 0x11
99#define IPC_ROUTER_LOG_EVENT_RX 0x12
100
101static LIST_HEAD(control_ports);
102static DEFINE_MUTEX(control_ports_lock);
103
104#define LP_HASH_SIZE 32
105static struct list_head local_ports[LP_HASH_SIZE];
106static DEFINE_MUTEX(local_ports_lock);
107
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600108/*
109 * Server info is organized as a hash table. The server's service ID is
110 * used to index into the hash table. The instance ID of most of the servers
111 * are 1 or 2. The service IDs are well distributed compared to the instance
112 * IDs and hence choosing service ID to index into this hash table optimizes
113 * the hash table operations like add, lookup, destroy.
114 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115#define SRV_HASH_SIZE 32
116static struct list_head server_list[SRV_HASH_SIZE];
117static DEFINE_MUTEX(server_list_lock);
118static wait_queue_head_t newserver_wait;
119
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;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600147 uint32_t restart_state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148 uint32_t tx_quota_cnt;
149 struct mutex quota_lock;
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530150 struct list_head resume_tx_port_list;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600151 void *sec_rule;
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;
161 struct mutex rx_lock;
162 struct mutex tx_lock;
163 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;
176 struct mutex lock;
177 unsigned long num_tx_bytes;
178 unsigned long num_rx_bytes;
179};
180
181static struct list_head routing_table[RT_HASH_SIZE];
182static DEFINE_MUTEX(routing_table_lock);
183static int routing_table_inited;
184
185static LIST_HEAD(msm_ipc_board_dev_list);
186static DEFINE_MUTEX(msm_ipc_board_dev_list_lock);
187
188static void do_read_data(struct work_struct *work);
189
190#define RR_STATE_IDLE 0
191#define RR_STATE_HEADER 1
192#define RR_STATE_BODY 2
193#define RR_STATE_ERROR 3
194
195#define RESTART_NORMAL 0
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600196#define RESTART_PEND 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197
198/* State for remote ep following restart */
199#define RESTART_QUOTA_ABORT 1
200
201static LIST_HEAD(xprt_info_list);
202static DEFINE_MUTEX(xprt_info_list_lock);
203
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -0600204static DECLARE_COMPLETION(msm_ipc_local_router_up);
205#define IPC_ROUTER_INIT_TIMEOUT (10 * HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206
207static uint32_t next_port_id;
208static DEFINE_MUTEX(next_port_id_lock);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600209static struct workqueue_struct *msm_ipc_router_workqueue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210
211enum {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 DOWN,
213 UP,
214};
215
216static void init_routing_table(void)
217{
218 int i;
219 for (i = 0; i < RT_HASH_SIZE; i++)
220 INIT_LIST_HEAD(&routing_table[i]);
221}
222
223static struct msm_ipc_routing_table_entry *alloc_routing_table_entry(
224 uint32_t node_id)
225{
226 int i;
227 struct msm_ipc_routing_table_entry *rt_entry;
228
229 rt_entry = kmalloc(sizeof(struct msm_ipc_routing_table_entry),
230 GFP_KERNEL);
231 if (!rt_entry) {
232 pr_err("%s: rt_entry allocation failed for %d\n",
233 __func__, node_id);
234 return NULL;
235 }
236
237 for (i = 0; i < RP_HASH_SIZE; i++)
238 INIT_LIST_HEAD(&rt_entry->remote_port_list[i]);
239
240 mutex_init(&rt_entry->lock);
241 rt_entry->node_id = node_id;
242 rt_entry->xprt_info = NULL;
243 return rt_entry;
244}
245
246/*Please take routing_table_lock before calling this function*/
247static int add_routing_table_entry(
248 struct msm_ipc_routing_table_entry *rt_entry)
249{
250 uint32_t key;
251
252 if (!rt_entry)
253 return -EINVAL;
254
255 key = (rt_entry->node_id % RT_HASH_SIZE);
256 list_add_tail(&rt_entry->list, &routing_table[key]);
257 return 0;
258}
259
260/*Please take routing_table_lock before calling this function*/
261static struct msm_ipc_routing_table_entry *lookup_routing_table(
262 uint32_t node_id)
263{
264 uint32_t key = (node_id % RT_HASH_SIZE);
265 struct msm_ipc_routing_table_entry *rt_entry;
266
267 list_for_each_entry(rt_entry, &routing_table[key], list) {
268 if (rt_entry->node_id == node_id)
269 return rt_entry;
270 }
271 return NULL;
272}
273
274struct rr_packet *rr_read(struct msm_ipc_router_xprt_info *xprt_info)
275{
276 struct rr_packet *temp_pkt;
277
278 if (!xprt_info)
279 return NULL;
280
281 mutex_lock(&xprt_info->rx_lock);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600282 if (xprt_info->abort_data_read) {
283 mutex_unlock(&xprt_info->rx_lock);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -0600284 pr_err("%s detected SSR & exiting now\n",
285 xprt_info->xprt->name);
286 return NULL;
287 }
288
289 if (list_empty(&xprt_info->pkt_list)) {
290 mutex_unlock(&xprt_info->rx_lock);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600291 return NULL;
292 }
293
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 temp_pkt = list_first_entry(&xprt_info->pkt_list,
295 struct rr_packet, list);
296 list_del(&temp_pkt->list);
297 if (list_empty(&xprt_info->pkt_list))
298 wake_unlock(&xprt_info->wakelock);
299 mutex_unlock(&xprt_info->rx_lock);
300 return temp_pkt;
301}
302
303struct rr_packet *clone_pkt(struct rr_packet *pkt)
304{
305 struct rr_packet *cloned_pkt;
306 struct sk_buff *temp_skb, *cloned_skb;
307 struct sk_buff_head *pkt_fragment_q;
308
309 cloned_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
310 if (!cloned_pkt) {
311 pr_err("%s: failure\n", __func__);
312 return NULL;
313 }
314
315 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
316 if (!pkt_fragment_q) {
317 pr_err("%s: pkt_frag_q alloc failure\n", __func__);
318 kfree(cloned_pkt);
319 return NULL;
320 }
321 skb_queue_head_init(pkt_fragment_q);
322
323 skb_queue_walk(pkt->pkt_fragment_q, temp_skb) {
324 cloned_skb = skb_clone(temp_skb, GFP_KERNEL);
325 if (!cloned_skb)
326 goto fail_clone;
327 skb_queue_tail(pkt_fragment_q, cloned_skb);
328 }
329 cloned_pkt->pkt_fragment_q = pkt_fragment_q;
330 cloned_pkt->length = pkt->length;
331 return cloned_pkt;
332
333fail_clone:
334 while (!skb_queue_empty(pkt_fragment_q)) {
335 temp_skb = skb_dequeue(pkt_fragment_q);
336 kfree_skb(temp_skb);
337 }
338 kfree(pkt_fragment_q);
339 kfree(cloned_pkt);
340 return NULL;
341}
342
343struct rr_packet *create_pkt(struct sk_buff_head *data)
344{
345 struct rr_packet *pkt;
346 struct sk_buff *temp_skb;
347
348 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
349 if (!pkt) {
350 pr_err("%s: failure\n", __func__);
351 return NULL;
352 }
353
354 pkt->pkt_fragment_q = data;
355 skb_queue_walk(pkt->pkt_fragment_q, temp_skb)
356 pkt->length += temp_skb->len;
357 return pkt;
358}
359
360void release_pkt(struct rr_packet *pkt)
361{
362 struct sk_buff *temp_skb;
363
364 if (!pkt)
365 return;
366
367 if (!pkt->pkt_fragment_q) {
368 kfree(pkt);
369 return;
370 }
371
372 while (!skb_queue_empty(pkt->pkt_fragment_q)) {
373 temp_skb = skb_dequeue(pkt->pkt_fragment_q);
374 kfree_skb(temp_skb);
375 }
376 kfree(pkt->pkt_fragment_q);
377 kfree(pkt);
378 return;
379}
380
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600381static struct sk_buff_head *msm_ipc_router_buf_to_skb(void *buf,
382 unsigned int buf_len)
383{
384 struct sk_buff_head *skb_head;
385 struct sk_buff *skb;
386 int first = 1, offset = 0;
387 int skb_size, data_size;
388 void *data;
389
390 skb_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
391 if (!skb_head) {
392 pr_err("%s: Couldnot allocate skb_head\n", __func__);
393 return NULL;
394 }
395 skb_queue_head_init(skb_head);
396
397 data_size = buf_len;
398 while (offset != buf_len) {
399 skb_size = data_size;
400 if (first)
401 skb_size += IPC_ROUTER_HDR_SIZE;
402
403 skb = alloc_skb(skb_size, GFP_KERNEL);
404 if (!skb) {
405 if (skb_size <= (PAGE_SIZE/2)) {
406 pr_err("%s: cannot allocate skb\n", __func__);
407 goto buf_to_skb_error;
408 }
409 data_size = data_size / 2;
410 continue;
411 }
412
413 if (first) {
414 skb_reserve(skb, IPC_ROUTER_HDR_SIZE);
415 first = 0;
416 }
417
418 data = skb_put(skb, data_size);
419 memcpy(skb->data, buf + offset, data_size);
420 skb_queue_tail(skb_head, skb);
421 offset += data_size;
422 data_size = buf_len - offset;
423 }
424 return skb_head;
425
426buf_to_skb_error:
427 while (!skb_queue_empty(skb_head)) {
428 skb = skb_dequeue(skb_head);
429 kfree_skb(skb);
430 }
431 kfree(skb_head);
432 return NULL;
433}
434
435static void *msm_ipc_router_skb_to_buf(struct sk_buff_head *skb_head,
436 unsigned int len)
437{
438 struct sk_buff *temp;
439 int offset = 0, buf_len = 0, copy_len;
440 void *buf;
441
442 if (!skb_head) {
443 pr_err("%s: NULL skb_head\n", __func__);
444 return NULL;
445 }
446
447 temp = skb_peek(skb_head);
448 buf_len = len;
449 buf = kmalloc(buf_len, GFP_KERNEL);
450 if (!buf) {
451 pr_err("%s: cannot allocate buf\n", __func__);
452 return NULL;
453 }
454 skb_queue_walk(skb_head, temp) {
455 copy_len = buf_len < temp->len ? buf_len : temp->len;
456 memcpy(buf + offset, temp->data, copy_len);
457 offset += copy_len;
458 buf_len -= copy_len;
459 }
460 return buf;
461}
462
463static void msm_ipc_router_free_skb(struct sk_buff_head *skb_head)
464{
465 struct sk_buff *temp_skb;
466
467 if (!skb_head)
468 return;
469
470 while (!skb_queue_empty(skb_head)) {
471 temp_skb = skb_dequeue(skb_head);
472 kfree_skb(temp_skb);
473 }
474 kfree(skb_head);
475}
476
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700477static int post_control_ports(struct rr_packet *pkt)
478{
479 struct msm_ipc_port *port_ptr;
480 struct rr_packet *cloned_pkt;
481
482 if (!pkt)
483 return -EINVAL;
484
485 mutex_lock(&control_ports_lock);
486 list_for_each_entry(port_ptr, &control_ports, list) {
487 mutex_lock(&port_ptr->port_rx_q_lock);
488 cloned_pkt = clone_pkt(pkt);
489 wake_lock(&port_ptr->port_rx_wake_lock);
490 list_add_tail(&cloned_pkt->list, &port_ptr->port_rx_q);
491 wake_up(&port_ptr->port_rx_wait_q);
492 mutex_unlock(&port_ptr->port_rx_q_lock);
493 }
494 mutex_unlock(&control_ports_lock);
495 return 0;
496}
497
498static uint32_t allocate_port_id(void)
499{
500 uint32_t port_id = 0, prev_port_id, key;
501 struct msm_ipc_port *port_ptr;
502
503 mutex_lock(&next_port_id_lock);
504 prev_port_id = next_port_id;
505 mutex_lock(&local_ports_lock);
506 do {
507 next_port_id++;
508 if ((next_port_id & 0xFFFFFFFE) == 0xFFFFFFFE)
509 next_port_id = 1;
510
511 key = (next_port_id & (LP_HASH_SIZE - 1));
512 if (list_empty(&local_ports[key])) {
513 port_id = next_port_id;
514 break;
515 }
516 list_for_each_entry(port_ptr, &local_ports[key], list) {
517 if (port_ptr->this_port.port_id == next_port_id) {
518 port_id = next_port_id;
519 break;
520 }
521 }
522 if (!port_id) {
523 port_id = next_port_id;
524 break;
525 }
526 port_id = 0;
527 } while (next_port_id != prev_port_id);
528 mutex_unlock(&local_ports_lock);
529 mutex_unlock(&next_port_id_lock);
530
531 return port_id;
532}
533
534void msm_ipc_router_add_local_port(struct msm_ipc_port *port_ptr)
535{
536 uint32_t key;
537
538 if (!port_ptr)
539 return;
540
541 key = (port_ptr->this_port.port_id & (LP_HASH_SIZE - 1));
542 mutex_lock(&local_ports_lock);
543 list_add_tail(&port_ptr->list, &local_ports[key]);
544 mutex_unlock(&local_ports_lock);
545}
546
547struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600548 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549 void *priv)
550{
551 struct msm_ipc_port *port_ptr;
552
553 port_ptr = kzalloc(sizeof(struct msm_ipc_port), GFP_KERNEL);
554 if (!port_ptr)
555 return NULL;
556
557 port_ptr->this_port.node_id = IPC_ROUTER_NID_LOCAL;
558 port_ptr->this_port.port_id = allocate_port_id();
559 if (!port_ptr->this_port.port_id) {
560 pr_err("%s: All port ids are in use\n", __func__);
561 kfree(port_ptr);
562 return NULL;
563 }
564
565 spin_lock_init(&port_ptr->port_lock);
566 INIT_LIST_HEAD(&port_ptr->incomplete);
567 mutex_init(&port_ptr->incomplete_lock);
568 INIT_LIST_HEAD(&port_ptr->port_rx_q);
569 mutex_init(&port_ptr->port_rx_q_lock);
570 init_waitqueue_head(&port_ptr->port_rx_wait_q);
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600571 snprintf(port_ptr->rx_wakelock_name, MAX_WAKELOCK_NAME_SZ,
Karthikeyan Ramasubramanian090486e2013-02-14 13:53:20 -0700572 "ipc%08x_%s",
573 port_ptr->this_port.port_id,
574 current->comm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575 wake_lock_init(&port_ptr->port_rx_wake_lock,
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600576 WAKE_LOCK_SUSPEND, port_ptr->rx_wakelock_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577
578 port_ptr->endpoint = endpoint;
579 port_ptr->notify = notify;
580 port_ptr->priv = priv;
581
582 msm_ipc_router_add_local_port(port_ptr);
583 return port_ptr;
584}
585
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -0600586/*
587 * Should be called with local_ports_lock locked
588 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589static struct msm_ipc_port *msm_ipc_router_lookup_local_port(uint32_t port_id)
590{
591 int key = (port_id & (LP_HASH_SIZE - 1));
592 struct msm_ipc_port *port_ptr;
593
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594 list_for_each_entry(port_ptr, &local_ports[key], list) {
595 if (port_ptr->this_port.port_id == port_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596 return port_ptr;
597 }
598 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599 return NULL;
600}
601
602static struct msm_ipc_router_remote_port *msm_ipc_router_lookup_remote_port(
603 uint32_t node_id,
604 uint32_t port_id)
605{
606 struct msm_ipc_router_remote_port *rport_ptr;
607 struct msm_ipc_routing_table_entry *rt_entry;
608 int key = (port_id & (RP_HASH_SIZE - 1));
609
610 mutex_lock(&routing_table_lock);
611 rt_entry = lookup_routing_table(node_id);
612 if (!rt_entry) {
613 mutex_unlock(&routing_table_lock);
614 pr_err("%s: Node is not up\n", __func__);
615 return NULL;
616 }
617
618 mutex_lock(&rt_entry->lock);
619 list_for_each_entry(rport_ptr,
620 &rt_entry->remote_port_list[key], list) {
621 if (rport_ptr->port_id == port_id) {
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600622 if (rport_ptr->restart_state != RESTART_NORMAL)
623 rport_ptr = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624 mutex_unlock(&rt_entry->lock);
625 mutex_unlock(&routing_table_lock);
626 return rport_ptr;
627 }
628 }
629 mutex_unlock(&rt_entry->lock);
630 mutex_unlock(&routing_table_lock);
631 return NULL;
632}
633
634static struct msm_ipc_router_remote_port *msm_ipc_router_create_remote_port(
635 uint32_t node_id,
636 uint32_t port_id)
637{
638 struct msm_ipc_router_remote_port *rport_ptr;
639 struct msm_ipc_routing_table_entry *rt_entry;
640 int key = (port_id & (RP_HASH_SIZE - 1));
641
642 mutex_lock(&routing_table_lock);
643 rt_entry = lookup_routing_table(node_id);
644 if (!rt_entry) {
645 mutex_unlock(&routing_table_lock);
646 pr_err("%s: Node is not up\n", __func__);
647 return NULL;
648 }
649
650 mutex_lock(&rt_entry->lock);
651 rport_ptr = kmalloc(sizeof(struct msm_ipc_router_remote_port),
652 GFP_KERNEL);
653 if (!rport_ptr) {
654 mutex_unlock(&rt_entry->lock);
655 mutex_unlock(&routing_table_lock);
656 pr_err("%s: Remote port alloc failed\n", __func__);
657 return NULL;
658 }
659 rport_ptr->port_id = port_id;
660 rport_ptr->node_id = node_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600661 rport_ptr->restart_state = RESTART_NORMAL;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600662 rport_ptr->sec_rule = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700663 rport_ptr->tx_quota_cnt = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664 mutex_init(&rport_ptr->quota_lock);
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530665 INIT_LIST_HEAD(&rport_ptr->resume_tx_port_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700666 list_add_tail(&rport_ptr->list,
667 &rt_entry->remote_port_list[key]);
668 mutex_unlock(&rt_entry->lock);
669 mutex_unlock(&routing_table_lock);
670 return rport_ptr;
671}
672
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530673/**
674 * msm_ipc_router_free_resume_tx_port() - Free the resume_tx ports
675 * @rport_ptr: Pointer to the remote port.
676 *
677 * This function deletes all the resume_tx ports associated with a remote port
678 * and frees the memory allocated to each resume_tx port.
679 *
680 * Must be called with rport_ptr->quota_lock locked.
681 */
682static void msm_ipc_router_free_resume_tx_port(
683 struct msm_ipc_router_remote_port *rport_ptr)
684{
685 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
686
687 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
688 &rport_ptr->resume_tx_port_list, list) {
689 list_del(&rtx_port->list);
690 kfree(rtx_port);
691 }
692}
693
694/**
695 * msm_ipc_router_lookup_resume_tx_port() - Lookup resume_tx port list
696 * @rport_ptr: Remote port whose resume_tx port list needs to be looked.
697 * @port_id: Port ID which needs to be looked from the list.
698 *
699 * return 1 if the port_id is found in the list, else 0.
700 *
701 * This function is used to lookup the existence of a local port in
702 * remote port's resume_tx list. This function is used to ensure that
703 * the same port is not added to the remote_port's resume_tx list repeatedly.
704 *
705 * Must be called with rport_ptr->quota_lock locked.
706 */
707static int msm_ipc_router_lookup_resume_tx_port(
708 struct msm_ipc_router_remote_port *rport_ptr, uint32_t port_id)
709{
710 struct msm_ipc_resume_tx_port *rtx_port;
711
712 list_for_each_entry(rtx_port, &rport_ptr->resume_tx_port_list, list) {
713 if (port_id == rtx_port->port_id)
714 return 1;
715 }
716 return 0;
717}
718
719/**
720 * post_resume_tx() - Post the resume_tx event
721 * @rport_ptr: Pointer to the remote port
722 * @pkt : The data packet that is received on a resume_tx event
723 *
724 * This function informs about the reception of the resume_tx message from a
725 * remote port pointed by rport_ptr to all the local ports that are in the
726 * resume_tx_ports_list of this remote port. On posting the information, this
727 * function sequentially deletes each entry in the resume_tx_port_list of the
728 * remote port.
729 *
730 * Must be called with rport_ptr->quota_lock locked.
731 */
732static void post_resume_tx(struct msm_ipc_router_remote_port *rport_ptr,
733 struct rr_packet *pkt)
734{
735 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
736 struct msm_ipc_port *local_port;
737 struct rr_packet *cloned_pkt;
738
739 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
740 &rport_ptr->resume_tx_port_list, list) {
741 mutex_lock(&local_ports_lock);
742 local_port =
743 msm_ipc_router_lookup_local_port(rtx_port->port_id);
744 if (local_port) {
745 cloned_pkt = clone_pkt(pkt);
746 if (cloned_pkt) {
747 mutex_lock(&local_port->port_rx_q_lock);
748 list_add_tail(&cloned_pkt->list,
749 &local_port->port_rx_q);
750 wake_up(&local_port->port_rx_wait_q);
751 mutex_unlock(&local_port->port_rx_q_lock);
752 } else {
753 pr_err("%s: Clone_pkt failed for %08x:%08x\n",
754 __func__, local_port->this_port.node_id,
755 local_port->this_port.port_id);
756 }
757 }
758 mutex_unlock(&local_ports_lock);
759 list_del(&rtx_port->list);
760 kfree(rtx_port);
761 }
762}
763
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700764static void msm_ipc_router_destroy_remote_port(
765 struct msm_ipc_router_remote_port *rport_ptr)
766{
767 uint32_t node_id;
768 struct msm_ipc_routing_table_entry *rt_entry;
769
770 if (!rport_ptr)
771 return;
772
773 node_id = rport_ptr->node_id;
774 mutex_lock(&routing_table_lock);
775 rt_entry = lookup_routing_table(node_id);
776 if (!rt_entry) {
777 mutex_unlock(&routing_table_lock);
778 pr_err("%s: Node %d is not up\n", __func__, node_id);
779 return;
780 }
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530781 mutex_lock(&rport_ptr->quota_lock);
782 msm_ipc_router_free_resume_tx_port(rport_ptr);
783 mutex_unlock(&rport_ptr->quota_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784 mutex_lock(&rt_entry->lock);
785 list_del(&rport_ptr->list);
786 kfree(rport_ptr);
787 mutex_unlock(&rt_entry->lock);
788 mutex_unlock(&routing_table_lock);
789 return;
790}
791
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600792/**
793 * msm_ipc_router_lookup_server() - Lookup server information
794 * @service: Service ID of the server info to be looked up.
795 * @instance: Instance ID of the server info to be looked up.
796 * @node_id: Node/Processor ID in which the server is hosted.
797 * @port_id: Port ID within the node in which the server is hosted.
798 *
799 * @return: If found Pointer to server structure, else NULL.
800 *
801 * Note1: Lock the server_list_lock before accessing this function.
802 * Note2: If the <node_id:port_id> are <0:0>, then the lookup is restricted
803 * to <service:instance>. Used only when a client wants to send a
804 * message to any QMI server.
805 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700806static struct msm_ipc_server *msm_ipc_router_lookup_server(
807 uint32_t service,
808 uint32_t instance,
809 uint32_t node_id,
810 uint32_t port_id)
811{
812 struct msm_ipc_server *server;
813 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600814 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700815
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816 list_for_each_entry(server, &server_list[key], list) {
817 if ((server->name.service != service) ||
818 (server->name.instance != instance))
819 continue;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600820 if ((node_id == 0) && (port_id == 0))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700821 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700822 list_for_each_entry(server_port, &server->server_port_list,
823 list) {
824 if ((server_port->server_addr.node_id == node_id) &&
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600825 (server_port->server_addr.port_id == port_id))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700827 }
828 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700829 return NULL;
830}
831
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600832static void dummy_release(struct device *dev)
833{
834}
835
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600836/**
837 * msm_ipc_router_create_server() - Add server info to hash table
838 * @service: Service ID of the server info to be created.
839 * @instance: Instance ID of the server info to be created.
840 * @node_id: Node/Processor ID in which the server is hosted.
841 * @port_id: Port ID within the node in which the server is hosted.
842 * @xprt_info: XPRT through which the node hosting the server is reached.
843 *
844 * @return: Pointer to server structure on success, else NULL.
845 *
846 * This function adds the server info to the hash table. If the same
847 * server(i.e. <service_id:instance_id>) is hosted in different nodes,
848 * they are maintained as list of "server_port" under "server" structure.
849 * Note: Lock the server_list_lock before accessing this function.
850 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700851static struct msm_ipc_server *msm_ipc_router_create_server(
852 uint32_t service,
853 uint32_t instance,
854 uint32_t node_id,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600855 uint32_t port_id,
856 struct msm_ipc_router_xprt_info *xprt_info)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700857{
858 struct msm_ipc_server *server = NULL;
859 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600860 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700862 list_for_each_entry(server, &server_list[key], list) {
863 if ((server->name.service == service) &&
864 (server->name.instance == instance))
865 goto create_srv_port;
866 }
867
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600868 server = kzalloc(sizeof(struct msm_ipc_server), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869 if (!server) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700870 pr_err("%s: Server allocation failed\n", __func__);
871 return NULL;
872 }
873 server->name.service = service;
874 server->name.instance = instance;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600875 server->synced_sec_rule = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700876 INIT_LIST_HEAD(&server->server_port_list);
877 list_add_tail(&server->list, &server_list[key]);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600878 scnprintf(server->pdev_name, sizeof(server->pdev_name),
879 "QMI%08x:%08x", service, instance);
880 server->next_pdev_id = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700881
882create_srv_port:
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600883 server_port = kzalloc(sizeof(struct msm_ipc_server_port), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700884 if (!server_port) {
885 if (list_empty(&server->server_port_list)) {
886 list_del(&server->list);
887 kfree(server);
888 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700889 pr_err("%s: Server Port allocation failed\n", __func__);
890 return NULL;
891 }
892 server_port->server_addr.node_id = node_id;
893 server_port->server_addr.port_id = port_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600894 server_port->xprt_info = xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895 list_add_tail(&server_port->list, &server->server_port_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600897 server_port->pdev.name = server->pdev_name;
898 server_port->pdev.id = server->next_pdev_id++;
899 server_port->pdev.dev.release = dummy_release;
900 platform_device_register(&server_port->pdev);
901
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700902 return server;
903}
904
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600905/**
906 * msm_ipc_router_destroy_server() - Remove server info from hash table
907 * @server: Server info to be removed.
908 * @node_id: Node/Processor ID in which the server is hosted.
909 * @port_id: Port ID within the node in which the server is hosted.
910 *
911 * This function removes the server_port identified using <node_id:port_id>
912 * from the server structure. If the server_port list under server structure
913 * is empty after removal, then remove the server structure from the server
914 * hash table.
915 * Note: Lock the server_list_lock before accessing this function.
916 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700917static void msm_ipc_router_destroy_server(struct msm_ipc_server *server,
918 uint32_t node_id, uint32_t port_id)
919{
920 struct msm_ipc_server_port *server_port;
921
922 if (!server)
923 return;
924
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700925 list_for_each_entry(server_port, &server->server_port_list, list) {
926 if ((server_port->server_addr.node_id == node_id) &&
927 (server_port->server_addr.port_id == port_id))
928 break;
929 }
930 if (server_port) {
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600931 platform_device_unregister(&server_port->pdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700932 list_del(&server_port->list);
933 kfree(server_port);
934 }
935 if (list_empty(&server->server_port_list)) {
936 list_del(&server->list);
937 kfree(server);
938 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939 return;
940}
941
942static int msm_ipc_router_send_control_msg(
943 struct msm_ipc_router_xprt_info *xprt_info,
944 union rr_control_msg *msg)
945{
946 struct rr_packet *pkt;
947 struct sk_buff *ipc_rtr_pkt;
948 struct rr_header *hdr;
949 int pkt_size;
950 void *data;
951 struct sk_buff_head *pkt_fragment_q;
952 int ret;
953
954 if (!xprt_info || ((msg->cmd != IPC_ROUTER_CTRL_CMD_HELLO) &&
955 !xprt_info->initialized)) {
956 pr_err("%s: xprt_info not initialized\n", __func__);
957 return -EINVAL;
958 }
959
960 if (xprt_info->remote_node_id == IPC_ROUTER_NID_LOCAL)
961 return 0;
962
963 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
964 if (!pkt) {
965 pr_err("%s: pkt alloc failed\n", __func__);
966 return -ENOMEM;
967 }
968
969 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
970 if (!pkt_fragment_q) {
971 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
972 kfree(pkt);
973 return -ENOMEM;
974 }
975 skb_queue_head_init(pkt_fragment_q);
976
977 pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg);
978 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
979 if (!ipc_rtr_pkt) {
980 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
981 kfree(pkt_fragment_q);
982 kfree(pkt);
983 return -ENOMEM;
984 }
985
986 skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
987 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
988 memcpy(data, msg, sizeof(*msg));
989 hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
990 if (!hdr) {
991 pr_err("%s: skb_push failed\n", __func__);
992 kfree_skb(ipc_rtr_pkt);
993 kfree(pkt_fragment_q);
994 kfree(pkt);
995 return -ENOMEM;
996 }
997
998 hdr->version = IPC_ROUTER_VERSION;
999 hdr->type = msg->cmd;
1000 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
1001 hdr->src_port_id = IPC_ROUTER_ADDRESS;
1002 hdr->confirm_rx = 0;
1003 hdr->size = sizeof(*msg);
1004 hdr->dst_node_id = xprt_info->remote_node_id;
1005 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
1006 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
1007 pkt->pkt_fragment_q = pkt_fragment_q;
1008 pkt->length = pkt_size;
1009
1010 mutex_lock(&xprt_info->tx_lock);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07001011 ret = xprt_info->xprt->write(pkt, pkt_size, xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001012 mutex_unlock(&xprt_info->tx_lock);
1013
1014 release_pkt(pkt);
1015 return ret;
1016}
1017
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001018static int msm_ipc_router_send_server_list(uint32_t node_id,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001019 struct msm_ipc_router_xprt_info *xprt_info)
1020{
1021 union rr_control_msg ctl;
1022 struct msm_ipc_server *server;
1023 struct msm_ipc_server_port *server_port;
1024 int i;
1025
1026 if (!xprt_info || !xprt_info->initialized) {
1027 pr_err("%s: Xprt info not initialized\n", __func__);
1028 return -EINVAL;
1029 }
1030
1031 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
1032
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001033 for (i = 0; i < SRV_HASH_SIZE; i++) {
1034 list_for_each_entry(server, &server_list[i], list) {
1035 ctl.srv.service = server->name.service;
1036 ctl.srv.instance = server->name.instance;
1037 list_for_each_entry(server_port,
1038 &server->server_port_list, list) {
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001039 if (server_port->server_addr.node_id !=
1040 node_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001041 continue;
1042
1043 ctl.srv.node_id =
1044 server_port->server_addr.node_id;
1045 ctl.srv.port_id =
1046 server_port->server_addr.port_id;
1047 msm_ipc_router_send_control_msg(xprt_info,
1048 &ctl);
1049 }
1050 }
1051 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001052
1053 return 0;
1054}
1055
1056#if defined(DEBUG)
1057static char *type_to_str(int i)
1058{
1059 switch (i) {
1060 case IPC_ROUTER_CTRL_CMD_DATA:
1061 return "data ";
1062 case IPC_ROUTER_CTRL_CMD_HELLO:
1063 return "hello ";
1064 case IPC_ROUTER_CTRL_CMD_BYE:
1065 return "bye ";
1066 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
1067 return "new_srvr";
1068 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
1069 return "rmv_srvr";
1070 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
1071 return "rmv_clnt";
1072 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
1073 return "resum_tx";
1074 case IPC_ROUTER_CTRL_CMD_EXIT:
1075 return "cmd_exit";
1076 default:
1077 return "invalid";
1078 }
1079}
1080#endif
1081
1082static int broadcast_ctl_msg_locally(union rr_control_msg *msg)
1083{
1084 struct rr_packet *pkt;
1085 struct sk_buff *ipc_rtr_pkt;
1086 struct rr_header *hdr;
1087 int pkt_size;
1088 void *data;
1089 struct sk_buff_head *pkt_fragment_q;
1090 int ret;
1091
1092 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
1093 if (!pkt) {
1094 pr_err("%s: pkt alloc failed\n", __func__);
1095 return -ENOMEM;
1096 }
1097
1098 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
1099 if (!pkt_fragment_q) {
1100 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
1101 kfree(pkt);
1102 return -ENOMEM;
1103 }
1104 skb_queue_head_init(pkt_fragment_q);
1105
1106 pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg);
1107 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
1108 if (!ipc_rtr_pkt) {
1109 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
1110 kfree(pkt_fragment_q);
1111 kfree(pkt);
1112 return -ENOMEM;
1113 }
1114
1115 skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
1116 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
1117 memcpy(data, msg, sizeof(*msg));
1118 hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
1119 if (!hdr) {
1120 pr_err("%s: skb_push failed\n", __func__);
1121 kfree_skb(ipc_rtr_pkt);
1122 kfree(pkt_fragment_q);
1123 kfree(pkt);
1124 return -ENOMEM;
1125 }
1126 hdr->version = IPC_ROUTER_VERSION;
1127 hdr->type = msg->cmd;
1128 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
1129 hdr->src_port_id = IPC_ROUTER_ADDRESS;
1130 hdr->confirm_rx = 0;
1131 hdr->size = sizeof(*msg);
1132 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
1133 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
1134 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
1135 pkt->pkt_fragment_q = pkt_fragment_q;
1136 pkt->length = pkt_size;
1137
1138 ret = post_control_ports(pkt);
1139 release_pkt(pkt);
1140 return ret;
1141}
1142
1143static int broadcast_ctl_msg(union rr_control_msg *ctl)
1144{
1145 struct msm_ipc_router_xprt_info *xprt_info;
1146
1147 mutex_lock(&xprt_info_list_lock);
1148 list_for_each_entry(xprt_info, &xprt_info_list, list) {
1149 msm_ipc_router_send_control_msg(xprt_info, ctl);
1150 }
1151 mutex_unlock(&xprt_info_list_lock);
1152
1153 return 0;
1154}
1155
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001156static int relay_ctl_msg(struct msm_ipc_router_xprt_info *xprt_info,
1157 union rr_control_msg *ctl)
1158{
1159 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1160
1161 if (!xprt_info || !ctl)
1162 return -EINVAL;
1163
1164 mutex_lock(&xprt_info_list_lock);
1165 list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
1166 if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
1167 msm_ipc_router_send_control_msg(fwd_xprt_info, ctl);
1168 }
1169 mutex_unlock(&xprt_info_list_lock);
1170
1171 return 0;
1172}
1173
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001174static int relay_msg(struct msm_ipc_router_xprt_info *xprt_info,
1175 struct rr_packet *pkt)
1176{
1177 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1178
1179 if (!xprt_info || !pkt)
1180 return -EINVAL;
1181
1182 mutex_lock(&xprt_info_list_lock);
1183 list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
1184 mutex_lock(&fwd_xprt_info->tx_lock);
1185 if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07001186 fwd_xprt_info->xprt->write(pkt, pkt->length,
1187 fwd_xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001188 mutex_unlock(&fwd_xprt_info->tx_lock);
1189 }
1190 mutex_unlock(&xprt_info_list_lock);
1191 return 0;
1192}
1193
1194static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info,
1195 struct rr_packet *pkt)
1196{
1197 uint32_t dst_node_id;
1198 struct sk_buff *head_pkt;
1199 struct rr_header *hdr;
1200 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1201 struct msm_ipc_routing_table_entry *rt_entry;
1202
1203 if (!xprt_info || !pkt)
1204 return -EINVAL;
1205
1206 head_pkt = skb_peek(pkt->pkt_fragment_q);
1207 if (!head_pkt)
1208 return -EINVAL;
1209
1210 hdr = (struct rr_header *)head_pkt->data;
1211 dst_node_id = hdr->dst_node_id;
1212 mutex_lock(&routing_table_lock);
1213 rt_entry = lookup_routing_table(dst_node_id);
1214 if (!(rt_entry) || !(rt_entry->xprt_info)) {
1215 mutex_unlock(&routing_table_lock);
1216 pr_err("%s: Routing table not initialized\n", __func__);
1217 return -ENODEV;
1218 }
1219
1220 mutex_lock(&rt_entry->lock);
1221 fwd_xprt_info = rt_entry->xprt_info;
1222 mutex_lock(&fwd_xprt_info->tx_lock);
1223 if (xprt_info->remote_node_id == fwd_xprt_info->remote_node_id) {
1224 mutex_unlock(&fwd_xprt_info->tx_lock);
1225 mutex_unlock(&rt_entry->lock);
1226 mutex_unlock(&routing_table_lock);
1227 pr_err("%s: Discarding Command to route back\n", __func__);
1228 return -EINVAL;
1229 }
1230
1231 if (xprt_info->xprt->link_id == fwd_xprt_info->xprt->link_id) {
1232 mutex_unlock(&fwd_xprt_info->tx_lock);
1233 mutex_unlock(&rt_entry->lock);
1234 mutex_unlock(&routing_table_lock);
1235 pr_err("%s: DST in the same cluster\n", __func__);
1236 return 0;
1237 }
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07001238 fwd_xprt_info->xprt->write(pkt, pkt->length, fwd_xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001239 mutex_unlock(&fwd_xprt_info->tx_lock);
1240 mutex_unlock(&rt_entry->lock);
1241 mutex_unlock(&routing_table_lock);
1242
1243 return 0;
1244}
1245
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001246static void reset_remote_port_info(uint32_t node_id, uint32_t port_id)
1247{
1248 struct msm_ipc_router_remote_port *rport_ptr;
1249
1250 rport_ptr = msm_ipc_router_lookup_remote_port(node_id, port_id);
1251 if (!rport_ptr) {
1252 pr_err("%s: No such remote port %08x:%08x\n",
1253 __func__, node_id, port_id);
1254 return;
1255 }
1256 mutex_lock(&rport_ptr->quota_lock);
1257 rport_ptr->restart_state = RESTART_PEND;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301258 msm_ipc_router_free_resume_tx_port(rport_ptr);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001259 mutex_unlock(&rport_ptr->quota_lock);
1260 return;
1261}
1262
1263static void msm_ipc_cleanup_remote_server_info(
1264 struct msm_ipc_router_xprt_info *xprt_info)
1265{
1266 struct msm_ipc_server *svr, *tmp_svr;
1267 struct msm_ipc_server_port *svr_port, *tmp_svr_port;
1268 int i;
1269 union rr_control_msg ctl;
1270
1271 if (!xprt_info) {
1272 pr_err("%s: Invalid xprt_info\n", __func__);
1273 return;
1274 }
1275
1276 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
1277 mutex_lock(&server_list_lock);
1278 for (i = 0; i < SRV_HASH_SIZE; i++) {
1279 list_for_each_entry_safe(svr, tmp_svr, &server_list[i], list) {
1280 ctl.srv.service = svr->name.service;
1281 ctl.srv.instance = svr->name.instance;
1282 list_for_each_entry_safe(svr_port, tmp_svr_port,
1283 &svr->server_port_list, list) {
1284 if (svr_port->xprt_info != xprt_info)
1285 continue;
1286 D("Remove server %08x:%08x - %08x:%08x",
1287 ctl.srv.service, ctl.srv.instance,
1288 svr_port->server_addr.node_id,
1289 svr_port->server_addr.port_id);
1290 reset_remote_port_info(
1291 svr_port->server_addr.node_id,
1292 svr_port->server_addr.port_id);
1293 ctl.srv.node_id = svr_port->server_addr.node_id;
1294 ctl.srv.port_id = svr_port->server_addr.port_id;
1295 relay_ctl_msg(xprt_info, &ctl);
1296 broadcast_ctl_msg_locally(&ctl);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001297 platform_device_unregister(&svr_port->pdev);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001298 list_del(&svr_port->list);
1299 kfree(svr_port);
1300 }
1301 if (list_empty(&svr->server_port_list)) {
1302 list_del(&svr->list);
1303 kfree(svr);
1304 }
1305 }
1306 }
1307 mutex_unlock(&server_list_lock);
1308}
1309
1310static void msm_ipc_cleanup_remote_client_info(
1311 struct msm_ipc_router_xprt_info *xprt_info)
1312{
1313 struct msm_ipc_routing_table_entry *rt_entry;
1314 struct msm_ipc_router_remote_port *rport_ptr;
1315 int i, j;
1316 union rr_control_msg ctl;
1317
1318 if (!xprt_info) {
1319 pr_err("%s: Invalid xprt_info\n", __func__);
1320 return;
1321 }
1322
1323 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
1324 mutex_lock(&routing_table_lock);
1325 for (i = 0; i < RT_HASH_SIZE; i++) {
1326 list_for_each_entry(rt_entry, &routing_table[i], list) {
1327 mutex_lock(&rt_entry->lock);
1328 if (rt_entry->xprt_info != xprt_info) {
1329 mutex_unlock(&rt_entry->lock);
1330 continue;
1331 }
1332 for (j = 0; j < RP_HASH_SIZE; j++) {
1333 list_for_each_entry(rport_ptr,
1334 &rt_entry->remote_port_list[j], list) {
1335 if (rport_ptr->restart_state ==
1336 RESTART_PEND)
1337 continue;
1338 mutex_lock(&rport_ptr->quota_lock);
1339 rport_ptr->restart_state = RESTART_PEND;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301340 msm_ipc_router_free_resume_tx_port(
1341 rport_ptr);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001342 mutex_unlock(&rport_ptr->quota_lock);
1343 ctl.cli.node_id = rport_ptr->node_id;
1344 ctl.cli.port_id = rport_ptr->port_id;
1345 broadcast_ctl_msg_locally(&ctl);
1346 }
1347 }
1348 mutex_unlock(&rt_entry->lock);
1349 }
1350 }
1351 mutex_unlock(&routing_table_lock);
1352}
1353
1354static void msm_ipc_cleanup_remote_port_info(uint32_t node_id)
1355{
1356 struct msm_ipc_routing_table_entry *rt_entry, *tmp_rt_entry;
1357 struct msm_ipc_router_remote_port *rport_ptr, *tmp_rport_ptr;
1358 int i, j;
1359
1360 mutex_lock(&routing_table_lock);
1361 for (i = 0; i < RT_HASH_SIZE; i++) {
1362 list_for_each_entry_safe(rt_entry, tmp_rt_entry,
1363 &routing_table[i], list) {
1364 mutex_lock(&rt_entry->lock);
1365 if (rt_entry->neighbor_node_id != node_id) {
1366 mutex_unlock(&rt_entry->lock);
1367 continue;
1368 }
1369 for (j = 0; j < RP_HASH_SIZE; j++) {
1370 list_for_each_entry_safe(rport_ptr,
1371 tmp_rport_ptr,
1372 &rt_entry->remote_port_list[j], list) {
1373 list_del(&rport_ptr->list);
1374 kfree(rport_ptr);
1375 }
1376 }
1377 mutex_unlock(&rt_entry->lock);
1378 }
1379 }
1380 mutex_unlock(&routing_table_lock);
1381}
1382
1383static void msm_ipc_cleanup_routing_table(
1384 struct msm_ipc_router_xprt_info *xprt_info)
1385{
1386 int i;
1387 struct msm_ipc_routing_table_entry *rt_entry;
1388
1389 if (!xprt_info) {
1390 pr_err("%s: Invalid xprt_info\n", __func__);
1391 return;
1392 }
1393
1394 mutex_lock(&routing_table_lock);
1395 for (i = 0; i < RT_HASH_SIZE; i++) {
1396 list_for_each_entry(rt_entry, &routing_table[i], list) {
1397 mutex_lock(&rt_entry->lock);
1398 if (rt_entry->xprt_info == xprt_info)
1399 rt_entry->xprt_info = NULL;
1400 mutex_unlock(&rt_entry->lock);
1401 }
1402 }
1403 mutex_unlock(&routing_table_lock);
1404}
1405
1406static void modem_reset_cleanup(struct msm_ipc_router_xprt_info *xprt_info)
1407{
1408
1409 if (!xprt_info) {
1410 pr_err("%s: Invalid xprt_info\n", __func__);
1411 return;
1412 }
1413
1414 msm_ipc_cleanup_remote_server_info(xprt_info);
1415 msm_ipc_cleanup_remote_client_info(xprt_info);
1416 msm_ipc_cleanup_routing_table(xprt_info);
1417}
1418
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001419/**
1420 * sync_sec_rule() - Synchrnoize the security rule into the server structure
1421 * @server: Server structure where the rule has to be synchronized.
1422 * @rule: Security tule to be synchronized.
1423 *
1424 * This function is used to update the server structure with the security
1425 * rule configured for the <service:instance> corresponding to that server.
1426 */
1427static void sync_sec_rule(struct msm_ipc_server *server, void *rule)
1428{
1429 struct msm_ipc_server_port *server_port;
1430 struct msm_ipc_router_remote_port *rport_ptr = NULL;
1431
1432 list_for_each_entry(server_port, &server->server_port_list, list) {
1433 rport_ptr = msm_ipc_router_lookup_remote_port(
1434 server_port->server_addr.node_id,
1435 server_port->server_addr.port_id);
1436 if (!rport_ptr)
1437 continue;
1438 rport_ptr->sec_rule = rule;
1439 }
1440 server->synced_sec_rule = 1;
1441}
1442
1443/**
1444 * msm_ipc_sync_sec_rule() - Sync the security rule to the service
1445 * @service: Service for which the rule has to be synchronized.
1446 * @instance: Instance for which the rule has to be synchronized.
1447 * @rule: Security rule to be synchronized.
1448 *
1449 * This function is used to syncrhonize the security rule with the server
1450 * hash table, if the user-space script configures the rule after the service
1451 * has come up. This function is used to synchronize the security rule to a
1452 * specific service and optionally a specific instance.
1453 */
1454void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule)
1455{
1456 int key = (service & (SRV_HASH_SIZE - 1));
1457 struct msm_ipc_server *server;
1458
1459 mutex_lock(&server_list_lock);
1460 list_for_each_entry(server, &server_list[key], list) {
1461 if (server->name.service != service)
1462 continue;
1463
1464 if (server->name.instance != instance &&
1465 instance != ALL_INSTANCE)
1466 continue;
1467
1468 /*
1469 * If the rule applies to all instances and if the specific
1470 * instance of a service has a rule synchronized already,
1471 * do not apply the rule for that specific instance.
1472 */
1473 if (instance == ALL_INSTANCE && server->synced_sec_rule)
1474 continue;
1475
1476 sync_sec_rule(server, rule);
1477 }
1478 mutex_unlock(&server_list_lock);
1479}
1480
1481/**
1482 * msm_ipc_sync_default_sec_rule() - Default security rule to all services
1483 * @rule: Security rule to be synchronized.
1484 *
1485 * This function is used to syncrhonize the security rule with the server
1486 * hash table, if the user-space script configures the rule after the service
1487 * has come up. This function is used to synchronize the security rule that
1488 * applies to all services, if the concerned service do not have any rule
1489 * defined.
1490 */
1491void msm_ipc_sync_default_sec_rule(void *rule)
1492{
1493 int key;
1494 struct msm_ipc_server *server;
1495
1496 mutex_lock(&server_list_lock);
1497 for (key = 0; key < SRV_HASH_SIZE; key++) {
1498 list_for_each_entry(server, &server_list[key], list) {
1499 if (server->synced_sec_rule)
1500 continue;
1501
1502 sync_sec_rule(server, rule);
1503 }
1504 }
1505 mutex_unlock(&server_list_lock);
1506}
1507
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001508static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
1509 struct rr_header *hdr)
1510{
1511 int i, rc = 0;
1512 union rr_control_msg ctl;
1513 struct msm_ipc_routing_table_entry *rt_entry;
1514
1515 if (!hdr)
1516 return -EINVAL;
1517
1518 RR("o HELLO NID %d\n", hdr->src_node_id);
1519
1520 xprt_info->remote_node_id = hdr->src_node_id;
1521 /*
1522 * Find the entry from Routing Table corresponding to Node ID.
1523 * Under SSR, an entry will be found. When the system boots up
1524 * for the 1st time, an entry will not be found and hence allocate
1525 * an entry. Update the entry with the Node ID that it corresponds
1526 * to and the XPRT through which it can be reached.
1527 */
1528 mutex_lock(&routing_table_lock);
1529 rt_entry = lookup_routing_table(hdr->src_node_id);
1530 if (!rt_entry) {
1531 rt_entry = alloc_routing_table_entry(hdr->src_node_id);
1532 if (!rt_entry) {
1533 mutex_unlock(&routing_table_lock);
1534 pr_err("%s: rt_entry allocation failed\n", __func__);
1535 return -ENOMEM;
1536 }
1537 add_routing_table_entry(rt_entry);
1538 }
1539 mutex_lock(&rt_entry->lock);
1540 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1541 rt_entry->xprt_info = xprt_info;
1542 mutex_unlock(&rt_entry->lock);
1543 mutex_unlock(&routing_table_lock);
1544
1545 /* Cleanup any remote ports, if the node is coming out of reset */
1546 msm_ipc_cleanup_remote_port_info(xprt_info->remote_node_id);
1547
1548 /* Send a reply HELLO message */
1549 memset(&ctl, 0, sizeof(ctl));
1550 ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
1551 rc = msm_ipc_router_send_control_msg(xprt_info, &ctl);
1552 if (rc < 0) {
1553 pr_err("%s: Error sending reply HELLO message\n", __func__);
1554 return rc;
1555 }
1556 xprt_info->initialized = 1;
1557
1558 /*
1559 * Send list of servers from the local node and from nodes
1560 * outside the mesh network in which this XPRT is part of.
1561 */
Karthikeyan Ramasubramaniane8ad5352012-10-31 15:12:08 -06001562 mutex_lock(&server_list_lock);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001563 mutex_lock(&routing_table_lock);
1564 for (i = 0; i < RT_HASH_SIZE; i++) {
1565 list_for_each_entry(rt_entry, &routing_table[i], list) {
1566 if ((rt_entry->node_id != IPC_ROUTER_NID_LOCAL) &&
Karthikeyan Ramasubramanian72ad5792013-01-30 14:17:57 -07001567 (!rt_entry->xprt_info ||
1568 (rt_entry->xprt_info->xprt->link_id ==
1569 xprt_info->xprt->link_id)))
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001570 continue;
1571 rc = msm_ipc_router_send_server_list(rt_entry->node_id,
1572 xprt_info);
1573 if (rc < 0) {
1574 mutex_unlock(&routing_table_lock);
Karthikeyan Ramasubramaniane8ad5352012-10-31 15:12:08 -06001575 mutex_unlock(&server_list_lock);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001576 return rc;
1577 }
1578 }
1579 }
1580 mutex_unlock(&routing_table_lock);
Karthikeyan Ramasubramaniane8ad5352012-10-31 15:12:08 -06001581 mutex_unlock(&server_list_lock);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001582 RR("HELLO message processed\n");
1583 return rc;
1584}
1585
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001586static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info,
1587 struct rr_packet *pkt)
1588{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001589 union rr_control_msg *msg;
1590 struct msm_ipc_router_remote_port *rport_ptr;
1591 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001592 struct sk_buff *temp_ptr;
1593 struct rr_header *hdr;
1594 struct msm_ipc_server *server;
1595 struct msm_ipc_routing_table_entry *rt_entry;
1596
1597 if (pkt->length != (IPC_ROUTER_HDR_SIZE + sizeof(*msg))) {
1598 pr_err("%s: r2r msg size %d != %d\n", __func__, pkt->length,
1599 (IPC_ROUTER_HDR_SIZE + sizeof(*msg)));
1600 return -EINVAL;
1601 }
1602
1603 temp_ptr = skb_peek(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001604 if (!temp_ptr) {
1605 pr_err("%s: pkt_fragment_q is empty\n", __func__);
1606 return -EINVAL;
1607 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001608 hdr = (struct rr_header *)temp_ptr->data;
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001609 if (!hdr) {
1610 pr_err("%s: No data inside the skb\n", __func__);
1611 return -EINVAL;
1612 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001613 msg = (union rr_control_msg *)((char *)hdr + IPC_ROUTER_HDR_SIZE);
1614
1615 switch (msg->cmd) {
1616 case IPC_ROUTER_CTRL_CMD_HELLO:
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001617 rc = process_hello_msg(xprt_info, hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001618 break;
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001619
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001620 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
1621 RR("o RESUME_TX id=%d:%08x\n",
1622 msg->cli.node_id, msg->cli.port_id);
1623
1624 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
1625 msg->cli.port_id);
1626 if (!rport_ptr) {
1627 pr_err("%s: Unable to resume client\n", __func__);
1628 break;
1629 }
1630 mutex_lock(&rport_ptr->quota_lock);
1631 rport_ptr->tx_quota_cnt = 0;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301632 post_resume_tx(rport_ptr, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001633 mutex_unlock(&rport_ptr->quota_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001634 break;
1635
1636 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
1637 if (msg->srv.instance == 0) {
1638 pr_err(
1639 "rpcrouter: Server create rejected, version = 0, "
1640 "service = %08x\n", msg->srv.service);
1641 break;
1642 }
1643
1644 RR("o NEW_SERVER id=%d:%08x service=%08x:%08x\n",
1645 msg->srv.node_id, msg->srv.port_id,
1646 msg->srv.service, msg->srv.instance);
1647
1648 mutex_lock(&routing_table_lock);
1649 rt_entry = lookup_routing_table(msg->srv.node_id);
1650 if (!rt_entry) {
1651 rt_entry = alloc_routing_table_entry(msg->srv.node_id);
1652 if (!rt_entry) {
1653 mutex_unlock(&routing_table_lock);
1654 pr_err("%s: rt_entry allocation failed\n",
1655 __func__);
1656 return -ENOMEM;
1657 }
1658 mutex_lock(&rt_entry->lock);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001659 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001660 rt_entry->xprt_info = xprt_info;
1661 mutex_unlock(&rt_entry->lock);
1662 add_routing_table_entry(rt_entry);
1663 }
1664 mutex_unlock(&routing_table_lock);
1665
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001666 mutex_lock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001667 server = msm_ipc_router_lookup_server(msg->srv.service,
1668 msg->srv.instance,
1669 msg->srv.node_id,
1670 msg->srv.port_id);
1671 if (!server) {
1672 server = msm_ipc_router_create_server(
1673 msg->srv.service, msg->srv.instance,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001674 msg->srv.node_id, msg->srv.port_id, xprt_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001675 if (!server) {
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001676 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001677 pr_err("%s: Server Create failed\n", __func__);
1678 return -ENOMEM;
1679 }
1680
1681 if (!msm_ipc_router_lookup_remote_port(
1682 msg->srv.node_id, msg->srv.port_id)) {
1683 rport_ptr = msm_ipc_router_create_remote_port(
1684 msg->srv.node_id, msg->srv.port_id);
1685 if (!rport_ptr)
1686 pr_err("%s: Remote port create "
1687 "failed\n", __func__);
Brent Hronik0e83d3b2013-05-01 16:25:00 -06001688 else
1689 rport_ptr->sec_rule =
1690 msm_ipc_get_security_rule(
1691 msg->srv.service,
1692 msg->srv.instance);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001693 }
1694 wake_up(&newserver_wait);
1695 }
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001696 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001697
1698 relay_msg(xprt_info, pkt);
1699 post_control_ports(pkt);
1700 break;
1701 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
1702 RR("o REMOVE_SERVER service=%08x:%d\n",
1703 msg->srv.service, msg->srv.instance);
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001704 mutex_lock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001705 server = msm_ipc_router_lookup_server(msg->srv.service,
1706 msg->srv.instance,
1707 msg->srv.node_id,
1708 msg->srv.port_id);
1709 if (server) {
1710 msm_ipc_router_destroy_server(server,
1711 msg->srv.node_id,
1712 msg->srv.port_id);
1713 relay_msg(xprt_info, pkt);
1714 post_control_ports(pkt);
1715 }
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001716 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001717 break;
1718 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
1719 RR("o REMOVE_CLIENT id=%d:%08x\n",
1720 msg->cli.node_id, msg->cli.port_id);
1721 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
1722 msg->cli.port_id);
1723 if (rport_ptr)
1724 msm_ipc_router_destroy_remote_port(rport_ptr);
1725
1726 relay_msg(xprt_info, pkt);
1727 post_control_ports(pkt);
1728 break;
1729 case IPC_ROUTER_CTRL_CMD_PING:
1730 /* No action needed for ping messages received */
1731 RR("o PING\n");
1732 break;
1733 default:
1734 RR("o UNKNOWN(%08x)\n", msg->cmd);
1735 rc = -ENOSYS;
1736 }
1737
1738 return rc;
1739}
1740
1741static void do_read_data(struct work_struct *work)
1742{
1743 struct rr_header *hdr;
1744 struct rr_packet *pkt = NULL;
1745 struct msm_ipc_port *port_ptr;
1746 struct sk_buff *head_skb;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001747 struct msm_ipc_router_remote_port *rport_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001748 uint32_t resume_tx, resume_tx_node_id, resume_tx_port_id;
1749
1750 struct msm_ipc_router_xprt_info *xprt_info =
1751 container_of(work,
1752 struct msm_ipc_router_xprt_info,
1753 read_data);
1754
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001755 while ((pkt = rr_read(xprt_info)) != NULL) {
1756 if (pkt->length < IPC_ROUTER_HDR_SIZE ||
1757 pkt->length > MAX_IPC_PKT_SIZE) {
1758 pr_err("%s: Invalid pkt length %d\n",
1759 __func__, pkt->length);
1760 goto fail_data;
1761 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001762
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001763 head_skb = skb_peek(pkt->pkt_fragment_q);
1764 if (!head_skb) {
1765 pr_err("%s: head_skb is invalid\n", __func__);
1766 goto fail_data;
1767 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001768
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001769 hdr = (struct rr_header *)(head_skb->data);
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06001770 RAW("ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
1771 hdr->version, hdr->type, hdr->src_node_id,
1772 hdr->src_port_id, hdr->confirm_rx, hdr->size,
1773 hdr->dst_node_id, hdr->dst_port_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001774
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001775 if (hdr->version != IPC_ROUTER_VERSION) {
1776 pr_err("version %d != %d\n",
1777 hdr->version, IPC_ROUTER_VERSION);
1778 goto fail_data;
1779 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001780
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001781 if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) &&
1782 ((hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX) ||
1783 (hdr->type == IPC_ROUTER_CTRL_CMD_DATA))) {
1784 forward_msg(xprt_info, pkt);
1785 release_pkt(pkt);
1786 continue;
1787 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001788
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001789 if ((hdr->dst_port_id == IPC_ROUTER_ADDRESS) ||
1790 (hdr->type == IPC_ROUTER_CTRL_CMD_HELLO)) {
1791 process_control_msg(xprt_info, pkt);
1792 release_pkt(pkt);
1793 continue;
1794 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001795#if defined(CONFIG_MSM_SMD_LOGGING)
1796#if defined(DEBUG)
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001797 if (msm_ipc_router_debug_mask & SMEM_LOG) {
1798 smem_log_event((SMEM_LOG_PROC_ID_APPS |
1799 SMEM_LOG_RPC_ROUTER_EVENT_BASE |
1800 IPC_ROUTER_LOG_EVENT_RX),
1801 (hdr->src_node_id << 24) |
1802 (hdr->src_port_id & 0xffffff),
1803 (hdr->dst_node_id << 24) |
1804 (hdr->dst_port_id & 0xffffff),
1805 (hdr->type << 24) | (hdr->confirm_rx << 16) |
1806 (hdr->size & 0xffff));
1807 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001808#endif
1809#endif
1810
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001811 resume_tx = hdr->confirm_rx;
1812 resume_tx_node_id = hdr->dst_node_id;
1813 resume_tx_port_id = hdr->dst_port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001814
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001815 rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06001816 hdr->src_port_id);
1817
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001818 mutex_lock(&local_ports_lock);
1819 port_ptr = msm_ipc_router_lookup_local_port(hdr->dst_port_id);
1820 if (!port_ptr) {
1821 pr_err("%s: No local port id %08x\n", __func__,
1822 hdr->dst_port_id);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06001823 mutex_unlock(&local_ports_lock);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001824 release_pkt(pkt);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001825 goto process_done;
1826 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001827
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001828 if (!rport_ptr) {
1829 rport_ptr = msm_ipc_router_create_remote_port(
1830 hdr->src_node_id,
1831 hdr->src_port_id);
1832 if (!rport_ptr) {
1833 pr_err("%s: Rmt Prt %08x:%08x create failed\n",
1834 __func__, hdr->src_node_id,
1835 hdr->src_port_id);
1836 mutex_unlock(&local_ports_lock);
1837 goto process_done;
1838 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001839 }
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001840
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001841 mutex_lock(&port_ptr->port_rx_q_lock);
1842 wake_lock(&port_ptr->port_rx_wake_lock);
1843 list_add_tail(&pkt->list, &port_ptr->port_rx_q);
1844 wake_up(&port_ptr->port_rx_wait_q);
1845 if (port_ptr->notify)
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001846 port_ptr->notify(MSM_IPC_ROUTER_READ_CB,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001847 port_ptr->priv);
1848 mutex_unlock(&port_ptr->port_rx_q_lock);
1849 mutex_unlock(&local_ports_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001850
1851process_done:
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001852 if (resume_tx) {
1853 union rr_control_msg msg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001854
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001855 msg.cmd = IPC_ROUTER_CTRL_CMD_RESUME_TX;
1856 msg.cli.node_id = resume_tx_node_id;
1857 msg.cli.port_id = resume_tx_port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001858
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06001859 RR("x RESUME_TX id=%d:%08x\n",
1860 msg.cli.node_id, msg.cli.port_id);
1861 msm_ipc_router_send_control_msg(xprt_info, &msg);
1862 }
1863
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001864 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001865 return;
1866
1867fail_data:
1868 release_pkt(pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001869 pr_err("ipc_router has died\n");
1870}
1871
1872int msm_ipc_router_register_server(struct msm_ipc_port *port_ptr,
1873 struct msm_ipc_addr *name)
1874{
1875 struct msm_ipc_server *server;
1876 unsigned long flags;
1877 union rr_control_msg ctl;
1878
1879 if (!port_ptr || !name)
1880 return -EINVAL;
1881
1882 if (name->addrtype != MSM_IPC_ADDR_NAME)
1883 return -EINVAL;
1884
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001885 mutex_lock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001886 server = msm_ipc_router_lookup_server(name->addr.port_name.service,
1887 name->addr.port_name.instance,
1888 IPC_ROUTER_NID_LOCAL,
1889 port_ptr->this_port.port_id);
1890 if (server) {
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001891 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001892 pr_err("%s: Server already present\n", __func__);
1893 return -EINVAL;
1894 }
1895
1896 server = msm_ipc_router_create_server(name->addr.port_name.service,
1897 name->addr.port_name.instance,
1898 IPC_ROUTER_NID_LOCAL,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001899 port_ptr->this_port.port_id,
1900 NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001901 if (!server) {
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001902 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001903 pr_err("%s: Server Creation failed\n", __func__);
1904 return -EINVAL;
1905 }
1906
1907 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
1908 ctl.srv.service = server->name.service;
1909 ctl.srv.instance = server->name.instance;
1910 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
1911 ctl.srv.port_id = port_ptr->this_port.port_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001912 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001913 broadcast_ctl_msg(&ctl);
1914 spin_lock_irqsave(&port_ptr->port_lock, flags);
1915 port_ptr->type = SERVER_PORT;
1916 port_ptr->port_name.service = server->name.service;
1917 port_ptr->port_name.instance = server->name.instance;
1918 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
1919 return 0;
1920}
1921
1922int msm_ipc_router_unregister_server(struct msm_ipc_port *port_ptr)
1923{
1924 struct msm_ipc_server *server;
1925 unsigned long flags;
1926 union rr_control_msg ctl;
1927
1928 if (!port_ptr)
1929 return -EINVAL;
1930
1931 if (port_ptr->type != SERVER_PORT) {
1932 pr_err("%s: Trying to unregister a non-server port\n",
1933 __func__);
1934 return -EINVAL;
1935 }
1936
1937 if (port_ptr->this_port.node_id != IPC_ROUTER_NID_LOCAL) {
1938 pr_err("%s: Trying to unregister a remote server locally\n",
1939 __func__);
1940 return -EINVAL;
1941 }
1942
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001943 mutex_lock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001944 server = msm_ipc_router_lookup_server(port_ptr->port_name.service,
1945 port_ptr->port_name.instance,
1946 port_ptr->this_port.node_id,
1947 port_ptr->this_port.port_id);
1948 if (!server) {
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001949 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001950 pr_err("%s: Server lookup failed\n", __func__);
1951 return -ENODEV;
1952 }
1953
1954 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
1955 ctl.srv.service = server->name.service;
1956 ctl.srv.instance = server->name.instance;
1957 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
1958 ctl.srv.port_id = port_ptr->this_port.port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001959 msm_ipc_router_destroy_server(server, port_ptr->this_port.node_id,
1960 port_ptr->this_port.port_id);
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001961 mutex_unlock(&server_list_lock);
1962 broadcast_ctl_msg(&ctl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001963 spin_lock_irqsave(&port_ptr->port_lock, flags);
1964 port_ptr->type = CLIENT_PORT;
1965 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
1966 return 0;
1967}
1968
1969static int loopback_data(struct msm_ipc_port *src,
1970 uint32_t port_id,
1971 struct sk_buff_head *data)
1972{
1973 struct sk_buff *head_skb;
1974 struct rr_header *hdr;
1975 struct msm_ipc_port *port_ptr;
1976 struct rr_packet *pkt;
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07001977 int ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001978
1979 if (!data) {
1980 pr_err("%s: Invalid pkt pointer\n", __func__);
1981 return -EINVAL;
1982 }
1983
1984 pkt = create_pkt(data);
1985 if (!pkt) {
1986 pr_err("%s: New pkt create failed\n", __func__);
1987 return -ENOMEM;
1988 }
1989
1990 head_skb = skb_peek(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001991 if (!head_skb) {
1992 pr_err("%s: pkt_fragment_q is empty\n", __func__);
Brent Hronik0e83d3b2013-05-01 16:25:00 -06001993 release_pkt(pkt);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07001994 return -EINVAL;
1995 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001996 hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
1997 if (!hdr) {
1998 pr_err("%s: Prepend Header failed\n", __func__);
1999 release_pkt(pkt);
2000 return -ENOMEM;
2001 }
2002 hdr->version = IPC_ROUTER_VERSION;
2003 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2004 hdr->src_node_id = src->this_port.node_id;
2005 hdr->src_port_id = src->this_port.port_id;
2006 hdr->size = pkt->length;
2007 hdr->confirm_rx = 0;
2008 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
2009 hdr->dst_port_id = port_id;
2010 pkt->length += IPC_ROUTER_HDR_SIZE;
2011
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002012 mutex_lock(&local_ports_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002013 port_ptr = msm_ipc_router_lookup_local_port(port_id);
2014 if (!port_ptr) {
2015 pr_err("%s: Local port %d not present\n", __func__, port_id);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002016 mutex_unlock(&local_ports_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002017 release_pkt(pkt);
2018 return -ENODEV;
2019 }
2020
2021 mutex_lock(&port_ptr->port_rx_q_lock);
2022 wake_lock(&port_ptr->port_rx_wake_lock);
2023 list_add_tail(&pkt->list, &port_ptr->port_rx_q);
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07002024 ret_len = pkt->length;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002025 wake_up(&port_ptr->port_rx_wait_q);
2026 mutex_unlock(&port_ptr->port_rx_q_lock);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002027 mutex_unlock(&local_ports_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002028
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07002029 return ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002030}
2031
2032static int msm_ipc_router_write_pkt(struct msm_ipc_port *src,
2033 struct msm_ipc_router_remote_port *rport_ptr,
2034 struct rr_packet *pkt)
2035{
2036 struct sk_buff *head_skb;
2037 struct rr_header *hdr;
2038 struct msm_ipc_router_xprt_info *xprt_info;
2039 struct msm_ipc_routing_table_entry *rt_entry;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302040 struct msm_ipc_resume_tx_port *resume_tx_port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002041 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002042
2043 if (!rport_ptr || !src || !pkt)
2044 return -EINVAL;
2045
2046 head_skb = skb_peek(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07002047 if (!head_skb) {
2048 pr_err("%s: pkt_fragment_q is empty\n", __func__);
2049 return -EINVAL;
2050 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002051 hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
2052 if (!hdr) {
2053 pr_err("%s: Prepend Header failed\n", __func__);
2054 return -ENOMEM;
2055 }
2056 hdr->version = IPC_ROUTER_VERSION;
2057 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2058 hdr->src_node_id = src->this_port.node_id;
2059 hdr->src_port_id = src->this_port.port_id;
2060 hdr->size = pkt->length;
2061 hdr->confirm_rx = 0;
2062 hdr->dst_node_id = rport_ptr->node_id;
2063 hdr->dst_port_id = rport_ptr->port_id;
2064 pkt->length += IPC_ROUTER_HDR_SIZE;
2065
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302066 mutex_lock(&rport_ptr->quota_lock);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002067 if (rport_ptr->restart_state != RESTART_NORMAL) {
2068 mutex_unlock(&rport_ptr->quota_lock);
2069 return -ENETRESET;
2070 }
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302071 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) {
2072 if (msm_ipc_router_lookup_resume_tx_port(
2073 rport_ptr, src->this_port.port_id)) {
2074 mutex_unlock(&rport_ptr->quota_lock);
2075 return -EAGAIN;
2076 }
2077 resume_tx_port =
2078 kzalloc(sizeof(struct msm_ipc_resume_tx_port),
2079 GFP_KERNEL);
2080 if (!resume_tx_port) {
2081 pr_err("%s: Resume_Tx port allocation failed\n",
2082 __func__);
2083 mutex_unlock(&rport_ptr->quota_lock);
2084 return -ENOMEM;
2085 }
2086 INIT_LIST_HEAD(&resume_tx_port->list);
2087 resume_tx_port->port_id = src->this_port.port_id;
2088 resume_tx_port->node_id = src->this_port.node_id;
2089 list_add_tail(&resume_tx_port->list,
2090 &rport_ptr->resume_tx_port_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002091 mutex_unlock(&rport_ptr->quota_lock);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302092 return -EAGAIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002093 }
2094 rport_ptr->tx_quota_cnt++;
2095 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA)
2096 hdr->confirm_rx = 1;
2097 mutex_unlock(&rport_ptr->quota_lock);
2098
2099 mutex_lock(&routing_table_lock);
2100 rt_entry = lookup_routing_table(hdr->dst_node_id);
2101 if (!rt_entry || !rt_entry->xprt_info) {
2102 mutex_unlock(&routing_table_lock);
2103 pr_err("%s: Remote node %d not up\n",
2104 __func__, hdr->dst_node_id);
2105 return -ENODEV;
2106 }
2107 mutex_lock(&rt_entry->lock);
2108 xprt_info = rt_entry->xprt_info;
2109 mutex_lock(&xprt_info->tx_lock);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002110 ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002111 mutex_unlock(&xprt_info->tx_lock);
2112 mutex_unlock(&rt_entry->lock);
2113 mutex_unlock(&routing_table_lock);
2114
2115 if (ret < 0) {
2116 pr_err("%s: Write on XPRT failed\n", __func__);
2117 return ret;
2118 }
2119
2120 RAW_HDR("[w rr_h] "
2121 "ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
2122 "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
2123 hdr->version, type_to_str(hdr->type),
2124 hdr->src_node_id, hdr->src_port_id,
2125 hdr->confirm_rx, hdr->size,
2126 hdr->dst_node_id, hdr->dst_port_id);
2127
2128#if defined(CONFIG_MSM_SMD_LOGGING)
2129#if defined(DEBUG)
2130 if (msm_ipc_router_debug_mask & SMEM_LOG) {
2131 smem_log_event((SMEM_LOG_PROC_ID_APPS |
2132 SMEM_LOG_RPC_ROUTER_EVENT_BASE |
2133 IPC_ROUTER_LOG_EVENT_TX),
2134 (hdr->src_node_id << 24) |
2135 (hdr->src_port_id & 0xffffff),
2136 (hdr->dst_node_id << 24) |
2137 (hdr->dst_port_id & 0xffffff),
2138 (hdr->type << 24) | (hdr->confirm_rx << 16) |
2139 (hdr->size & 0xffff));
2140 }
2141#endif
2142#endif
2143
2144 return pkt->length;
2145}
2146
2147int msm_ipc_router_send_to(struct msm_ipc_port *src,
2148 struct sk_buff_head *data,
2149 struct msm_ipc_addr *dest)
2150{
2151 uint32_t dst_node_id = 0, dst_port_id = 0;
2152 struct msm_ipc_server *server;
2153 struct msm_ipc_server_port *server_port;
2154 struct msm_ipc_router_remote_port *rport_ptr = NULL;
2155 struct rr_packet *pkt;
2156 int ret;
2157
2158 if (!src || !data || !dest) {
2159 pr_err("%s: Invalid Parameters\n", __func__);
2160 return -EINVAL;
2161 }
2162
2163 /* Resolve Address*/
2164 if (dest->addrtype == MSM_IPC_ADDR_ID) {
2165 dst_node_id = dest->addr.port_addr.node_id;
2166 dst_port_id = dest->addr.port_addr.port_id;
2167 } else if (dest->addrtype == MSM_IPC_ADDR_NAME) {
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002168 mutex_lock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002169 server = msm_ipc_router_lookup_server(
2170 dest->addr.port_name.service,
2171 dest->addr.port_name.instance,
2172 0, 0);
2173 if (!server) {
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002174 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002175 pr_err("%s: Destination not reachable\n", __func__);
2176 return -ENODEV;
2177 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002178 server_port = list_first_entry(&server->server_port_list,
2179 struct msm_ipc_server_port,
2180 list);
2181 dst_node_id = server_port->server_addr.node_id;
2182 dst_port_id = server_port->server_addr.port_id;
2183 mutex_unlock(&server_list_lock);
2184 }
2185 if (dst_node_id == IPC_ROUTER_NID_LOCAL) {
2186 ret = loopback_data(src, dst_port_id, data);
2187 return ret;
2188 }
2189
2190 /* Achieve Flow control */
2191 rport_ptr = msm_ipc_router_lookup_remote_port(dst_node_id,
2192 dst_port_id);
2193 if (!rport_ptr) {
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002194 pr_err("%s: Could not create remote port\n", __func__);
2195 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002196 }
2197
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002198 if (src->check_send_permissions) {
2199 ret = src->check_send_permissions(rport_ptr->sec_rule);
2200 if (ret <= 0) {
2201 pr_err("%s: permission failure for %s\n",
2202 __func__, current->comm);
2203 return -EPERM;
2204 }
2205 }
2206
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002207 pkt = create_pkt(data);
2208 if (!pkt) {
2209 pr_err("%s: Pkt creation failed\n", __func__);
2210 return -ENOMEM;
2211 }
2212
2213 ret = msm_ipc_router_write_pkt(src, rport_ptr, pkt);
2214 release_pkt(pkt);
2215
2216 return ret;
2217}
2218
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002219int msm_ipc_router_send_msg(struct msm_ipc_port *src,
2220 struct msm_ipc_addr *dest,
2221 void *data, unsigned int data_len)
2222{
2223 struct sk_buff_head *out_skb_head;
2224 int ret;
2225
2226 out_skb_head = msm_ipc_router_buf_to_skb(data, data_len);
2227 if (!out_skb_head) {
2228 pr_err("%s: SKB conversion failed\n", __func__);
2229 return -EFAULT;
2230 }
2231
2232 ret = msm_ipc_router_send_to(src, out_skb_head, dest);
2233 if (ret < 0) {
2234 pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
2235 __func__, ret);
2236 msm_ipc_router_free_skb(out_skb_head);
2237 }
2238 return 0;
2239}
2240
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002241int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
2242 struct sk_buff_head **data,
2243 size_t buf_len)
2244{
2245 struct rr_packet *pkt;
2246 int ret;
2247
2248 if (!port_ptr || !data)
2249 return -EINVAL;
2250
2251 mutex_lock(&port_ptr->port_rx_q_lock);
2252 if (list_empty(&port_ptr->port_rx_q)) {
2253 mutex_unlock(&port_ptr->port_rx_q_lock);
2254 return -EAGAIN;
2255 }
2256
2257 pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list);
2258 if ((buf_len) && ((pkt->length - IPC_ROUTER_HDR_SIZE) > buf_len)) {
2259 mutex_unlock(&port_ptr->port_rx_q_lock);
2260 return -ETOOSMALL;
2261 }
2262 list_del(&pkt->list);
2263 if (list_empty(&port_ptr->port_rx_q))
2264 wake_unlock(&port_ptr->port_rx_wake_lock);
2265 *data = pkt->pkt_fragment_q;
2266 ret = pkt->length;
2267 kfree(pkt);
2268 mutex_unlock(&port_ptr->port_rx_q_lock);
2269
2270 return ret;
2271}
2272
2273int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
2274 struct sk_buff_head **data,
2275 struct msm_ipc_addr *src,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002276 long timeout)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002277{
2278 int ret, data_len, align_size;
2279 struct sk_buff *temp_skb;
2280 struct rr_header *hdr = NULL;
2281
2282 if (!port_ptr || !data) {
2283 pr_err("%s: Invalid pointers being passed\n", __func__);
2284 return -EINVAL;
2285 }
2286
2287 *data = NULL;
2288 mutex_lock(&port_ptr->port_rx_q_lock);
2289 while (list_empty(&port_ptr->port_rx_q)) {
2290 mutex_unlock(&port_ptr->port_rx_q_lock);
2291 if (timeout < 0) {
2292 ret = wait_event_interruptible(
2293 port_ptr->port_rx_wait_q,
2294 !list_empty(&port_ptr->port_rx_q));
2295 if (ret)
2296 return ret;
2297 } else if (timeout > 0) {
2298 timeout = wait_event_interruptible_timeout(
2299 port_ptr->port_rx_wait_q,
2300 !list_empty(&port_ptr->port_rx_q),
2301 timeout);
2302 if (timeout < 0)
2303 return -EFAULT;
2304 }
2305 if (timeout == 0)
2306 return -ETIMEDOUT;
2307 mutex_lock(&port_ptr->port_rx_q_lock);
2308 }
2309 mutex_unlock(&port_ptr->port_rx_q_lock);
2310
2311 ret = msm_ipc_router_read(port_ptr, data, 0);
2312 if (ret <= 0 || !(*data))
2313 return ret;
2314
2315 temp_skb = skb_peek(*data);
2316 hdr = (struct rr_header *)(temp_skb->data);
2317 if (src) {
2318 src->addrtype = MSM_IPC_ADDR_ID;
2319 src->addr.port_addr.node_id = hdr->src_node_id;
2320 src->addr.port_addr.port_id = hdr->src_port_id;
2321 }
2322
2323 data_len = hdr->size;
2324 skb_pull(temp_skb, IPC_ROUTER_HDR_SIZE);
2325 align_size = ALIGN_SIZE(data_len);
2326 if (align_size) {
2327 temp_skb = skb_peek_tail(*data);
2328 skb_trim(temp_skb, (temp_skb->len - align_size));
2329 }
2330 return data_len;
2331}
2332
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002333int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
2334 struct msm_ipc_addr *src,
2335 unsigned char **data,
2336 unsigned int *len)
2337{
2338 struct sk_buff_head *in_skb_head;
2339 int ret;
2340
2341 ret = msm_ipc_router_recv_from(port_ptr, &in_skb_head, src, -1);
2342 if (ret < 0) {
2343 pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
2344 __func__, ret);
2345 return ret;
2346 }
2347
2348 *data = msm_ipc_router_skb_to_buf(in_skb_head, ret);
2349 if (!(*data))
2350 pr_err("%s: Buf conversion failed\n", __func__);
2351
2352 *len = ret;
2353 msm_ipc_router_free_skb(in_skb_head);
2354 return 0;
2355}
2356
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002357struct msm_ipc_port *msm_ipc_router_create_port(
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002358 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002359 void *priv)
2360{
2361 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002362 int ret;
2363
2364 ret = wait_for_completion_interruptible(&msm_ipc_local_router_up);
2365 if (ret < 0) {
2366 pr_err("%s: Error waiting for local router\n", __func__);
2367 return NULL;
2368 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002369
2370 port_ptr = msm_ipc_router_create_raw_port(NULL, notify, priv);
2371 if (!port_ptr)
2372 pr_err("%s: port_ptr alloc failed\n", __func__);
2373
2374 return port_ptr;
2375}
2376
2377int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
2378{
2379 union rr_control_msg msg;
2380 struct rr_packet *pkt, *temp_pkt;
2381 struct msm_ipc_server *server;
2382
2383 if (!port_ptr)
2384 return -EINVAL;
2385
2386 if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) {
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002387 mutex_lock(&local_ports_lock);
2388 list_del(&port_ptr->list);
2389 mutex_unlock(&local_ports_lock);
2390
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002391 if (port_ptr->type == SERVER_PORT) {
2392 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
2393 msg.srv.service = port_ptr->port_name.service;
2394 msg.srv.instance = port_ptr->port_name.instance;
2395 msg.srv.node_id = port_ptr->this_port.node_id;
2396 msg.srv.port_id = port_ptr->this_port.port_id;
2397 RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
2398 msg.srv.service, msg.srv.instance,
2399 msg.srv.node_id, msg.srv.port_id);
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002400 broadcast_ctl_msg(&msg);
2401 broadcast_ctl_msg_locally(&msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002402 }
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002403
2404 /*
2405 * Server port could have been a client port earlier.
2406 * Send REMOVE_CLIENT message in either case.
2407 */
2408 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
2409 msg.cli.node_id = port_ptr->this_port.node_id;
2410 msg.cli.port_id = port_ptr->this_port.port_id;
2411 RR("x REMOVE_CLIENT id=%d:%08x\n",
2412 msg.cli.node_id, msg.cli.port_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002413 broadcast_ctl_msg(&msg);
2414 broadcast_ctl_msg_locally(&msg);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002415 } else if (port_ptr->type == CONTROL_PORT) {
2416 mutex_lock(&control_ports_lock);
2417 list_del(&port_ptr->list);
2418 mutex_unlock(&control_ports_lock);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002419 } else if (port_ptr->type == IRSC_PORT) {
2420 mutex_lock(&local_ports_lock);
2421 list_del(&port_ptr->list);
2422 mutex_unlock(&local_ports_lock);
2423 signal_irsc_completion();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002424 }
2425
2426 mutex_lock(&port_ptr->port_rx_q_lock);
2427 list_for_each_entry_safe(pkt, temp_pkt, &port_ptr->port_rx_q, list) {
2428 list_del(&pkt->list);
2429 release_pkt(pkt);
2430 }
2431 mutex_unlock(&port_ptr->port_rx_q_lock);
2432
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002433 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002434 mutex_lock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002435 server = msm_ipc_router_lookup_server(
2436 port_ptr->port_name.service,
2437 port_ptr->port_name.instance,
2438 port_ptr->this_port.node_id,
2439 port_ptr->this_port.port_id);
2440 if (server)
2441 msm_ipc_router_destroy_server(server,
2442 port_ptr->this_port.node_id,
2443 port_ptr->this_port.port_id);
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002444 mutex_unlock(&server_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002445 }
2446
Karthikeyan Ramasubramaniandd8c3b52011-11-30 16:26:12 -07002447 wake_lock_destroy(&port_ptr->port_rx_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002448 kfree(port_ptr);
2449 return 0;
2450}
2451
2452int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr)
2453{
2454 struct rr_packet *pkt;
2455 int rc = 0;
2456
2457 if (!port_ptr)
2458 return -EINVAL;
2459
2460 mutex_lock(&port_ptr->port_rx_q_lock);
2461 if (!list_empty(&port_ptr->port_rx_q)) {
2462 pkt = list_first_entry(&port_ptr->port_rx_q,
2463 struct rr_packet, list);
2464 rc = pkt->length;
2465 }
2466 mutex_unlock(&port_ptr->port_rx_q_lock);
2467
2468 return rc;
2469}
2470
2471int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr)
2472{
2473 if (!port_ptr)
2474 return -EINVAL;
2475
2476 mutex_lock(&local_ports_lock);
2477 list_del(&port_ptr->list);
2478 mutex_unlock(&local_ports_lock);
2479 port_ptr->type = CONTROL_PORT;
2480 mutex_lock(&control_ports_lock);
2481 list_add_tail(&port_ptr->list, &control_ports);
2482 mutex_unlock(&control_ports_lock);
2483
2484 return 0;
2485}
2486
2487int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002488 struct msm_ipc_server_info *srv_info,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002489 int num_entries_in_array,
2490 uint32_t lookup_mask)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002491{
2492 struct msm_ipc_server *server;
2493 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002494 int key, i = 0; /*num_entries_found*/
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002495
2496 if (!srv_name) {
2497 pr_err("%s: Invalid srv_name\n", __func__);
2498 return -EINVAL;
2499 }
2500
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002501 if (num_entries_in_array && !srv_info) {
2502 pr_err("%s: srv_info NULL\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002503 return -EINVAL;
2504 }
2505
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002506 mutex_lock(&server_list_lock);
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002507 if (!lookup_mask)
2508 lookup_mask = 0xFFFFFFFF;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002509 key = (srv_name->service & (SRV_HASH_SIZE - 1));
2510 list_for_each_entry(server, &server_list[key], list) {
2511 if ((server->name.service != srv_name->service) ||
2512 ((server->name.instance & lookup_mask) !=
2513 srv_name->instance))
2514 continue;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002515
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002516 list_for_each_entry(server_port,
2517 &server->server_port_list, list) {
2518 if (i < num_entries_in_array) {
2519 srv_info[i].node_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002520 server_port->server_addr.node_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002521 srv_info[i].port_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002522 server_port->server_addr.port_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002523 srv_info[i].service = server->name.service;
2524 srv_info[i].instance = server->name.instance;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002525 }
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002526 i++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002527 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002528 }
2529 mutex_unlock(&server_list_lock);
2530
2531 return i;
2532}
2533
2534int msm_ipc_router_close(void)
2535{
2536 struct msm_ipc_router_xprt_info *xprt_info, *tmp_xprt_info;
2537
2538 mutex_lock(&xprt_info_list_lock);
2539 list_for_each_entry_safe(xprt_info, tmp_xprt_info,
2540 &xprt_info_list, list) {
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002541 xprt_info->xprt->close(xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002542 list_del(&xprt_info->list);
2543 kfree(xprt_info);
2544 }
2545 mutex_unlock(&xprt_info_list_lock);
2546 return 0;
2547}
2548
2549#if defined(CONFIG_DEBUG_FS)
2550static int dump_routing_table(char *buf, int max)
2551{
2552 int i = 0, j;
2553 struct msm_ipc_routing_table_entry *rt_entry;
2554
2555 for (j = 0; j < RT_HASH_SIZE; j++) {
2556 mutex_lock(&routing_table_lock);
2557 list_for_each_entry(rt_entry, &routing_table[j], list) {
2558 mutex_lock(&rt_entry->lock);
2559 i += scnprintf(buf + i, max - i,
2560 "Node Id: 0x%08x\n", rt_entry->node_id);
Karthikeyan Ramasubramanianc1a4e3a2012-09-10 16:10:24 -06002561 if (rt_entry->node_id == IPC_ROUTER_NID_LOCAL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002562 i += scnprintf(buf + i, max - i,
2563 "XPRT Name: Loopback\n");
2564 i += scnprintf(buf + i, max - i,
2565 "Next Hop: %d\n", rt_entry->node_id);
2566 } else {
2567 i += scnprintf(buf + i, max - i,
2568 "XPRT Name: %s\n",
2569 rt_entry->xprt_info->xprt->name);
2570 i += scnprintf(buf + i, max - i,
2571 "Next Hop: 0x%08x\n",
2572 rt_entry->xprt_info->remote_node_id);
2573 }
2574 i += scnprintf(buf + i, max - i, "\n");
2575 mutex_unlock(&rt_entry->lock);
2576 }
2577 mutex_unlock(&routing_table_lock);
2578 }
2579
2580 return i;
2581}
2582
2583static int dump_xprt_info(char *buf, int max)
2584{
2585 int i = 0;
2586 struct msm_ipc_router_xprt_info *xprt_info;
2587
2588 mutex_lock(&xprt_info_list_lock);
2589 list_for_each_entry(xprt_info, &xprt_info_list, list) {
2590 i += scnprintf(buf + i, max - i, "XPRT Name: %s\n",
2591 xprt_info->xprt->name);
2592 i += scnprintf(buf + i, max - i, "Link Id: %d\n",
2593 xprt_info->xprt->link_id);
2594 i += scnprintf(buf + i, max - i, "Initialized: %s\n",
2595 (xprt_info->initialized ? "Y" : "N"));
2596 i += scnprintf(buf + i, max - i, "Remote Node Id: 0x%08x\n",
2597 xprt_info->remote_node_id);
2598 i += scnprintf(buf + i, max - i, "\n");
2599 }
2600 mutex_unlock(&xprt_info_list_lock);
2601
2602 return i;
2603}
2604
2605static int dump_servers(char *buf, int max)
2606{
2607 int i = 0, j;
2608 struct msm_ipc_server *server;
2609 struct msm_ipc_server_port *server_port;
2610
2611 mutex_lock(&server_list_lock);
2612 for (j = 0; j < SRV_HASH_SIZE; j++) {
2613 list_for_each_entry(server, &server_list[j], list) {
2614 list_for_each_entry(server_port,
2615 &server->server_port_list,
2616 list) {
2617 i += scnprintf(buf + i, max - i, "Service: "
2618 "0x%08x\n", server->name.service);
2619 i += scnprintf(buf + i, max - i, "Instance: "
2620 "0x%08x\n", server->name.instance);
2621 i += scnprintf(buf + i, max - i,
2622 "Node_id: 0x%08x\n",
2623 server_port->server_addr.node_id);
2624 i += scnprintf(buf + i, max - i,
2625 "Port_id: 0x%08x\n",
2626 server_port->server_addr.port_id);
2627 i += scnprintf(buf + i, max - i, "\n");
2628 }
2629 }
2630 }
2631 mutex_unlock(&server_list_lock);
2632
2633 return i;
2634}
2635
2636static int dump_remote_ports(char *buf, int max)
2637{
2638 int i = 0, j, k;
2639 struct msm_ipc_router_remote_port *rport_ptr;
2640 struct msm_ipc_routing_table_entry *rt_entry;
2641
2642 for (j = 0; j < RT_HASH_SIZE; j++) {
2643 mutex_lock(&routing_table_lock);
2644 list_for_each_entry(rt_entry, &routing_table[j], list) {
2645 mutex_lock(&rt_entry->lock);
2646 for (k = 0; k < RP_HASH_SIZE; k++) {
2647 list_for_each_entry(rport_ptr,
2648 &rt_entry->remote_port_list[k],
2649 list) {
2650 i += scnprintf(buf + i, max - i,
2651 "Node_id: 0x%08x\n",
2652 rport_ptr->node_id);
2653 i += scnprintf(buf + i, max - i,
2654 "Port_id: 0x%08x\n",
2655 rport_ptr->port_id);
2656 i += scnprintf(buf + i, max - i,
2657 "Quota_cnt: %d\n",
2658 rport_ptr->tx_quota_cnt);
2659 i += scnprintf(buf + i, max - i, "\n");
2660 }
2661 }
2662 mutex_unlock(&rt_entry->lock);
2663 }
2664 mutex_unlock(&routing_table_lock);
2665 }
2666
2667 return i;
2668}
2669
2670static int dump_control_ports(char *buf, int max)
2671{
2672 int i = 0;
2673 struct msm_ipc_port *port_ptr;
2674
2675 mutex_lock(&control_ports_lock);
2676 list_for_each_entry(port_ptr, &control_ports, list) {
2677 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
2678 port_ptr->this_port.node_id);
2679 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
2680 port_ptr->this_port.port_id);
2681 i += scnprintf(buf + i, max - i, "\n");
2682 }
2683 mutex_unlock(&control_ports_lock);
2684
2685 return i;
2686}
2687
2688static int dump_local_ports(char *buf, int max)
2689{
2690 int i = 0, j;
2691 unsigned long flags;
2692 struct msm_ipc_port *port_ptr;
2693
2694 mutex_lock(&local_ports_lock);
2695 for (j = 0; j < LP_HASH_SIZE; j++) {
2696 list_for_each_entry(port_ptr, &local_ports[j], list) {
2697 spin_lock_irqsave(&port_ptr->port_lock, flags);
2698 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
2699 port_ptr->this_port.node_id);
2700 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
2701 port_ptr->this_port.port_id);
2702 i += scnprintf(buf + i, max - i, "# pkts tx'd %d\n",
2703 port_ptr->num_tx);
2704 i += scnprintf(buf + i, max - i, "# pkts rx'd %d\n",
2705 port_ptr->num_rx);
2706 i += scnprintf(buf + i, max - i, "# bytes tx'd %ld\n",
2707 port_ptr->num_tx_bytes);
2708 i += scnprintf(buf + i, max - i, "# bytes rx'd %ld\n",
2709 port_ptr->num_rx_bytes);
2710 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
2711 i += scnprintf(buf + i, max - i, "\n");
2712 }
2713 }
2714 mutex_unlock(&local_ports_lock);
2715
2716 return i;
2717}
2718
2719#define DEBUG_BUFMAX 4096
2720static char debug_buffer[DEBUG_BUFMAX];
2721
2722static ssize_t debug_read(struct file *file, char __user *buf,
2723 size_t count, loff_t *ppos)
2724{
2725 int (*fill)(char *buf, int max) = file->private_data;
2726 int bsize = fill(debug_buffer, DEBUG_BUFMAX);
2727 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
2728}
2729
2730static int debug_open(struct inode *inode, struct file *file)
2731{
2732 file->private_data = inode->i_private;
2733 return 0;
2734}
2735
2736static const struct file_operations debug_ops = {
2737 .read = debug_read,
2738 .open = debug_open,
2739};
2740
2741static void debug_create(const char *name, mode_t mode,
2742 struct dentry *dent,
2743 int (*fill)(char *buf, int max))
2744{
2745 debugfs_create_file(name, mode, dent, fill, &debug_ops);
2746}
2747
2748static void debugfs_init(void)
2749{
2750 struct dentry *dent;
2751
2752 dent = debugfs_create_dir("msm_ipc_router", 0);
2753 if (IS_ERR(dent))
2754 return;
2755
2756 debug_create("dump_local_ports", 0444, dent,
2757 dump_local_ports);
2758 debug_create("dump_remote_ports", 0444, dent,
2759 dump_remote_ports);
2760 debug_create("dump_control_ports", 0444, dent,
2761 dump_control_ports);
2762 debug_create("dump_servers", 0444, dent,
2763 dump_servers);
2764 debug_create("dump_xprt_info", 0444, dent,
2765 dump_xprt_info);
2766 debug_create("dump_routing_table", 0444, dent,
2767 dump_routing_table);
2768}
2769
2770#else
2771static void debugfs_init(void) {}
2772#endif
2773
2774static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
2775{
2776 struct msm_ipc_router_xprt_info *xprt_info;
2777 struct msm_ipc_routing_table_entry *rt_entry;
2778
2779 xprt_info = kmalloc(sizeof(struct msm_ipc_router_xprt_info),
2780 GFP_KERNEL);
2781 if (!xprt_info)
2782 return -ENOMEM;
2783
2784 xprt_info->xprt = xprt;
2785 xprt_info->initialized = 0;
2786 xprt_info->remote_node_id = -1;
2787 INIT_LIST_HEAD(&xprt_info->pkt_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002788 mutex_init(&xprt_info->rx_lock);
2789 mutex_init(&xprt_info->tx_lock);
2790 wake_lock_init(&xprt_info->wakelock,
2791 WAKE_LOCK_SUSPEND, xprt->name);
2792 xprt_info->need_len = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002793 xprt_info->abort_data_read = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002794 INIT_WORK(&xprt_info->read_data, do_read_data);
2795 INIT_LIST_HEAD(&xprt_info->list);
2796
2797 xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
2798 if (!xprt_info->workqueue) {
2799 kfree(xprt_info);
2800 return -ENOMEM;
2801 }
2802
2803 if (!strcmp(xprt->name, "msm_ipc_router_loopback_xprt")) {
2804 xprt_info->remote_node_id = IPC_ROUTER_NID_LOCAL;
2805 xprt_info->initialized = 1;
2806 }
2807
2808 mutex_lock(&xprt_info_list_lock);
2809 list_add_tail(&xprt_info->list, &xprt_info_list);
2810 mutex_unlock(&xprt_info_list_lock);
2811
2812 mutex_lock(&routing_table_lock);
2813 if (!routing_table_inited) {
2814 init_routing_table();
2815 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
2816 add_routing_table_entry(rt_entry);
2817 routing_table_inited = 1;
2818 }
2819 mutex_unlock(&routing_table_lock);
2820
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002821 xprt->priv = xprt_info;
2822
2823 return 0;
2824}
2825
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002826static void msm_ipc_router_remove_xprt(struct msm_ipc_router_xprt *xprt)
2827{
2828 struct msm_ipc_router_xprt_info *xprt_info;
2829
2830 if (xprt && xprt->priv) {
2831 xprt_info = xprt->priv;
2832
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002833 mutex_lock(&xprt_info->rx_lock);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002834 xprt_info->abort_data_read = 1;
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002835 mutex_unlock(&xprt_info->rx_lock);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002836
2837 mutex_lock(&xprt_info_list_lock);
2838 list_del(&xprt_info->list);
2839 mutex_unlock(&xprt_info_list_lock);
2840
2841 flush_workqueue(xprt_info->workqueue);
2842 destroy_workqueue(xprt_info->workqueue);
2843 wake_lock_destroy(&xprt_info->wakelock);
2844
2845 xprt->priv = 0;
2846 kfree(xprt_info);
2847 }
2848}
2849
2850
2851struct msm_ipc_router_xprt_work {
2852 struct msm_ipc_router_xprt *xprt;
2853 struct work_struct work;
2854};
2855
2856static void xprt_open_worker(struct work_struct *work)
2857{
2858 struct msm_ipc_router_xprt_work *xprt_work =
2859 container_of(work, struct msm_ipc_router_xprt_work, work);
2860
2861 msm_ipc_router_add_xprt(xprt_work->xprt);
2862 kfree(xprt_work);
2863}
2864
2865static void xprt_close_worker(struct work_struct *work)
2866{
2867 struct msm_ipc_router_xprt_work *xprt_work =
2868 container_of(work, struct msm_ipc_router_xprt_work, work);
2869
2870 modem_reset_cleanup(xprt_work->xprt->priv);
2871 msm_ipc_router_remove_xprt(xprt_work->xprt);
Zaheerulla Meer35893a62013-06-19 16:54:44 +05302872 xprt_work->xprt->sft_close_done(xprt_work->xprt);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002873 kfree(xprt_work);
2874}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002875
2876void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
2877 unsigned event,
2878 void *data)
2879{
2880 struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002881 struct msm_ipc_router_xprt_work *xprt_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002882 struct rr_packet *pkt;
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06002883 unsigned long ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002884
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06002885 if (!msm_ipc_router_workqueue) {
2886 ret = wait_for_completion_timeout(&msm_ipc_local_router_up,
2887 IPC_ROUTER_INIT_TIMEOUT);
2888 if (!ret || !msm_ipc_router_workqueue) {
2889 pr_err("%s: IPC Router not initialized\n", __func__);
2890 return;
2891 }
2892 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002893
2894 switch (event) {
2895 case IPC_ROUTER_XPRT_EVENT_OPEN:
2896 D("open event for '%s'\n", xprt->name);
2897 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
2898 GFP_ATOMIC);
2899 xprt_work->xprt = xprt;
2900 INIT_WORK(&xprt_work->work, xprt_open_worker);
2901 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
2902 break;
2903
2904 case IPC_ROUTER_XPRT_EVENT_CLOSE:
2905 D("close event for '%s'\n", xprt->name);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002906 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
2907 GFP_ATOMIC);
2908 xprt_work->xprt = xprt;
2909 INIT_WORK(&xprt_work->work, xprt_close_worker);
2910 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
2911 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002912 }
2913
2914 if (!data)
2915 return;
2916
2917 while (!xprt_info) {
2918 msleep(100);
2919 xprt_info = xprt->priv;
2920 }
2921
2922 pkt = clone_pkt((struct rr_packet *)data);
2923 if (!pkt)
2924 return;
2925
2926 mutex_lock(&xprt_info->rx_lock);
2927 list_add_tail(&pkt->list, &xprt_info->pkt_list);
2928 wake_lock(&xprt_info->wakelock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002929 mutex_unlock(&xprt_info->rx_lock);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002930 queue_work(xprt_info->workqueue, &xprt_info->read_data);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002931}
2932
2933static int __init msm_ipc_router_init(void)
2934{
2935 int i, ret;
2936 struct msm_ipc_routing_table_entry *rt_entry;
2937
2938 msm_ipc_router_debug_mask |= SMEM_LOG;
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06002939 ipc_rtr_log_ctxt = ipc_log_context_create(IPC_RTR_LOG_PAGES,
2940 "ipc_router");
2941 if (!ipc_rtr_log_ctxt)
2942 pr_err("%s: Unable to create IPC logging for IPC RTR",
2943 __func__);
2944
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002945 msm_ipc_router_workqueue =
2946 create_singlethread_workqueue("msm_ipc_router");
2947 if (!msm_ipc_router_workqueue)
2948 return -ENOMEM;
2949
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002950 debugfs_init();
2951
2952 for (i = 0; i < SRV_HASH_SIZE; i++)
2953 INIT_LIST_HEAD(&server_list[i]);
2954
2955 for (i = 0; i < LP_HASH_SIZE; i++)
2956 INIT_LIST_HEAD(&local_ports[i]);
2957
2958 mutex_lock(&routing_table_lock);
2959 if (!routing_table_inited) {
2960 init_routing_table();
2961 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
2962 add_routing_table_entry(rt_entry);
2963 routing_table_inited = 1;
2964 }
2965 mutex_unlock(&routing_table_lock);
2966
2967 init_waitqueue_head(&newserver_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002968 ret = msm_ipc_router_init_sockets();
2969 if (ret < 0)
2970 pr_err("%s: Init sockets failed\n", __func__);
2971
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002972 ret = msm_ipc_router_security_init();
2973 if (ret < 0)
2974 pr_err("%s: Security Init failed\n", __func__);
2975
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06002976 complete_all(&msm_ipc_local_router_up);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002977 return ret;
2978}
2979
2980module_init(msm_ipc_router_init);
2981MODULE_DESCRIPTION("MSM IPC Router");
2982MODULE_LICENSE("GPL v2");