[FIX] warning in ktd_exit()
[kernel/swap-modules.git] / us_manager / pf / pf_group.c
1 /*
2  *  SWAP uprobe manager
3  *  modules/us_manager/pf/pf_group.c
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Copyright (C) Samsung Electronics, 2013
20  *
21  * 2013  Vyacheslav Cherkashin: SWAP us_manager implement
22  *
23  */
24
25
26 #include <linux/module.h>
27 #include <linux/slab.h>
28 #include <linux/list.h>
29 #include <linux/namei.h>
30 #include <linux/mman.h>
31 #include <linux/spinlock.h>
32 #include "pf_group.h"
33 #include "proc_filters.h"
34 #include "../sspt/sspt_filter.h"
35 #include "../us_manager_common.h"
36 #include <us_manager/img/img_proc.h>
37 #include <us_manager/img/img_file.h>
38 #include <us_manager/img/img_ip.h>
39 #include <us_manager/sspt/sspt_proc.h>
40 #include <us_manager/helper.h>
41 #include <task_ctx/task_ctx.h>
42
43
44 struct pf_group {
45         struct list_head list;
46         struct img_proc *i_proc;
47         struct proc_filter filter;
48         struct pfg_msg_cb *msg_cb;
49         atomic_t usage;
50
51         spinlock_t pl_lock;     /* for proc_list */
52         struct list_head proc_list;
53 };
54
55 struct pl_struct {
56         struct list_head list;
57         struct sspt_proc *proc;
58 };
59
60
61 static LIST_HEAD(pfg_list);
62 static DECLARE_RWSEM(pfg_list_sem);
63
64 static void pfg_list_rlock(void)
65 {
66         down_read(&pfg_list_sem);
67 }
68
69 static void pfg_list_runlock(void)
70 {
71         up_read(&pfg_list_sem);
72 }
73
74 static void pfg_list_wlock(void)
75 {
76         down_write(&pfg_list_sem);
77 }
78
79 static void pfg_list_wunlock(void)
80 {
81         up_write(&pfg_list_sem);
82 }
83
84
85 /* struct pl_struct */
86 static struct pl_struct *create_pl_struct(struct sspt_proc *proc)
87 {
88         struct pl_struct *pls = kmalloc(sizeof(*pls), GFP_ATOMIC);
89
90         if (pls) {
91                 INIT_LIST_HEAD(&pls->list);
92                 pls->proc = sspt_proc_get(proc);
93         }
94
95         return pls;
96 }
97
98 static void free_pl_struct(struct pl_struct *pls)
99 {
100         sspt_proc_put(pls->proc);
101         kfree(pls);
102 }
103 /* struct pl_struct */
104
105 static struct pf_group *pfg_create(void)
106 {
107         struct pf_group *pfg = kmalloc(sizeof(*pfg), GFP_ATOMIC);
108
109         if (pfg == NULL)
110                 return NULL;
111
112         pfg->i_proc = img_proc_create();
113         if (pfg->i_proc == NULL)
114                 goto create_pfg_fail;
115
116         INIT_LIST_HEAD(&pfg->list);
117         memset(&pfg->filter, 0, sizeof(pfg->filter));
118         spin_lock_init(&pfg->pl_lock);
119         INIT_LIST_HEAD(&pfg->proc_list);
120         pfg->msg_cb = NULL;
121         atomic_set(&pfg->usage, 1);
122
123         return pfg;
124
125 create_pfg_fail:
126
127         kfree(pfg);
128
129         return NULL;
130 }
131
132 static void pfg_free(struct pf_group *pfg)
133 {
134         struct pl_struct *pl, *n;
135
136         img_proc_free(pfg->i_proc);
137         free_pf(&pfg->filter);
138         list_for_each_entry_safe(pl, n, &pfg->proc_list, list) {
139                 sspt_proc_del_filter(pl->proc, pfg);
140                 free_pl_struct(pl);
141         }
142
143         kfree(pfg);
144 }
145
146 static int pfg_add_proc(struct pf_group *pfg, struct sspt_proc *proc)
147 {
148         struct pl_struct *pls;
149
150         pls = create_pl_struct(proc);
151         if (pls == NULL)
152                 return -ENOMEM;
153
154         spin_lock(&pfg->pl_lock);
155         list_add(&pls->list, &pfg->proc_list);
156         spin_unlock(&pfg->pl_lock);
157
158         return 0;
159 }
160
161 static int pfg_del_proc(struct pf_group *pfg, struct sspt_proc *proc)
162 {
163         struct pl_struct *pls, *pls_free = NULL;
164
165         spin_lock(&pfg->pl_lock);
166         list_for_each_entry(pls, &pfg->proc_list, list) {
167                 if (pls->proc == proc) {
168                         list_del(&pls->list);
169                         pls_free = pls;
170                         break;
171                 }
172         }
173         spin_unlock(&pfg->pl_lock);
174
175         if (pls_free)
176                 free_pl_struct(pls_free);
177
178         return !!pls_free;
179 }
180
181
182 /* called with pfg_list_lock held */
183 static void pfg_add_to_list(struct pf_group *pfg)
184 {
185         list_add(&pfg->list, &pfg_list);
186 }
187
188 /* called with pfg_list_lock held */
189 static void pfg_del_from_list(struct pf_group *pfg)
190 {
191         list_del(&pfg->list);
192 }
193
194
195 static void msg_info(struct sspt_filter *f, void *data)
196 {
197         if (f->pfg_is_inst == false) {
198                 struct pfg_msg_cb *cb;
199
200                 f->pfg_is_inst = true;
201
202                 cb = pfg_msg_cb_get(f->pfg);
203                 if (cb) {
204                         struct dentry *dentry;
205
206                         dentry = (struct dentry *)f->pfg->filter.priv;
207
208                         if (cb->msg_info)
209                                 cb->msg_info(f->proc->leader, dentry);
210
211                         if (cb->msg_status_info)
212                                 cb->msg_status_info(f->proc->leader);
213                 }
214         }
215 }
216
217 static void first_install(struct task_struct *task, struct sspt_proc *proc)
218 {
219         sspt_proc_priv_create(proc);
220
221         down_write(&task->mm->mmap_sem);
222         sspt_proc_on_each_filter(proc, msg_info, NULL);
223         sspt_proc_install(proc);
224         up_write(&task->mm->mmap_sem);
225 }
226
227 static void subsequent_install(struct task_struct *task,
228                                struct sspt_proc *proc, unsigned long page_addr)
229 {
230         down_write(&task->mm->mmap_sem);
231         sspt_proc_install_page(proc, page_addr);
232         up_write(&task->mm->mmap_sem);
233 }
234
235 /**
236  * @brief Get dentry struct by path
237  *
238  * @param path Path to file
239  * @return Pointer on dentry struct on NULL
240  */
241 struct dentry *dentry_by_path(const char *path)
242 {
243         struct dentry *dentry;
244 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
245         struct path st_path;
246         if (kern_path(path, LOOKUP_FOLLOW, &st_path) != 0) {
247 #else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
248         struct nameidata nd;
249         if (path_lookup(path, LOOKUP_FOLLOW, &nd) != 0) {
250 #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
251                 printk("failed to lookup dentry for path %s!\n", path);
252                 return NULL;
253         }
254
255 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
256         dentry = nd.dentry;
257         path_release(&nd);
258 #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38)
259         dentry = nd.path.dentry;
260         path_put(&nd.path);
261 #else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
262         dentry = st_path.dentry;
263         path_put(&st_path);
264 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) */
265         return dentry;
266 }
267 EXPORT_SYMBOL_GPL(dentry_by_path);
268
269
270 int pfg_msg_cb_set(struct pf_group *pfg, struct pfg_msg_cb *msg_cb)
271 {
272         if (pfg->msg_cb)
273                 return -EBUSY;
274
275         pfg->msg_cb = msg_cb;
276
277         return 0;
278 }
279 EXPORT_SYMBOL_GPL(pfg_msg_cb_set);
280
281 void pfg_msg_cb_reset(struct pf_group *pfg)
282 {
283         pfg->msg_cb = NULL;
284 }
285 EXPORT_SYMBOL_GPL(pfg_msg_cb_reset);
286
287 struct pfg_msg_cb *pfg_msg_cb_get(struct pf_group *pfg)
288 {
289         return pfg->msg_cb;
290 }
291
292 /**
293  * @brief Get pf_group struct by dentry
294  *
295  * @param dentry Dentry of file
296  * @param priv Private data
297  * @return Pointer on pf_group struct
298  */
299 struct pf_group *get_pf_group_by_dentry(struct dentry *dentry, void *priv)
300 {
301         struct pf_group *pfg;
302
303         pfg_list_wlock();
304         list_for_each_entry(pfg, &pfg_list, list) {
305                 if (check_pf_by_dentry(&pfg->filter, dentry)) {
306                         atomic_inc(&pfg->usage);
307                         goto unlock;
308                 }
309         }
310
311         pfg = pfg_create();
312         if (pfg == NULL)
313                 goto unlock;
314
315         set_pf_by_dentry(&pfg->filter, dentry, priv);
316
317         pfg_add_to_list(pfg);
318
319 unlock:
320         pfg_list_wunlock();
321         return pfg;
322 }
323 EXPORT_SYMBOL_GPL(get_pf_group_by_dentry);
324
325 /**
326  * @brief Get pf_group struct by TGID
327  *
328  * @param tgid Thread group ID
329  * @param priv Private data
330  * @return Pointer on pf_group struct
331  */
332 struct pf_group *get_pf_group_by_tgid(pid_t tgid, void *priv)
333 {
334         struct pf_group *pfg;
335
336         pfg_list_wlock();
337         list_for_each_entry(pfg, &pfg_list, list) {
338                 if (check_pf_by_tgid(&pfg->filter, tgid)) {
339                         atomic_inc(&pfg->usage);
340                         goto unlock;
341                 }
342         }
343
344         pfg = pfg_create();
345         if (pfg == NULL)
346                 goto unlock;
347
348         set_pf_by_tgid(&pfg->filter, tgid, priv);
349
350         pfg_add_to_list(pfg);
351
352 unlock:
353         pfg_list_wunlock();
354         return pfg;
355 }
356 EXPORT_SYMBOL_GPL(get_pf_group_by_tgid);
357
358 /**
359  * @brief Get pf_group struct by comm
360  *
361  * @param comm Task comm
362  * @param priv Private data
363  * @return Pointer on pf_group struct
364  */
365 struct pf_group *get_pf_group_by_comm(char *comm, void *priv)
366 {
367         int ret;
368         struct pf_group *pfg;
369
370         pfg_list_wlock();
371         list_for_each_entry(pfg, &pfg_list, list) {
372                 if (check_pf_by_comm(&pfg->filter, comm)) {
373                         atomic_inc(&pfg->usage);
374                         goto unlock;
375                 }
376         }
377
378         pfg = pfg_create();
379         if (pfg == NULL)
380                 goto unlock;
381
382         ret = set_pf_by_comm(&pfg->filter, comm, priv);
383         if (ret) {
384                 printk(KERN_ERR "ERROR: set_pf_by_comm, ret=%d\n", ret);
385                 pfg_free(pfg);
386                 pfg = NULL;
387                 goto unlock;
388         }
389
390         pfg_add_to_list(pfg);
391 unlock:
392         pfg_list_wunlock();
393         return pfg;
394 }
395 EXPORT_SYMBOL_GPL(get_pf_group_by_comm);
396
397 /**
398  * @brief Get pf_group struct for each process
399  *
400  * @param priv Private data
401  * @return Pointer on pf_group struct
402  */
403 struct pf_group *get_pf_group_dumb(void *priv)
404 {
405         struct pf_group *pfg;
406
407         pfg_list_wlock();
408         list_for_each_entry(pfg, &pfg_list, list) {
409                 if (check_pf_dumb(&pfg->filter)) {
410                         atomic_inc(&pfg->usage);
411                         goto unlock;
412                 }
413         }
414
415         pfg = pfg_create();
416         if (pfg == NULL)
417                 goto unlock;
418
419         set_pf_dumb(&pfg->filter, priv);
420
421         pfg_add_to_list(pfg);
422
423 unlock:
424         pfg_list_wunlock();
425         return pfg;
426 }
427 EXPORT_SYMBOL_GPL(get_pf_group_dumb);
428
429 /**
430  * @brief Put pf_group struct
431  *
432  * @param pfg Pointer to the pf_group struct
433  * @return Void
434  */
435 void put_pf_group(struct pf_group *pfg)
436 {
437         if (atomic_dec_and_test(&pfg->usage)) {
438                 pfg_list_wlock();
439                 pfg_del_from_list(pfg);
440                 pfg_list_wunlock();
441
442                 pfg_free(pfg);
443         }
444 }
445 EXPORT_SYMBOL_GPL(put_pf_group);
446
447 /**
448  * @brief Register prober for pf_grpup struct
449  *
450  * @param pfg Pointer to the pf_group struct
451  * @param dentry Dentry of file
452  * @param offset Function offset
453  * @param probe_info Pointer to the related probe_info struct
454  * @return Error code
455  */
456 int pf_register_probe(struct pf_group *pfg, struct dentry *dentry,
457                       unsigned long offset, struct probe_desc *pd)
458 {
459         return img_proc_add_ip(pfg->i_proc, dentry, offset, pd);
460 }
461 EXPORT_SYMBOL_GPL(pf_register_probe);
462
463 /**
464  * @brief Unregister prober from pf_grpup struct
465  *
466  * @param pfg Pointer to the pf_group struct
467  * @param dentry Dentry of file
468  * @param offset Function offset
469  * @return Error code
470  */
471 int pf_unregister_probe(struct pf_group *pfg, struct dentry *dentry,
472                         unsigned long offset, struct probe_desc *pd)
473 {
474         return img_proc_del_ip(pfg->i_proc, dentry, offset, pd);
475 }
476 EXPORT_SYMBOL_GPL(pf_unregister_probe);
477
478 static int check_task_on_filters(struct task_struct *task)
479 {
480         int ret = 0;
481         struct pf_group *pfg;
482
483         pfg_list_rlock();
484         list_for_each_entry(pfg, &pfg_list, list) {
485                 if (check_task_f(&pfg->filter, task)) {
486                         ret = 1;
487                         goto unlock;
488                 }
489         }
490
491 unlock:
492         pfg_list_runlock();
493         return ret;
494 }
495
496 enum pf_inst_flag {
497         PIF_NONE,
498         PIF_FIRST,
499         PIF_SECOND,
500         PIF_ADD_PFG
501 };
502
503 static enum pf_inst_flag pfg_check_task(struct task_struct *task)
504 {
505         struct pf_group *pfg;
506         struct sspt_proc *proc = NULL;
507         enum pf_inst_flag flag = PIF_NONE;
508
509         pfg_list_rlock();
510         list_for_each_entry(pfg, &pfg_list, list) {
511                 bool put_flag = false;
512
513                 if (check_task_f(&pfg->filter, task) == NULL)
514                         continue;
515
516                 if (proc == NULL) {
517                         proc = sspt_proc_get_by_task(task);
518                         put_flag = !!proc;
519                 }
520
521                 if (proc) {
522                         flag = flag == PIF_NONE ? PIF_SECOND : flag;
523                 } else if (task->tgid == task->pid) {
524                         proc = sspt_proc_get_by_task_or_new(task);
525                         if (proc == NULL) {
526                                 printk(KERN_ERR "cannot create sspt_proc\n");
527                                 break;
528                         }
529                         put_flag = true;
530                         flag = PIF_FIRST;
531                 }
532
533                 if (proc) {
534                         mutex_lock(&proc->filters.mtx);
535                                 if (sspt_proc_is_filter_new(proc, pfg)) {
536                                         img_proc_copy_to_sspt(pfg->i_proc, proc);
537                                         sspt_proc_add_filter(proc, pfg);
538                                         pfg_add_proc(pfg, proc);
539                                         flag = flag == PIF_FIRST ? flag : PIF_ADD_PFG;
540                         }
541                         mutex_unlock(&proc->filters.mtx);
542                         if (put_flag)
543                                 sspt_proc_put(proc);
544                 }
545         }
546         pfg_list_runlock();
547
548         return flag;
549 }
550
551 static void pfg_all_del_proc(struct sspt_proc *proc)
552 {
553         struct pf_group *pfg;
554
555         pfg_list_rlock();
556         list_for_each_entry(pfg, &pfg_list, list)
557                 pfg_del_proc(pfg, proc);
558         pfg_list_runlock();
559 }
560
561 /**
562  * @brief Check task and install probes on demand
563  *
564  * @prarm task Pointer on the task_struct struct
565  * @return Void
566  */
567 void check_task_and_install(struct task_struct *task)
568 {
569         struct sspt_proc *proc;
570         enum pf_inst_flag flag;
571
572         flag = pfg_check_task(task);
573         switch (flag) {
574         case PIF_FIRST:
575         case PIF_ADD_PFG:
576                 proc = sspt_proc_get_by_task(task);
577                 if (proc) {
578                         first_install(task, proc);
579                         sspt_proc_put(proc);
580                 }
581                 break;
582
583         case PIF_NONE:
584         case PIF_SECOND:
585                 break;
586         }
587 }
588
589 /**
590  * @brief Check task and install probes on demand
591  *
592  * @prarm task Pointer on the task_struct struct
593  * @param page_addr Page fault address
594  * @return Void
595  */
596 void call_page_fault(struct task_struct *task, unsigned long page_addr)
597 {
598         struct sspt_proc *proc;
599         enum pf_inst_flag flag;
600
601         flag = pfg_check_task(task);
602         switch (flag) {
603         case PIF_FIRST:
604         case PIF_ADD_PFG:
605                 proc = sspt_proc_get_by_task(task);
606                 if (proc) {
607                         first_install(task, proc);
608                         sspt_proc_put(proc);
609                 }
610                 break;
611
612         case PIF_SECOND:
613                 proc = sspt_proc_get_by_task(task);
614                 if (proc) {
615                         subsequent_install(task, proc, page_addr);
616                         sspt_proc_put(proc);
617                 }
618                 break;
619
620         case PIF_NONE:
621                 break;
622         }
623 }
624
625 /**
626  * @brief Uninstall probes from the sspt_proc struct
627  *
628  * @prarm proc Pointer on the sspt_proc struct
629  * @return Void
630  */
631
632 /* called with sspt_proc_write_lock() */
633 void uninstall_proc(struct sspt_proc *proc)
634 {
635         struct task_struct *task = proc->leader;
636
637         sspt_proc_uninstall(proc, task, US_UNREGS_PROBE);
638         sspt_proc_cleanup(proc);
639 }
640
641
642 static void mmr_from_exit(struct sspt_proc *proc)
643 {
644         BUG_ON(proc->leader != current);
645
646         sspt_proc_write_lock();
647         list_del(&proc->list);
648         sspt_proc_write_unlock();
649
650         uninstall_proc(proc);
651
652         pfg_all_del_proc(proc);
653 }
654
655 static void mmr_from_exec(struct sspt_proc *proc)
656 {
657         BUG_ON(proc->leader != current);
658
659         if (proc->suspect.after_exec) {
660                 sspt_proc_uninstall(proc, proc->leader, US_UNREGS_PROBE);
661         } else {
662                 mmr_from_exit(proc);
663         }
664 }
665
666 /**
667  * @brief Remove probes from the task on demand
668  *
669  * @prarm task Pointer on the task_struct struct
670  * @return Void
671  */
672 void call_mm_release(struct task_struct *task)
673 {
674         struct sspt_proc *proc;
675
676         proc = sspt_proc_get_by_task(task);
677         if (proc) {
678                 if (task->flags & PF_EXITING)
679                         mmr_from_exit(proc);
680                 else
681                         mmr_from_exec(proc);
682                 sspt_proc_put(proc);
683         }
684 }
685
686 /**
687  * @brief Legacy code, it is need remove
688  *
689  * @param addr Page address
690  * @return Void
691  */
692 void uninstall_page(unsigned long addr)
693 {
694
695 }
696
697
698 static void install_cb(void *unused)
699 {
700         check_task_and_install(current);
701 }
702
703
704
705
706 struct task_item {
707         struct list_head list;
708         struct task_struct *task;
709 };
710
711 static void tasks_get(struct list_head *head)
712 {
713         struct task_item *item;
714         struct task_struct *task;
715
716         rcu_read_lock();
717         for_each_process(task) {
718                 if (task->flags & PF_KTHREAD)
719                         continue;
720
721                 if (sspt_proc_by_task(task))
722                         continue;
723
724                 /* TODO: get rid of GFP_ATOMIC */
725                 item = kmalloc(sizeof(*item), GFP_ATOMIC);
726                 if (item == NULL) {
727                         WARN(1, "out of memory\n");
728                         goto unlock;
729                 }
730
731                 get_task_struct(task);
732                 item->task = task;
733                 list_add(&item->list, head);
734         }
735
736 unlock:
737         rcu_read_unlock();
738 }
739
740 static void tasks_install_and_put(struct list_head *head)
741 {
742         struct task_item *item, *n;
743
744         list_for_each_entry_safe(item, n, head, list) {
745                 int ret;
746                 struct task_struct *task;
747
748                 task = item->task;
749                 if (!check_task_on_filters(task))
750                         goto put_task;
751
752                 ret = taskctx_run(task, install_cb, NULL);
753                 if (ret) {
754                         pr_err("cannot tracking task[%u %u %s] ret=%d\n",
755                                task->tgid, task->pid, task->comm, ret);
756                 }
757
758 put_task:
759                 put_task_struct(task);
760                 list_del(&item->list);
761                 kfree(item);
762         }
763 }
764
765 static void do_install_all(void)
766 {
767         LIST_HEAD(head);
768
769         tasks_get(&head);
770         tasks_install_and_put(&head);
771 }
772
773 /**
774  * @brief Install probes on running processes
775  *
776  * @return Void
777  */
778 void install_all(void)
779 {
780         int ret;
781
782         ret = taskctx_get();
783         if (!ret) {
784                 do_install_all();
785                 taskctx_put();
786         } else {
787                 pr_err("taskctx_get ret=%d\n", ret);
788         }
789 }
790
791 /**
792  * @brief Uninstall probes from all processes
793  *
794  * @return Void
795  */
796 void uninstall_all(void)
797 {
798         struct list_head *proc_list = sspt_proc_list();
799
800         sspt_proc_write_lock();
801         while (!list_empty(proc_list)) {
802                 struct sspt_proc *proc;
803                 proc = list_first_entry(proc_list, struct sspt_proc, list);
804
805                 list_del(&proc->list);
806
807                 sspt_proc_write_unlock();
808                 uninstall_proc(proc);
809                 sspt_proc_write_lock();
810         }
811         sspt_proc_write_unlock();
812 }
813
814 static void __do_get_proc(struct sspt_proc *proc, void *data)
815 {
816         struct task_struct *task = proc->leader;
817
818         get_task_struct(task);
819         proc->__task = task;
820         proc->__mm = get_task_mm(task);
821 }
822
823 static void __do_put_proc(struct sspt_proc *proc, void *data)
824 {
825         if (proc->__mm) {
826                 mmput(proc->__mm);
827                 proc->__mm = NULL;
828         }
829
830         if (proc->__task) {
831                 put_task_struct(proc->__task);
832                 proc->__task = NULL;
833         }
834 }
835
836 void get_all_procs(void)
837 {
838         sspt_proc_read_lock();
839         on_each_proc_no_lock(__do_get_proc, NULL);
840         sspt_proc_read_unlock();
841 }
842
843 void put_all_procs(void)
844 {
845         sspt_proc_read_lock();
846         on_each_proc_no_lock(__do_put_proc, NULL);
847         sspt_proc_read_unlock();
848 }
849
850 /**
851  * @brief For debug
852  *
853  * @param pfg Pointer to the pf_group struct
854  * @return Void
855  */
856
857 /* debug */
858 void pfg_print(struct pf_group *pfg)
859 {
860         img_proc_print(pfg->i_proc);
861 }
862 EXPORT_SYMBOL_GPL(pfg_print);
863 /* debug */