[FIX] race condition when use sspt_proc
[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                 if (check_task_f(&pfg->filter, task) == NULL)
512                         continue;
513
514                 if (proc == NULL)
515                         proc = sspt_proc_get_by_task(task);
516
517                 if (proc) {
518                         flag = flag == PIF_NONE ? PIF_SECOND : flag;
519                 } else if (task->tgid == task->pid) {
520                         proc = sspt_proc_get_by_task_or_new(task);
521                         if (proc == NULL) {
522                                 printk(KERN_ERR "cannot create sspt_proc\n");
523                                 break;
524                         }
525                         flag = PIF_FIRST;
526                 }
527
528                 if (proc) {
529                         mutex_lock(&proc->filters.mtx);
530                                 if (sspt_proc_is_filter_new(proc, pfg)) {
531                                         img_proc_copy_to_sspt(pfg->i_proc, proc);
532                                         sspt_proc_add_filter(proc, pfg);
533                                         pfg_add_proc(pfg, proc);
534                                         flag = flag == PIF_FIRST ? flag : PIF_ADD_PFG;
535                         }
536                         mutex_unlock(&proc->filters.mtx);
537                         sspt_proc_put(proc);
538                 }
539         }
540         pfg_list_runlock();
541
542         return flag;
543 }
544
545 static void pfg_all_del_proc(struct sspt_proc *proc)
546 {
547         struct pf_group *pfg;
548
549         pfg_list_rlock();
550         list_for_each_entry(pfg, &pfg_list, list)
551                 pfg_del_proc(pfg, proc);
552         pfg_list_runlock();
553 }
554
555 /**
556  * @brief Check task and install probes on demand
557  *
558  * @prarm task Pointer on the task_struct struct
559  * @return Void
560  */
561 void check_task_and_install(struct task_struct *task)
562 {
563         struct sspt_proc *proc;
564         enum pf_inst_flag flag;
565
566         flag = pfg_check_task(task);
567         switch (flag) {
568         case PIF_FIRST:
569         case PIF_ADD_PFG:
570                 proc = sspt_proc_get_by_task(task);
571                 if (proc) {
572                         first_install(task, proc);
573                         sspt_proc_put(proc);
574                 }
575                 break;
576
577         case PIF_NONE:
578         case PIF_SECOND:
579                 break;
580         }
581 }
582
583 /**
584  * @brief Check task and install probes on demand
585  *
586  * @prarm task Pointer on the task_struct struct
587  * @param page_addr Page fault address
588  * @return Void
589  */
590 void call_page_fault(struct task_struct *task, unsigned long page_addr)
591 {
592         struct sspt_proc *proc;
593         enum pf_inst_flag flag;
594
595         flag = pfg_check_task(task);
596         switch (flag) {
597         case PIF_FIRST:
598         case PIF_ADD_PFG:
599                 proc = sspt_proc_get_by_task(task);
600                 if (proc) {
601                         first_install(task, proc);
602                         sspt_proc_put(proc);
603                 }
604                 break;
605
606         case PIF_SECOND:
607                 proc = sspt_proc_get_by_task(task);
608                 if (proc) {
609                         subsequent_install(task, proc, page_addr);
610                         sspt_proc_put(proc);
611                 }
612                 break;
613
614         case PIF_NONE:
615                 break;
616         }
617 }
618
619 /**
620  * @brief Uninstall probes from the sspt_proc struct
621  *
622  * @prarm proc Pointer on the sspt_proc struct
623  * @return Void
624  */
625
626 /* called with sspt_proc_write_lock() */
627 void uninstall_proc(struct sspt_proc *proc)
628 {
629         struct task_struct *task = proc->leader;
630
631         sspt_proc_uninstall(proc, task, US_UNREGS_PROBE);
632         sspt_proc_cleanup(proc);
633 }
634
635
636 static void mmr_from_exit(struct sspt_proc *proc)
637 {
638         BUG_ON(proc->leader != current);
639
640         sspt_proc_write_lock();
641         list_del(&proc->list);
642         sspt_proc_write_unlock();
643
644         uninstall_proc(proc);
645
646         pfg_all_del_proc(proc);
647         sspt_reset_proc(proc->leader);
648 }
649
650 static void mmr_from_exec(struct sspt_proc *proc)
651 {
652         BUG_ON(proc->leader != current);
653
654         if (proc->suspect.after_exec) {
655                 sspt_proc_uninstall(proc, proc->leader, US_UNREGS_PROBE);
656         } else {
657                 mmr_from_exit(proc);
658         }
659 }
660
661 /**
662  * @brief Remove probes from the task on demand
663  *
664  * @prarm task Pointer on the task_struct struct
665  * @return Void
666  */
667 void call_mm_release(struct task_struct *task)
668 {
669         struct sspt_proc *proc;
670
671         proc = sspt_proc_get_by_task(task);
672         if (proc) {
673                 if (task->flags & PF_EXITING)
674                         mmr_from_exit(proc);
675                 else
676                         mmr_from_exec(proc);
677                 sspt_proc_put(proc);
678         }
679 }
680
681 /**
682  * @brief Legacy code, it is need remove
683  *
684  * @param addr Page address
685  * @return Void
686  */
687 void uninstall_page(unsigned long addr)
688 {
689
690 }
691
692
693 static void install_cb(void *unused)
694 {
695         check_task_and_install(current);
696 }
697
698
699
700
701 struct task_item {
702         struct list_head list;
703         struct task_struct *task;
704 };
705
706 static void tasks_get(struct list_head *head)
707 {
708         struct task_item *item;
709         struct task_struct *task;
710
711         rcu_read_lock();
712         for_each_process(task) {
713                 if (task->flags & PF_KTHREAD)
714                         continue;
715
716                 if (sspt_proc_by_task(task))
717                         continue;
718
719                 /* TODO: get rid of GFP_ATOMIC */
720                 item = kmalloc(sizeof(*item), GFP_ATOMIC);
721                 if (item == NULL) {
722                         WARN(1, "out of memory\n");
723                         goto unlock;
724                 }
725
726                 get_task_struct(task);
727                 item->task = task;
728                 list_add(&item->list, head);
729         }
730
731 unlock:
732         rcu_read_unlock();
733 }
734
735 static void tasks_install_and_put(struct list_head *head)
736 {
737         struct task_item *item, *n;
738
739         list_for_each_entry_safe(item, n, head, list) {
740                 int ret;
741                 struct task_struct *task;
742
743                 task = item->task;
744                 if (!check_task_on_filters(task))
745                         goto put_task;
746
747                 ret = taskctx_run(task, install_cb, NULL);
748                 if (ret) {
749                         pr_err("cannot tracking task[%u %u %s] ret=%d\n",
750                                task->tgid, task->pid, task->comm, ret);
751                 }
752
753 put_task:
754                 put_task_struct(task);
755                 list_del(&item->list);
756                 kfree(item);
757         }
758 }
759
760 static void do_install_all(void)
761 {
762         LIST_HEAD(head);
763
764         tasks_get(&head);
765         tasks_install_and_put(&head);
766 }
767
768 /**
769  * @brief Install probes on running processes
770  *
771  * @return Void
772  */
773 void install_all(void)
774 {
775         int ret;
776
777         ret = taskctx_get();
778         if (!ret) {
779                 do_install_all();
780                 taskctx_put();
781         } else {
782                 pr_err("taskctx_get ret=%d\n", ret);
783         }
784 }
785
786 /**
787  * @brief Uninstall probes from all processes
788  *
789  * @return Void
790  */
791 void uninstall_all(void)
792 {
793         struct list_head *proc_list = sspt_proc_list();
794
795         sspt_proc_write_lock();
796         while (!list_empty(proc_list)) {
797                 struct sspt_proc *proc;
798                 proc = list_first_entry(proc_list, struct sspt_proc, list);
799
800                 list_del(&proc->list);
801
802                 sspt_proc_write_unlock();
803                 uninstall_proc(proc);
804                 sspt_proc_write_lock();
805         }
806         sspt_proc_write_unlock();
807 }
808
809 static void __do_get_proc(struct sspt_proc *proc, void *data)
810 {
811         struct task_struct *task = proc->leader;
812
813         get_task_struct(task);
814         proc->__task = task;
815         proc->__mm = get_task_mm(task);
816 }
817
818 static void __do_put_proc(struct sspt_proc *proc, void *data)
819 {
820         if (proc->__mm) {
821                 mmput(proc->__mm);
822                 proc->__mm = NULL;
823         }
824
825         if (proc->__task) {
826                 put_task_struct(proc->__task);
827                 proc->__task = NULL;
828         }
829 }
830
831 void get_all_procs(void)
832 {
833         sspt_proc_read_lock();
834         on_each_proc_no_lock(__do_get_proc, NULL);
835         sspt_proc_read_unlock();
836 }
837
838 void put_all_procs(void)
839 {
840         sspt_proc_read_lock();
841         on_each_proc_no_lock(__do_put_proc, NULL);
842         sspt_proc_read_unlock();
843 }
844
845 /**
846  * @brief For debug
847  *
848  * @param pfg Pointer to the pf_group struct
849  * @return Void
850  */
851
852 /* debug */
853 void pfg_print(struct pf_group *pfg)
854 {
855         img_proc_print(pfg->i_proc);
856 }
857 EXPORT_SYMBOL_GPL(pfg_print);
858 /* debug */