OSDN Git Service

Merge "power: qpnp-fg-gen3: Fine tune the monotonic SOC calculation"
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / char / diag / diag_memorydevice.c
1 /* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
2  *
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 #include <linux/slab.h>
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/device.h>
17 #include <linux/err.h>
18 #include <linux/sched.h>
19 #include <linux/ratelimit.h>
20 #include <linux/workqueue.h>
21 #include <linux/diagchar.h>
22 #include <linux/delay.h>
23 #include <linux/kmemleak.h>
24 #include <linux/uaccess.h>
25 #include "diagchar.h"
26 #include "diag_memorydevice.h"
27 #include "diagfwd_bridge.h"
28 #include "diag_mux.h"
29 #include "diagmem.h"
30 #include "diagfwd.h"
31 #include "diagfwd_peripheral.h"
32 #include "diag_ipc_logging.h"
33
34 struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = {
35         {
36                 .id = DIAG_MD_LOCAL,
37                 .ctx = 0,
38                 .mempool = POOL_TYPE_MUX_APPS,
39                 .num_tbl_entries = 0,
40                 .tbl = NULL,
41                 .ops = NULL,
42         },
43 #ifdef CONFIG_DIAGFWD_BRIDGE_CODE
44         {
45                 .id = DIAG_MD_MDM,
46                 .ctx = 0,
47                 .mempool = POOL_TYPE_MDM_MUX,
48                 .num_tbl_entries = 0,
49                 .tbl = NULL,
50                 .ops = NULL,
51         },
52         {
53                 .id = DIAG_MD_MDM2,
54                 .ctx = 0,
55                 .mempool = POOL_TYPE_MDM2_MUX,
56                 .num_tbl_entries = 0,
57                 .tbl = NULL,
58                 .ops = NULL,
59         },
60         {
61                 .id = DIAG_MD_SMUX,
62                 .ctx = 0,
63                 .mempool = POOL_TYPE_QSC_MUX,
64                 .num_tbl_entries = 0,
65                 .tbl = NULL,
66                 .ops = NULL,
67         }
68 #endif
69 };
70
71 int diag_md_register(int id, int ctx, struct diag_mux_ops *ops)
72 {
73         if (id < 0 || id >= NUM_DIAG_MD_DEV || !ops)
74                 return -EINVAL;
75
76         diag_md[id].ops = ops;
77         diag_md[id].ctx = ctx;
78         return 0;
79 }
80
81 void diag_md_open_all()
82 {
83         int i;
84         struct diag_md_info *ch = NULL;
85
86         for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
87                 ch = &diag_md[i];
88                 if (ch->ops && ch->ops->open)
89                         ch->ops->open(ch->ctx, DIAG_MEMORY_DEVICE_MODE);
90         }
91
92         return;
93 }
94
95 void diag_md_close_all()
96 {
97         int i, j;
98         unsigned long flags;
99         struct diag_md_info *ch = NULL;
100         struct diag_buf_tbl_t *entry = NULL;
101
102         for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
103                 ch = &diag_md[i];
104
105                 if (ch->ops && ch->ops->close)
106                         ch->ops->close(ch->ctx, DIAG_MEMORY_DEVICE_MODE);
107
108                 /*
109                  * When we close the Memory device mode, make sure we flush the
110                  * internal buffers in the table so that there are no stale
111                  * entries.
112                  */
113                 spin_lock_irqsave(&ch->lock, flags);
114                 for (j = 0; j < ch->num_tbl_entries; j++) {
115                         entry = &ch->tbl[j];
116                         if (entry->len <= 0)
117                                 continue;
118                         if (ch->ops && ch->ops->write_done)
119                                 ch->ops->write_done(entry->buf, entry->len,
120                                                     entry->ctx,
121                                                     DIAG_MEMORY_DEVICE_MODE);
122                         entry->buf = NULL;
123                         entry->len = 0;
124                         entry->ctx = 0;
125                 }
126                 spin_unlock_irqrestore(&ch->lock, flags);
127         }
128
129         diag_ws_reset(DIAG_WS_MUX);
130 }
131
132 int diag_md_write(int id, unsigned char *buf, int len, int ctx)
133 {
134         int i;
135         uint8_t found = 0;
136         unsigned long flags;
137         struct diag_md_info *ch = NULL;
138         uint8_t peripheral;
139         struct diag_md_session_t *session_info = NULL;
140
141         if (id < 0 || id >= NUM_DIAG_MD_DEV || id >= DIAG_NUM_PROC)
142                 return -EINVAL;
143
144         if (!buf || len < 0)
145                 return -EINVAL;
146
147         peripheral =
148                 diag_md_get_peripheral(ctx);
149         if (peripheral < 0)
150                 return -EINVAL;
151
152         session_info =
153                 diag_md_session_get_peripheral(peripheral);
154         if (!session_info)
155                 return -EIO;
156
157         ch = &diag_md[id];
158         if (!ch)
159                 return -EINVAL;
160
161         spin_lock_irqsave(&ch->lock, flags);
162         for (i = 0; i < ch->num_tbl_entries && !found; i++) {
163                 if (ch->tbl[i].buf != buf)
164                         continue;
165                 found = 1;
166                 pr_err_ratelimited("diag: trying to write the same buffer buf: %pK, len: %d, back to the table for p: %d, t: %d, buf_num: %d, proc: %d, i: %d\n",
167                                    buf, ch->tbl[i].len, GET_BUF_PERIPHERAL(ctx),
168                                    GET_BUF_TYPE(ctx), GET_BUF_NUM(ctx), id, i);
169                 ch->tbl[i].buf = NULL;
170                 ch->tbl[i].len = 0;
171                 ch->tbl[i].ctx = 0;
172         }
173         spin_unlock_irqrestore(&ch->lock, flags);
174
175         if (found)
176                 return -ENOMEM;
177
178         spin_lock_irqsave(&ch->lock, flags);
179         for (i = 0; i < ch->num_tbl_entries && !found; i++) {
180                 if (ch->tbl[i].len == 0) {
181                         ch->tbl[i].buf = buf;
182                         ch->tbl[i].len = len;
183                         ch->tbl[i].ctx = ctx;
184                         found = 1;
185                         diag_ws_on_read(DIAG_WS_MUX, len);
186                 }
187         }
188         spin_unlock_irqrestore(&ch->lock, flags);
189
190         if (!found) {
191                 pr_err_ratelimited("diag: Unable to find an empty space in table, please reduce logging rate, proc: %d\n",
192                                    id);
193                 return -ENOMEM;
194         }
195
196         found = 0;
197         for (i = 0; i < driver->num_clients && !found; i++) {
198                 if ((driver->client_map[i].pid !=
199                      session_info->pid) ||
200                     (driver->client_map[i].pid == 0))
201                         continue;
202
203                 found = 1;
204                 driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
205                 pr_debug("diag: wake up logging process\n");
206                 wake_up_interruptible(&driver->wait_q);
207         }
208
209         if (!found)
210                 return -EINVAL;
211
212         return 0;
213 }
214
215 int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
216                         struct diag_md_session_t *info)
217 {
218         int i, j;
219         int err = 0;
220         int ret = *pret;
221         int num_data = 0;
222         int remote_token;
223         unsigned long flags;
224         struct diag_md_info *ch = NULL;
225         struct diag_buf_tbl_t *entry = NULL;
226         uint8_t drain_again = 0;
227         uint8_t peripheral = 0;
228         struct diag_md_session_t *session_info = NULL;
229         struct pid *pid_struct = NULL;
230
231         for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
232                 ch = &diag_md[i];
233                 for (j = 0; j < ch->num_tbl_entries && !err; j++) {
234                         entry = &ch->tbl[j];
235                         if (entry->len <= 0 || entry->buf == NULL)
236                                 continue;
237
238                         peripheral = diag_md_get_peripheral(entry->ctx);
239                         if (peripheral < 0)
240                                 goto drop_data;
241
242                         session_info =
243                         diag_md_session_get_peripheral(peripheral);
244                         if (!session_info) {
245                                 goto drop_data;
246                         }
247
248                         if (session_info && info &&
249                                 (session_info->pid != info->pid))
250                                 continue;
251                         if ((info && (info->peripheral_mask &
252                             MD_PERIPHERAL_MASK(peripheral)) == 0))
253                                 goto drop_data;
254                         pid_struct = find_get_pid(session_info->pid);
255                         if (!pid_struct) {
256                                 err = -ESRCH;
257                                 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
258                                         "diag: No such md_session_map[%d] with pid = %d err=%d exists..\n",
259                                         peripheral, session_info->pid, err);
260                                 goto drop_data;
261                         }
262                         /*
263                          * If the data is from remote processor, copy the remote
264                          * token first
265                          */
266                         if (i > 0) {
267                                 if ((ret + (3 * sizeof(int)) + entry->len) >=
268                                                         buf_size) {
269                                         drain_again = 1;
270                                         break;
271                                 }
272                         } else {
273                                 if ((ret + (2 * sizeof(int)) + entry->len) >=
274                                                 buf_size) {
275                                         drain_again = 1;
276                                         break;
277                                 }
278                         }
279                         if (i > 0) {
280                                 remote_token = diag_get_remote(i);
281                                 if (get_pid_task(pid_struct, PIDTYPE_PID)) {
282                                         err = copy_to_user(buf + ret,
283                                                         &remote_token,
284                                                         sizeof(int));
285                                         if (err)
286                                                 goto drop_data;
287                                         ret += sizeof(int);
288                                 }
289                         }
290
291                         /* Copy the length of data being passed */
292                         if (get_pid_task(pid_struct, PIDTYPE_PID)) {
293                                 err = copy_to_user(buf + ret,
294                                                 (void *)&(entry->len),
295                                                 sizeof(int));
296                                 if (err)
297                                         goto drop_data;
298                                 ret += sizeof(int);
299                         }
300
301                         /* Copy the actual data being passed */
302                         if (get_pid_task(pid_struct, PIDTYPE_PID)) {
303                                 err = copy_to_user(buf + ret,
304                                                 (void *)entry->buf,
305                                                 entry->len);
306                                 if (err)
307                                         goto drop_data;
308                                 ret += entry->len;
309                         }
310                         /*
311                          * The data is now copied to the user space client,
312                          * Notify that the write is complete and delete its
313                          * entry from the table
314                          */
315                         num_data++;
316 drop_data:
317                         spin_lock_irqsave(&ch->lock, flags);
318                         if (ch->ops && ch->ops->write_done)
319                                 ch->ops->write_done(entry->buf, entry->len,
320                                                     entry->ctx,
321                                                     DIAG_MEMORY_DEVICE_MODE);
322                         diag_ws_on_copy(DIAG_WS_MUX);
323                         entry->buf = NULL;
324                         entry->len = 0;
325                         entry->ctx = 0;
326                         spin_unlock_irqrestore(&ch->lock, flags);
327                 }
328         }
329
330         *pret = ret;
331         if (pid_struct && get_pid_task(pid_struct, PIDTYPE_PID)) {
332                 err = copy_to_user(buf + sizeof(int),
333                                 (void *)&num_data,
334                                 sizeof(int));
335         }
336         diag_ws_on_copy_complete(DIAG_WS_MUX);
337         if (drain_again)
338                 chk_logging_wakeup();
339
340         return err;
341 }
342
343 int diag_md_close_peripheral(int id, uint8_t peripheral)
344 {
345         int i;
346         uint8_t found = 0;
347         unsigned long flags;
348         struct diag_md_info *ch = NULL;
349         struct diag_buf_tbl_t *entry = NULL;
350
351         if (id < 0 || id >= NUM_DIAG_MD_DEV || id >= DIAG_NUM_PROC)
352                 return -EINVAL;
353
354         ch = &diag_md[id];
355
356         spin_lock_irqsave(&ch->lock, flags);
357         for (i = 0; i < ch->num_tbl_entries && !found; i++) {
358                 entry = &ch->tbl[i];
359
360                 if (peripheral > NUM_PERIPHERALS) {
361                         if (GET_PD_CTXT(entry->ctx) != peripheral)
362                                 continue;
363                 } else {
364                         if (GET_BUF_PERIPHERAL(entry->ctx) !=
365                                         peripheral)
366                                 continue;
367                 }
368                 found = 1;
369                 if (ch->ops && ch->ops->write_done) {
370                         ch->ops->write_done(entry->buf, entry->len,
371                                             entry->ctx,
372                                             DIAG_MEMORY_DEVICE_MODE);
373                         entry->buf = NULL;
374                         entry->len = 0;
375                         entry->ctx = 0;
376                 }
377         }
378         spin_unlock_irqrestore(&ch->lock, flags);
379         return 0;
380 }
381
382 int diag_md_init()
383 {
384         int i, j;
385         struct diag_md_info *ch = NULL;
386
387         for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
388                 ch = &diag_md[i];
389                 ch->num_tbl_entries = diag_mempools[ch->mempool].poolsize;
390                 ch->tbl = kzalloc(ch->num_tbl_entries *
391                                   sizeof(struct diag_buf_tbl_t),
392                                   GFP_KERNEL);
393                 if (!ch->tbl)
394                         goto fail;
395
396                 for (j = 0; j < ch->num_tbl_entries; j++) {
397                         ch->tbl[j].buf = NULL;
398                         ch->tbl[j].len = 0;
399                         ch->tbl[j].ctx = 0;
400                 }
401                 spin_lock_init(&(ch->lock));
402         }
403
404         return 0;
405
406 fail:
407         diag_md_exit();
408         return -ENOMEM;
409 }
410
411 void diag_md_exit()
412 {
413         int i;
414         struct diag_md_info *ch = NULL;
415
416         for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
417                 ch = &diag_md[i];
418                 kfree(ch->tbl);
419                 ch->num_tbl_entries = 0;
420                 ch->ops = NULL;
421         }
422 }