blob: ce66531fb277d50c741247003a328a91ff3a81b6 [file] [log] [blame]
Karthikeyan Ramasubramanian5c568fb2013-01-10 17:09:13 -07001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define DEBUG
14
15#include <linux/slab.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/string.h>
19#include <linux/errno.h>
20#include <linux/init.h>
21#include <linux/types.h>
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/sched.h>
25#include <linux/poll.h>
26#include <linux/wakelock.h>
27#include <linux/platform_device.h>
28#include <linux/uaccess.h>
29#include <linux/debugfs.h>
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -060030#include <linux/rwsem.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include <asm/uaccess.h>
33#include <asm/byteorder.h>
34
35#include <mach/smem_log.h>
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060036#include <mach/subsystem_notif.h>
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -060037#include <mach/msm_ipc_router.h>
Karthikeyan Ramasubramanian2bfe8ec2013-03-22 10:47:20 -060038#include <mach/msm_ipc_logging.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
40#include "ipc_router.h"
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060041#include "modem_notifier.h"
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -060042#include "msm_ipc_router_security.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043
44enum {
45 SMEM_LOG = 1U << 0,
46 RTR_DBG = 1U << 1,
47 R2R_MSG = 1U << 2,
48 R2R_RAW = 1U << 3,
49 NTFY_MSG = 1U << 4,
50 R2R_RAW_HDR = 1U << 5,
51};
52
53static int msm_ipc_router_debug_mask;
54module_param_named(debug_mask, msm_ipc_router_debug_mask,
55 int, S_IRUGO | S_IWUSR | S_IWGRP);
56
Karthikeyan Ramasubramanian2bfe8ec2013-03-22 10:47:20 -060057static void *ipc_rtr_log_ctxt;
58#define IPC_RTR_LOG_PAGES 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#define DIAG(x...) pr_info("[RR] ERROR " x)
60
61#if defined(DEBUG)
62#define D(x...) do { \
Karthikeyan Ramasubramanian2bfe8ec2013-03-22 10:47:20 -060063if (ipc_rtr_log_ctxt) \
64 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065if (msm_ipc_router_debug_mask & RTR_DBG) \
66 pr_info(x); \
67} while (0)
68
69#define RR(x...) do { \
Karthikeyan Ramasubramanian2bfe8ec2013-03-22 10:47:20 -060070if (ipc_rtr_log_ctxt) \
71 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072if (msm_ipc_router_debug_mask & R2R_MSG) \
73 pr_info("[RR] "x); \
74} while (0)
75
76#define RAW(x...) do { \
77if (msm_ipc_router_debug_mask & R2R_RAW) \
78 pr_info("[RAW] "x); \
79} while (0)
80
81#define NTFY(x...) do { \
82if (msm_ipc_router_debug_mask & NTFY_MSG) \
83 pr_info("[NOTIFY] "x); \
84} while (0)
85
86#define RAW_HDR(x...) do { \
87if (msm_ipc_router_debug_mask & R2R_RAW_HDR) \
88 pr_info("[HDR] "x); \
89} while (0)
90#else
91#define D(x...) do { } while (0)
92#define RR(x...) do { } while (0)
93#define RAW(x...) do { } while (0)
94#define RAW_HDR(x...) do { } while (0)
95#define NTFY(x...) do { } while (0)
96#endif
97
Zaheerulla Meer83e6cbb2013-06-19 16:31:17 +053098#define IPC_ROUTER_LOG_EVENT_ERROR 0x00
99#define IPC_ROUTER_LOG_EVENT_TX 0x01
100#define IPC_ROUTER_LOG_EVENT_RX 0x02
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +0530101#define IPC_ROUTER_DUMMY_DEST_NODE 0xFFFFFFFF
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102
103static LIST_HEAD(control_ports);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600104static DECLARE_RWSEM(control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105
106#define LP_HASH_SIZE 32
107static struct list_head local_ports[LP_HASH_SIZE];
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600108static DECLARE_RWSEM(local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -0600110/*
111 * Server info is organized as a hash table. The server's service ID is
112 * used to index into the hash table. The instance ID of most of the servers
113 * are 1 or 2. The service IDs are well distributed compared to the instance
114 * IDs and hence choosing service ID to index into this hash table optimizes
115 * the hash table operations like add, lookup, destroy.
116 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117#define SRV_HASH_SIZE 32
118static struct list_head server_list[SRV_HASH_SIZE];
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600119static DECLARE_RWSEM(server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120
121struct msm_ipc_server {
122 struct list_head list;
123 struct msm_ipc_port_name name;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600124 char pdev_name[32];
125 int next_pdev_id;
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600126 int synced_sec_rule;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 struct list_head server_port_list;
128};
129
130struct msm_ipc_server_port {
131 struct list_head list;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600132 struct platform_device pdev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700133 struct msm_ipc_port_addr server_addr;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600134 struct msm_ipc_router_xprt_info *xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135};
136
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +0530137struct msm_ipc_resume_tx_port {
138 struct list_head list;
139 uint32_t port_id;
140 uint32_t node_id;
141};
142
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143#define RP_HASH_SIZE 32
144struct msm_ipc_router_remote_port {
145 struct list_head list;
146 uint32_t node_id;
147 uint32_t port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148 uint32_t tx_quota_cnt;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600149 struct mutex quota_lock_lhb2;
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +0530150 struct list_head resume_tx_port_list;
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600151 void *sec_rule;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600152 struct msm_ipc_server *server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153};
154
155struct msm_ipc_router_xprt_info {
156 struct list_head list;
157 struct msm_ipc_router_xprt *xprt;
158 uint32_t remote_node_id;
159 uint32_t initialized;
160 struct list_head pkt_list;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 struct wake_lock wakelock;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600162 struct mutex rx_lock_lhb2;
163 struct mutex tx_lock_lhb2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 uint32_t need_len;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600165 uint32_t abort_data_read;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166 struct work_struct read_data;
167 struct workqueue_struct *workqueue;
168};
169
170#define RT_HASH_SIZE 4
171struct msm_ipc_routing_table_entry {
172 struct list_head list;
173 uint32_t node_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600174 uint32_t neighbor_node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 struct list_head remote_port_list[RP_HASH_SIZE];
176 struct msm_ipc_router_xprt_info *xprt_info;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600177 struct rw_semaphore lock_lha4;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178 unsigned long num_tx_bytes;
179 unsigned long num_rx_bytes;
180};
181
182static struct list_head routing_table[RT_HASH_SIZE];
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600183static DECLARE_RWSEM(routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184static int routing_table_inited;
185
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186static void do_read_data(struct work_struct *work);
187
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188static LIST_HEAD(xprt_info_list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600189static DECLARE_RWSEM(xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -0600191static DECLARE_COMPLETION(msm_ipc_local_router_up);
192#define IPC_ROUTER_INIT_TIMEOUT (10 * HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193
194static uint32_t next_port_id;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600195static DEFINE_MUTEX(next_port_id_lock_lha1);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600196static struct workqueue_struct *msm_ipc_router_workqueue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197
198enum {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 DOWN,
200 UP,
201};
202
203static void init_routing_table(void)
204{
205 int i;
206 for (i = 0; i < RT_HASH_SIZE; i++)
207 INIT_LIST_HEAD(&routing_table[i]);
208}
209
210static struct msm_ipc_routing_table_entry *alloc_routing_table_entry(
211 uint32_t node_id)
212{
213 int i;
214 struct msm_ipc_routing_table_entry *rt_entry;
215
216 rt_entry = kmalloc(sizeof(struct msm_ipc_routing_table_entry),
217 GFP_KERNEL);
218 if (!rt_entry) {
219 pr_err("%s: rt_entry allocation failed for %d\n",
220 __func__, node_id);
221 return NULL;
222 }
223
224 for (i = 0; i < RP_HASH_SIZE; i++)
225 INIT_LIST_HEAD(&rt_entry->remote_port_list[i]);
226
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600227 init_rwsem(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700228 rt_entry->node_id = node_id;
229 rt_entry->xprt_info = NULL;
230 return rt_entry;
231}
232
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600233/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234static int add_routing_table_entry(
235 struct msm_ipc_routing_table_entry *rt_entry)
236{
237 uint32_t key;
238
239 if (!rt_entry)
240 return -EINVAL;
241
242 key = (rt_entry->node_id % RT_HASH_SIZE);
243 list_add_tail(&rt_entry->list, &routing_table[key]);
244 return 0;
245}
246
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600247/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248static struct msm_ipc_routing_table_entry *lookup_routing_table(
249 uint32_t node_id)
250{
251 uint32_t key = (node_id % RT_HASH_SIZE);
252 struct msm_ipc_routing_table_entry *rt_entry;
253
254 list_for_each_entry(rt_entry, &routing_table[key], list) {
255 if (rt_entry->node_id == node_id)
256 return rt_entry;
257 }
258 return NULL;
259}
260
261struct rr_packet *rr_read(struct msm_ipc_router_xprt_info *xprt_info)
262{
263 struct rr_packet *temp_pkt;
264
265 if (!xprt_info)
266 return NULL;
267
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600268 mutex_lock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600269 if (xprt_info->abort_data_read) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600270 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -0600271 pr_err("%s detected SSR & exiting now\n",
272 xprt_info->xprt->name);
273 return NULL;
274 }
275
276 if (list_empty(&xprt_info->pkt_list)) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600277 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600278 return NULL;
279 }
280
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281 temp_pkt = list_first_entry(&xprt_info->pkt_list,
282 struct rr_packet, list);
283 list_del(&temp_pkt->list);
284 if (list_empty(&xprt_info->pkt_list))
285 wake_unlock(&xprt_info->wakelock);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600286 mutex_unlock(&xprt_info->rx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 return temp_pkt;
288}
289
290struct rr_packet *clone_pkt(struct rr_packet *pkt)
291{
292 struct rr_packet *cloned_pkt;
293 struct sk_buff *temp_skb, *cloned_skb;
294 struct sk_buff_head *pkt_fragment_q;
295
296 cloned_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
297 if (!cloned_pkt) {
298 pr_err("%s: failure\n", __func__);
299 return NULL;
300 }
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600301 memcpy(&(cloned_pkt->hdr), &(pkt->hdr), sizeof(struct rr_header_v1));
302 /* TODO: Copy optional headers, if available */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303
304 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
305 if (!pkt_fragment_q) {
306 pr_err("%s: pkt_frag_q alloc failure\n", __func__);
307 kfree(cloned_pkt);
308 return NULL;
309 }
310 skb_queue_head_init(pkt_fragment_q);
311
312 skb_queue_walk(pkt->pkt_fragment_q, temp_skb) {
313 cloned_skb = skb_clone(temp_skb, GFP_KERNEL);
314 if (!cloned_skb)
315 goto fail_clone;
316 skb_queue_tail(pkt_fragment_q, cloned_skb);
317 }
318 cloned_pkt->pkt_fragment_q = pkt_fragment_q;
319 cloned_pkt->length = pkt->length;
320 return cloned_pkt;
321
322fail_clone:
323 while (!skb_queue_empty(pkt_fragment_q)) {
324 temp_skb = skb_dequeue(pkt_fragment_q);
325 kfree_skb(temp_skb);
326 }
327 kfree(pkt_fragment_q);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600328 /* TODO: Free optional headers, if present */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 kfree(cloned_pkt);
330 return NULL;
331}
332
333struct rr_packet *create_pkt(struct sk_buff_head *data)
334{
335 struct rr_packet *pkt;
336 struct sk_buff *temp_skb;
337
338 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
339 if (!pkt) {
340 pr_err("%s: failure\n", __func__);
341 return NULL;
342 }
343
344 pkt->pkt_fragment_q = data;
345 skb_queue_walk(pkt->pkt_fragment_q, temp_skb)
346 pkt->length += temp_skb->len;
347 return pkt;
348}
349
350void release_pkt(struct rr_packet *pkt)
351{
352 struct sk_buff *temp_skb;
353
354 if (!pkt)
355 return;
356
357 if (!pkt->pkt_fragment_q) {
358 kfree(pkt);
359 return;
360 }
361
362 while (!skb_queue_empty(pkt->pkt_fragment_q)) {
363 temp_skb = skb_dequeue(pkt->pkt_fragment_q);
364 kfree_skb(temp_skb);
365 }
366 kfree(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600367 /* TODO: Free Optional headers, if present */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 kfree(pkt);
369 return;
370}
371
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600372static struct sk_buff_head *msm_ipc_router_buf_to_skb(void *buf,
373 unsigned int buf_len)
374{
375 struct sk_buff_head *skb_head;
376 struct sk_buff *skb;
377 int first = 1, offset = 0;
378 int skb_size, data_size;
379 void *data;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600380 int last = 1;
381 int align_size;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600382
383 skb_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
384 if (!skb_head) {
385 pr_err("%s: Couldnot allocate skb_head\n", __func__);
386 return NULL;
387 }
388 skb_queue_head_init(skb_head);
389
390 data_size = buf_len;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600391 align_size = ALIGN_SIZE(data_size);
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600392 while (offset != buf_len) {
393 skb_size = data_size;
394 if (first)
395 skb_size += IPC_ROUTER_HDR_SIZE;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600396 if (last)
397 skb_size += align_size;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600398
399 skb = alloc_skb(skb_size, GFP_KERNEL);
400 if (!skb) {
401 if (skb_size <= (PAGE_SIZE/2)) {
402 pr_err("%s: cannot allocate skb\n", __func__);
403 goto buf_to_skb_error;
404 }
405 data_size = data_size / 2;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600406 last = 0;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600407 continue;
408 }
409
410 if (first) {
411 skb_reserve(skb, IPC_ROUTER_HDR_SIZE);
412 first = 0;
413 }
414
415 data = skb_put(skb, data_size);
416 memcpy(skb->data, buf + offset, data_size);
417 skb_queue_tail(skb_head, skb);
418 offset += data_size;
419 data_size = buf_len - offset;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600420 last = 1;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600421 }
422 return skb_head;
423
424buf_to_skb_error:
425 while (!skb_queue_empty(skb_head)) {
426 skb = skb_dequeue(skb_head);
427 kfree_skb(skb);
428 }
429 kfree(skb_head);
430 return NULL;
431}
432
433static void *msm_ipc_router_skb_to_buf(struct sk_buff_head *skb_head,
434 unsigned int len)
435{
436 struct sk_buff *temp;
437 int offset = 0, buf_len = 0, copy_len;
438 void *buf;
439
440 if (!skb_head) {
441 pr_err("%s: NULL skb_head\n", __func__);
442 return NULL;
443 }
444
445 temp = skb_peek(skb_head);
446 buf_len = len;
447 buf = kmalloc(buf_len, GFP_KERNEL);
448 if (!buf) {
449 pr_err("%s: cannot allocate buf\n", __func__);
450 return NULL;
451 }
452 skb_queue_walk(skb_head, temp) {
453 copy_len = buf_len < temp->len ? buf_len : temp->len;
454 memcpy(buf + offset, temp->data, copy_len);
455 offset += copy_len;
456 buf_len -= copy_len;
457 }
458 return buf;
459}
460
Zaheerulla Meer74425252013-08-20 12:02:31 +0530461void msm_ipc_router_free_skb(struct sk_buff_head *skb_head)
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600462{
463 struct sk_buff *temp_skb;
464
465 if (!skb_head)
466 return;
467
468 while (!skb_queue_empty(skb_head)) {
469 temp_skb = skb_dequeue(skb_head);
470 kfree_skb(temp_skb);
471 }
472 kfree(skb_head);
473}
474
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600475/**
476 * extract_header_v1() - Extract IPC Router header of version 1
477 * @pkt: Packet structure into which the header has to be extraced.
478 * @skb: SKB from which the header has to be extracted.
479 *
480 * @return: 0 on success, standard Linux error codes on failure.
481 */
482static int extract_header_v1(struct rr_packet *pkt, struct sk_buff *skb)
483{
484 if (!pkt || !skb) {
485 pr_err("%s: Invalid pkt or skb\n", __func__);
486 return -EINVAL;
487 }
488
489 memcpy(&pkt->hdr, skb->data, sizeof(struct rr_header_v1));
490 skb_pull(skb, sizeof(struct rr_header_v1));
491 pkt->length -= sizeof(struct rr_header_v1);
492 return 0;
493}
494
495/**
496 * extract_header_v2() - Extract IPC Router header of version 2
497 * @pkt: Packet structure into which the header has to be extraced.
498 * @skb: SKB from which the header has to be extracted.
499 *
500 * @return: 0 on success, standard Linux error codes on failure.
501 */
502static int extract_header_v2(struct rr_packet *pkt, struct sk_buff *skb)
503{
504 struct rr_header_v2 *hdr;
505
506 if (!pkt || !skb) {
507 pr_err("%s: Invalid pkt or skb\n", __func__);
508 return -EINVAL;
509 }
510
511 hdr = (struct rr_header_v2 *)skb->data;
512 pkt->hdr.version = (uint32_t)hdr->version;
513 pkt->hdr.type = (uint32_t)hdr->type;
514 pkt->hdr.src_node_id = (uint32_t)hdr->src_node_id;
515 pkt->hdr.src_port_id = (uint32_t)hdr->src_port_id;
516 pkt->hdr.size = (uint32_t)hdr->size;
517 pkt->hdr.control_flag = (uint32_t)hdr->control_flag;
518 pkt->hdr.dst_node_id = (uint32_t)hdr->dst_node_id;
519 pkt->hdr.dst_port_id = (uint32_t)hdr->dst_port_id;
520 skb_pull(skb, sizeof(struct rr_header_v2));
521 pkt->length -= sizeof(struct rr_header_v2);
522 return 0;
523}
524
525/**
526 * extract_header() - Extract IPC Router header
527 * @pkt: Packet from which the header has to be extraced.
528 *
529 * @return: 0 on success, standard Linux error codes on failure.
530 *
531 * This function will check if the header version is v1 or v2 and invoke
532 * the corresponding helper function to extract the IPC Router header.
533 */
534static int extract_header(struct rr_packet *pkt)
535{
536 struct sk_buff *temp_skb;
537 int ret;
538
539 if (!pkt) {
540 pr_err("%s: NULL PKT\n", __func__);
541 return -EINVAL;
542 }
543
544 temp_skb = skb_peek(pkt->pkt_fragment_q);
545 if (!temp_skb || !temp_skb->data) {
546 pr_err("%s: No SKBs in skb_queue\n", __func__);
547 return -EINVAL;
548 }
549
550 if (temp_skb->data[0] == IPC_ROUTER_V1) {
551 ret = extract_header_v1(pkt, temp_skb);
552 } else if (temp_skb->data[0] == IPC_ROUTER_V2) {
553 ret = extract_header_v2(pkt, temp_skb);
554 /* TODO: Extract optional headers if present */
555 } else {
556 pr_err("%s: Invalid Header version %02x\n",
557 __func__, temp_skb->data[0]);
558 print_hex_dump(KERN_ERR, "Header: ", DUMP_PREFIX_ADDRESS,
559 16, 1, temp_skb->data, pkt->length, true);
560 return -EINVAL;
561 }
562 return ret;
563}
564
565/**
566 * calc_tx_header_size() - Calculate header size to be reserved in SKB
567 * @pkt: Packet in which the space for header has to be reserved.
568 * @dst_xprt_info: XPRT through which the destination is reachable.
569 *
570 * @return: required header size on success,
571 * starndard Linux error codes on failure.
572 *
573 * This function is used to calculate the header size that has to be reserved
574 * in a transmit SKB. The header size is calculated based on the XPRT through
575 * which the destination node is reachable.
576 */
577static int calc_tx_header_size(struct rr_packet *pkt,
578 struct msm_ipc_router_xprt_info *dst_xprt_info)
579{
580 int hdr_size = 0;
581 int xprt_version = 0;
582 struct msm_ipc_routing_table_entry *rt_entry;
583 struct msm_ipc_router_xprt_info *xprt_info = dst_xprt_info;
584
585 if (!pkt) {
586 pr_err("%s: NULL PKT\n", __func__);
587 return -EINVAL;
588 }
589
590 if (!xprt_info) {
591 rt_entry = lookup_routing_table(pkt->hdr.dst_node_id);
592 if (!rt_entry || !(rt_entry->xprt_info)) {
593 pr_err("%s: Node %d is not up\n",
594 __func__, pkt->hdr.dst_node_id);
595 return -ENODEV;
596 }
597
598 xprt_info = rt_entry->xprt_info;
599 }
600 if (xprt_info)
601 xprt_version = xprt_info->xprt->get_version(xprt_info->xprt);
602
603 if (xprt_version == IPC_ROUTER_V1) {
604 pkt->hdr.version = IPC_ROUTER_V1;
605 hdr_size = sizeof(struct rr_header_v1);
606 } else if (xprt_version == IPC_ROUTER_V2) {
607 pkt->hdr.version = IPC_ROUTER_V2;
608 hdr_size = sizeof(struct rr_header_v2);
609 /* TODO: Calculate optional header length, if present */
610 } else {
611 pr_err("%s: Invalid xprt_version %d\n",
612 __func__, xprt_version);
613 hdr_size = -EINVAL;
614 }
615
616 return hdr_size;
617}
618
619/**
620 * prepend_header_v1() - Prepend IPC Router header of version 1
621 * @pkt: Packet structure which contains the header info to be prepended.
622 * @hdr_size: Size of the header
623 *
624 * @return: 0 on success, standard Linux error codes on failure.
625 */
626static int prepend_header_v1(struct rr_packet *pkt, int hdr_size)
627{
628 struct sk_buff *temp_skb;
629 struct rr_header_v1 *hdr;
630
631 if (!pkt || hdr_size <= 0) {
632 pr_err("%s: Invalid input parameters\n", __func__);
633 return -EINVAL;
634 }
635
636 temp_skb = skb_peek(pkt->pkt_fragment_q);
637 if (!temp_skb || !temp_skb->data) {
638 pr_err("%s: No SKBs in skb_queue\n", __func__);
639 return -EINVAL;
640 }
641
642 if (skb_headroom(temp_skb) < hdr_size) {
643 temp_skb = alloc_skb(hdr_size, GFP_KERNEL);
644 if (!temp_skb) {
645 pr_err("%s: Could not allocate SKB of size %d\n",
646 __func__, hdr_size);
647 return -ENOMEM;
648 }
649 }
650
651 hdr = (struct rr_header_v1 *)skb_push(temp_skb, hdr_size);
652 memcpy(hdr, &pkt->hdr, hdr_size);
653 if (temp_skb != skb_peek(pkt->pkt_fragment_q))
654 skb_queue_head(pkt->pkt_fragment_q, temp_skb);
655 pkt->length += hdr_size;
656 return 0;
657}
658
659/**
660 * prepend_header_v2() - Prepend IPC Router header of version 2
661 * @pkt: Packet structure which contains the header info to be prepended.
662 * @hdr_size: Size of the header
663 *
664 * @return: 0 on success, standard Linux error codes on failure.
665 */
666static int prepend_header_v2(struct rr_packet *pkt, int hdr_size)
667{
668 struct sk_buff *temp_skb;
669 struct rr_header_v2 *hdr;
670
671 if (!pkt || hdr_size <= 0) {
672 pr_err("%s: Invalid input parameters\n", __func__);
673 return -EINVAL;
674 }
675
676 temp_skb = skb_peek(pkt->pkt_fragment_q);
677 if (!temp_skb || !temp_skb->data) {
678 pr_err("%s: No SKBs in skb_queue\n", __func__);
679 return -EINVAL;
680 }
681
682 if (skb_headroom(temp_skb) < hdr_size) {
683 temp_skb = alloc_skb(hdr_size, GFP_KERNEL);
684 if (!temp_skb) {
685 pr_err("%s: Could not allocate SKB of size %d\n",
686 __func__, hdr_size);
687 return -ENOMEM;
688 }
689 }
690
691 hdr = (struct rr_header_v2 *)skb_push(temp_skb, hdr_size);
692 hdr->version = (uint8_t)pkt->hdr.version;
693 hdr->type = (uint8_t)pkt->hdr.type;
694 hdr->control_flag = (uint16_t)pkt->hdr.control_flag;
695 hdr->size = (uint32_t)pkt->hdr.size;
696 hdr->src_node_id = (uint16_t)pkt->hdr.src_node_id;
697 hdr->src_port_id = (uint16_t)pkt->hdr.src_port_id;
698 hdr->dst_node_id = (uint16_t)pkt->hdr.dst_node_id;
699 hdr->dst_port_id = (uint16_t)pkt->hdr.dst_port_id;
700 /* TODO: Add optional headers, if present */
701 if (temp_skb != skb_peek(pkt->pkt_fragment_q))
702 skb_queue_head(pkt->pkt_fragment_q, temp_skb);
703 pkt->length += hdr_size;
704 return 0;
705}
706
707/**
708 * prepend_header() - Prepend IPC Router header
709 * @pkt: Packet structure which contains the header info to be prepended.
710 * @xprt_info: XPRT through which the packet is transmitted.
711 *
712 * @return: 0 on success, standard Linux error codes on failure.
713 *
714 * This function prepends the header to the packet to be transmitted. The
715 * IPC Router header version to be prepended depends on the XPRT through
716 * which the destination is reachable.
717 */
718static int prepend_header(struct rr_packet *pkt,
719 struct msm_ipc_router_xprt_info *xprt_info)
720{
721 int hdr_size;
722 struct sk_buff *temp_skb;
723
724 if (!pkt) {
725 pr_err("%s: NULL PKT\n", __func__);
726 return -EINVAL;
727 }
728
729 temp_skb = skb_peek(pkt->pkt_fragment_q);
730 if (!temp_skb || !temp_skb->data) {
731 pr_err("%s: No SKBs in skb_queue\n", __func__);
732 return -EINVAL;
733 }
734
735 hdr_size = calc_tx_header_size(pkt, xprt_info);
736 if (hdr_size <= 0)
737 return hdr_size;
738
739 if (pkt->hdr.version == IPC_ROUTER_V1)
740 return prepend_header_v1(pkt, hdr_size);
741 else if (pkt->hdr.version == IPC_ROUTER_V2)
742 return prepend_header_v2(pkt, hdr_size);
743 else
744 return -EINVAL;
745}
746
747/**
748 * defragment_pkt() - Defragment and linearize the packet
749 * @pkt: Packet to be linearized.
750 *
751 * @return: 0 on success, standard Linux error codes on failure.
752 *
753 * Some packets contain fragments of data over multiple SKBs. If an XPRT
754 * does not supported fragmented writes, linearize multiple SKBs into one
755 * single SKB.
756 */
757static int defragment_pkt(struct rr_packet *pkt)
758{
759 struct sk_buff *dst_skb, *src_skb, *temp_skb;
760 int offset = 0, buf_len = 0, copy_len;
761 void *buf;
762 int align_size;
763
764 if (!pkt || pkt->length <= 0) {
765 pr_err("%s: Invalid PKT\n", __func__);
766 return -EINVAL;
767 }
768
769 if (skb_queue_len(pkt->pkt_fragment_q) == 1)
770 return 0;
771
772 align_size = ALIGN_SIZE(pkt->length);
773 dst_skb = alloc_skb(pkt->length + align_size, GFP_KERNEL);
774 if (!dst_skb) {
775 pr_err("%s: could not allocate one skb of size %d\n",
776 __func__, pkt->length);
777 return -ENOMEM;
778 }
779 buf = skb_put(dst_skb, pkt->length);
780 buf_len = pkt->length;
781
782 skb_queue_walk(pkt->pkt_fragment_q, src_skb) {
783 copy_len = buf_len < src_skb->len ? buf_len : src_skb->len;
784 memcpy(buf + offset, src_skb->data, copy_len);
785 offset += copy_len;
786 buf_len -= copy_len;
787 }
788
789 while (!skb_queue_empty(pkt->pkt_fragment_q)) {
790 temp_skb = skb_dequeue(pkt->pkt_fragment_q);
791 kfree_skb(temp_skb);
792 }
793 skb_queue_tail(pkt->pkt_fragment_q, dst_skb);
794 return 0;
795}
796
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -0600797static int post_pkt_to_port(struct msm_ipc_port *port_ptr,
798 struct rr_packet *pkt, int clone)
799{
800 struct rr_packet *temp_pkt = pkt;
Zaheerulla Meer84d0bd562013-05-24 21:19:18 +0530801 void (*notify)(unsigned event, void *priv);
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -0600802
803 if (unlikely(!port_ptr || !pkt))
804 return -EINVAL;
805
806 if (clone) {
807 temp_pkt = clone_pkt(pkt);
808 if (!temp_pkt) {
809 pr_err("%s: Error cloning packet for port %08x:%08x\n",
810 __func__, port_ptr->this_port.node_id,
811 port_ptr->this_port.port_id);
812 return -ENOMEM;
813 }
814 }
815
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600816 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -0600817 wake_lock(&port_ptr->port_rx_wake_lock);
818 list_add_tail(&temp_pkt->list, &port_ptr->port_rx_q);
819 wake_up(&port_ptr->port_rx_wait_q);
Zaheerulla Meer84d0bd562013-05-24 21:19:18 +0530820 notify = port_ptr->notify;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600821 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Zaheerulla Meer84d0bd562013-05-24 21:19:18 +0530822 if (notify)
823 notify(MSM_IPC_ROUTER_READ_CB, port_ptr->priv);
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -0600824 return 0;
825}
826
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700827static int post_control_ports(struct rr_packet *pkt)
828{
829 struct msm_ipc_port *port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700830
831 if (!pkt)
832 return -EINVAL;
833
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600834 down_read(&control_ports_lock_lha5);
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -0600835 list_for_each_entry(port_ptr, &control_ports, list)
836 post_pkt_to_port(port_ptr, pkt, 1);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600837 up_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838 return 0;
839}
840
841static uint32_t allocate_port_id(void)
842{
843 uint32_t port_id = 0, prev_port_id, key;
844 struct msm_ipc_port *port_ptr;
845
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600846 mutex_lock(&next_port_id_lock_lha1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700847 prev_port_id = next_port_id;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600848 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700849 do {
850 next_port_id++;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600851 if ((next_port_id & IPC_ROUTER_ADDRESS) == IPC_ROUTER_ADDRESS)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 next_port_id = 1;
853
854 key = (next_port_id & (LP_HASH_SIZE - 1));
855 if (list_empty(&local_ports[key])) {
856 port_id = next_port_id;
857 break;
858 }
859 list_for_each_entry(port_ptr, &local_ports[key], list) {
860 if (port_ptr->this_port.port_id == next_port_id) {
861 port_id = next_port_id;
862 break;
863 }
864 }
865 if (!port_id) {
866 port_id = next_port_id;
867 break;
868 }
869 port_id = 0;
870 } while (next_port_id != prev_port_id);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600871 up_read(&local_ports_lock_lha2);
872 mutex_unlock(&next_port_id_lock_lha1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700873
874 return port_id;
875}
876
877void msm_ipc_router_add_local_port(struct msm_ipc_port *port_ptr)
878{
879 uint32_t key;
880
881 if (!port_ptr)
882 return;
883
884 key = (port_ptr->this_port.port_id & (LP_HASH_SIZE - 1));
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600885 down_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700886 list_add_tail(&port_ptr->list, &local_ports[key]);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600887 up_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700888}
889
890struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint,
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -0600891 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700892 void *priv)
893{
894 struct msm_ipc_port *port_ptr;
895
896 port_ptr = kzalloc(sizeof(struct msm_ipc_port), GFP_KERNEL);
897 if (!port_ptr)
898 return NULL;
899
900 port_ptr->this_port.node_id = IPC_ROUTER_NID_LOCAL;
901 port_ptr->this_port.port_id = allocate_port_id();
902 if (!port_ptr->this_port.port_id) {
903 pr_err("%s: All port ids are in use\n", __func__);
904 kfree(port_ptr);
905 return NULL;
906 }
907
908 spin_lock_init(&port_ptr->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700909 INIT_LIST_HEAD(&port_ptr->port_rx_q);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600910 mutex_init(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700911 init_waitqueue_head(&port_ptr->port_rx_wait_q);
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600912 snprintf(port_ptr->rx_wakelock_name, MAX_WAKELOCK_NAME_SZ,
Karthikeyan Ramasubramanian6396bdd2013-02-14 13:53:20 -0700913 "ipc%08x_%s",
914 port_ptr->this_port.port_id,
915 current->comm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700916 wake_lock_init(&port_ptr->port_rx_wake_lock,
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600917 WAKE_LOCK_SUSPEND, port_ptr->rx_wakelock_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918
919 port_ptr->endpoint = endpoint;
920 port_ptr->notify = notify;
921 port_ptr->priv = priv;
922
923 msm_ipc_router_add_local_port(port_ptr);
924 return port_ptr;
925}
926
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600927/* Must be called with local_ports_lock_lha2 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700928static struct msm_ipc_port *msm_ipc_router_lookup_local_port(uint32_t port_id)
929{
930 int key = (port_id & (LP_HASH_SIZE - 1));
931 struct msm_ipc_port *port_ptr;
932
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700933 list_for_each_entry(port_ptr, &local_ports[key], list) {
934 if (port_ptr->this_port.port_id == port_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700935 return port_ptr;
936 }
937 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938 return NULL;
939}
940
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600941/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942static struct msm_ipc_router_remote_port *msm_ipc_router_lookup_remote_port(
943 uint32_t node_id,
944 uint32_t port_id)
945{
946 struct msm_ipc_router_remote_port *rport_ptr;
947 struct msm_ipc_routing_table_entry *rt_entry;
948 int key = (port_id & (RP_HASH_SIZE - 1));
949
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950 rt_entry = lookup_routing_table(node_id);
951 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952 pr_err("%s: Node is not up\n", __func__);
953 return NULL;
954 }
955
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600956 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700957 list_for_each_entry(rport_ptr,
958 &rt_entry->remote_port_list[key], list) {
959 if (rport_ptr->port_id == port_id) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600960 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961 return rport_ptr;
962 }
963 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600964 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700965 return NULL;
966}
967
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600968/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700969static struct msm_ipc_router_remote_port *msm_ipc_router_create_remote_port(
970 uint32_t node_id,
971 uint32_t port_id)
972{
973 struct msm_ipc_router_remote_port *rport_ptr;
974 struct msm_ipc_routing_table_entry *rt_entry;
975 int key = (port_id & (RP_HASH_SIZE - 1));
976
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700977 rt_entry = lookup_routing_table(node_id);
978 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979 pr_err("%s: Node is not up\n", __func__);
980 return NULL;
981 }
982
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700983 rport_ptr = kmalloc(sizeof(struct msm_ipc_router_remote_port),
984 GFP_KERNEL);
985 if (!rport_ptr) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700986 pr_err("%s: Remote port alloc failed\n", __func__);
987 return NULL;
988 }
989 rport_ptr->port_id = port_id;
990 rport_ptr->node_id = node_id;
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600991 rport_ptr->sec_rule = NULL;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600992 rport_ptr->server = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993 rport_ptr->tx_quota_cnt = 0;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600994 mutex_init(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +0530995 INIT_LIST_HEAD(&rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600996 down_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700997 list_add_tail(&rport_ptr->list,
998 &rt_entry->remote_port_list[key]);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -0600999 up_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001000 return rport_ptr;
1001}
1002
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05301003/**
1004 * msm_ipc_router_free_resume_tx_port() - Free the resume_tx ports
1005 * @rport_ptr: Pointer to the remote port.
1006 *
1007 * This function deletes all the resume_tx ports associated with a remote port
1008 * and frees the memory allocated to each resume_tx port.
1009 *
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001010 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05301011 */
1012static void msm_ipc_router_free_resume_tx_port(
1013 struct msm_ipc_router_remote_port *rport_ptr)
1014{
1015 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
1016
1017 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
1018 &rport_ptr->resume_tx_port_list, list) {
1019 list_del(&rtx_port->list);
1020 kfree(rtx_port);
1021 }
1022}
1023
1024/**
1025 * msm_ipc_router_lookup_resume_tx_port() - Lookup resume_tx port list
1026 * @rport_ptr: Remote port whose resume_tx port list needs to be looked.
1027 * @port_id: Port ID which needs to be looked from the list.
1028 *
1029 * return 1 if the port_id is found in the list, else 0.
1030 *
1031 * This function is used to lookup the existence of a local port in
1032 * remote port's resume_tx list. This function is used to ensure that
1033 * the same port is not added to the remote_port's resume_tx list repeatedly.
1034 *
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001035 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05301036 */
1037static int msm_ipc_router_lookup_resume_tx_port(
1038 struct msm_ipc_router_remote_port *rport_ptr, uint32_t port_id)
1039{
1040 struct msm_ipc_resume_tx_port *rtx_port;
1041
1042 list_for_each_entry(rtx_port, &rport_ptr->resume_tx_port_list, list) {
1043 if (port_id == rtx_port->port_id)
1044 return 1;
1045 }
1046 return 0;
1047}
1048
1049/**
1050 * post_resume_tx() - Post the resume_tx event
1051 * @rport_ptr: Pointer to the remote port
1052 * @pkt : The data packet that is received on a resume_tx event
1053 *
1054 * This function informs about the reception of the resume_tx message from a
1055 * remote port pointed by rport_ptr to all the local ports that are in the
1056 * resume_tx_ports_list of this remote port. On posting the information, this
1057 * function sequentially deletes each entry in the resume_tx_port_list of the
1058 * remote port.
1059 *
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001060 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05301061 */
1062static void post_resume_tx(struct msm_ipc_router_remote_port *rport_ptr,
1063 struct rr_packet *pkt)
1064{
1065 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
1066 struct msm_ipc_port *local_port;
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05301067
1068 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
1069 &rport_ptr->resume_tx_port_list, list) {
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05301070 local_port =
1071 msm_ipc_router_lookup_local_port(rtx_port->port_id);
Zaheerulla Meer51dc3a12013-05-08 19:27:27 +05301072 if (local_port && local_port->notify)
1073 local_port->notify(MSM_IPC_ROUTER_RESUME_TX,
1074 local_port->priv);
1075 else if (local_port)
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -06001076 post_pkt_to_port(local_port, pkt, 1);
Zaheerulla Meer51dc3a12013-05-08 19:27:27 +05301077 else
1078 pr_err("%s: Local Port %d not Found",
1079 __func__, rtx_port->port_id);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05301080 list_del(&rtx_port->list);
1081 kfree(rtx_port);
1082 }
1083}
1084
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001085/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001086static void msm_ipc_router_destroy_remote_port(
1087 struct msm_ipc_router_remote_port *rport_ptr)
1088{
1089 uint32_t node_id;
1090 struct msm_ipc_routing_table_entry *rt_entry;
1091
1092 if (!rport_ptr)
1093 return;
1094
1095 node_id = rport_ptr->node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001096 rt_entry = lookup_routing_table(node_id);
1097 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001098 pr_err("%s: Node %d is not up\n", __func__, node_id);
1099 return;
1100 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001101 down_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001102 list_del(&rport_ptr->list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001103 up_write(&rt_entry->lock_lha4);
1104 mutex_lock(&rport_ptr->quota_lock_lhb2);
1105 msm_ipc_router_free_resume_tx_port(rport_ptr);
1106 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001107 kfree(rport_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001108 return;
1109}
1110
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001111/**
1112 * msm_ipc_router_lookup_server() - Lookup server information
1113 * @service: Service ID of the server info to be looked up.
1114 * @instance: Instance ID of the server info to be looked up.
1115 * @node_id: Node/Processor ID in which the server is hosted.
1116 * @port_id: Port ID within the node in which the server is hosted.
1117 *
1118 * @return: If found Pointer to server structure, else NULL.
1119 *
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001120 * Note1: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001121 * Note2: If the <node_id:port_id> are <0:0>, then the lookup is restricted
1122 * to <service:instance>. Used only when a client wants to send a
1123 * message to any QMI server.
1124 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001125static struct msm_ipc_server *msm_ipc_router_lookup_server(
1126 uint32_t service,
1127 uint32_t instance,
1128 uint32_t node_id,
1129 uint32_t port_id)
1130{
1131 struct msm_ipc_server *server;
1132 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001133 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001134
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001135 list_for_each_entry(server, &server_list[key], list) {
1136 if ((server->name.service != service) ||
1137 (server->name.instance != instance))
1138 continue;
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001139 if ((node_id == 0) && (port_id == 0))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001140 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001141 list_for_each_entry(server_port, &server->server_port_list,
1142 list) {
1143 if ((server_port->server_addr.node_id == node_id) &&
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001144 (server_port->server_addr.port_id == port_id))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001145 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001146 }
1147 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001148 return NULL;
1149}
1150
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06001151static void dummy_release(struct device *dev)
1152{
1153}
1154
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001155/**
1156 * msm_ipc_router_create_server() - Add server info to hash table
1157 * @service: Service ID of the server info to be created.
1158 * @instance: Instance ID of the server info to be created.
1159 * @node_id: Node/Processor ID in which the server is hosted.
1160 * @port_id: Port ID within the node in which the server is hosted.
1161 * @xprt_info: XPRT through which the node hosting the server is reached.
1162 *
1163 * @return: Pointer to server structure on success, else NULL.
1164 *
1165 * This function adds the server info to the hash table. If the same
1166 * server(i.e. <service_id:instance_id>) is hosted in different nodes,
1167 * they are maintained as list of "server_port" under "server" structure.
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001168 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001169 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001170static struct msm_ipc_server *msm_ipc_router_create_server(
1171 uint32_t service,
1172 uint32_t instance,
1173 uint32_t node_id,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001174 uint32_t port_id,
1175 struct msm_ipc_router_xprt_info *xprt_info)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001176{
1177 struct msm_ipc_server *server = NULL;
1178 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001179 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001180
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001181 list_for_each_entry(server, &server_list[key], list) {
1182 if ((server->name.service == service) &&
1183 (server->name.instance == instance))
1184 goto create_srv_port;
1185 }
1186
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06001187 server = kzalloc(sizeof(struct msm_ipc_server), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001188 if (!server) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001189 pr_err("%s: Server allocation failed\n", __func__);
1190 return NULL;
1191 }
1192 server->name.service = service;
1193 server->name.instance = instance;
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001194 server->synced_sec_rule = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001195 INIT_LIST_HEAD(&server->server_port_list);
1196 list_add_tail(&server->list, &server_list[key]);
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06001197 scnprintf(server->pdev_name, sizeof(server->pdev_name),
1198 "QMI%08x:%08x", service, instance);
1199 server->next_pdev_id = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001200
1201create_srv_port:
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06001202 server_port = kzalloc(sizeof(struct msm_ipc_server_port), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001203 if (!server_port) {
1204 if (list_empty(&server->server_port_list)) {
1205 list_del(&server->list);
1206 kfree(server);
1207 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001208 pr_err("%s: Server Port allocation failed\n", __func__);
1209 return NULL;
1210 }
1211 server_port->server_addr.node_id = node_id;
1212 server_port->server_addr.port_id = port_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001213 server_port->xprt_info = xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001214 list_add_tail(&server_port->list, &server->server_port_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001215
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06001216 server_port->pdev.name = server->pdev_name;
1217 server_port->pdev.id = server->next_pdev_id++;
1218 server_port->pdev.dev.release = dummy_release;
1219 platform_device_register(&server_port->pdev);
1220
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001221 return server;
1222}
1223
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001224/**
1225 * msm_ipc_router_destroy_server() - Remove server info from hash table
1226 * @server: Server info to be removed.
1227 * @node_id: Node/Processor ID in which the server is hosted.
1228 * @port_id: Port ID within the node in which the server is hosted.
1229 *
1230 * This function removes the server_port identified using <node_id:port_id>
1231 * from the server structure. If the server_port list under server structure
1232 * is empty after removal, then remove the server structure from the server
1233 * hash table.
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001234 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06001235 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001236static void msm_ipc_router_destroy_server(struct msm_ipc_server *server,
1237 uint32_t node_id, uint32_t port_id)
1238{
1239 struct msm_ipc_server_port *server_port;
1240
1241 if (!server)
1242 return;
1243
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001244 list_for_each_entry(server_port, &server->server_port_list, list) {
1245 if ((server_port->server_addr.node_id == node_id) &&
1246 (server_port->server_addr.port_id == port_id))
1247 break;
1248 }
1249 if (server_port) {
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06001250 platform_device_unregister(&server_port->pdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001251 list_del(&server_port->list);
1252 kfree(server_port);
1253 }
1254 if (list_empty(&server->server_port_list)) {
1255 list_del(&server->list);
1256 kfree(server);
1257 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001258 return;
1259}
1260
1261static int msm_ipc_router_send_control_msg(
1262 struct msm_ipc_router_xprt_info *xprt_info,
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05301263 union rr_control_msg *msg,
1264 uint32_t dst_node_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001265{
1266 struct rr_packet *pkt;
1267 struct sk_buff *ipc_rtr_pkt;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001268 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001269 int pkt_size;
1270 void *data;
1271 struct sk_buff_head *pkt_fragment_q;
1272 int ret;
1273
1274 if (!xprt_info || ((msg->cmd != IPC_ROUTER_CTRL_CMD_HELLO) &&
1275 !xprt_info->initialized)) {
1276 pr_err("%s: xprt_info not initialized\n", __func__);
1277 return -EINVAL;
1278 }
1279
1280 if (xprt_info->remote_node_id == IPC_ROUTER_NID_LOCAL)
1281 return 0;
1282
1283 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
1284 if (!pkt) {
1285 pr_err("%s: pkt alloc failed\n", __func__);
1286 return -ENOMEM;
1287 }
1288
1289 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
1290 if (!pkt_fragment_q) {
1291 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
1292 kfree(pkt);
1293 return -ENOMEM;
1294 }
1295 skb_queue_head_init(pkt_fragment_q);
1296
1297 pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg);
1298 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
1299 if (!ipc_rtr_pkt) {
1300 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
1301 kfree(pkt_fragment_q);
1302 kfree(pkt);
1303 return -ENOMEM;
1304 }
1305
1306 skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
1307 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
1308 memcpy(data, msg, sizeof(*msg));
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001309 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
1310 pkt->pkt_fragment_q = pkt_fragment_q;
1311 pkt->length = sizeof(*msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001312
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001313 hdr = &(pkt->hdr);
1314 hdr->version = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001315 hdr->type = msg->cmd;
1316 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
1317 hdr->src_port_id = IPC_ROUTER_ADDRESS;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001318 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001319 hdr->size = sizeof(*msg);
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05301320 if (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)
1321 hdr->dst_node_id = dst_node_id;
1322 else
1323 hdr->dst_node_id = xprt_info->remote_node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001324 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001325
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001326 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001327 ret = prepend_header(pkt, xprt_info);
1328 if (ret < 0) {
1329 mutex_unlock(&xprt_info->tx_lock_lhb2);
1330 pr_err("%s: Prepend Header failed\n", __func__);
1331 release_pkt(pkt);
1332 return ret;
1333 }
1334
1335 ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001336 mutex_unlock(&xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001337
1338 release_pkt(pkt);
1339 return ret;
1340}
1341
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001342static int msm_ipc_router_send_server_list(uint32_t node_id,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001343 struct msm_ipc_router_xprt_info *xprt_info)
1344{
1345 union rr_control_msg ctl;
1346 struct msm_ipc_server *server;
1347 struct msm_ipc_server_port *server_port;
1348 int i;
1349
1350 if (!xprt_info || !xprt_info->initialized) {
1351 pr_err("%s: Xprt info not initialized\n", __func__);
1352 return -EINVAL;
1353 }
1354
Karthikeyan Ramasubramanian972c2d22013-07-19 15:58:38 -06001355 memset(&ctl, 0, sizeof(ctl));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001356 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
1357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001358 for (i = 0; i < SRV_HASH_SIZE; i++) {
1359 list_for_each_entry(server, &server_list[i], list) {
1360 ctl.srv.service = server->name.service;
1361 ctl.srv.instance = server->name.instance;
1362 list_for_each_entry(server_port,
1363 &server->server_port_list, list) {
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001364 if (server_port->server_addr.node_id !=
1365 node_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001366 continue;
1367
1368 ctl.srv.node_id =
1369 server_port->server_addr.node_id;
1370 ctl.srv.port_id =
1371 server_port->server_addr.port_id;
1372 msm_ipc_router_send_control_msg(xprt_info,
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05301373 &ctl, IPC_ROUTER_DUMMY_DEST_NODE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001374 }
1375 }
1376 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001377
1378 return 0;
1379}
1380
1381#if defined(DEBUG)
1382static char *type_to_str(int i)
1383{
1384 switch (i) {
1385 case IPC_ROUTER_CTRL_CMD_DATA:
1386 return "data ";
1387 case IPC_ROUTER_CTRL_CMD_HELLO:
1388 return "hello ";
1389 case IPC_ROUTER_CTRL_CMD_BYE:
1390 return "bye ";
1391 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
1392 return "new_srvr";
1393 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
1394 return "rmv_srvr";
1395 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
1396 return "rmv_clnt";
1397 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
1398 return "resum_tx";
1399 case IPC_ROUTER_CTRL_CMD_EXIT:
1400 return "cmd_exit";
1401 default:
1402 return "invalid";
1403 }
1404}
1405#endif
1406
1407static int broadcast_ctl_msg_locally(union rr_control_msg *msg)
1408{
1409 struct rr_packet *pkt;
1410 struct sk_buff *ipc_rtr_pkt;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001411 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001412 int pkt_size;
1413 void *data;
1414 struct sk_buff_head *pkt_fragment_q;
1415 int ret;
1416
1417 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
1418 if (!pkt) {
1419 pr_err("%s: pkt alloc failed\n", __func__);
1420 return -ENOMEM;
1421 }
1422
1423 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
1424 if (!pkt_fragment_q) {
1425 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
1426 kfree(pkt);
1427 return -ENOMEM;
1428 }
1429 skb_queue_head_init(pkt_fragment_q);
1430
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001431 pkt_size = sizeof(*msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001432 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
1433 if (!ipc_rtr_pkt) {
1434 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
1435 kfree(pkt_fragment_q);
1436 kfree(pkt);
1437 return -ENOMEM;
1438 }
1439
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001440 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
1441 memcpy(data, msg, sizeof(*msg));
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001442 hdr = &(pkt->hdr);
1443 hdr->version = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001444 hdr->type = msg->cmd;
1445 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
1446 hdr->src_port_id = IPC_ROUTER_ADDRESS;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001447 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001448 hdr->size = sizeof(*msg);
1449 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
1450 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
1451 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
1452 pkt->pkt_fragment_q = pkt_fragment_q;
1453 pkt->length = pkt_size;
1454
1455 ret = post_control_ports(pkt);
1456 release_pkt(pkt);
1457 return ret;
1458}
1459
1460static int broadcast_ctl_msg(union rr_control_msg *ctl)
1461{
1462 struct msm_ipc_router_xprt_info *xprt_info;
1463
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001464 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001465 list_for_each_entry(xprt_info, &xprt_info_list, list) {
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05301466 msm_ipc_router_send_control_msg(xprt_info, ctl,
1467 IPC_ROUTER_DUMMY_DEST_NODE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001468 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001469 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001470
1471 return 0;
1472}
1473
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001474static int relay_ctl_msg(struct msm_ipc_router_xprt_info *xprt_info,
1475 union rr_control_msg *ctl)
1476{
1477 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1478
1479 if (!xprt_info || !ctl)
1480 return -EINVAL;
1481
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001482 down_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001483 list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
1484 if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05301485 msm_ipc_router_send_control_msg(fwd_xprt_info, ctl,
1486 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001487 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001488 up_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001489
1490 return 0;
1491}
1492
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001493static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info,
1494 struct rr_packet *pkt)
1495{
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001496 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001497 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1498 struct msm_ipc_routing_table_entry *rt_entry;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001499 int ret = 0;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001500 int fwd_xprt_option;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001501
1502 if (!xprt_info || !pkt)
1503 return -EINVAL;
1504
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001505 hdr = &(pkt->hdr);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001506 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001507 rt_entry = lookup_routing_table(hdr->dst_node_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001508 if (!(rt_entry) || !(rt_entry->xprt_info)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001509 pr_err("%s: Routing table not initialized\n", __func__);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001510 ret = -ENODEV;
1511 goto fm_error1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001512 }
1513
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001514 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001515 fwd_xprt_info = rt_entry->xprt_info;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001516 ret = prepend_header(pkt, fwd_xprt_info);
1517 if (ret < 0) {
1518 pr_err("%s: Prepend Header failed\n", __func__);
1519 goto fm_error2;
1520 }
1521 fwd_xprt_option = fwd_xprt_info->xprt->get_option(fwd_xprt_info->xprt);
1522 if (!(fwd_xprt_option & FRAG_PKT_WRITE_ENABLE)) {
1523 ret = defragment_pkt(pkt);
1524 if (ret < 0)
1525 goto fm_error2;
1526 }
1527
1528 mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001529 if (xprt_info->remote_node_id == fwd_xprt_info->remote_node_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001530 pr_err("%s: Discarding Command to route back\n", __func__);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001531 ret = -EINVAL;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001532 goto fm_error3;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001533 }
1534
1535 if (xprt_info->xprt->link_id == fwd_xprt_info->xprt->link_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001536 pr_err("%s: DST in the same cluster\n", __func__);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001537 ret = 0;
1538 goto fm_error3;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001539 }
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07001540 fwd_xprt_info->xprt->write(pkt, pkt->length, fwd_xprt_info->xprt);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001541
1542fm_error3:
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001543 mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001544fm_error2:
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001545 up_read(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001546fm_error1:
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001547 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001548
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001549 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001550}
1551
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06001552static int msm_ipc_router_send_remove_client(struct comm_mode_info *mode_info,
1553 uint32_t node_id, uint32_t port_id)
1554{
1555 union rr_control_msg msg;
1556 struct msm_ipc_router_xprt_info *tmp_xprt_info;
1557 int mode;
1558 void *xprt_info;
1559 int rc = 0;
1560
1561 if (!mode_info) {
1562 pr_err("%s: NULL mode_info\n", __func__);
1563 return -EINVAL;
1564 }
1565 mode = mode_info->mode;
1566 xprt_info = mode_info->xprt_info;
1567
Karthikeyan Ramasubramanian972c2d22013-07-19 15:58:38 -06001568 memset(&msg, 0, sizeof(msg));
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06001569 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
1570 msg.cli.node_id = node_id;
1571 msg.cli.port_id = port_id;
1572
1573 if ((mode == SINGLE_LINK_MODE) && xprt_info) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001574 down_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06001575 list_for_each_entry(tmp_xprt_info, &xprt_info_list, list) {
1576 if (tmp_xprt_info != xprt_info)
1577 continue;
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05301578 msm_ipc_router_send_control_msg(tmp_xprt_info, &msg,
1579 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06001580 break;
1581 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001582 up_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06001583 } else if ((mode == SINGLE_LINK_MODE) && !xprt_info) {
1584 broadcast_ctl_msg_locally(&msg);
1585 } else if (mode == MULTI_LINK_MODE) {
1586 broadcast_ctl_msg(&msg);
1587 broadcast_ctl_msg_locally(&msg);
1588 } else if (mode != NULL_MODE) {
1589 pr_err("%s: Invalid mode(%d) + xprt_inf(%p) for %08x:%08x\n",
1590 __func__, mode, xprt_info, node_id, port_id);
1591 rc = -EINVAL;
1592 }
1593 return rc;
1594}
1595
1596static void update_comm_mode_info(struct comm_mode_info *mode_info,
1597 struct msm_ipc_router_xprt_info *xprt_info)
1598{
1599 if (!mode_info) {
1600 pr_err("%s: NULL mode_info\n", __func__);
1601 return;
1602 }
1603
1604 if (mode_info->mode == NULL_MODE) {
1605 mode_info->xprt_info = xprt_info;
1606 mode_info->mode = SINGLE_LINK_MODE;
1607 } else if (mode_info->mode == SINGLE_LINK_MODE &&
1608 mode_info->xprt_info != xprt_info) {
1609 mode_info->mode = MULTI_LINK_MODE;
1610 }
1611
1612 return;
1613}
1614
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001615static void cleanup_rmt_server(struct msm_ipc_router_xprt_info *xprt_info,
1616 struct msm_ipc_router_remote_port *rport_ptr)
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001617{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001618 union rr_control_msg ctl;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001619 struct msm_ipc_server *server = rport_ptr->server;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001620
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001621 D("Remove server %08x:%08x - %08x:%08x",
1622 server->name.service, server->name.instance,
1623 rport_ptr->node_id, rport_ptr->port_id);
Karthikeyan Ramasubramanian972c2d22013-07-19 15:58:38 -06001624 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001625 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001626 ctl.srv.service = server->name.service;
1627 ctl.srv.instance = server->name.instance;
1628 ctl.srv.node_id = rport_ptr->node_id;
1629 ctl.srv.port_id = rport_ptr->port_id;
1630 relay_ctl_msg(xprt_info, &ctl);
1631 broadcast_ctl_msg_locally(&ctl);
1632 msm_ipc_router_destroy_server(server,
1633 rport_ptr->node_id, rport_ptr->port_id);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001634}
1635
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001636static void cleanup_rmt_ports(struct msm_ipc_router_xprt_info *xprt_info,
1637 struct msm_ipc_routing_table_entry *rt_entry)
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001638{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001639 struct msm_ipc_router_remote_port *rport_ptr, *tmp_rport_ptr;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001640 union rr_control_msg ctl;
1641 int j;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001642
Karthikeyan Ramasubramanian972c2d22013-07-19 15:58:38 -06001643 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001644 for (j = 0; j < RP_HASH_SIZE; j++) {
1645 list_for_each_entry_safe(rport_ptr, tmp_rport_ptr,
1646 &rt_entry->remote_port_list[j], list) {
1647 list_del(&rport_ptr->list);
1648 mutex_lock(&rport_ptr->quota_lock_lhb2);
1649 msm_ipc_router_free_resume_tx_port(rport_ptr);
1650 mutex_unlock(&rport_ptr->quota_lock_lhb2);
1651
1652 if (rport_ptr->server)
1653 cleanup_rmt_server(xprt_info, rport_ptr);
1654
1655 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
1656 ctl.cli.node_id = rport_ptr->node_id;
1657 ctl.cli.port_id = rport_ptr->port_id;
1658 relay_ctl_msg(xprt_info, &ctl);
1659 broadcast_ctl_msg_locally(&ctl);
1660 kfree(rport_ptr);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001661 }
1662 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001663}
1664
1665static void msm_ipc_cleanup_routing_table(
1666 struct msm_ipc_router_xprt_info *xprt_info)
1667{
1668 int i;
Karthikeyan Ramasubramanian2142f1e2013-08-06 18:15:52 -06001669 struct msm_ipc_routing_table_entry *rt_entry, *tmp_rt_entry;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001670
1671 if (!xprt_info) {
1672 pr_err("%s: Invalid xprt_info\n", __func__);
1673 return;
1674 }
1675
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001676 down_write(&server_list_lock_lha2);
1677 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001678 for (i = 0; i < RT_HASH_SIZE; i++) {
Karthikeyan Ramasubramanian2142f1e2013-08-06 18:15:52 -06001679 list_for_each_entry_safe(rt_entry, tmp_rt_entry,
1680 &routing_table[i], list) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001681 down_write(&rt_entry->lock_lha4);
1682 if (rt_entry->xprt_info != xprt_info) {
1683 up_write(&rt_entry->lock_lha4);
1684 continue;
1685 }
1686 cleanup_rmt_ports(xprt_info, rt_entry);
1687 rt_entry->xprt_info = NULL;
1688 up_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian2142f1e2013-08-06 18:15:52 -06001689 list_del(&rt_entry->list);
1690 kfree(rt_entry);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001691 }
1692 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001693 up_write(&routing_table_lock_lha3);
1694 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001695}
1696
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001697/**
1698 * sync_sec_rule() - Synchrnoize the security rule into the server structure
1699 * @server: Server structure where the rule has to be synchronized.
1700 * @rule: Security tule to be synchronized.
1701 *
1702 * This function is used to update the server structure with the security
1703 * rule configured for the <service:instance> corresponding to that server.
1704 */
1705static void sync_sec_rule(struct msm_ipc_server *server, void *rule)
1706{
1707 struct msm_ipc_server_port *server_port;
1708 struct msm_ipc_router_remote_port *rport_ptr = NULL;
1709
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001710 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001711 list_for_each_entry(server_port, &server->server_port_list, list) {
1712 rport_ptr = msm_ipc_router_lookup_remote_port(
1713 server_port->server_addr.node_id,
1714 server_port->server_addr.port_id);
1715 if (!rport_ptr)
1716 continue;
1717 rport_ptr->sec_rule = rule;
1718 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001719 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001720 server->synced_sec_rule = 1;
1721}
1722
1723/**
1724 * msm_ipc_sync_sec_rule() - Sync the security rule to the service
1725 * @service: Service for which the rule has to be synchronized.
1726 * @instance: Instance for which the rule has to be synchronized.
1727 * @rule: Security rule to be synchronized.
1728 *
1729 * This function is used to syncrhonize the security rule with the server
1730 * hash table, if the user-space script configures the rule after the service
1731 * has come up. This function is used to synchronize the security rule to a
1732 * specific service and optionally a specific instance.
1733 */
1734void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule)
1735{
1736 int key = (service & (SRV_HASH_SIZE - 1));
1737 struct msm_ipc_server *server;
1738
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001739 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001740 list_for_each_entry(server, &server_list[key], list) {
1741 if (server->name.service != service)
1742 continue;
1743
1744 if (server->name.instance != instance &&
1745 instance != ALL_INSTANCE)
1746 continue;
1747
1748 /*
1749 * If the rule applies to all instances and if the specific
1750 * instance of a service has a rule synchronized already,
1751 * do not apply the rule for that specific instance.
1752 */
1753 if (instance == ALL_INSTANCE && server->synced_sec_rule)
1754 continue;
1755
1756 sync_sec_rule(server, rule);
1757 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001758 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001759}
1760
1761/**
1762 * msm_ipc_sync_default_sec_rule() - Default security rule to all services
1763 * @rule: Security rule to be synchronized.
1764 *
1765 * This function is used to syncrhonize the security rule with the server
1766 * hash table, if the user-space script configures the rule after the service
1767 * has come up. This function is used to synchronize the security rule that
1768 * applies to all services, if the concerned service do not have any rule
1769 * defined.
1770 */
1771void msm_ipc_sync_default_sec_rule(void *rule)
1772{
1773 int key;
1774 struct msm_ipc_server *server;
1775
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001776 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001777 for (key = 0; key < SRV_HASH_SIZE; key++) {
1778 list_for_each_entry(server, &server_list[key], list) {
1779 if (server->synced_sec_rule)
1780 continue;
1781
1782 sync_sec_rule(server, rule);
1783 }
1784 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001785 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06001786}
1787
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001788static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001789 struct rr_header_v1 *hdr)
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001790{
1791 int i, rc = 0;
1792 union rr_control_msg ctl;
1793 struct msm_ipc_routing_table_entry *rt_entry;
1794
1795 if (!hdr)
1796 return -EINVAL;
1797
1798 RR("o HELLO NID %d\n", hdr->src_node_id);
1799
1800 xprt_info->remote_node_id = hdr->src_node_id;
1801 /*
1802 * Find the entry from Routing Table corresponding to Node ID.
1803 * Under SSR, an entry will be found. When the system boots up
1804 * for the 1st time, an entry will not be found and hence allocate
1805 * an entry. Update the entry with the Node ID that it corresponds
1806 * to and the XPRT through which it can be reached.
1807 */
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001808 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001809 rt_entry = lookup_routing_table(hdr->src_node_id);
1810 if (!rt_entry) {
1811 rt_entry = alloc_routing_table_entry(hdr->src_node_id);
1812 if (!rt_entry) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001813 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001814 pr_err("%s: rt_entry allocation failed\n", __func__);
1815 return -ENOMEM;
1816 }
1817 add_routing_table_entry(rt_entry);
1818 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001819 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001820 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1821 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001822 up_write(&rt_entry->lock_lha4);
1823 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001824
1825 /* Send a reply HELLO message */
1826 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001827 ctl.hello.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05301828 rc = msm_ipc_router_send_control_msg(xprt_info, &ctl,
1829 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001830 if (rc < 0) {
1831 pr_err("%s: Error sending reply HELLO message\n", __func__);
1832 return rc;
1833 }
1834 xprt_info->initialized = 1;
1835
1836 /*
1837 * Send list of servers from the local node and from nodes
1838 * outside the mesh network in which this XPRT is part of.
1839 */
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001840 down_read(&server_list_lock_lha2);
1841 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001842 for (i = 0; i < RT_HASH_SIZE; i++) {
1843 list_for_each_entry(rt_entry, &routing_table[i], list) {
1844 if ((rt_entry->node_id != IPC_ROUTER_NID_LOCAL) &&
Karthikeyan Ramasubramanianc4c0aaa2013-01-30 14:17:57 -07001845 (!rt_entry->xprt_info ||
1846 (rt_entry->xprt_info->xprt->link_id ==
1847 xprt_info->xprt->link_id)))
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001848 continue;
1849 rc = msm_ipc_router_send_server_list(rt_entry->node_id,
1850 xprt_info);
1851 if (rc < 0) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001852 up_read(&routing_table_lock_lha3);
1853 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001854 return rc;
1855 }
1856 }
1857 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001858 up_read(&routing_table_lock_lha3);
1859 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06001860 RR("HELLO message processed\n");
1861 return rc;
1862}
1863
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001864static int process_resume_tx_msg(union rr_control_msg *msg,
1865 struct rr_packet *pkt)
1866{
1867 struct msm_ipc_router_remote_port *rport_ptr;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001868 int ret = 0;
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001869
1870 RR("o RESUME_TX id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
1871
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001872 down_read(&local_ports_lock_lha2);
1873 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001874 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
1875 msg->cli.port_id);
1876 if (!rport_ptr) {
1877 pr_err("%s: Unable to resume client\n", __func__);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001878 ret = -ENODEV;
1879 goto prtm_out;
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001880 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001881 mutex_lock(&rport_ptr->quota_lock_lhb2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001882 rport_ptr->tx_quota_cnt = 0;
1883 post_resume_tx(rport_ptr, pkt);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001884 mutex_unlock(&rport_ptr->quota_lock_lhb2);
1885prtm_out:
1886 up_read(&routing_table_lock_lha3);
1887 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001888 return 0;
1889}
1890
1891static int process_new_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
1892 union rr_control_msg *msg, struct rr_packet *pkt)
1893{
1894 struct msm_ipc_routing_table_entry *rt_entry;
1895 struct msm_ipc_server *server;
1896 struct msm_ipc_router_remote_port *rport_ptr;
1897
1898 if (msg->srv.instance == 0) {
1899 pr_err("%s: Server %08x create rejected, version = 0\n",
1900 __func__, msg->srv.service);
1901 return -EINVAL;
1902 }
1903
1904 RR("o NEW_SERVER id=%d:%08x service=%08x:%08x\n", msg->srv.node_id,
1905 msg->srv.port_id, msg->srv.service, msg->srv.instance);
1906 /*
1907 * Find the entry from Routing Table corresponding to Node ID.
1908 * Under SSR, an entry will be found. When the subsystem hosting
1909 * service is not adjacent, an entry will not be found and hence
1910 * allocate an entry. Update the entry with the Node ID that it
1911 * corresponds to and the XPRT through which it can be reached.
1912 */
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001913 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001914 rt_entry = lookup_routing_table(msg->srv.node_id);
1915 if (!rt_entry) {
1916 rt_entry = alloc_routing_table_entry(msg->srv.node_id);
1917 if (!rt_entry) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001918 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001919 pr_err("%s: rt_entry allocation failed\n", __func__);
1920 return -ENOMEM;
1921 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001922 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001923 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1924 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001925 up_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001926 add_routing_table_entry(rt_entry);
1927 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001928 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001929
1930 /*
1931 * If the service does not exist already in the database, create and
1932 * store the service info. Create a remote port structure in which
1933 * the service is hosted and cache the security rule for the service
1934 * in that remote port structure.
1935 */
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001936 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001937 server = msm_ipc_router_lookup_server(msg->srv.service,
1938 msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
1939 if (!server) {
1940 server = msm_ipc_router_create_server(
1941 msg->srv.service, msg->srv.instance,
1942 msg->srv.node_id, msg->srv.port_id, xprt_info);
1943 if (!server) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001944 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001945 pr_err("%s: Server Create failed\n", __func__);
1946 return -ENOMEM;
1947 }
1948
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001949 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001950 if (!msm_ipc_router_lookup_remote_port(
1951 msg->srv.node_id, msg->srv.port_id)) {
1952 rport_ptr = msm_ipc_router_create_remote_port(
1953 msg->srv.node_id, msg->srv.port_id);
1954 if (!rport_ptr) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001955 up_read(&routing_table_lock_lha3);
1956 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001957 return -ENOMEM;
1958 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001959 rport_ptr->server = server;
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001960 rport_ptr->sec_rule = msm_ipc_get_security_rule(
1961 msg->srv.service,
1962 msg->srv.instance);
1963 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001964 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001965 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001966 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001967
1968 /*
1969 * Relay the new server message to other subsystems that do not belong
1970 * to the cluster from which this message is received. Notify the
1971 * local clients waiting for this service.
1972 */
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001973 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001974 post_control_ports(pkt);
1975 return 0;
1976}
1977
1978static int process_rmv_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
1979 union rr_control_msg *msg, struct rr_packet *pkt)
1980{
1981 struct msm_ipc_server *server;
1982
1983 RR("o REMOVE_SERVER service=%08x:%d\n",
1984 msg->srv.service, msg->srv.instance);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001985 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001986 server = msm_ipc_router_lookup_server(msg->srv.service,
1987 msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
1988 if (server) {
1989 msm_ipc_router_destroy_server(server, msg->srv.node_id,
1990 msg->srv.port_id);
1991 /*
1992 * Relay the new server message to other subsystems that do not
1993 * belong to the cluster from which this message is received.
1994 * Notify the local clients communicating with the service.
1995 */
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06001996 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06001997 post_control_ports(pkt);
1998 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06001999 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002000 return 0;
2001}
2002
2003static int process_rmv_client_msg(struct msm_ipc_router_xprt_info *xprt_info,
2004 union rr_control_msg *msg, struct rr_packet *pkt)
2005{
2006 struct msm_ipc_router_remote_port *rport_ptr;
2007
2008 RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002009 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002010 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
2011 msg->cli.port_id);
2012 if (rport_ptr)
2013 msm_ipc_router_destroy_remote_port(rport_ptr);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002014 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002015
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002016 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002017 post_control_ports(pkt);
2018 return 0;
2019}
2020
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002021static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info,
2022 struct rr_packet *pkt)
2023{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002024 union rr_control_msg *msg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002025 int rc = 0;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002026 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002027
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002028 if (pkt->length != sizeof(*msg)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002029 pr_err("%s: r2r msg size %d != %d\n", __func__, pkt->length,
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002030 sizeof(*msg));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002031 return -EINVAL;
2032 }
2033
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002034 hdr = &(pkt->hdr);
2035 msg = msm_ipc_router_skb_to_buf(pkt->pkt_fragment_q, sizeof(*msg));
2036 if (!msg) {
2037 pr_err("%s: Error extracting control msg\n", __func__);
2038 return -ENOMEM;
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07002039 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002040
2041 switch (msg->cmd) {
2042 case IPC_ROUTER_CTRL_CMD_HELLO:
Karthikeyan Ramasubramaniana2b7fdb2012-10-23 13:12:44 -06002043 rc = process_hello_msg(xprt_info, hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002044 break;
2045 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002046 rc = process_resume_tx_msg(msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002047 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002048 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002049 rc = process_new_server_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002050 break;
2051 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002052 rc = process_rmv_server_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002053 break;
2054 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
Karthikeyan Ramasubramaniane2e15fa2013-05-17 18:18:20 -06002055 rc = process_rmv_client_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002056 break;
2057 case IPC_ROUTER_CTRL_CMD_PING:
2058 /* No action needed for ping messages received */
2059 RR("o PING\n");
2060 break;
2061 default:
2062 RR("o UNKNOWN(%08x)\n", msg->cmd);
2063 rc = -ENOSYS;
2064 }
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002065 kfree(msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002066 return rc;
2067}
2068
2069static void do_read_data(struct work_struct *work)
2070{
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002071 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002072 struct rr_packet *pkt = NULL;
2073 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002074 struct msm_ipc_router_remote_port *rport_ptr;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002075 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002076
2077 struct msm_ipc_router_xprt_info *xprt_info =
2078 container_of(work,
2079 struct msm_ipc_router_xprt_info,
2080 read_data);
2081
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002082 while ((pkt = rr_read(xprt_info)) != NULL) {
2083 if (pkt->length < IPC_ROUTER_HDR_SIZE ||
2084 pkt->length > MAX_IPC_PKT_SIZE) {
2085 pr_err("%s: Invalid pkt length %d\n",
2086 __func__, pkt->length);
2087 goto fail_data;
2088 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002089
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002090 ret = extract_header(pkt);
2091 if (ret < 0)
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002092 goto fail_data;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002093 hdr = &(pkt->hdr);
Karthikeyan Ramasubramanian2bfe8ec2013-03-22 10:47:20 -06002094 RAW("ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
2095 hdr->version, hdr->type, hdr->src_node_id,
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002096 hdr->src_port_id, hdr->control_flag, hdr->size,
Karthikeyan Ramasubramanian2bfe8ec2013-03-22 10:47:20 -06002097 hdr->dst_node_id, hdr->dst_port_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002098
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002099 if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) &&
2100 ((hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX) ||
2101 (hdr->type == IPC_ROUTER_CTRL_CMD_DATA))) {
2102 forward_msg(xprt_info, pkt);
2103 release_pkt(pkt);
2104 continue;
2105 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002106
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002107 if (hdr->type != IPC_ROUTER_CTRL_CMD_DATA) {
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002108 process_control_msg(xprt_info, pkt);
2109 release_pkt(pkt);
2110 continue;
2111 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002112#if defined(CONFIG_MSM_SMD_LOGGING)
2113#if defined(DEBUG)
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002114 if (msm_ipc_router_debug_mask & SMEM_LOG) {
2115 smem_log_event((SMEM_LOG_PROC_ID_APPS |
Zaheerulla Meer83e6cbb2013-06-19 16:31:17 +05302116 SMEM_LOG_IPC_ROUTER_EVENT_BASE |
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002117 IPC_ROUTER_LOG_EVENT_RX),
2118 (hdr->src_node_id << 24) |
2119 (hdr->src_port_id & 0xffffff),
2120 (hdr->dst_node_id << 24) |
2121 (hdr->dst_port_id & 0xffffff),
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002122 (hdr->type << 24) | (hdr->control_flag << 16) |
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002123 (hdr->size & 0xffff));
2124 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002125#endif
2126#endif
2127
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002128 down_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002129 port_ptr = msm_ipc_router_lookup_local_port(hdr->dst_port_id);
2130 if (!port_ptr) {
2131 pr_err("%s: No local port id %08x\n", __func__,
2132 hdr->dst_port_id);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002133 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002134 release_pkt(pkt);
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05302135 return;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002136 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002137
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002138 down_read(&routing_table_lock_lha3);
2139 rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
2140 hdr->src_port_id);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002141 if (!rport_ptr) {
2142 rport_ptr = msm_ipc_router_create_remote_port(
2143 hdr->src_node_id,
2144 hdr->src_port_id);
2145 if (!rport_ptr) {
2146 pr_err("%s: Rmt Prt %08x:%08x create failed\n",
2147 __func__, hdr->src_node_id,
2148 hdr->src_port_id);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002149 up_read(&routing_table_lock_lha3);
2150 up_read(&local_ports_lock_lha2);
Zaheerulla Meer34274332013-07-12 16:16:30 +05302151 release_pkt(pkt);
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05302152 return;
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002153 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002154 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002155 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -06002156 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002157 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002158 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002159 return;
2160
2161fail_data:
2162 release_pkt(pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002163 pr_err("ipc_router has died\n");
2164}
2165
2166int msm_ipc_router_register_server(struct msm_ipc_port *port_ptr,
2167 struct msm_ipc_addr *name)
2168{
2169 struct msm_ipc_server *server;
2170 unsigned long flags;
2171 union rr_control_msg ctl;
2172
2173 if (!port_ptr || !name)
2174 return -EINVAL;
2175
2176 if (name->addrtype != MSM_IPC_ADDR_NAME)
2177 return -EINVAL;
2178
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002179 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002180 server = msm_ipc_router_lookup_server(name->addr.port_name.service,
2181 name->addr.port_name.instance,
2182 IPC_ROUTER_NID_LOCAL,
2183 port_ptr->this_port.port_id);
2184 if (server) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002185 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002186 pr_err("%s: Server already present\n", __func__);
2187 return -EINVAL;
2188 }
2189
2190 server = msm_ipc_router_create_server(name->addr.port_name.service,
2191 name->addr.port_name.instance,
2192 IPC_ROUTER_NID_LOCAL,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002193 port_ptr->this_port.port_id,
2194 NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002195 if (!server) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002196 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002197 pr_err("%s: Server Creation failed\n", __func__);
2198 return -EINVAL;
2199 }
2200
Karthikeyan Ramasubramanian972c2d22013-07-19 15:58:38 -06002201 memset(&ctl, 0, sizeof(ctl));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002202 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
2203 ctl.srv.service = server->name.service;
2204 ctl.srv.instance = server->name.instance;
2205 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
2206 ctl.srv.port_id = port_ptr->this_port.port_id;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002207 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002208 broadcast_ctl_msg(&ctl);
Karthikeyan Ramasubramanian9670bf12013-08-06 18:04:12 -06002209 broadcast_ctl_msg_locally(&ctl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002210 spin_lock_irqsave(&port_ptr->port_lock, flags);
2211 port_ptr->type = SERVER_PORT;
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06002212 port_ptr->mode_info.mode = MULTI_LINK_MODE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002213 port_ptr->port_name.service = server->name.service;
2214 port_ptr->port_name.instance = server->name.instance;
2215 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
2216 return 0;
2217}
2218
2219int msm_ipc_router_unregister_server(struct msm_ipc_port *port_ptr)
2220{
2221 struct msm_ipc_server *server;
2222 unsigned long flags;
2223 union rr_control_msg ctl;
2224
2225 if (!port_ptr)
2226 return -EINVAL;
2227
2228 if (port_ptr->type != SERVER_PORT) {
2229 pr_err("%s: Trying to unregister a non-server port\n",
2230 __func__);
2231 return -EINVAL;
2232 }
2233
2234 if (port_ptr->this_port.node_id != IPC_ROUTER_NID_LOCAL) {
2235 pr_err("%s: Trying to unregister a remote server locally\n",
2236 __func__);
2237 return -EINVAL;
2238 }
2239
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002240 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002241 server = msm_ipc_router_lookup_server(port_ptr->port_name.service,
2242 port_ptr->port_name.instance,
2243 port_ptr->this_port.node_id,
2244 port_ptr->this_port.port_id);
2245 if (!server) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002246 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002247 pr_err("%s: Server lookup failed\n", __func__);
2248 return -ENODEV;
2249 }
2250
Karthikeyan Ramasubramanian972c2d22013-07-19 15:58:38 -06002251 memset(&ctl, 0, sizeof(ctl));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002252 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
2253 ctl.srv.service = server->name.service;
2254 ctl.srv.instance = server->name.instance;
2255 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
2256 ctl.srv.port_id = port_ptr->this_port.port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002257 msm_ipc_router_destroy_server(server, port_ptr->this_port.node_id,
2258 port_ptr->this_port.port_id);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002259 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06002260 broadcast_ctl_msg(&ctl);
Karthikeyan Ramasubramanian9670bf12013-08-06 18:04:12 -06002261 broadcast_ctl_msg_locally(&ctl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002262 spin_lock_irqsave(&port_ptr->port_lock, flags);
2263 port_ptr->type = CLIENT_PORT;
2264 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
2265 return 0;
2266}
2267
2268static int loopback_data(struct msm_ipc_port *src,
2269 uint32_t port_id,
2270 struct sk_buff_head *data)
2271{
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002272 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002273 struct msm_ipc_port *port_ptr;
2274 struct rr_packet *pkt;
Karthikeyan Ramasubramanianb0e23c52013-02-08 13:07:42 -07002275 int ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002276
2277 if (!data) {
2278 pr_err("%s: Invalid pkt pointer\n", __func__);
2279 return -EINVAL;
2280 }
2281
2282 pkt = create_pkt(data);
2283 if (!pkt) {
2284 pr_err("%s: New pkt create failed\n", __func__);
2285 return -ENOMEM;
2286 }
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002287 hdr = &(pkt->hdr);
2288 hdr->version = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002289 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2290 hdr->src_node_id = src->this_port.node_id;
2291 hdr->src_port_id = src->this_port.port_id;
2292 hdr->size = pkt->length;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002293 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002294 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
2295 hdr->dst_port_id = port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002296
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002297 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002298 port_ptr = msm_ipc_router_lookup_local_port(port_id);
2299 if (!port_ptr) {
2300 pr_err("%s: Local port %d not present\n", __func__, port_id);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002301 up_read(&local_ports_lock_lha2);
Zaheerulla Meer74425252013-08-20 12:02:31 +05302302 pkt->pkt_fragment_q = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002303 release_pkt(pkt);
2304 return -ENODEV;
2305 }
2306
Karthikeyan Ramasubramanianb0e23c52013-02-08 13:07:42 -07002307 ret_len = pkt->length;
Karthikeyan Ramasubramanian6dbb00f2013-05-17 15:19:56 -06002308 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06002309 update_comm_mode_info(&src->mode_info, NULL);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002310 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002311
Karthikeyan Ramasubramanianb0e23c52013-02-08 13:07:42 -07002312 return ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002313}
2314
2315static int msm_ipc_router_write_pkt(struct msm_ipc_port *src,
2316 struct msm_ipc_router_remote_port *rport_ptr,
2317 struct rr_packet *pkt)
2318{
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002319 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002320 struct msm_ipc_router_xprt_info *xprt_info;
2321 struct msm_ipc_routing_table_entry *rt_entry;
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05302322 struct msm_ipc_resume_tx_port *resume_tx_port;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002323 struct sk_buff *temp_skb;
2324 int xprt_option;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002325 int ret;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002326 int align_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002327
2328 if (!rport_ptr || !src || !pkt)
2329 return -EINVAL;
2330
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002331 hdr = &(pkt->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002332 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2333 hdr->src_node_id = src->this_port.node_id;
2334 hdr->src_port_id = src->this_port.port_id;
2335 hdr->size = pkt->length;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002336 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002337 hdr->dst_node_id = rport_ptr->node_id;
2338 hdr->dst_port_id = rport_ptr->port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002339
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002340 mutex_lock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05302341 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) {
2342 if (msm_ipc_router_lookup_resume_tx_port(
2343 rport_ptr, src->this_port.port_id)) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002344 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05302345 return -EAGAIN;
2346 }
2347 resume_tx_port =
2348 kzalloc(sizeof(struct msm_ipc_resume_tx_port),
2349 GFP_KERNEL);
2350 if (!resume_tx_port) {
2351 pr_err("%s: Resume_Tx port allocation failed\n",
2352 __func__);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002353 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05302354 return -ENOMEM;
2355 }
2356 INIT_LIST_HEAD(&resume_tx_port->list);
2357 resume_tx_port->port_id = src->this_port.port_id;
2358 resume_tx_port->node_id = src->this_port.node_id;
2359 list_add_tail(&resume_tx_port->list,
2360 &rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002361 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +05302362 return -EAGAIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002363 }
2364 rport_ptr->tx_quota_cnt++;
2365 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA)
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002366 hdr->control_flag |= CONTROL_FLAG_CONFIRM_RX;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002367 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002368
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002369 rt_entry = lookup_routing_table(hdr->dst_node_id);
2370 if (!rt_entry || !rt_entry->xprt_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002371 pr_err("%s: Remote node %d not up\n",
2372 __func__, hdr->dst_node_id);
2373 return -ENODEV;
2374 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002375 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002376 xprt_info = rt_entry->xprt_info;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002377 ret = prepend_header(pkt, xprt_info);
2378 if (ret < 0) {
2379 up_read(&rt_entry->lock_lha4);
2380 pr_err("%s: Prepend Header failed\n", __func__);
2381 return ret;
2382 }
2383 xprt_option = xprt_info->xprt->get_option(xprt_info->xprt);
2384 if (!(xprt_option & FRAG_PKT_WRITE_ENABLE)) {
2385 ret = defragment_pkt(pkt);
2386 if (ret < 0) {
2387 up_read(&rt_entry->lock_lha4);
2388 return ret;
2389 }
2390 }
2391
2392 temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
2393 align_size = ALIGN_SIZE(pkt->length);
2394 skb_put(temp_skb, align_size);
2395 pkt->length += align_size;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002396 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002397 ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002398 mutex_unlock(&xprt_info->tx_lock_lhb2);
2399 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002400
2401 if (ret < 0) {
2402 pr_err("%s: Write on XPRT failed\n", __func__);
2403 return ret;
2404 }
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06002405 update_comm_mode_info(&src->mode_info, xprt_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002406
2407 RAW_HDR("[w rr_h] "
2408 "ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002409 "control_flag=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002410 hdr->version, type_to_str(hdr->type),
2411 hdr->src_node_id, hdr->src_port_id,
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002412 hdr->control_flag, hdr->size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002413 hdr->dst_node_id, hdr->dst_port_id);
2414
2415#if defined(CONFIG_MSM_SMD_LOGGING)
2416#if defined(DEBUG)
2417 if (msm_ipc_router_debug_mask & SMEM_LOG) {
2418 smem_log_event((SMEM_LOG_PROC_ID_APPS |
Zaheerulla Meer83e6cbb2013-06-19 16:31:17 +05302419 SMEM_LOG_IPC_ROUTER_EVENT_BASE |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002420 IPC_ROUTER_LOG_EVENT_TX),
2421 (hdr->src_node_id << 24) |
2422 (hdr->src_port_id & 0xffffff),
2423 (hdr->dst_node_id << 24) |
2424 (hdr->dst_port_id & 0xffffff),
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002425 (hdr->type << 24) | (hdr->control_flag << 16) |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002426 (hdr->size & 0xffff));
2427 }
2428#endif
2429#endif
2430
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002431 return hdr->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002432}
2433
2434int msm_ipc_router_send_to(struct msm_ipc_port *src,
2435 struct sk_buff_head *data,
2436 struct msm_ipc_addr *dest)
2437{
2438 uint32_t dst_node_id = 0, dst_port_id = 0;
2439 struct msm_ipc_server *server;
2440 struct msm_ipc_server_port *server_port;
2441 struct msm_ipc_router_remote_port *rport_ptr = NULL;
2442 struct rr_packet *pkt;
2443 int ret;
2444
2445 if (!src || !data || !dest) {
2446 pr_err("%s: Invalid Parameters\n", __func__);
2447 return -EINVAL;
2448 }
2449
2450 /* Resolve Address*/
2451 if (dest->addrtype == MSM_IPC_ADDR_ID) {
2452 dst_node_id = dest->addr.port_addr.node_id;
2453 dst_port_id = dest->addr.port_addr.port_id;
2454 } else if (dest->addrtype == MSM_IPC_ADDR_NAME) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002455 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002456 server = msm_ipc_router_lookup_server(
2457 dest->addr.port_name.service,
2458 dest->addr.port_name.instance,
2459 0, 0);
2460 if (!server) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002461 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002462 pr_err("%s: Destination not reachable\n", __func__);
2463 return -ENODEV;
2464 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002465 server_port = list_first_entry(&server->server_port_list,
2466 struct msm_ipc_server_port,
2467 list);
2468 dst_node_id = server_port->server_addr.node_id;
2469 dst_port_id = server_port->server_addr.port_id;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002470 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002471 }
2472 if (dst_node_id == IPC_ROUTER_NID_LOCAL) {
2473 ret = loopback_data(src, dst_port_id, data);
2474 return ret;
2475 }
2476
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002477 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002478 rport_ptr = msm_ipc_router_lookup_remote_port(dst_node_id,
2479 dst_port_id);
2480 if (!rport_ptr) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002481 up_read(&routing_table_lock_lha3);
Zaheerulla Meer3d8b0a02013-05-10 15:51:28 +05302482 pr_err("%s: Remote port not found\n", __func__);
2483 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002484 }
2485
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06002486 if (src->check_send_permissions) {
2487 ret = src->check_send_permissions(rport_ptr->sec_rule);
2488 if (ret <= 0) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002489 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06002490 pr_err("%s: permission failure for %s\n",
2491 __func__, current->comm);
2492 return -EPERM;
2493 }
2494 }
2495
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002496 pkt = create_pkt(data);
2497 if (!pkt) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002498 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002499 pr_err("%s: Pkt creation failed\n", __func__);
2500 return -ENOMEM;
2501 }
2502
2503 ret = msm_ipc_router_write_pkt(src, rport_ptr, pkt);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002504 up_read(&routing_table_lock_lha3);
Zaheerulla Meer74425252013-08-20 12:02:31 +05302505 if (ret < 0)
2506 pkt->pkt_fragment_q = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002507 release_pkt(pkt);
2508
2509 return ret;
2510}
2511
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002512int msm_ipc_router_send_msg(struct msm_ipc_port *src,
2513 struct msm_ipc_addr *dest,
2514 void *data, unsigned int data_len)
2515{
2516 struct sk_buff_head *out_skb_head;
2517 int ret;
2518
2519 out_skb_head = msm_ipc_router_buf_to_skb(data, data_len);
2520 if (!out_skb_head) {
2521 pr_err("%s: SKB conversion failed\n", __func__);
2522 return -EFAULT;
2523 }
2524
2525 ret = msm_ipc_router_send_to(src, out_skb_head, dest);
2526 if (ret < 0) {
Zaheerulla Meer74425252013-08-20 12:02:31 +05302527 if (ret != -EAGAIN)
2528 pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
2529 __func__, ret);
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002530 msm_ipc_router_free_skb(out_skb_head);
Zaheerulla Meer2f1eb072013-06-26 22:39:00 +05302531 return ret;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002532 }
2533 return 0;
2534}
2535
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05302536/**
2537 * msm_ipc_router_send_resume_tx() - Send Resume_Tx message
2538 * @data: Pointer to received data packet that has confirm_rx bit set
2539 *
2540 * @return: On success, number of bytes transferred is returned, else
2541 * standard linux error code is returned.
2542 *
2543 * This function sends the Resume_Tx event to the remote node that
2544 * sent the data with confirm_rx field set. In case of a multi-hop
2545 * scenario also, this function makes sure that the destination node_id
2546 * to which the resume_tx event should reach is right.
2547 */
2548static int msm_ipc_router_send_resume_tx(void *data)
2549{
2550 union rr_control_msg msg;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002551 struct rr_header_v1 *hdr = (struct rr_header_v1 *)data;
Zaheerulla Meerfbc5f902013-07-11 19:24:40 +05302552 struct msm_ipc_routing_table_entry *rt_entry;
2553 int ret;
2554
2555 memset(&msg, 0, sizeof(msg));
2556 msg.cmd = IPC_ROUTER_CTRL_CMD_RESUME_TX;
2557 msg.cli.node_id = hdr->dst_node_id;
2558 msg.cli.port_id = hdr->dst_port_id;
2559 down_read(&routing_table_lock_lha3);
2560 rt_entry = lookup_routing_table(hdr->src_node_id);
2561 if (!rt_entry) {
2562 pr_err("%s: %d Node is not present",
2563 __func__, hdr->src_node_id);
2564 up_read(&routing_table_lock_lha3);
2565 return -ENODEV;
2566 }
2567 RR("x RESUME_TX id=%d:%08x\n",
2568 msg.cli.node_id, msg.cli.port_id);
2569 ret = msm_ipc_router_send_control_msg(rt_entry->xprt_info, &msg,
2570 hdr->src_node_id);
2571 up_read(&routing_table_lock_lha3);
2572 if (ret < 0)
2573 pr_err("%s: Send Resume_Tx Failed SRC_NODE: %d SRC_PORT: %d DEST_NODE: %d",
2574 __func__, hdr->dst_node_id, hdr->dst_port_id,
2575 hdr->src_node_id);
2576
2577 return ret;
2578}
2579
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002580int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002581 struct rr_packet **read_pkt,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002582 size_t buf_len)
2583{
2584 struct rr_packet *pkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002585
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002586 if (!port_ptr || !read_pkt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002587 return -EINVAL;
2588
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002589 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002590 if (list_empty(&port_ptr->port_rx_q)) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002591 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002592 return -EAGAIN;
2593 }
2594
2595 pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002596 if ((buf_len) && (pkt->hdr.size > buf_len)) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002597 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002598 return -ETOOSMALL;
2599 }
2600 list_del(&pkt->list);
2601 if (list_empty(&port_ptr->port_rx_q))
2602 wake_unlock(&port_ptr->port_rx_wake_lock);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002603 *read_pkt = pkt;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002604 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002605 if (pkt->hdr.control_flag & CONTROL_FLAG_CONFIRM_RX)
2606 msm_ipc_router_send_resume_tx(&pkt->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002607
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002608 return pkt->length;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002609}
2610
Zaheerulla Meerabb0eba2013-05-22 23:31:59 +05302611/**
Arun Kumar Neelakantam0af96762013-06-21 17:57:18 +05302612 * msm_ipc_router_rx_data_wait() - Wait for new message destined to a local port.
2613 * @port_ptr: Pointer to the local port
2614 * @timeout: < 0 timeout indicates infinite wait till a message arrives.
2615 * > 0 timeout indicates the wait time.
2616 * 0 indicates that we do not wait.
2617 * @return: 0 if there are pending messages to read,
2618 * standard Linux error code otherwise.
2619 *
2620 * Checks for the availability of messages that are destined to a local port.
2621 * If no messages are present then waits as per @timeout.
2622 */
2623int msm_ipc_router_rx_data_wait(struct msm_ipc_port *port_ptr, long timeout)
2624{
2625 int ret = 0;
2626
2627 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
2628 while (list_empty(&port_ptr->port_rx_q)) {
2629 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
2630 if (timeout < 0) {
2631 ret = wait_event_interruptible(
2632 port_ptr->port_rx_wait_q,
2633 !list_empty(&port_ptr->port_rx_q));
2634 if (ret)
2635 return ret;
2636 } else if (timeout > 0) {
2637 timeout = wait_event_interruptible_timeout(
2638 port_ptr->port_rx_wait_q,
2639 !list_empty(&port_ptr->port_rx_q),
2640 timeout);
2641 if (timeout < 0)
2642 return -EFAULT;
2643 }
2644 if (timeout == 0)
2645 return -ENOMSG;
2646 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
2647 }
2648 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
2649
2650 return ret;
2651}
2652
2653/**
Zaheerulla Meerabb0eba2013-05-22 23:31:59 +05302654 * msm_ipc_router_recv_from() - Recieve messages destined to a local port.
2655 * @port_ptr: Pointer to the local port
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002656 * @pkt : Pointer to the router-to-router packet
Zaheerulla Meerabb0eba2013-05-22 23:31:59 +05302657 * @src: Pointer to local port address
2658 * @timeout: < 0 timeout indicates infinite wait till a message arrives.
2659 * > 0 timeout indicates the wait time.
2660 * 0 indicates that we do not wait.
2661 * @return: = Number of bytes read(On successful read operation).
Arun Kumar Neelakantam0af96762013-06-21 17:57:18 +05302662 * = -ENOMSG (If there are no pending messages and timeout is 0).
Zaheerulla Meerabb0eba2013-05-22 23:31:59 +05302663 * = -EINVAL (If either of the arguments, port_ptr or data is invalid)
2664 * = -EFAULT (If there are no pending messages when timeout is > 0
2665 * and the wait_event_interruptible_timeout has returned value > 0)
2666 * = -ERESTARTSYS (If there are no pending messages when timeout
2667 * is < 0 and wait_event_interruptible was interrupted by a signal)
2668 *
2669 * This function reads the messages that are destined for a local port. It
2670 * is used by modules that exist with-in the kernel and use IPC Router for
2671 * transport. The function checks if there are any messages that are already
2672 * received. If yes, it reads them, else it waits as per the timeout value.
2673 * On a successful read, the return value of the function indicates the number
2674 * of bytes that are read.
2675 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002676int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002677 struct rr_packet **pkt,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002678 struct msm_ipc_addr *src,
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002679 long timeout)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002680{
2681 int ret, data_len, align_size;
2682 struct sk_buff *temp_skb;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002683 struct rr_header_v1 *hdr = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002684
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002685 if (!port_ptr || !pkt) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002686 pr_err("%s: Invalid pointers being passed\n", __func__);
2687 return -EINVAL;
2688 }
2689
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002690 *pkt = NULL;
Arun Kumar Neelakantam0af96762013-06-21 17:57:18 +05302691
2692 ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
2693 if (ret)
2694 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002695
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002696 ret = msm_ipc_router_read(port_ptr, pkt, 0);
2697 if (ret <= 0 || !(*pkt))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002698 return ret;
2699
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002700 hdr = &((*pkt)->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002701 if (src) {
2702 src->addrtype = MSM_IPC_ADDR_ID;
2703 src->addr.port_addr.node_id = hdr->src_node_id;
2704 src->addr.port_addr.port_id = hdr->src_port_id;
2705 }
2706
2707 data_len = hdr->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002708 align_size = ALIGN_SIZE(data_len);
2709 if (align_size) {
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002710 temp_skb = skb_peek_tail((*pkt)->pkt_fragment_q);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002711 skb_trim(temp_skb, (temp_skb->len - align_size));
2712 }
2713 return data_len;
2714}
2715
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002716int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
2717 struct msm_ipc_addr *src,
2718 unsigned char **data,
2719 unsigned int *len)
2720{
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002721 struct rr_packet *pkt;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002722 int ret;
2723
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002724 ret = msm_ipc_router_recv_from(port_ptr, &pkt, src, 0);
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002725 if (ret < 0) {
Arun Kumar Neelakantam0af96762013-06-21 17:57:18 +05302726 if (ret != -ENOMSG)
2727 pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
2728 __func__, ret);
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002729 return ret;
2730 }
2731
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002732 *data = msm_ipc_router_skb_to_buf(pkt->pkt_fragment_q, ret);
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002733 if (!(*data))
2734 pr_err("%s: Buf conversion failed\n", __func__);
2735
2736 *len = ret;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -06002737 release_pkt(pkt);
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002738 return 0;
2739}
2740
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002741struct msm_ipc_port *msm_ipc_router_create_port(
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002742 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002743 void *priv)
2744{
2745 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -06002746 int ret;
2747
2748 ret = wait_for_completion_interruptible(&msm_ipc_local_router_up);
2749 if (ret < 0) {
2750 pr_err("%s: Error waiting for local router\n", __func__);
2751 return NULL;
2752 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002753
2754 port_ptr = msm_ipc_router_create_raw_port(NULL, notify, priv);
2755 if (!port_ptr)
2756 pr_err("%s: port_ptr alloc failed\n", __func__);
2757
2758 return port_ptr;
2759}
2760
2761int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
2762{
2763 union rr_control_msg msg;
2764 struct rr_packet *pkt, *temp_pkt;
2765 struct msm_ipc_server *server;
2766
2767 if (!port_ptr)
2768 return -EINVAL;
2769
2770 if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002771 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002772 list_del(&port_ptr->list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002773 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002774
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002775 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramanian972c2d22013-07-19 15:58:38 -06002776 memset(&msg, 0, sizeof(msg));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002777 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
2778 msg.srv.service = port_ptr->port_name.service;
2779 msg.srv.instance = port_ptr->port_name.instance;
2780 msg.srv.node_id = port_ptr->this_port.node_id;
2781 msg.srv.port_id = port_ptr->this_port.port_id;
2782 RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
2783 msg.srv.service, msg.srv.instance,
2784 msg.srv.node_id, msg.srv.port_id);
Karthikeyan Ramasubramanian5c568fb2013-01-10 17:09:13 -07002785 broadcast_ctl_msg(&msg);
2786 broadcast_ctl_msg_locally(&msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002787 }
Karthikeyan Ramasubramanian5c568fb2013-01-10 17:09:13 -07002788
2789 /*
2790 * Server port could have been a client port earlier.
2791 * Send REMOVE_CLIENT message in either case.
2792 */
Karthikeyan Ramasubramanian5c568fb2013-01-10 17:09:13 -07002793 RR("x REMOVE_CLIENT id=%d:%08x\n",
Karthikeyan Ramasubramanian96cced72013-05-02 17:25:54 -06002794 port_ptr->this_port.node_id, port_ptr->this_port.port_id);
2795 msm_ipc_router_send_remove_client(&port_ptr->mode_info,
2796 port_ptr->this_port.node_id,
2797 port_ptr->this_port.port_id);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002798 } else if (port_ptr->type == CONTROL_PORT) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002799 down_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002800 list_del(&port_ptr->list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002801 up_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -07002802 } else if (port_ptr->type == IRSC_PORT) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002803 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -07002804 list_del(&port_ptr->list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002805 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -07002806 signal_irsc_completion();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002807 }
2808
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002809 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002810 list_for_each_entry_safe(pkt, temp_pkt, &port_ptr->port_rx_q, list) {
2811 list_del(&pkt->list);
2812 release_pkt(pkt);
2813 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002814 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002815
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002816 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002817 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002818 server = msm_ipc_router_lookup_server(
2819 port_ptr->port_name.service,
2820 port_ptr->port_name.instance,
2821 port_ptr->this_port.node_id,
2822 port_ptr->this_port.port_id);
2823 if (server)
2824 msm_ipc_router_destroy_server(server,
2825 port_ptr->this_port.node_id,
2826 port_ptr->this_port.port_id);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002827 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002828 }
2829
Karthikeyan Ramasubramaniandd8c3b52011-11-30 16:26:12 -07002830 wake_lock_destroy(&port_ptr->port_rx_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002831 kfree(port_ptr);
2832 return 0;
2833}
2834
2835int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr)
2836{
2837 struct rr_packet *pkt;
2838 int rc = 0;
2839
2840 if (!port_ptr)
2841 return -EINVAL;
2842
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002843 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002844 if (!list_empty(&port_ptr->port_rx_q)) {
2845 pkt = list_first_entry(&port_ptr->port_rx_q,
2846 struct rr_packet, list);
2847 rc = pkt->length;
2848 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002849 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002850
2851 return rc;
2852}
2853
2854int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr)
2855{
2856 if (!port_ptr)
2857 return -EINVAL;
2858
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002859 down_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002860 list_del(&port_ptr->list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002861 up_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002862 port_ptr->type = CONTROL_PORT;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002863 down_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002864 list_add_tail(&port_ptr->list, &control_ports);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002865 up_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002866
2867 return 0;
2868}
2869
2870int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002871 struct msm_ipc_server_info *srv_info,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002872 int num_entries_in_array,
2873 uint32_t lookup_mask)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002874{
2875 struct msm_ipc_server *server;
2876 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002877 int key, i = 0; /*num_entries_found*/
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002878
2879 if (!srv_name) {
2880 pr_err("%s: Invalid srv_name\n", __func__);
2881 return -EINVAL;
2882 }
2883
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002884 if (num_entries_in_array && !srv_info) {
2885 pr_err("%s: srv_info NULL\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002886 return -EINVAL;
2887 }
2888
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002889 down_read(&server_list_lock_lha2);
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002890 if (!lookup_mask)
2891 lookup_mask = 0xFFFFFFFF;
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06002892 key = (srv_name->service & (SRV_HASH_SIZE - 1));
2893 list_for_each_entry(server, &server_list[key], list) {
2894 if ((server->name.service != srv_name->service) ||
2895 ((server->name.instance & lookup_mask) !=
2896 srv_name->instance))
2897 continue;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002898
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06002899 list_for_each_entry(server_port,
2900 &server->server_port_list, list) {
2901 if (i < num_entries_in_array) {
2902 srv_info[i].node_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002903 server_port->server_addr.node_id;
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06002904 srv_info[i].port_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002905 server_port->server_addr.port_id;
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06002906 srv_info[i].service = server->name.service;
2907 srv_info[i].instance = server->name.instance;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002908 }
Karthikeyan Ramasubramanianbb8306b2012-10-25 15:40:45 -06002909 i++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002910 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002911 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002912 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002913
2914 return i;
2915}
2916
2917int msm_ipc_router_close(void)
2918{
2919 struct msm_ipc_router_xprt_info *xprt_info, *tmp_xprt_info;
2920
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002921 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002922 list_for_each_entry_safe(xprt_info, tmp_xprt_info,
2923 &xprt_info_list, list) {
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002924 xprt_info->xprt->close(xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002925 list_del(&xprt_info->list);
2926 kfree(xprt_info);
2927 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002928 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002929 return 0;
2930}
2931
2932#if defined(CONFIG_DEBUG_FS)
2933static int dump_routing_table(char *buf, int max)
2934{
2935 int i = 0, j;
2936 struct msm_ipc_routing_table_entry *rt_entry;
2937
2938 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002939 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002940 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002941 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002942 i += scnprintf(buf + i, max - i,
2943 "Node Id: 0x%08x\n", rt_entry->node_id);
Karthikeyan Ramasubramanianbddeec72012-09-10 16:10:24 -06002944 if (rt_entry->node_id == IPC_ROUTER_NID_LOCAL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002945 i += scnprintf(buf + i, max - i,
2946 "XPRT Name: Loopback\n");
2947 i += scnprintf(buf + i, max - i,
2948 "Next Hop: %d\n", rt_entry->node_id);
2949 } else {
2950 i += scnprintf(buf + i, max - i,
2951 "XPRT Name: %s\n",
2952 rt_entry->xprt_info->xprt->name);
2953 i += scnprintf(buf + i, max - i,
2954 "Next Hop: 0x%08x\n",
2955 rt_entry->xprt_info->remote_node_id);
2956 }
2957 i += scnprintf(buf + i, max - i, "\n");
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002958 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002959 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002960 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002961 }
2962
2963 return i;
2964}
2965
2966static int dump_xprt_info(char *buf, int max)
2967{
2968 int i = 0;
2969 struct msm_ipc_router_xprt_info *xprt_info;
2970
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002971 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002972 list_for_each_entry(xprt_info, &xprt_info_list, list) {
2973 i += scnprintf(buf + i, max - i, "XPRT Name: %s\n",
2974 xprt_info->xprt->name);
2975 i += scnprintf(buf + i, max - i, "Link Id: %d\n",
2976 xprt_info->xprt->link_id);
2977 i += scnprintf(buf + i, max - i, "Initialized: %s\n",
2978 (xprt_info->initialized ? "Y" : "N"));
2979 i += scnprintf(buf + i, max - i, "Remote Node Id: 0x%08x\n",
2980 xprt_info->remote_node_id);
2981 i += scnprintf(buf + i, max - i, "\n");
2982 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002983 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002984
2985 return i;
2986}
2987
2988static int dump_servers(char *buf, int max)
2989{
2990 int i = 0, j;
2991 struct msm_ipc_server *server;
2992 struct msm_ipc_server_port *server_port;
2993
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06002994 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002995 for (j = 0; j < SRV_HASH_SIZE; j++) {
2996 list_for_each_entry(server, &server_list[j], list) {
2997 list_for_each_entry(server_port,
2998 &server->server_port_list,
2999 list) {
3000 i += scnprintf(buf + i, max - i, "Service: "
3001 "0x%08x\n", server->name.service);
3002 i += scnprintf(buf + i, max - i, "Instance: "
3003 "0x%08x\n", server->name.instance);
3004 i += scnprintf(buf + i, max - i,
3005 "Node_id: 0x%08x\n",
3006 server_port->server_addr.node_id);
3007 i += scnprintf(buf + i, max - i,
3008 "Port_id: 0x%08x\n",
3009 server_port->server_addr.port_id);
3010 i += scnprintf(buf + i, max - i, "\n");
3011 }
3012 }
3013 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003014 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003015
3016 return i;
3017}
3018
3019static int dump_remote_ports(char *buf, int max)
3020{
3021 int i = 0, j, k;
3022 struct msm_ipc_router_remote_port *rport_ptr;
3023 struct msm_ipc_routing_table_entry *rt_entry;
3024
3025 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003026 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003027 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003028 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003029 for (k = 0; k < RP_HASH_SIZE; k++) {
3030 list_for_each_entry(rport_ptr,
3031 &rt_entry->remote_port_list[k],
3032 list) {
3033 i += scnprintf(buf + i, max - i,
3034 "Node_id: 0x%08x\n",
3035 rport_ptr->node_id);
3036 i += scnprintf(buf + i, max - i,
3037 "Port_id: 0x%08x\n",
3038 rport_ptr->port_id);
3039 i += scnprintf(buf + i, max - i,
3040 "Quota_cnt: %d\n",
3041 rport_ptr->tx_quota_cnt);
3042 i += scnprintf(buf + i, max - i, "\n");
3043 }
3044 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003045 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003046 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003047 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003048 }
3049
3050 return i;
3051}
3052
3053static int dump_control_ports(char *buf, int max)
3054{
3055 int i = 0;
3056 struct msm_ipc_port *port_ptr;
3057
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003058 down_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003059 list_for_each_entry(port_ptr, &control_ports, list) {
3060 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
3061 port_ptr->this_port.node_id);
3062 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
3063 port_ptr->this_port.port_id);
3064 i += scnprintf(buf + i, max - i, "\n");
3065 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003066 up_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003067
3068 return i;
3069}
3070
3071static int dump_local_ports(char *buf, int max)
3072{
3073 int i = 0, j;
3074 unsigned long flags;
3075 struct msm_ipc_port *port_ptr;
3076
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003077 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003078 for (j = 0; j < LP_HASH_SIZE; j++) {
3079 list_for_each_entry(port_ptr, &local_ports[j], list) {
3080 spin_lock_irqsave(&port_ptr->port_lock, flags);
3081 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
3082 port_ptr->this_port.node_id);
3083 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
3084 port_ptr->this_port.port_id);
3085 i += scnprintf(buf + i, max - i, "# pkts tx'd %d\n",
3086 port_ptr->num_tx);
3087 i += scnprintf(buf + i, max - i, "# pkts rx'd %d\n",
3088 port_ptr->num_rx);
3089 i += scnprintf(buf + i, max - i, "# bytes tx'd %ld\n",
3090 port_ptr->num_tx_bytes);
3091 i += scnprintf(buf + i, max - i, "# bytes rx'd %ld\n",
3092 port_ptr->num_rx_bytes);
3093 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
3094 i += scnprintf(buf + i, max - i, "\n");
3095 }
3096 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003097 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003098
3099 return i;
3100}
3101
3102#define DEBUG_BUFMAX 4096
3103static char debug_buffer[DEBUG_BUFMAX];
3104
3105static ssize_t debug_read(struct file *file, char __user *buf,
3106 size_t count, loff_t *ppos)
3107{
3108 int (*fill)(char *buf, int max) = file->private_data;
3109 int bsize = fill(debug_buffer, DEBUG_BUFMAX);
3110 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
3111}
3112
3113static int debug_open(struct inode *inode, struct file *file)
3114{
3115 file->private_data = inode->i_private;
3116 return 0;
3117}
3118
3119static const struct file_operations debug_ops = {
3120 .read = debug_read,
3121 .open = debug_open,
3122};
3123
3124static void debug_create(const char *name, mode_t mode,
3125 struct dentry *dent,
3126 int (*fill)(char *buf, int max))
3127{
3128 debugfs_create_file(name, mode, dent, fill, &debug_ops);
3129}
3130
3131static void debugfs_init(void)
3132{
3133 struct dentry *dent;
3134
3135 dent = debugfs_create_dir("msm_ipc_router", 0);
3136 if (IS_ERR(dent))
3137 return;
3138
3139 debug_create("dump_local_ports", 0444, dent,
3140 dump_local_ports);
3141 debug_create("dump_remote_ports", 0444, dent,
3142 dump_remote_ports);
3143 debug_create("dump_control_ports", 0444, dent,
3144 dump_control_ports);
3145 debug_create("dump_servers", 0444, dent,
3146 dump_servers);
3147 debug_create("dump_xprt_info", 0444, dent,
3148 dump_xprt_info);
3149 debug_create("dump_routing_table", 0444, dent,
3150 dump_routing_table);
3151}
3152
3153#else
3154static void debugfs_init(void) {}
3155#endif
3156
3157static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
3158{
3159 struct msm_ipc_router_xprt_info *xprt_info;
3160 struct msm_ipc_routing_table_entry *rt_entry;
3161
3162 xprt_info = kmalloc(sizeof(struct msm_ipc_router_xprt_info),
3163 GFP_KERNEL);
3164 if (!xprt_info)
3165 return -ENOMEM;
3166
3167 xprt_info->xprt = xprt;
3168 xprt_info->initialized = 0;
3169 xprt_info->remote_node_id = -1;
3170 INIT_LIST_HEAD(&xprt_info->pkt_list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003171 mutex_init(&xprt_info->rx_lock_lhb2);
3172 mutex_init(&xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003173 wake_lock_init(&xprt_info->wakelock,
3174 WAKE_LOCK_SUSPEND, xprt->name);
3175 xprt_info->need_len = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003176 xprt_info->abort_data_read = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003177 INIT_WORK(&xprt_info->read_data, do_read_data);
3178 INIT_LIST_HEAD(&xprt_info->list);
3179
3180 xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
3181 if (!xprt_info->workqueue) {
3182 kfree(xprt_info);
3183 return -ENOMEM;
3184 }
3185
3186 if (!strcmp(xprt->name, "msm_ipc_router_loopback_xprt")) {
3187 xprt_info->remote_node_id = IPC_ROUTER_NID_LOCAL;
3188 xprt_info->initialized = 1;
3189 }
3190
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003191 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003192 list_add_tail(&xprt_info->list, &xprt_info_list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003193 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003194
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003195 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003196 if (!routing_table_inited) {
3197 init_routing_table();
3198 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
3199 add_routing_table_entry(rt_entry);
3200 routing_table_inited = 1;
3201 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003202 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003203
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003204 xprt->priv = xprt_info;
3205
3206 return 0;
3207}
3208
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003209static void msm_ipc_router_remove_xprt(struct msm_ipc_router_xprt *xprt)
3210{
3211 struct msm_ipc_router_xprt_info *xprt_info;
3212
3213 if (xprt && xprt->priv) {
3214 xprt_info = xprt->priv;
3215
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003216 mutex_lock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003217 xprt_info->abort_data_read = 1;
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003218 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003219
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003220 down_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003221 list_del(&xprt_info->list);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003222 up_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003223
3224 flush_workqueue(xprt_info->workqueue);
3225 destroy_workqueue(xprt_info->workqueue);
3226 wake_lock_destroy(&xprt_info->wakelock);
3227
3228 xprt->priv = 0;
3229 kfree(xprt_info);
3230 }
3231}
3232
3233
3234struct msm_ipc_router_xprt_work {
3235 struct msm_ipc_router_xprt *xprt;
3236 struct work_struct work;
3237};
3238
3239static void xprt_open_worker(struct work_struct *work)
3240{
3241 struct msm_ipc_router_xprt_work *xprt_work =
3242 container_of(work, struct msm_ipc_router_xprt_work, work);
3243
3244 msm_ipc_router_add_xprt(xprt_work->xprt);
3245 kfree(xprt_work);
3246}
3247
3248static void xprt_close_worker(struct work_struct *work)
3249{
3250 struct msm_ipc_router_xprt_work *xprt_work =
3251 container_of(work, struct msm_ipc_router_xprt_work, work);
3252
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003253 msm_ipc_cleanup_routing_table(xprt_work->xprt->priv);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003254 msm_ipc_router_remove_xprt(xprt_work->xprt);
Karthikeyan Ramasubramaniane6ef0a22013-06-12 11:49:26 -06003255 xprt_work->xprt->sft_close_done(xprt_work->xprt);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003256 kfree(xprt_work);
3257}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003258
3259void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
3260 unsigned event,
3261 void *data)
3262{
3263 struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003264 struct msm_ipc_router_xprt_work *xprt_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003265 struct rr_packet *pkt;
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003266 unsigned long ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003267
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003268 if (!msm_ipc_router_workqueue) {
3269 ret = wait_for_completion_timeout(&msm_ipc_local_router_up,
3270 IPC_ROUTER_INIT_TIMEOUT);
3271 if (!ret || !msm_ipc_router_workqueue) {
3272 pr_err("%s: IPC Router not initialized\n", __func__);
3273 return;
3274 }
3275 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003276
3277 switch (event) {
3278 case IPC_ROUTER_XPRT_EVENT_OPEN:
3279 D("open event for '%s'\n", xprt->name);
3280 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
3281 GFP_ATOMIC);
Karthikeyan Ramasubramanian840bed12013-05-16 15:51:29 -06003282 if (xprt_work) {
3283 xprt_work->xprt = xprt;
3284 INIT_WORK(&xprt_work->work, xprt_open_worker);
3285 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
3286 } else {
3287 pr_err("%s: malloc failure - Couldn't notify OPEN event",
3288 __func__);
3289 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003290 break;
3291
3292 case IPC_ROUTER_XPRT_EVENT_CLOSE:
3293 D("close event for '%s'\n", xprt->name);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003294 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
3295 GFP_ATOMIC);
Karthikeyan Ramasubramanian840bed12013-05-16 15:51:29 -06003296 if (xprt_work) {
3297 xprt_work->xprt = xprt;
3298 INIT_WORK(&xprt_work->work, xprt_close_worker);
3299 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
3300 } else {
3301 pr_err("%s: malloc failure - Couldn't notify CLOSE event",
3302 __func__);
3303 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003304 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003305 }
3306
3307 if (!data)
3308 return;
3309
3310 while (!xprt_info) {
3311 msleep(100);
3312 xprt_info = xprt->priv;
3313 }
3314
3315 pkt = clone_pkt((struct rr_packet *)data);
3316 if (!pkt)
3317 return;
3318
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003319 mutex_lock(&xprt_info->rx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003320 list_add_tail(&pkt->list, &xprt_info->pkt_list);
3321 wake_lock(&xprt_info->wakelock);
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003322 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06003323 queue_work(xprt_info->workqueue, &xprt_info->read_data);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003324}
3325
3326static int __init msm_ipc_router_init(void)
3327{
3328 int i, ret;
3329 struct msm_ipc_routing_table_entry *rt_entry;
3330
3331 msm_ipc_router_debug_mask |= SMEM_LOG;
Karthikeyan Ramasubramanian2bfe8ec2013-03-22 10:47:20 -06003332 ipc_rtr_log_ctxt = ipc_log_context_create(IPC_RTR_LOG_PAGES,
3333 "ipc_router");
3334 if (!ipc_rtr_log_ctxt)
3335 pr_err("%s: Unable to create IPC logging for IPC RTR",
3336 __func__);
3337
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003338 msm_ipc_router_workqueue =
3339 create_singlethread_workqueue("msm_ipc_router");
3340 if (!msm_ipc_router_workqueue)
3341 return -ENOMEM;
3342
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003343 debugfs_init();
3344
3345 for (i = 0; i < SRV_HASH_SIZE; i++)
3346 INIT_LIST_HEAD(&server_list[i]);
3347
3348 for (i = 0; i < LP_HASH_SIZE; i++)
3349 INIT_LIST_HEAD(&local_ports[i]);
3350
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003351 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003352 if (!routing_table_inited) {
3353 init_routing_table();
3354 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
3355 add_routing_table_entry(rt_entry);
3356 routing_table_inited = 1;
3357 }
Karthikeyan Ramasubramanian4b88e5d2013-05-21 11:49:21 -06003358 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003359
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003360 ret = msm_ipc_router_init_sockets();
3361 if (ret < 0)
3362 pr_err("%s: Init sockets failed\n", __func__);
3363
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -06003364 ret = msm_ipc_router_security_init();
3365 if (ret < 0)
3366 pr_err("%s: Security Init failed\n", __func__);
3367
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003368 complete_all(&msm_ipc_local_router_up);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003369 return ret;
3370}
3371
3372module_init(msm_ipc_router_init);
3373MODULE_DESCRIPTION("MSM IPC Router");
3374MODULE_LICENSE("GPL v2");