Merge branch 'dev' into kernel
[kernel/swap-modules.git] / driver / device_driver.c
1 ////////////////////////////////////////////////////////////////////////////////////
2 //
3 //      FILE:           device_driver.c
4 //
5 //      DESCRIPTION:
6 //      This file is C source for SWAP driver.
7 //
8 //      SEE ALSO:       device_driver.h
9 //      AUTHOR:         L.Komkov, S.Dianov, S.Grekhov, A.Gerenkov
10 //      COMPANY NAME:   Samsung Research Center in Moscow
11 //      DEPT NAME:      Advanced Software Group
12 //      CREATED:        2008.02.15
13 //      VERSION:        1.0
14 //      REVISION DATE:  2008.12.03
15 //
16 ////////////////////////////////////////////////////////////////////////////////////
17
18 #include "module.h"
19 #include "device_driver.h"      // device driver
20 #include "handlers_core.h"
21 #include <linux/notifier.h>
22 #include <sspt/sspt_proc.h>
23
24
25 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
26 static BLOCKING_NOTIFIER_HEAD(swap_notifier_list);
27 #endif
28 pid_t gl_nNotifyTgid;
29 EXPORT_SYMBOL_GPL(gl_nNotifyTgid);
30
31 static DECLARE_WAIT_QUEUE_HEAD (notification_waiters_queue);
32 static volatile unsigned notification_count;
33
34 static int device_mmap (struct file *filp, struct vm_area_struct *vma);
35 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
36 static int device_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
37 #else
38 static long device_ioctl (struct file *file, unsigned int cmd, unsigned long arg);
39 #endif
40 static int device_open(struct inode *, struct file *);
41 static int device_release(struct inode *, struct file *);
42 static ssize_t device_read(struct file *, char __user *, size_t, loff_t *);
43 static ssize_t device_write(struct file *, const char __user *, size_t, loff_t *);
44
45 static int gl_nDeviceOpened = 0;
46 static struct file_operations device_fops = {
47         .owner = THIS_MODULE,
48         .mmap = device_mmap,
49 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
50         .ioctl = device_ioctl,
51 #else
52         .unlocked_ioctl = device_ioctl,
53 #endif
54         .read = device_read,
55         .write = device_write,
56         .open = device_open,
57         .release = device_release
58 };
59
60 typedef void (* dbi_module_callback)(void);
61
62 int device_init (void)
63 {
64         int nReserved = 0;
65         nReserved = register_chrdev(0, device_name, &device_fops);
66         if(nReserved < 0)
67         {
68                 unregister_chrdev(nReserved, device_name);
69                 EPRINTF("Cannot register character device!");
70                 return -1;
71         }
72         EPRINTF("New device node with major number [%d], was created\n", nReserved);
73         device_major = nReserved;
74         return 0;
75 }
76
77 void device_down (void)
78 {
79         unregister_chrdev(device_major, device_name);
80 }
81
82 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
83 void swap_register_notify (struct notifier_block *nb)
84 {
85         blocking_notifier_chain_register(&swap_notifier_list, nb);
86 }
87 EXPORT_SYMBOL_GPL(swap_register_notify);
88
89 void swap_unregister_notify (struct notifier_block *nb)
90 {
91         blocking_notifier_chain_unregister(&swap_notifier_list, nb);
92 }
93 EXPORT_SYMBOL_GPL(swap_unregister_notify);
94 #endif
95
96 void notify_user (event_id_t event_id)
97 {
98         ec_info.events_counters[event_id] += 1;
99
100         if (EVENT_EC_PROBE_RECORD == event_id)
101         {
102                 // EC_PROBE_RECORD events happen to often. To reduce overhead user
103                 // space will be notified only once per each EVENTS_AGGREGATION_USEC
104                 static uint64_t timestamp_usec = 0;
105
106                 uint64_t current_usec;
107                 uint64_t delta_usec;
108
109                 struct timeval tv;
110
111                 do_gettimeofday (&tv);
112                 current_usec = 1000000ULL * (unsigned) tv.tv_sec + (unsigned) tv.tv_usec;
113
114                 if (current_usec < timestamp_usec)
115                 {
116                         // Note: time from do_gettimeofday() may go backward
117                         EPRINTF ("current_usec=%llu timestamp_usec=%llu", current_usec, timestamp_usec);
118                 }
119                 else
120                 {
121                         delta_usec = current_usec - timestamp_usec;
122                         if (EVENTS_AGGREGATION_USEC > delta_usec)
123                         {
124                                 // wait the time left
125 #if defined(__DEBUG)
126                                 unsigned UNUSED left_usec = EVENTS_AGGREGATION_USEC - delta_usec;
127 #endif /* defined(__DEBUG) */
128                                 return; // supress notification
129                         }
130                 }
131                 timestamp_usec = current_usec;  // remember new time for the future use
132         } else if (EVENT_EC_START_CONDITION_SEEN == event_id) {
133                 return;         // supress notification
134         } else if (EVENT_EC_STOP_CONDITION_SEEN == event_id) {
135                 return;         // supress notification
136         }
137
138         ++notification_count;
139         wake_up_interruptible (&notification_waiters_queue);
140 }
141
142 static int device_mmap (struct file *filp UNUSED, struct vm_area_struct *vma)
143 {
144         if(!p_buffer) {
145                 EPRINTF("Null pointer to buffer!");
146                 return -1;
147         }
148         return remap_vmalloc_range (vma, p_buffer, 0);
149 }
150
151 static int device_open(struct inode *inode, struct file *file)
152 {
153         /*if (gl_nDeviceOpened)
154                 return -EBUSY;*/
155         gl_nDeviceOpened++;
156         // TODO
157         try_module_get(THIS_MODULE);
158         return 0;
159 }
160
161 static int device_release(struct inode *inode, struct file *file)
162 {
163         gl_nDeviceOpened--;
164         module_put(THIS_MODULE);
165
166         return 0;
167 }
168
169 static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t * offset)
170 {
171         EPRINTF("Operation <<read>> not supported!");
172         return -1;
173 }
174
175 static ssize_t device_write(struct file *filp, const char __user *buff, size_t len, loff_t * off)
176 {
177         EPRINTF("Operation <<write>> not supported!");
178         return -1;
179 }
180
181 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
182 static int device_ioctl (struct inode *inode UNUSED, struct file *file UNUSED, unsigned int cmd, unsigned long arg)
183 #else
184 static long device_ioctl (struct file *file UNUSED, unsigned int cmd, unsigned long arg)
185 #endif
186 {
187         unsigned long spinlock_flags = 0L;
188         int result = -1;
189     void __user * arg_pointer = (void __user *) arg;
190 //      DPRINTF("Command=%d", cmd);
191         switch (cmd)
192         {
193         case EC_IOCTL_SET_EC_MODE:
194                 {
195                         ioctl_general_t param;
196                         unsigned long nIgnoredBytes = 0;
197                         memset(&param, '0', sizeof(ioctl_general_t));
198                         nIgnoredBytes = copy_from_user (&param, arg_pointer, sizeof(ioctl_general_t));
199                         if (nIgnoredBytes > 0) {
200                                 result = -1;
201                                 break;
202                         }
203                         if(SetECMode(param.m_unsignedLong) == -1) {
204                                 result = -1;
205                                 break;
206                         }
207                         result = 0;
208                         DPRINTF("Set EC Mode = %lu", param.m_unsignedLong);
209                         break;
210                 }
211         case EC_IOCTL_GET_EC_MODE:
212                 {
213                         ioctl_general_t param;
214                         unsigned long nIgnoredBytes = 0;
215                         memset(&param, '0', sizeof(ioctl_general_t));
216                         param.m_unsignedLong = GetECMode();
217                         nIgnoredBytes = copy_to_user (arg_pointer, &param, sizeof (ioctl_general_t));
218                         if (nIgnoredBytes > 0) {
219                                 result = -1;
220                                 break;
221                         }
222                         result = 0;
223 //                      DPRINTF("Get EC Mode = %lu", param.m_unsignedLong);  // Frequent call
224                         break;
225                 }
226         case EC_IOCTL_SET_BUFFER_SIZE:
227                 {
228                         ioctl_general_t param;
229                         unsigned long nIgnoredBytes = 0;
230                         memset(&param, '0', sizeof(ioctl_general_t));
231                         nIgnoredBytes = copy_from_user (&param, arg_pointer, sizeof(ioctl_general_t));
232                         if (nIgnoredBytes > 0) {
233                                 result = -1;
234                                 break;
235                         }
236                         if (SetBufferSize(param.m_unsignedLong) == -1) {
237                                 result = -1;
238                                 break;
239                         }
240                         result = 0;
241                         DPRINTF("Set Buffer Size = %lu", param.m_unsignedLong);
242                         break;
243                 }
244         case EC_IOCTL_GET_BUFFER_SIZE:
245                 {
246                         ioctl_general_t param;
247                         unsigned long nIgnoredBytes = 0;
248                         memset(&param, '0', sizeof(ioctl_general_t));
249                         param.m_unsignedLong = GetBufferSize();
250                         nIgnoredBytes = copy_to_user (arg_pointer, &param, sizeof (ioctl_general_t));
251                         if (nIgnoredBytes > 0) {
252                                 result = -1;
253                                 break;
254                         }
255                         result = 0;
256                         DPRINTF("Get Buffer Size = %lu", param.m_unsignedLong);
257                         break;
258                 }
259         case EC_IOCTL_RESET_BUFFER:
260                 {
261                         if (ResetBuffer() == -1) {
262                                 result = -1;
263                                 break;
264                         }
265                         result = 0;
266                         DPRINTF("Reset Buffer");
267                         break;
268                 }
269         case EC_IOCTL_GET_EC_INFO:
270                 {
271                         if (copy_ec_info_to_user_space ((ec_info_t *) arg) != 0) {
272                                 result = -1;
273                                 break;
274                         }
275                         result = 0;
276 //                      DPRINTF("Get Buffer Status"); // Frequent call
277                         break;
278                 }
279         case EC_IOCTL_CONSUME_BUFFER:
280                 {
281                         static ec_info_t ec_info_copy;
282                         int nIgnoredBytes = 0;
283
284                         nIgnoredBytes = copy_from_user (&ec_info_copy, (const void __user *) arg, sizeof (ec_info_t));
285                         if(nIgnoredBytes > 0)
286                         {
287                                 EPRINTF ("copy_from_user(%08X,%08X)=%d", (unsigned) arg, (unsigned) &ec_info_copy, nIgnoredBytes);
288                                 result = -1;
289                                 break;
290                         }
291
292                         spin_lock_irqsave (&ec_spinlock, spinlock_flags);
293
294                         // Original buffer
295                         if(ec_info.after_last > ec_info.first) {
296                                 ec_info.buffer_effect = ec_info.buffer_size;
297                         }
298                         if (ec_info.after_last == ec_info.buffer_effect) {
299                                  ec_info.first = 0;
300                         } else {
301                                  ec_info.first = ec_info_copy.after_last;
302                         }
303                         ec_info.trace_size = ec_info.trace_size - ec_info_copy.trace_size;
304
305                         spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
306                         result = 0;
307 //                      DPRINTF("Consume Buffer"); // Frequent call
308                         break;
309                 }
310         case EC_IOCTL_ADD_PROBE:
311                 {
312                         unsigned long addr = arg;
313                         unsigned long pre_handler = 0, jp_handler = 0, rp_handler = 0;
314
315                         dbi_find_and_set_handler_for_probe(addr, &pre_handler, &jp_handler, &rp_handler);
316                         result = add_probe(addr, pre_handler, jp_handler, rp_handler);
317
318                         break;
319                 }
320         //@AGv: remove_probe expects probe address instead of name
321         /*case EC_IOCTL_REMOVE_PROBE:
322                 {
323                         char *probe_name = (char *) arg;
324                         result = remove_probe (probe_name);
325
326                         break;
327                 }*/
328         case EC_IOCTL_SET_APPDEPS:
329         {
330                 size_t size;
331                 result = copy_from_user(&size, arg_pointer, sizeof(size_t));
332                 if (result) {
333                         EPRINTF("Cannot copy deps size!");
334                         result = -1;
335                         break;
336                 }
337                 DPRINTF("Deps size has been copied (%d)", size);
338
339                 if (size == 0) {
340                         DPRINTF("Deps are size of 0");
341                         break;
342                 }
343
344                 deps = vmalloc(size);
345                 if (deps == NULL) {
346                         EPRINTF("Cannot alloc mem for deps!");
347                         result = -1;
348                         break;
349                 }
350                 DPRINTF("Mem for deps has been allocated");
351
352                 result = copy_from_user(deps, arg_pointer, size);
353                 if (result) {
354                         EPRINTF("Cannot copy deps!");
355                         result = -1;
356                         break;
357                 }
358                 DPRINTF("Deps has been copied successfully");
359
360                 break;
361         }
362         case EC_IOCTL_SET_PID:
363         {
364                 unsigned int _pid;
365
366                 result = copy_from_user(&_pid, arg_pointer, sizeof(unsigned int));
367                 if (result) {
368                         EPRINTF("Cannot copy pid!");
369                         result = -1;
370                         break;
371                 }
372
373                 inst_pid = _pid;
374
375                 DPRINTF("EC_IOCTL_SET_PID pid:%d", inst_pid);
376
377                 break;
378         }
379         case EC_IOCTL_SET_PROFILEBUNDLE:
380         {
381                 size_t size;
382
383                 result = copy_from_user(&size, arg_pointer, sizeof(size_t));
384                 if (result) {
385                         EPRINTF("Cannot copy bundle size!");
386                         result = -1;
387                         break;
388                 }
389                 DPRINTF("Bundle size has been copied");
390
391                 bundle = vmalloc(size);
392                 if (bundle == NULL) {
393                         EPRINTF("Cannot alloc mem for bundle!");
394                         result = -1;
395                         break;
396                 }
397                 DPRINTF("Mem for bundle has been alloced");
398
399                 result = copy_from_user(bundle, arg_pointer, size);
400                 if (result) {
401                         EPRINTF("Cannot copy bundle!");
402                         result = -1;
403                         break;
404                 }
405                 DPRINTF("Bundle has been copied successfully");
406
407                 if (link_bundle() == -1 || has_last_error() == -1) {
408                         EPRINTF("Cannot link profile bundle!");
409                         result = -1;
410                         break;
411                 }
412
413                 break;
414         }
415         case EC_IOCTL_RESET_PROBES:
416                 {
417                         result = reset_probes();
418
419                         break;
420                 }
421         case EC_IOCTL_UPDATE_CONDS:
422                 {
423                         int args_cnt, i;
424                         struct cond *c, *c_tmp, *p_cond;
425                         unsigned char *p_data;
426                         int err;
427                         result = 0;
428                         err = copy_from_user(&args_cnt, arg_pointer, sizeof(int));
429                         if (err) {
430                                 result = -1;
431                                 break;
432                         }
433                         /* first, delete all the conds */
434                         list_for_each_entry_safe(c, c_tmp, &cond_list.list, list) {
435                                 list_del(&c->list);
436                                 kfree(c);
437                         }
438                         /* second, add new conds */
439                         p_data = (unsigned char *)(arg + sizeof(int));
440                         for (i = 0; i < args_cnt; i++) {
441                                 p_cond = kmalloc(sizeof(struct cond), GFP_KERNEL);
442                                 if (!p_cond) {
443                                         DPRINTF("Cannot alloc cond!");
444                                         result = -1;
445                                         break;
446                                 }
447                                 err = copy_from_user(&p_cond->tmpl, (const void __user *)p_data,
448                                                 sizeof(struct event_tmpl));
449                                 if (err) {
450                                         DPRINTF("Cannot copy cond from user!");
451                                         result = -1;
452                                         break;
453                                 }
454                                 p_cond->applied = 0;
455                                 list_add(&(p_cond->list), &(cond_list.list));
456                                 p_data += sizeof(struct event_tmpl);
457                         }
458                         break;
459                 }
460         case EC_IOCTL_ATTACH:
461                 {
462                         unsigned long dbi_flags;
463                         struct dbi_modules_handlers *local_mh;
464                         struct dbi_modules_handlers_info *local_mhi;
465                         int j;
466                         dbi_module_callback dmc_start;
467
468                         // call "start"-callback for all modules according module priority
469                         local_mh = get_dbi_modules_handlers();
470                         spin_lock_irqsave(&local_mh->lock, dbi_flags);
471                         for (j = 0; j <= MAX_PRIORITY; j++) {
472                                 list_for_each_entry_rcu(local_mhi, &local_mh->modules_handlers, dbi_list_head) {
473                                         if (local_mhi->dbi_module_priority_start == j) {
474                                                 if (local_mhi->dbi_module_callback_start != NULL) {
475                                                         printk("Started module callback (start) %s\n", local_mhi->dbi_module->name);
476                                                         dmc_start = (dbi_module_callback )local_mhi->dbi_module_callback_start;
477                                                         dmc_start();
478                                                 }
479                                         }
480                                 }
481                         }
482                         spin_unlock_irqrestore(&local_mh->lock, dbi_flags);
483
484                         result = ec_user_attach ();
485 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
486                         DPRINTF("EC_IOCTL_ATTACH calling notification chain");
487                         blocking_notifier_call_chain(&swap_notifier_list, EC_IOCTL_ATTACH, (void*)NULL);
488 #endif
489                         DPRINTF("Attach Probes");
490                         break;
491                 }
492         case EC_IOCTL_ACTIVATE:
493                 result = ec_user_activate ();
494                 DPRINTF("Activate Probes");
495                 break;
496         case EC_IOCTL_STOP_AND_DETACH:
497         {
498                 unsigned long nIgnoredBytes = 0;
499                 unsigned long dbi_flags;
500                 struct dbi_modules_handlers *local_mh;
501                 struct dbi_modules_handlers_info *local_mhi;
502                 unsigned int local_module_refcount = 0;
503                 int j;
504                 dbi_module_callback dmc_stop;
505
506                 local_mh = get_dbi_modules_handlers();
507                 if(ec_user_stop() != 0) {
508                         result = -1;
509                         goto sad_cleanup;
510                 }
511                 nIgnoredBytes = copy_ec_info_to_user_space ((ec_info_t*)arg);
512                 if(nIgnoredBytes > 0) {
513                         result = -1;
514                         goto sad_cleanup;
515                 }
516
517                 vfree(bundle);
518                 result = 0;
519                 DPRINTF("Stop and Detach Probes");
520 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
521                 DPRINTF("EC_IOCTL_STOP_AND_DETACH calling notification chain");
522                 blocking_notifier_call_chain(&swap_notifier_list, EC_IOCTL_STOP_AND_DETACH, (void*)&gl_nNotifyTgid);
523 #endif
524                 // call "stop"-callback for all modules according module priority
525                 spin_lock_irqsave(&local_mh->lock, dbi_flags);
526                 for (j = 0; j <= MAX_PRIORITY; j++) {
527                         list_for_each_entry_rcu(local_mhi, &local_mh->modules_handlers, dbi_list_head) {
528                                 if (local_mhi->dbi_module_priority_stop == j) {
529                                         if (local_mhi->dbi_module_callback_stop != NULL) {
530                                                 printk("Started module callback (stop) %s\n", local_mhi->dbi_module->name);
531                                                 dmc_stop = (dbi_module_callback )local_mhi->dbi_module_callback_stop;
532                                                 dmc_stop();
533                                         }
534                                 }
535                         }
536                 }
537                 spin_unlock_irqrestore(&local_mh->lock, dbi_flags);
538 sad_cleanup:
539                 spin_lock_irqsave(&local_mh->lock, dbi_flags);
540                 list_for_each_entry_rcu(local_mhi, &local_mh->modules_handlers, dbi_list_head) {
541                         local_module_refcount = module_refcount(local_mhi->dbi_module);
542                         if (local_module_refcount == 1) {
543                                 module_put(local_mhi->dbi_module);
544                         }
545                         else if (local_module_refcount > 1) {
546                                 printk("local_module_refcount too much - force set refcount to zero\n");
547                                 while (local_module_refcount--)
548                                         module_put(local_mhi->dbi_module);
549                         }
550                 }
551                 spin_unlock_irqrestore(&local_mh->lock, dbi_flags);
552                 break;
553         }
554         case EC_IOCTL_US_EVENT:
555                 {
556                         ioctl_us_event_t ioctl_args;
557                         result = copy_from_user (&ioctl_args, (const void __user *) arg, sizeof (ioctl_args));
558                         if (result)
559                         {
560                                 result = -1;
561                                 EPRINTF ("copy_from_user() failure");
562                         }
563                         else
564                         {
565                                 if(ioctl_args.len == 0){
566                                         result = -EINVAL;
567                                         EPRINTF ("invalid event length!");
568                                 }
569                                 else {
570                                         char *buf = kmalloc(ioctl_args.len, GFP_KERNEL);
571                                         if(!buf){
572                                                 result = -ENOMEM;
573                                                 EPRINTF ("failed to alloc mem for event!");
574                                         }
575                                         else {
576                                                 result = copy_from_user (buf, (const void __user *) ioctl_args.data, ioctl_args.len);
577                                                 if (result){
578                                                         result = -1;
579                                                         EPRINTF ("failed to copy event from user space!");
580                                                 }
581                                                 else
582                                                         result = put_us_event(buf, ioctl_args.len);
583                                                 kfree(buf);
584                                         }
585                                 }
586                         }
587 //                      DPRINTF("User Space Event"); // Frequent call
588                         break;
589                 }
590
591         case EC_IOCTL_SET_EVENT_MASK:
592                 {
593                         int mask;
594                         result = copy_from_user (&mask, arg_pointer, sizeof (mask));
595                         if (result)
596                         {
597                                 result = -EFAULT;
598                                 break;
599                         }
600
601                         result = set_event_mask (mask);
602                         if (result)
603                         {
604                                 break;
605                         }
606                         DPRINTF("Set Event Mask = %d", mask);
607                         break;
608                 }
609
610         case EC_IOCTL_GET_EVENT_MASK:
611                 {
612                         int mask = 0;
613                         result = get_event_mask(&mask);
614                         if (result)
615                         {
616                                 result = -EFAULT;
617                         }
618                         result = copy_to_user (arg_pointer, &mask, sizeof (mask));
619                         if (result)
620                         {
621                                 result = -EFAULT;
622                         }
623                         DPRINTF("Get Event Mask = %d", mask);
624                         break;
625                 }
626
627         case EC_IOCTL_GET_PREDEF_UPROBES:
628                 {
629                         result = get_predef_uprobes((ioctl_predef_uprobes_info_t *)arg);
630                         if (result)
631                         {
632                                 result = -EFAULT;
633                         }
634                         DPRINTF("Get Predefined User Space Probes");
635                         break;
636                 }
637
638         case EC_IOCTL_GET_PREDEF_UPROBES_SIZE:
639                 {
640                         int size = 0;
641                         result = get_predef_uprobes_size(&size);
642                         if (result)
643                         {
644                                 result = -EFAULT;
645                         }
646                         result = copy_to_user (arg_pointer, &size, sizeof (size));
647                         if (result)
648                         {
649                                 result = -EFAULT;
650                         }
651                         DPRINTF("Get Size of Predefined User Space Probes");
652                         break;
653                 }
654         case EC_IOCTL_GET_LAST_ERROR:
655                 {
656                         result = get_last_error((void*)arg);
657                         DPRINTF("Get last error buffer");
658                         break;
659                 }
660         default:
661                 EPRINTF ("Unknown driver command = %u", cmd);
662                 result = -EINVAL;
663                 break;
664         }
665
666         return result;
667 }