blob: 64e8035b7259d4d7872bf53baaa8612bd66f9cca [file] [log] [blame]
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define DEBUG
14
15#include <linux/slab.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/string.h>
19#include <linux/errno.h>
20#include <linux/init.h>
21#include <linux/types.h>
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/sched.h>
25#include <linux/poll.h>
26#include <linux/wakelock.h>
27#include <linux/platform_device.h>
28#include <linux/uaccess.h>
29#include <linux/debugfs.h>
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -060030#include <linux/rwsem.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include <asm/uaccess.h>
33#include <asm/byteorder.h>
34
35#include <mach/smem_log.h>
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060036#include <mach/subsystem_notif.h>
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -060037#include <mach/msm_ipc_router.h>
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060038#include <mach/msm_ipc_logging.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
40#include "ipc_router.h"
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060041#include "modem_notifier.h"
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -060042#include "msm_ipc_router_security.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043
44enum {
45 SMEM_LOG = 1U << 0,
46 RTR_DBG = 1U << 1,
47 R2R_MSG = 1U << 2,
48 R2R_RAW = 1U << 3,
49 NTFY_MSG = 1U << 4,
50 R2R_RAW_HDR = 1U << 5,
51};
52
53static int msm_ipc_router_debug_mask;
54module_param_named(debug_mask, msm_ipc_router_debug_mask,
55 int, S_IRUGO | S_IWUSR | S_IWGRP);
56
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060057static void *ipc_rtr_log_ctxt;
58#define IPC_RTR_LOG_PAGES 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#define DIAG(x...) pr_info("[RR] ERROR " x)
60
61#if defined(DEBUG)
62#define D(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060063if (ipc_rtr_log_ctxt) \
64 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065if (msm_ipc_router_debug_mask & RTR_DBG) \
66 pr_info(x); \
67} while (0)
68
69#define RR(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060070if (ipc_rtr_log_ctxt) \
71 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072if (msm_ipc_router_debug_mask & R2R_MSG) \
73 pr_info("[RR] "x); \
74} while (0)
75
76#define RAW(x...) do { \
77if (msm_ipc_router_debug_mask & R2R_RAW) \
78 pr_info("[RAW] "x); \
79} while (0)
80
81#define NTFY(x...) do { \
82if (msm_ipc_router_debug_mask & NTFY_MSG) \
83 pr_info("[NOTIFY] "x); \
84} while (0)
85
86#define RAW_HDR(x...) do { \
87if (msm_ipc_router_debug_mask & R2R_RAW_HDR) \
88 pr_info("[HDR] "x); \
89} while (0)
90#else
91#define D(x...) do { } while (0)
92#define RR(x...) do { } while (0)
93#define RAW(x...) do { } while (0)
94#define RAW_HDR(x...) do { } while (0)
95#define NTFY(x...) do { } while (0)
96#endif
97
Zaheerulla Meer1ee914e2013-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 Meer3ce2dfb2013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -0600108static DECLARE_RWSEM(local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109
Karthikeyan Ramasubramaniana85d09f2012-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 Ramasubramaniand06fe072013-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 Ramasubramanian1668cc62012-09-23 22:23:36 -0600124 int synced_sec_rule;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600125 char pdev_name[32];
126 int next_pdev_id;
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 Ramasubramanian70444192012-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 Meera34fc662013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -0600149 struct mutex quota_lock_lhb2;
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530150 struct list_head resume_tx_port_list;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600151 void *sec_rule;
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian70444192012-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600380 int last = 1;
381 int align_size;
Karthikeyan Ramasubramanian70444192012-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600391 align_size = ALIGN_SIZE(data_size);
Karthikeyan Ramasubramanian70444192012-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600396 if (last)
397 skb_size += align_size;
Karthikeyan Ramasubramanian70444192012-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600406 last = 0;
Karthikeyan Ramasubramanian70444192012-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600420 last = 1;
Karthikeyan Ramasubramanian70444192012-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 Meer7ccb9672013-08-20 12:02:31 +0530461void msm_ipc_router_free_skb(struct sk_buff_head *skb_head)
Karthikeyan Ramasubramanian70444192012-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 Ramasubramanian6e2c9a62013-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 Ramasubramaniand1af6252013-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 Meer29302cf2013-05-24 21:19:18 +0530801 void (*notify)(unsigned event, void *priv);
Karthikeyan Ramasubramaniand1af6252013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -0600816 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramaniand1af6252013-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 Meer29302cf2013-05-24 21:19:18 +0530820 notify = port_ptr->notify;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600821 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Zaheerulla Meer29302cf2013-05-24 21:19:18 +0530822 if (notify)
823 notify(MSM_IPC_ROUTER_READ_CB, port_ptr->priv);
Karthikeyan Ramasubramaniand1af6252013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -0600834 down_read(&control_ports_lock_lha5);
Karthikeyan Ramasubramaniand1af6252013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramanian6e2c9a62013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramanian70444192012-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 Ramasubramaniand06fe072013-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 Ramasubramanian090486e2013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramanian1668cc62012-09-23 22:23:36 -0600991 rport_ptr->sec_rule = NULL;
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -0600994 mutex_init(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530995 INIT_LIST_HEAD(&rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Meera34fc662013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001010 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001035 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001060 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-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 Meera34fc662013-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 Meera34fc662013-04-17 01:16:47 +05301070 local_port =
1071 msm_ipc_router_lookup_local_port(rtx_port->port_id);
Zaheerulla Meerc8400402013-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 Ramasubramaniand1af6252013-05-17 15:19:56 -06001076 post_pkt_to_port(local_port, pkt, 1);
Zaheerulla Meerc8400402013-05-08 19:27:27 +05301077 else
1078 pr_err("%s: Local Port %d not Found",
1079 __func__, rtx_port->port_id);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301080 list_del(&rtx_port->list);
1081 kfree(rtx_port);
1082 }
1083}
1084
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniana85d09f2012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001120 * Note1: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-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 Ramasubramaniana85d09f2012-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 Ramasubramaniana85d09f2012-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 Ramasubramaniana85d09f2012-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 Ramasubramanian70444192012-07-12 10:25:49 -06001151static void dummy_release(struct device *dev)
1152{
1153}
1154
Karthikeyan Ramasubramaniana85d09f2012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001168 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-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 Ramasubramaniana85d09f2012-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 Ramasubramanian70444192012-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 Ramasubramanian1668cc62012-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 Ramasubramanian70444192012-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 Ramasubramanian70444192012-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 Ramasubramanian70444192012-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 Ramasubramaniana85d09f2012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001234 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-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 Ramasubramanian70444192012-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 Meer3ce2dfb2013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001318 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001319 hdr->size = sizeof(*msg);
Zaheerulla Meer3ce2dfb2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001326 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian6e2c9a62013-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 Ramasubramaniand06fe072013-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 Ramasubramanianb234c242012-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 Ramasubramanian45008a62013-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 Ramasubramanianb234c242012-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 Meer3ce2dfb2013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramaniand06fe072013-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 Meer3ce2dfb2013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Meer3ce2dfb2013-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 Ramasubramaniand06fe072013-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 Ramasubramanian6e2c9a62013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001499 int ret = 0;
Karthikeyan Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001505 hdr = &(pkt->hdr);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001506 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001510 ret = -ENODEV;
1511 goto fm_error1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001512 }
1513
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramanian6e2c9a62013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001531 ret = -EINVAL;
Karthikeyan Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001541
1542fm_error3:
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001543 mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001544fm_error2:
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001545 up_read(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001546fm_error1:
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001547 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001548
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001549 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001550}
1551
Karthikeyan Ramasubramaniandc9c4442013-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 Ramasubramanian45008a62013-07-19 15:58:38 -06001568 memset(&msg, 0, sizeof(msg));
Karthikeyan Ramasubramaniandc9c4442013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001574 down_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramaniandc9c4442013-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 Meer3ce2dfb2013-07-11 19:24:40 +05301578 msm_ipc_router_send_control_msg(tmp_xprt_info, &msg,
1579 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001580 break;
1581 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001582 up_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramaniandc9c4442013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001619 struct msm_ipc_server *server = rport_ptr->server;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001620
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramanian45008a62013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001640 union rr_control_msg ctl;
1641 int j;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001642
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06001643 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramanian97eec852013-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 Ramasubramaniand06fe072013-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 Ramasubramanian97eec852013-08-06 18:15:52 -06001679 list_for_each_entry_safe(rt_entry, tmp_rt_entry,
1680 &routing_table[i], list) {
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramanian97eec852013-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 Ramasubramaniand06fe072013-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 Ramasubramanian1668cc62012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001710 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001719 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001739 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001758 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001776 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001785 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001786}
1787
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001788static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001789 struct rr_header_v1 *hdr)
Karthikeyan Ramasubramanianb234c242012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001808 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001813 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001819 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001820 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1821 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001822 up_write(&rt_entry->lock_lha4);
1823 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001824
1825 /* Send a reply HELLO message */
1826 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001827 ctl.hello.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301828 rc = msm_ipc_router_send_control_msg(xprt_info, &ctl,
1829 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramanianb234c242012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001840 down_read(&server_list_lock_lha2);
1841 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-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 Ramasubramanian72ad5792013-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 Ramasubramanianb234c242012-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001852 up_read(&routing_table_lock_lha3);
1853 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001854 return rc;
1855 }
1856 }
1857 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001858 up_read(&routing_table_lock_lha3);
1859 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001860 RR("HELLO message processed\n");
1861 return rc;
1862}
1863
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001868 int ret = 0;
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001872 down_read(&local_ports_lock_lha2);
1873 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001878 ret = -ENODEV;
1879 goto prtm_out;
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001880 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001881 mutex_lock(&rport_ptr->quota_lock_lhb2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001882 rport_ptr->tx_quota_cnt = 0;
1883 post_resume_tx(rport_ptr, pkt);
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001913 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001918 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001919 pr_err("%s: rt_entry allocation failed\n", __func__);
1920 return -ENOMEM;
1921 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001922 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001923 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1924 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001925 up_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001926 add_routing_table_entry(rt_entry);
1927 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001928 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001936 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001944 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001945 pr_err("%s: Server Create failed\n", __func__);
1946 return -ENOMEM;
1947 }
1948
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001949 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001955 up_read(&routing_table_lock_lha3);
1956 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001957 return -ENOMEM;
1958 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001959 rport_ptr->server = server;
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001964 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001965 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001966 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001973 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06001985 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001996 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001997 post_control_ports(pkt);
1998 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001999 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06002009 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06002014 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002015
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002016 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramanian4b4120e2013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002026 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002027
Karthikeyan Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002030 sizeof(*msg));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002031 return -EINVAL;
2032 }
2033
Karthikeyan Ramasubramanian6e2c9a62013-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 Ramasubramanianb234c242012-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 Ramasubramanian4b4120e2013-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 Ramasubramanian4b4120e2013-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 Ramasubramanian4b4120e2013-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 Ramasubramanian4b4120e2013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002093 hdr = &(pkt->hdr);
Karthikeyan Ramasubramanian682ebcc2013-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 Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002096 hdr->src_port_id, hdr->control_flag, hdr->size,
Karthikeyan Ramasubramanian682ebcc2013-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 Ramasubramanian6e2c9a62013-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 Meer1ee914e2013-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 Ramasubramanian6e2c9a62013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06002133 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002134 release_pkt(pkt);
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302135 return;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002136 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002137
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06002149 up_read(&routing_table_lock_lha3);
2150 up_read(&local_ports_lock_lha2);
Zaheerulla Meer6a309de2013-07-12 16:16:30 +05302151 release_pkt(pkt);
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302152 return;
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002153 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002154 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002155 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -06002156 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramanian45008a62013-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 Ramasubramaniand06fe072013-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 Ramasubramanianfa807952013-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 Ramasubramaniandc9c4442013-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 Ramasubramaniand06fe072013-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 Ramasubramaniand06fe072013-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 Ramasubramanian45008a62013-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 Ramasubramaniand06fe072013-05-21 11:49:21 -06002259 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002260 broadcast_ctl_msg(&ctl);
Karthikeyan Ramasubramanianfa807952013-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 Ramasubramanian6e2c9a62013-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 Ramasubramanian0a801c12013-02-08 13:07:42 -07002275 int ret_len;
Karthikeyan Ramasubramanian50765642013-09-04 23:34:38 -06002276 struct sk_buff *temp_skb;
2277 int align_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002278
2279 if (!data) {
2280 pr_err("%s: Invalid pkt pointer\n", __func__);
2281 return -EINVAL;
2282 }
2283
2284 pkt = create_pkt(data);
2285 if (!pkt) {
2286 pr_err("%s: New pkt create failed\n", __func__);
2287 return -ENOMEM;
2288 }
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002289 hdr = &(pkt->hdr);
2290 hdr->version = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002291 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2292 hdr->src_node_id = src->this_port.node_id;
2293 hdr->src_port_id = src->this_port.port_id;
2294 hdr->size = pkt->length;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002295 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002296 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
2297 hdr->dst_port_id = port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002298
Karthikeyan Ramasubramanian50765642013-09-04 23:34:38 -06002299 temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
2300 align_size = ALIGN_SIZE(pkt->length);
2301 skb_put(temp_skb, align_size);
2302 pkt->length += align_size;
2303
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002304 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002305 port_ptr = msm_ipc_router_lookup_local_port(port_id);
2306 if (!port_ptr) {
2307 pr_err("%s: Local port %d not present\n", __func__, port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002308 up_read(&local_ports_lock_lha2);
Zaheerulla Meer7ccb9672013-08-20 12:02:31 +05302309 pkt->pkt_fragment_q = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002310 release_pkt(pkt);
2311 return -ENODEV;
2312 }
2313
Karthikeyan Ramasubramanian50765642013-09-04 23:34:38 -06002314 ret_len = hdr->size;
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -06002315 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002316 update_comm_mode_info(&src->mode_info, NULL);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002317 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002318
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07002319 return ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002320}
2321
2322static int msm_ipc_router_write_pkt(struct msm_ipc_port *src,
2323 struct msm_ipc_router_remote_port *rport_ptr,
2324 struct rr_packet *pkt)
2325{
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002326 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002327 struct msm_ipc_router_xprt_info *xprt_info;
2328 struct msm_ipc_routing_table_entry *rt_entry;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302329 struct msm_ipc_resume_tx_port *resume_tx_port;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002330 struct sk_buff *temp_skb;
2331 int xprt_option;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002332 int ret;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002333 int align_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002334
2335 if (!rport_ptr || !src || !pkt)
2336 return -EINVAL;
2337
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002338 hdr = &(pkt->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002339 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2340 hdr->src_node_id = src->this_port.node_id;
2341 hdr->src_port_id = src->this_port.port_id;
2342 hdr->size = pkt->length;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002343 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002344 hdr->dst_node_id = rport_ptr->node_id;
2345 hdr->dst_port_id = rport_ptr->port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002346
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002347 mutex_lock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302348 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) {
2349 if (msm_ipc_router_lookup_resume_tx_port(
2350 rport_ptr, src->this_port.port_id)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002351 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302352 return -EAGAIN;
2353 }
2354 resume_tx_port =
2355 kzalloc(sizeof(struct msm_ipc_resume_tx_port),
2356 GFP_KERNEL);
2357 if (!resume_tx_port) {
2358 pr_err("%s: Resume_Tx port allocation failed\n",
2359 __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002360 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302361 return -ENOMEM;
2362 }
2363 INIT_LIST_HEAD(&resume_tx_port->list);
2364 resume_tx_port->port_id = src->this_port.port_id;
2365 resume_tx_port->node_id = src->this_port.node_id;
2366 list_add_tail(&resume_tx_port->list,
2367 &rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002368 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302369 return -EAGAIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002370 }
2371 rport_ptr->tx_quota_cnt++;
2372 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA)
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002373 hdr->control_flag |= CONTROL_FLAG_CONFIRM_RX;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002374 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002375
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002376 rt_entry = lookup_routing_table(hdr->dst_node_id);
2377 if (!rt_entry || !rt_entry->xprt_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002378 pr_err("%s: Remote node %d not up\n",
2379 __func__, hdr->dst_node_id);
2380 return -ENODEV;
2381 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002382 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002383 xprt_info = rt_entry->xprt_info;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002384 ret = prepend_header(pkt, xprt_info);
2385 if (ret < 0) {
2386 up_read(&rt_entry->lock_lha4);
2387 pr_err("%s: Prepend Header failed\n", __func__);
2388 return ret;
2389 }
2390 xprt_option = xprt_info->xprt->get_option(xprt_info->xprt);
2391 if (!(xprt_option & FRAG_PKT_WRITE_ENABLE)) {
2392 ret = defragment_pkt(pkt);
2393 if (ret < 0) {
2394 up_read(&rt_entry->lock_lha4);
2395 return ret;
2396 }
2397 }
2398
2399 temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
2400 align_size = ALIGN_SIZE(pkt->length);
2401 skb_put(temp_skb, align_size);
2402 pkt->length += align_size;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002403 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002404 ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002405 mutex_unlock(&xprt_info->tx_lock_lhb2);
2406 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002407
2408 if (ret < 0) {
2409 pr_err("%s: Write on XPRT failed\n", __func__);
2410 return ret;
2411 }
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002412 update_comm_mode_info(&src->mode_info, xprt_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002413
2414 RAW_HDR("[w rr_h] "
2415 "ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002416 "control_flag=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002417 hdr->version, type_to_str(hdr->type),
2418 hdr->src_node_id, hdr->src_port_id,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002419 hdr->control_flag, hdr->size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002420 hdr->dst_node_id, hdr->dst_port_id);
2421
2422#if defined(CONFIG_MSM_SMD_LOGGING)
2423#if defined(DEBUG)
2424 if (msm_ipc_router_debug_mask & SMEM_LOG) {
2425 smem_log_event((SMEM_LOG_PROC_ID_APPS |
Zaheerulla Meer1ee914e2013-06-19 16:31:17 +05302426 SMEM_LOG_IPC_ROUTER_EVENT_BASE |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002427 IPC_ROUTER_LOG_EVENT_TX),
2428 (hdr->src_node_id << 24) |
2429 (hdr->src_port_id & 0xffffff),
2430 (hdr->dst_node_id << 24) |
2431 (hdr->dst_port_id & 0xffffff),
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002432 (hdr->type << 24) | (hdr->control_flag << 16) |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002433 (hdr->size & 0xffff));
2434 }
2435#endif
2436#endif
2437
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002438 return hdr->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002439}
2440
2441int msm_ipc_router_send_to(struct msm_ipc_port *src,
2442 struct sk_buff_head *data,
2443 struct msm_ipc_addr *dest)
2444{
2445 uint32_t dst_node_id = 0, dst_port_id = 0;
2446 struct msm_ipc_server *server;
2447 struct msm_ipc_server_port *server_port;
2448 struct msm_ipc_router_remote_port *rport_ptr = NULL;
2449 struct rr_packet *pkt;
2450 int ret;
2451
2452 if (!src || !data || !dest) {
2453 pr_err("%s: Invalid Parameters\n", __func__);
2454 return -EINVAL;
2455 }
2456
2457 /* Resolve Address*/
2458 if (dest->addrtype == MSM_IPC_ADDR_ID) {
2459 dst_node_id = dest->addr.port_addr.node_id;
2460 dst_port_id = dest->addr.port_addr.port_id;
2461 } else if (dest->addrtype == MSM_IPC_ADDR_NAME) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002462 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002463 server = msm_ipc_router_lookup_server(
2464 dest->addr.port_name.service,
2465 dest->addr.port_name.instance,
2466 0, 0);
2467 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002468 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002469 pr_err("%s: Destination not reachable\n", __func__);
2470 return -ENODEV;
2471 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002472 server_port = list_first_entry(&server->server_port_list,
2473 struct msm_ipc_server_port,
2474 list);
2475 dst_node_id = server_port->server_addr.node_id;
2476 dst_port_id = server_port->server_addr.port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002477 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002478 }
2479 if (dst_node_id == IPC_ROUTER_NID_LOCAL) {
2480 ret = loopback_data(src, dst_port_id, data);
2481 return ret;
2482 }
2483
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002484 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002485 rport_ptr = msm_ipc_router_lookup_remote_port(dst_node_id,
2486 dst_port_id);
2487 if (!rport_ptr) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002488 up_read(&routing_table_lock_lha3);
Zaheerulla Meer2c515312013-05-10 15:51:28 +05302489 pr_err("%s: Remote port not found\n", __func__);
2490 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002491 }
2492
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002493 if (src->check_send_permissions) {
2494 ret = src->check_send_permissions(rport_ptr->sec_rule);
2495 if (ret <= 0) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002496 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002497 pr_err("%s: permission failure for %s\n",
2498 __func__, current->comm);
2499 return -EPERM;
2500 }
2501 }
2502
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002503 pkt = create_pkt(data);
2504 if (!pkt) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002505 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002506 pr_err("%s: Pkt creation failed\n", __func__);
2507 return -ENOMEM;
2508 }
2509
2510 ret = msm_ipc_router_write_pkt(src, rport_ptr, pkt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002511 up_read(&routing_table_lock_lha3);
Zaheerulla Meer7ccb9672013-08-20 12:02:31 +05302512 if (ret < 0)
2513 pkt->pkt_fragment_q = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002514 release_pkt(pkt);
2515
2516 return ret;
2517}
2518
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002519int msm_ipc_router_send_msg(struct msm_ipc_port *src,
2520 struct msm_ipc_addr *dest,
2521 void *data, unsigned int data_len)
2522{
2523 struct sk_buff_head *out_skb_head;
2524 int ret;
2525
2526 out_skb_head = msm_ipc_router_buf_to_skb(data, data_len);
2527 if (!out_skb_head) {
2528 pr_err("%s: SKB conversion failed\n", __func__);
2529 return -EFAULT;
2530 }
2531
2532 ret = msm_ipc_router_send_to(src, out_skb_head, dest);
2533 if (ret < 0) {
Zaheerulla Meer7ccb9672013-08-20 12:02:31 +05302534 if (ret != -EAGAIN)
2535 pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
2536 __func__, ret);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002537 msm_ipc_router_free_skb(out_skb_head);
Zaheerulla Meerfc59a8a2013-06-26 22:39:00 +05302538 return ret;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002539 }
2540 return 0;
2541}
2542
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302543/**
2544 * msm_ipc_router_send_resume_tx() - Send Resume_Tx message
2545 * @data: Pointer to received data packet that has confirm_rx bit set
2546 *
2547 * @return: On success, number of bytes transferred is returned, else
2548 * standard linux error code is returned.
2549 *
2550 * This function sends the Resume_Tx event to the remote node that
2551 * sent the data with confirm_rx field set. In case of a multi-hop
2552 * scenario also, this function makes sure that the destination node_id
2553 * to which the resume_tx event should reach is right.
2554 */
2555static int msm_ipc_router_send_resume_tx(void *data)
2556{
2557 union rr_control_msg msg;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002558 struct rr_header_v1 *hdr = (struct rr_header_v1 *)data;
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302559 struct msm_ipc_routing_table_entry *rt_entry;
2560 int ret;
2561
2562 memset(&msg, 0, sizeof(msg));
2563 msg.cmd = IPC_ROUTER_CTRL_CMD_RESUME_TX;
2564 msg.cli.node_id = hdr->dst_node_id;
2565 msg.cli.port_id = hdr->dst_port_id;
2566 down_read(&routing_table_lock_lha3);
2567 rt_entry = lookup_routing_table(hdr->src_node_id);
2568 if (!rt_entry) {
2569 pr_err("%s: %d Node is not present",
2570 __func__, hdr->src_node_id);
2571 up_read(&routing_table_lock_lha3);
2572 return -ENODEV;
2573 }
2574 RR("x RESUME_TX id=%d:%08x\n",
2575 msg.cli.node_id, msg.cli.port_id);
2576 ret = msm_ipc_router_send_control_msg(rt_entry->xprt_info, &msg,
2577 hdr->src_node_id);
2578 up_read(&routing_table_lock_lha3);
2579 if (ret < 0)
2580 pr_err("%s: Send Resume_Tx Failed SRC_NODE: %d SRC_PORT: %d DEST_NODE: %d",
2581 __func__, hdr->dst_node_id, hdr->dst_port_id,
2582 hdr->src_node_id);
2583
2584 return ret;
2585}
2586
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002587int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002588 struct rr_packet **read_pkt,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002589 size_t buf_len)
2590{
2591 struct rr_packet *pkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002592
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002593 if (!port_ptr || !read_pkt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002594 return -EINVAL;
2595
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002596 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002597 if (list_empty(&port_ptr->port_rx_q)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002598 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002599 return -EAGAIN;
2600 }
2601
2602 pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002603 if ((buf_len) && (pkt->hdr.size > buf_len)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002604 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002605 return -ETOOSMALL;
2606 }
2607 list_del(&pkt->list);
2608 if (list_empty(&port_ptr->port_rx_q))
2609 wake_unlock(&port_ptr->port_rx_wake_lock);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002610 *read_pkt = pkt;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002611 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002612 if (pkt->hdr.control_flag & CONTROL_FLAG_CONFIRM_RX)
2613 msm_ipc_router_send_resume_tx(&pkt->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002614
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002615 return pkt->length;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002616}
2617
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302618/**
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302619 * msm_ipc_router_rx_data_wait() - Wait for new message destined to a local port.
2620 * @port_ptr: Pointer to the local port
2621 * @timeout: < 0 timeout indicates infinite wait till a message arrives.
2622 * > 0 timeout indicates the wait time.
2623 * 0 indicates that we do not wait.
2624 * @return: 0 if there are pending messages to read,
2625 * standard Linux error code otherwise.
2626 *
2627 * Checks for the availability of messages that are destined to a local port.
2628 * If no messages are present then waits as per @timeout.
2629 */
2630int msm_ipc_router_rx_data_wait(struct msm_ipc_port *port_ptr, long timeout)
2631{
2632 int ret = 0;
2633
2634 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
2635 while (list_empty(&port_ptr->port_rx_q)) {
2636 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
2637 if (timeout < 0) {
2638 ret = wait_event_interruptible(
2639 port_ptr->port_rx_wait_q,
2640 !list_empty(&port_ptr->port_rx_q));
2641 if (ret)
2642 return ret;
2643 } else if (timeout > 0) {
2644 timeout = wait_event_interruptible_timeout(
2645 port_ptr->port_rx_wait_q,
2646 !list_empty(&port_ptr->port_rx_q),
2647 timeout);
2648 if (timeout < 0)
2649 return -EFAULT;
2650 }
2651 if (timeout == 0)
2652 return -ENOMSG;
2653 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
2654 }
2655 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
2656
2657 return ret;
2658}
2659
2660/**
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302661 * msm_ipc_router_recv_from() - Recieve messages destined to a local port.
2662 * @port_ptr: Pointer to the local port
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002663 * @pkt : Pointer to the router-to-router packet
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302664 * @src: Pointer to local port address
2665 * @timeout: < 0 timeout indicates infinite wait till a message arrives.
2666 * > 0 timeout indicates the wait time.
2667 * 0 indicates that we do not wait.
2668 * @return: = Number of bytes read(On successful read operation).
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302669 * = -ENOMSG (If there are no pending messages and timeout is 0).
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302670 * = -EINVAL (If either of the arguments, port_ptr or data is invalid)
2671 * = -EFAULT (If there are no pending messages when timeout is > 0
2672 * and the wait_event_interruptible_timeout has returned value > 0)
2673 * = -ERESTARTSYS (If there are no pending messages when timeout
2674 * is < 0 and wait_event_interruptible was interrupted by a signal)
2675 *
2676 * This function reads the messages that are destined for a local port. It
2677 * is used by modules that exist with-in the kernel and use IPC Router for
2678 * transport. The function checks if there are any messages that are already
2679 * received. If yes, it reads them, else it waits as per the timeout value.
2680 * On a successful read, the return value of the function indicates the number
2681 * of bytes that are read.
2682 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002683int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002684 struct rr_packet **pkt,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002685 struct msm_ipc_addr *src,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002686 long timeout)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002687{
2688 int ret, data_len, align_size;
2689 struct sk_buff *temp_skb;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002690 struct rr_header_v1 *hdr = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002691
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002692 if (!port_ptr || !pkt) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002693 pr_err("%s: Invalid pointers being passed\n", __func__);
2694 return -EINVAL;
2695 }
2696
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002697 *pkt = NULL;
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302698
2699 ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
2700 if (ret)
2701 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002702
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002703 ret = msm_ipc_router_read(port_ptr, pkt, 0);
2704 if (ret <= 0 || !(*pkt))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002705 return ret;
2706
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002707 hdr = &((*pkt)->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002708 if (src) {
2709 src->addrtype = MSM_IPC_ADDR_ID;
2710 src->addr.port_addr.node_id = hdr->src_node_id;
2711 src->addr.port_addr.port_id = hdr->src_port_id;
2712 }
2713
2714 data_len = hdr->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002715 align_size = ALIGN_SIZE(data_len);
2716 if (align_size) {
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002717 temp_skb = skb_peek_tail((*pkt)->pkt_fragment_q);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002718 skb_trim(temp_skb, (temp_skb->len - align_size));
2719 }
2720 return data_len;
2721}
2722
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002723int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
2724 struct msm_ipc_addr *src,
2725 unsigned char **data,
2726 unsigned int *len)
2727{
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002728 struct rr_packet *pkt;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002729 int ret;
2730
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002731 ret = msm_ipc_router_recv_from(port_ptr, &pkt, src, 0);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002732 if (ret < 0) {
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302733 if (ret != -ENOMSG)
2734 pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
2735 __func__, ret);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002736 return ret;
2737 }
2738
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002739 *data = msm_ipc_router_skb_to_buf(pkt->pkt_fragment_q, ret);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002740 if (!(*data))
2741 pr_err("%s: Buf conversion failed\n", __func__);
2742
2743 *len = ret;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002744 release_pkt(pkt);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002745 return 0;
2746}
2747
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002748struct msm_ipc_port *msm_ipc_router_create_port(
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002749 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002750 void *priv)
2751{
2752 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002753 int ret;
2754
2755 ret = wait_for_completion_interruptible(&msm_ipc_local_router_up);
2756 if (ret < 0) {
2757 pr_err("%s: Error waiting for local router\n", __func__);
2758 return NULL;
2759 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002760
2761 port_ptr = msm_ipc_router_create_raw_port(NULL, notify, priv);
2762 if (!port_ptr)
2763 pr_err("%s: port_ptr alloc failed\n", __func__);
2764
2765 return port_ptr;
2766}
2767
2768int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
2769{
2770 union rr_control_msg msg;
2771 struct rr_packet *pkt, *temp_pkt;
2772 struct msm_ipc_server *server;
2773
2774 if (!port_ptr)
2775 return -EINVAL;
2776
2777 if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002778 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002779 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002780 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002781
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002782 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06002783 memset(&msg, 0, sizeof(msg));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002784 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
2785 msg.srv.service = port_ptr->port_name.service;
2786 msg.srv.instance = port_ptr->port_name.instance;
2787 msg.srv.node_id = port_ptr->this_port.node_id;
2788 msg.srv.port_id = port_ptr->this_port.port_id;
2789 RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
2790 msg.srv.service, msg.srv.instance,
2791 msg.srv.node_id, msg.srv.port_id);
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002792 broadcast_ctl_msg(&msg);
2793 broadcast_ctl_msg_locally(&msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002794 }
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002795
2796 /*
2797 * Server port could have been a client port earlier.
2798 * Send REMOVE_CLIENT message in either case.
2799 */
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002800 RR("x REMOVE_CLIENT id=%d:%08x\n",
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002801 port_ptr->this_port.node_id, port_ptr->this_port.port_id);
2802 msm_ipc_router_send_remove_client(&port_ptr->mode_info,
2803 port_ptr->this_port.node_id,
2804 port_ptr->this_port.port_id);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002805 } else if (port_ptr->type == CONTROL_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002806 down_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002807 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002808 up_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002809 } else if (port_ptr->type == IRSC_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002810 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002811 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002812 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002813 signal_irsc_completion();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002814 }
2815
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002816 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002817 list_for_each_entry_safe(pkt, temp_pkt, &port_ptr->port_rx_q, list) {
2818 list_del(&pkt->list);
2819 release_pkt(pkt);
2820 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002821 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002822
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002823 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002824 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002825 server = msm_ipc_router_lookup_server(
2826 port_ptr->port_name.service,
2827 port_ptr->port_name.instance,
2828 port_ptr->this_port.node_id,
2829 port_ptr->this_port.port_id);
2830 if (server)
2831 msm_ipc_router_destroy_server(server,
2832 port_ptr->this_port.node_id,
2833 port_ptr->this_port.port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002834 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002835 }
2836
Karthikeyan Ramasubramaniandd8c3b52011-11-30 16:26:12 -07002837 wake_lock_destroy(&port_ptr->port_rx_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002838 kfree(port_ptr);
2839 return 0;
2840}
2841
2842int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr)
2843{
2844 struct rr_packet *pkt;
2845 int rc = 0;
2846
2847 if (!port_ptr)
2848 return -EINVAL;
2849
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002850 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002851 if (!list_empty(&port_ptr->port_rx_q)) {
2852 pkt = list_first_entry(&port_ptr->port_rx_q,
2853 struct rr_packet, list);
2854 rc = pkt->length;
2855 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002856 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002857
2858 return rc;
2859}
2860
2861int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr)
2862{
2863 if (!port_ptr)
2864 return -EINVAL;
2865
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002866 down_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002867 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002868 up_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002869 port_ptr->type = CONTROL_PORT;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002870 down_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002871 list_add_tail(&port_ptr->list, &control_ports);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002872 up_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002873
2874 return 0;
2875}
2876
2877int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002878 struct msm_ipc_server_info *srv_info,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002879 int num_entries_in_array,
2880 uint32_t lookup_mask)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002881{
2882 struct msm_ipc_server *server;
2883 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002884 int key, i = 0; /*num_entries_found*/
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002885
2886 if (!srv_name) {
2887 pr_err("%s: Invalid srv_name\n", __func__);
2888 return -EINVAL;
2889 }
2890
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002891 if (num_entries_in_array && !srv_info) {
2892 pr_err("%s: srv_info NULL\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002893 return -EINVAL;
2894 }
2895
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002896 down_read(&server_list_lock_lha2);
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002897 if (!lookup_mask)
2898 lookup_mask = 0xFFFFFFFF;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002899 key = (srv_name->service & (SRV_HASH_SIZE - 1));
2900 list_for_each_entry(server, &server_list[key], list) {
2901 if ((server->name.service != srv_name->service) ||
2902 ((server->name.instance & lookup_mask) !=
2903 srv_name->instance))
2904 continue;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002905
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002906 list_for_each_entry(server_port,
2907 &server->server_port_list, list) {
2908 if (i < num_entries_in_array) {
2909 srv_info[i].node_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002910 server_port->server_addr.node_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002911 srv_info[i].port_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002912 server_port->server_addr.port_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002913 srv_info[i].service = server->name.service;
2914 srv_info[i].instance = server->name.instance;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002915 }
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002916 i++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002917 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002918 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002919 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002920
2921 return i;
2922}
2923
2924int msm_ipc_router_close(void)
2925{
2926 struct msm_ipc_router_xprt_info *xprt_info, *tmp_xprt_info;
2927
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002928 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002929 list_for_each_entry_safe(xprt_info, tmp_xprt_info,
2930 &xprt_info_list, list) {
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002931 xprt_info->xprt->close(xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002932 list_del(&xprt_info->list);
2933 kfree(xprt_info);
2934 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002935 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002936 return 0;
2937}
2938
2939#if defined(CONFIG_DEBUG_FS)
2940static int dump_routing_table(char *buf, int max)
2941{
2942 int i = 0, j;
2943 struct msm_ipc_routing_table_entry *rt_entry;
2944
2945 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002946 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002947 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002948 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002949 i += scnprintf(buf + i, max - i,
2950 "Node Id: 0x%08x\n", rt_entry->node_id);
Karthikeyan Ramasubramanianc1a4e3a2012-09-10 16:10:24 -06002951 if (rt_entry->node_id == IPC_ROUTER_NID_LOCAL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002952 i += scnprintf(buf + i, max - i,
2953 "XPRT Name: Loopback\n");
2954 i += scnprintf(buf + i, max - i,
2955 "Next Hop: %d\n", rt_entry->node_id);
2956 } else {
2957 i += scnprintf(buf + i, max - i,
2958 "XPRT Name: %s\n",
2959 rt_entry->xprt_info->xprt->name);
2960 i += scnprintf(buf + i, max - i,
2961 "Next Hop: 0x%08x\n",
2962 rt_entry->xprt_info->remote_node_id);
2963 }
2964 i += scnprintf(buf + i, max - i, "\n");
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002965 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002966 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002967 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002968 }
2969
2970 return i;
2971}
2972
2973static int dump_xprt_info(char *buf, int max)
2974{
2975 int i = 0;
2976 struct msm_ipc_router_xprt_info *xprt_info;
2977
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002978 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002979 list_for_each_entry(xprt_info, &xprt_info_list, list) {
2980 i += scnprintf(buf + i, max - i, "XPRT Name: %s\n",
2981 xprt_info->xprt->name);
2982 i += scnprintf(buf + i, max - i, "Link Id: %d\n",
2983 xprt_info->xprt->link_id);
2984 i += scnprintf(buf + i, max - i, "Initialized: %s\n",
2985 (xprt_info->initialized ? "Y" : "N"));
2986 i += scnprintf(buf + i, max - i, "Remote Node Id: 0x%08x\n",
2987 xprt_info->remote_node_id);
2988 i += scnprintf(buf + i, max - i, "\n");
2989 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002990 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002991
2992 return i;
2993}
2994
2995static int dump_servers(char *buf, int max)
2996{
2997 int i = 0, j;
2998 struct msm_ipc_server *server;
2999 struct msm_ipc_server_port *server_port;
3000
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003001 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003002 for (j = 0; j < SRV_HASH_SIZE; j++) {
3003 list_for_each_entry(server, &server_list[j], list) {
3004 list_for_each_entry(server_port,
3005 &server->server_port_list,
3006 list) {
3007 i += scnprintf(buf + i, max - i, "Service: "
3008 "0x%08x\n", server->name.service);
3009 i += scnprintf(buf + i, max - i, "Instance: "
3010 "0x%08x\n", server->name.instance);
3011 i += scnprintf(buf + i, max - i,
3012 "Node_id: 0x%08x\n",
3013 server_port->server_addr.node_id);
3014 i += scnprintf(buf + i, max - i,
3015 "Port_id: 0x%08x\n",
3016 server_port->server_addr.port_id);
3017 i += scnprintf(buf + i, max - i, "\n");
3018 }
3019 }
3020 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003021 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003022
3023 return i;
3024}
3025
3026static int dump_remote_ports(char *buf, int max)
3027{
3028 int i = 0, j, k;
3029 struct msm_ipc_router_remote_port *rport_ptr;
3030 struct msm_ipc_routing_table_entry *rt_entry;
3031
3032 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003033 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003034 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003035 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003036 for (k = 0; k < RP_HASH_SIZE; k++) {
3037 list_for_each_entry(rport_ptr,
3038 &rt_entry->remote_port_list[k],
3039 list) {
3040 i += scnprintf(buf + i, max - i,
3041 "Node_id: 0x%08x\n",
3042 rport_ptr->node_id);
3043 i += scnprintf(buf + i, max - i,
3044 "Port_id: 0x%08x\n",
3045 rport_ptr->port_id);
3046 i += scnprintf(buf + i, max - i,
3047 "Quota_cnt: %d\n",
3048 rport_ptr->tx_quota_cnt);
3049 i += scnprintf(buf + i, max - i, "\n");
3050 }
3051 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003052 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003053 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003054 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003055 }
3056
3057 return i;
3058}
3059
3060static int dump_control_ports(char *buf, int max)
3061{
3062 int i = 0;
3063 struct msm_ipc_port *port_ptr;
3064
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003065 down_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003066 list_for_each_entry(port_ptr, &control_ports, list) {
3067 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
3068 port_ptr->this_port.node_id);
3069 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
3070 port_ptr->this_port.port_id);
3071 i += scnprintf(buf + i, max - i, "\n");
3072 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003073 up_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003074
3075 return i;
3076}
3077
3078static int dump_local_ports(char *buf, int max)
3079{
3080 int i = 0, j;
3081 unsigned long flags;
3082 struct msm_ipc_port *port_ptr;
3083
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003084 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003085 for (j = 0; j < LP_HASH_SIZE; j++) {
3086 list_for_each_entry(port_ptr, &local_ports[j], list) {
3087 spin_lock_irqsave(&port_ptr->port_lock, flags);
3088 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
3089 port_ptr->this_port.node_id);
3090 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
3091 port_ptr->this_port.port_id);
3092 i += scnprintf(buf + i, max - i, "# pkts tx'd %d\n",
3093 port_ptr->num_tx);
3094 i += scnprintf(buf + i, max - i, "# pkts rx'd %d\n",
3095 port_ptr->num_rx);
3096 i += scnprintf(buf + i, max - i, "# bytes tx'd %ld\n",
3097 port_ptr->num_tx_bytes);
3098 i += scnprintf(buf + i, max - i, "# bytes rx'd %ld\n",
3099 port_ptr->num_rx_bytes);
3100 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
3101 i += scnprintf(buf + i, max - i, "\n");
3102 }
3103 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003104 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003105
3106 return i;
3107}
3108
3109#define DEBUG_BUFMAX 4096
3110static char debug_buffer[DEBUG_BUFMAX];
3111
3112static ssize_t debug_read(struct file *file, char __user *buf,
3113 size_t count, loff_t *ppos)
3114{
3115 int (*fill)(char *buf, int max) = file->private_data;
3116 int bsize = fill(debug_buffer, DEBUG_BUFMAX);
3117 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
3118}
3119
3120static int debug_open(struct inode *inode, struct file *file)
3121{
3122 file->private_data = inode->i_private;
3123 return 0;
3124}
3125
3126static const struct file_operations debug_ops = {
3127 .read = debug_read,
3128 .open = debug_open,
3129};
3130
3131static void debug_create(const char *name, mode_t mode,
3132 struct dentry *dent,
3133 int (*fill)(char *buf, int max))
3134{
3135 debugfs_create_file(name, mode, dent, fill, &debug_ops);
3136}
3137
3138static void debugfs_init(void)
3139{
3140 struct dentry *dent;
3141
3142 dent = debugfs_create_dir("msm_ipc_router", 0);
3143 if (IS_ERR(dent))
3144 return;
3145
3146 debug_create("dump_local_ports", 0444, dent,
3147 dump_local_ports);
3148 debug_create("dump_remote_ports", 0444, dent,
3149 dump_remote_ports);
3150 debug_create("dump_control_ports", 0444, dent,
3151 dump_control_ports);
3152 debug_create("dump_servers", 0444, dent,
3153 dump_servers);
3154 debug_create("dump_xprt_info", 0444, dent,
3155 dump_xprt_info);
3156 debug_create("dump_routing_table", 0444, dent,
3157 dump_routing_table);
3158}
3159
3160#else
3161static void debugfs_init(void) {}
3162#endif
3163
3164static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
3165{
3166 struct msm_ipc_router_xprt_info *xprt_info;
3167 struct msm_ipc_routing_table_entry *rt_entry;
3168
3169 xprt_info = kmalloc(sizeof(struct msm_ipc_router_xprt_info),
3170 GFP_KERNEL);
3171 if (!xprt_info)
3172 return -ENOMEM;
3173
3174 xprt_info->xprt = xprt;
3175 xprt_info->initialized = 0;
3176 xprt_info->remote_node_id = -1;
3177 INIT_LIST_HEAD(&xprt_info->pkt_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003178 mutex_init(&xprt_info->rx_lock_lhb2);
3179 mutex_init(&xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003180 wake_lock_init(&xprt_info->wakelock,
3181 WAKE_LOCK_SUSPEND, xprt->name);
3182 xprt_info->need_len = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003183 xprt_info->abort_data_read = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003184 INIT_WORK(&xprt_info->read_data, do_read_data);
3185 INIT_LIST_HEAD(&xprt_info->list);
3186
3187 xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
3188 if (!xprt_info->workqueue) {
3189 kfree(xprt_info);
3190 return -ENOMEM;
3191 }
3192
3193 if (!strcmp(xprt->name, "msm_ipc_router_loopback_xprt")) {
3194 xprt_info->remote_node_id = IPC_ROUTER_NID_LOCAL;
3195 xprt_info->initialized = 1;
3196 }
3197
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003198 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003199 list_add_tail(&xprt_info->list, &xprt_info_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003200 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003201
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003202 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003203 if (!routing_table_inited) {
3204 init_routing_table();
3205 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
3206 add_routing_table_entry(rt_entry);
3207 routing_table_inited = 1;
3208 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003209 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003210
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003211 xprt->priv = xprt_info;
3212
3213 return 0;
3214}
3215
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003216static void msm_ipc_router_remove_xprt(struct msm_ipc_router_xprt *xprt)
3217{
3218 struct msm_ipc_router_xprt_info *xprt_info;
3219
3220 if (xprt && xprt->priv) {
3221 xprt_info = xprt->priv;
3222
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003223 mutex_lock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003224 xprt_info->abort_data_read = 1;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003225 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003226
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003227 down_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003228 list_del(&xprt_info->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003229 up_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003230
3231 flush_workqueue(xprt_info->workqueue);
3232 destroy_workqueue(xprt_info->workqueue);
3233 wake_lock_destroy(&xprt_info->wakelock);
3234
3235 xprt->priv = 0;
3236 kfree(xprt_info);
3237 }
3238}
3239
3240
3241struct msm_ipc_router_xprt_work {
3242 struct msm_ipc_router_xprt *xprt;
3243 struct work_struct work;
3244};
3245
3246static void xprt_open_worker(struct work_struct *work)
3247{
3248 struct msm_ipc_router_xprt_work *xprt_work =
3249 container_of(work, struct msm_ipc_router_xprt_work, work);
3250
3251 msm_ipc_router_add_xprt(xprt_work->xprt);
3252 kfree(xprt_work);
3253}
3254
3255static void xprt_close_worker(struct work_struct *work)
3256{
3257 struct msm_ipc_router_xprt_work *xprt_work =
3258 container_of(work, struct msm_ipc_router_xprt_work, work);
3259
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003260 msm_ipc_cleanup_routing_table(xprt_work->xprt->priv);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003261 msm_ipc_router_remove_xprt(xprt_work->xprt);
Zaheerulla Meer35893a62013-06-19 16:54:44 +05303262 xprt_work->xprt->sft_close_done(xprt_work->xprt);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003263 kfree(xprt_work);
3264}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003265
3266void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
3267 unsigned event,
3268 void *data)
3269{
3270 struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003271 struct msm_ipc_router_xprt_work *xprt_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003272 struct rr_packet *pkt;
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003273 unsigned long ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003274
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003275 if (!msm_ipc_router_workqueue) {
3276 ret = wait_for_completion_timeout(&msm_ipc_local_router_up,
3277 IPC_ROUTER_INIT_TIMEOUT);
3278 if (!ret || !msm_ipc_router_workqueue) {
3279 pr_err("%s: IPC Router not initialized\n", __func__);
3280 return;
3281 }
3282 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003283
3284 switch (event) {
3285 case IPC_ROUTER_XPRT_EVENT_OPEN:
3286 D("open event for '%s'\n", xprt->name);
3287 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
3288 GFP_ATOMIC);
Karthikeyan Ramasubramanianc51456c2013-05-16 15:51:29 -06003289 if (xprt_work) {
3290 xprt_work->xprt = xprt;
3291 INIT_WORK(&xprt_work->work, xprt_open_worker);
3292 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
3293 } else {
3294 pr_err("%s: malloc failure - Couldn't notify OPEN event",
3295 __func__);
3296 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003297 break;
3298
3299 case IPC_ROUTER_XPRT_EVENT_CLOSE:
3300 D("close event for '%s'\n", xprt->name);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003301 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
3302 GFP_ATOMIC);
Karthikeyan Ramasubramanianc51456c2013-05-16 15:51:29 -06003303 if (xprt_work) {
3304 xprt_work->xprt = xprt;
3305 INIT_WORK(&xprt_work->work, xprt_close_worker);
3306 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
3307 } else {
3308 pr_err("%s: malloc failure - Couldn't notify CLOSE event",
3309 __func__);
3310 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003311 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003312 }
3313
3314 if (!data)
3315 return;
3316
3317 while (!xprt_info) {
3318 msleep(100);
3319 xprt_info = xprt->priv;
3320 }
3321
3322 pkt = clone_pkt((struct rr_packet *)data);
3323 if (!pkt)
3324 return;
3325
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003326 mutex_lock(&xprt_info->rx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003327 list_add_tail(&pkt->list, &xprt_info->pkt_list);
3328 wake_lock(&xprt_info->wakelock);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003329 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06003330 queue_work(xprt_info->workqueue, &xprt_info->read_data);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003331}
3332
3333static int __init msm_ipc_router_init(void)
3334{
3335 int i, ret;
3336 struct msm_ipc_routing_table_entry *rt_entry;
3337
3338 msm_ipc_router_debug_mask |= SMEM_LOG;
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06003339 ipc_rtr_log_ctxt = ipc_log_context_create(IPC_RTR_LOG_PAGES,
3340 "ipc_router");
3341 if (!ipc_rtr_log_ctxt)
3342 pr_err("%s: Unable to create IPC logging for IPC RTR",
3343 __func__);
3344
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003345 msm_ipc_router_workqueue =
3346 create_singlethread_workqueue("msm_ipc_router");
3347 if (!msm_ipc_router_workqueue)
3348 return -ENOMEM;
3349
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003350 debugfs_init();
3351
3352 for (i = 0; i < SRV_HASH_SIZE; i++)
3353 INIT_LIST_HEAD(&server_list[i]);
3354
3355 for (i = 0; i < LP_HASH_SIZE; i++)
3356 INIT_LIST_HEAD(&local_ports[i]);
3357
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003358 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003359 if (!routing_table_inited) {
3360 init_routing_table();
3361 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
3362 add_routing_table_entry(rt_entry);
3363 routing_table_inited = 1;
3364 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003365 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003366
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003367 ret = msm_ipc_router_init_sockets();
3368 if (ret < 0)
3369 pr_err("%s: Init sockets failed\n", __func__);
3370
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06003371 ret = msm_ipc_router_security_init();
3372 if (ret < 0)
3373 pr_err("%s: Security Init failed\n", __func__);
3374
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003375 complete_all(&msm_ipc_local_router_up);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003376 return ret;
3377}
3378
3379module_init(msm_ipc_router_init);
3380MODULE_DESCRIPTION("MSM IPC Router");
3381MODULE_LICENSE("GPL v2");