diff options
Diffstat (limited to 'net/core/pktgen.c')
-rw-r--r-- | net/core/pktgen.c | 111 |
1 files changed, 55 insertions, 56 deletions
diff --git a/net/core/pktgen.c b/net/core/pktgen.c index fc17a9d309ac..8b849ddfef2e 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c | |||
@@ -69,8 +69,9 @@ | |||
69 | * for running devices in the if_list and sends packets until count is 0 it | 69 | * for running devices in the if_list and sends packets until count is 0 it |
70 | * also the thread checks the thread->control which is used for inter-process | 70 | * also the thread checks the thread->control which is used for inter-process |
71 | * communication. controlling process "posts" operations to the threads this | 71 | * communication. controlling process "posts" operations to the threads this |
72 | * way. The if_lock should be possible to remove when add/rem_device is merged | 72 | * way. |
73 | * into this too. | 73 | * The if_list is RCU protected, and the if_lock remains to protect updating |
74 | * of if_list, from "add_device" as it invoked from userspace (via proc write). | ||
74 | * | 75 | * |
75 | * By design there should only be *one* "controlling" process. In practice | 76 | * By design there should only be *one* "controlling" process. In practice |
76 | * multiple write accesses gives unpredictable result. Understood by "write" | 77 | * multiple write accesses gives unpredictable result. Understood by "write" |
@@ -208,7 +209,7 @@ | |||
208 | #define T_REMDEVALL (1<<2) /* Remove all devs */ | 209 | #define T_REMDEVALL (1<<2) /* Remove all devs */ |
209 | #define T_REMDEV (1<<3) /* Remove one dev */ | 210 | #define T_REMDEV (1<<3) /* Remove one dev */ |
210 | 211 | ||
211 | /* If lock -- can be removed after some work */ | 212 | /* If lock -- protects updating of if_list */ |
212 | #define if_lock(t) spin_lock(&(t->if_lock)); | 213 | #define if_lock(t) spin_lock(&(t->if_lock)); |
213 | #define if_unlock(t) spin_unlock(&(t->if_lock)); | 214 | #define if_unlock(t) spin_unlock(&(t->if_lock)); |
214 | 215 | ||
@@ -241,6 +242,7 @@ struct pktgen_dev { | |||
241 | struct proc_dir_entry *entry; /* proc file */ | 242 | struct proc_dir_entry *entry; /* proc file */ |
242 | struct pktgen_thread *pg_thread;/* the owner */ | 243 | struct pktgen_thread *pg_thread;/* the owner */ |
243 | struct list_head list; /* chaining in the thread's run-queue */ | 244 | struct list_head list; /* chaining in the thread's run-queue */ |
245 | struct rcu_head rcu; /* freed by RCU */ | ||
244 | 246 | ||
245 | int running; /* if false, the test will stop */ | 247 | int running; /* if false, the test will stop */ |
246 | 248 | ||
@@ -802,7 +804,6 @@ static int strn_len(const char __user * user_buffer, unsigned int maxlen) | |||
802 | case '\t': | 804 | case '\t': |
803 | case ' ': | 805 | case ' ': |
804 | goto done_str; | 806 | goto done_str; |
805 | break; | ||
806 | default: | 807 | default: |
807 | break; | 808 | break; |
808 | } | 809 | } |
@@ -1737,14 +1738,14 @@ static int pktgen_thread_show(struct seq_file *seq, void *v) | |||
1737 | 1738 | ||
1738 | seq_puts(seq, "Running: "); | 1739 | seq_puts(seq, "Running: "); |
1739 | 1740 | ||
1740 | if_lock(t); | 1741 | rcu_read_lock(); |
1741 | list_for_each_entry(pkt_dev, &t->if_list, list) | 1742 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) |
1742 | if (pkt_dev->running) | 1743 | if (pkt_dev->running) |
1743 | seq_printf(seq, "%s ", pkt_dev->odevname); | 1744 | seq_printf(seq, "%s ", pkt_dev->odevname); |
1744 | 1745 | ||
1745 | seq_puts(seq, "\nStopped: "); | 1746 | seq_puts(seq, "\nStopped: "); |
1746 | 1747 | ||
1747 | list_for_each_entry(pkt_dev, &t->if_list, list) | 1748 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) |
1748 | if (!pkt_dev->running) | 1749 | if (!pkt_dev->running) |
1749 | seq_printf(seq, "%s ", pkt_dev->odevname); | 1750 | seq_printf(seq, "%s ", pkt_dev->odevname); |
1750 | 1751 | ||
@@ -1753,7 +1754,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v) | |||
1753 | else | 1754 | else |
1754 | seq_puts(seq, "\nResult: NA\n"); | 1755 | seq_puts(seq, "\nResult: NA\n"); |
1755 | 1756 | ||
1756 | if_unlock(t); | 1757 | rcu_read_unlock(); |
1757 | 1758 | ||
1758 | return 0; | 1759 | return 0; |
1759 | } | 1760 | } |
@@ -1878,10 +1879,8 @@ static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn, | |||
1878 | pkt_dev = pktgen_find_dev(t, ifname, exact); | 1879 | pkt_dev = pktgen_find_dev(t, ifname, exact); |
1879 | if (pkt_dev) { | 1880 | if (pkt_dev) { |
1880 | if (remove) { | 1881 | if (remove) { |
1881 | if_lock(t); | ||
1882 | pkt_dev->removal_mark = 1; | 1882 | pkt_dev->removal_mark = 1; |
1883 | t->control |= T_REMDEV; | 1883 | t->control |= T_REMDEV; |
1884 | if_unlock(t); | ||
1885 | } | 1884 | } |
1886 | break; | 1885 | break; |
1887 | } | 1886 | } |
@@ -1931,7 +1930,8 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d | |||
1931 | list_for_each_entry(t, &pn->pktgen_threads, th_list) { | 1930 | list_for_each_entry(t, &pn->pktgen_threads, th_list) { |
1932 | struct pktgen_dev *pkt_dev; | 1931 | struct pktgen_dev *pkt_dev; |
1933 | 1932 | ||
1934 | list_for_each_entry(pkt_dev, &t->if_list, list) { | 1933 | rcu_read_lock(); |
1934 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) { | ||
1935 | if (pkt_dev->odev != dev) | 1935 | if (pkt_dev->odev != dev) |
1936 | continue; | 1936 | continue; |
1937 | 1937 | ||
@@ -1946,6 +1946,7 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d | |||
1946 | dev->name); | 1946 | dev->name); |
1947 | break; | 1947 | break; |
1948 | } | 1948 | } |
1949 | rcu_read_unlock(); | ||
1949 | } | 1950 | } |
1950 | } | 1951 | } |
1951 | 1952 | ||
@@ -2997,8 +2998,8 @@ static void pktgen_run(struct pktgen_thread *t) | |||
2997 | 2998 | ||
2998 | func_enter(); | 2999 | func_enter(); |
2999 | 3000 | ||
3000 | if_lock(t); | 3001 | rcu_read_lock(); |
3001 | list_for_each_entry(pkt_dev, &t->if_list, list) { | 3002 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) { |
3002 | 3003 | ||
3003 | /* | 3004 | /* |
3004 | * setup odev and create initial packet. | 3005 | * setup odev and create initial packet. |
@@ -3007,18 +3008,18 @@ static void pktgen_run(struct pktgen_thread *t) | |||
3007 | 3008 | ||
3008 | if (pkt_dev->odev) { | 3009 | if (pkt_dev->odev) { |
3009 | pktgen_clear_counters(pkt_dev); | 3010 | pktgen_clear_counters(pkt_dev); |
3010 | pkt_dev->running = 1; /* Cranke yeself! */ | ||
3011 | pkt_dev->skb = NULL; | 3011 | pkt_dev->skb = NULL; |
3012 | pkt_dev->started_at = pkt_dev->next_tx = ktime_get(); | 3012 | pkt_dev->started_at = pkt_dev->next_tx = ktime_get(); |
3013 | 3013 | ||
3014 | set_pkt_overhead(pkt_dev); | 3014 | set_pkt_overhead(pkt_dev); |
3015 | 3015 | ||
3016 | strcpy(pkt_dev->result, "Starting"); | 3016 | strcpy(pkt_dev->result, "Starting"); |
3017 | pkt_dev->running = 1; /* Cranke yeself! */ | ||
3017 | started++; | 3018 | started++; |
3018 | } else | 3019 | } else |
3019 | strcpy(pkt_dev->result, "Error starting"); | 3020 | strcpy(pkt_dev->result, "Error starting"); |
3020 | } | 3021 | } |
3021 | if_unlock(t); | 3022 | rcu_read_unlock(); |
3022 | if (started) | 3023 | if (started) |
3023 | t->control &= ~(T_STOP); | 3024 | t->control &= ~(T_STOP); |
3024 | } | 3025 | } |
@@ -3041,27 +3042,25 @@ static int thread_is_running(const struct pktgen_thread *t) | |||
3041 | { | 3042 | { |
3042 | const struct pktgen_dev *pkt_dev; | 3043 | const struct pktgen_dev *pkt_dev; |
3043 | 3044 | ||
3044 | list_for_each_entry(pkt_dev, &t->if_list, list) | 3045 | rcu_read_lock(); |
3045 | if (pkt_dev->running) | 3046 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) |
3047 | if (pkt_dev->running) { | ||
3048 | rcu_read_unlock(); | ||
3046 | return 1; | 3049 | return 1; |
3050 | } | ||
3051 | rcu_read_unlock(); | ||
3047 | return 0; | 3052 | return 0; |
3048 | } | 3053 | } |
3049 | 3054 | ||
3050 | static int pktgen_wait_thread_run(struct pktgen_thread *t) | 3055 | static int pktgen_wait_thread_run(struct pktgen_thread *t) |
3051 | { | 3056 | { |
3052 | if_lock(t); | ||
3053 | |||
3054 | while (thread_is_running(t)) { | 3057 | while (thread_is_running(t)) { |
3055 | 3058 | ||
3056 | if_unlock(t); | ||
3057 | |||
3058 | msleep_interruptible(100); | 3059 | msleep_interruptible(100); |
3059 | 3060 | ||
3060 | if (signal_pending(current)) | 3061 | if (signal_pending(current)) |
3061 | goto signal; | 3062 | goto signal; |
3062 | if_lock(t); | ||
3063 | } | 3063 | } |
3064 | if_unlock(t); | ||
3065 | return 1; | 3064 | return 1; |
3066 | signal: | 3065 | signal: |
3067 | return 0; | 3066 | return 0; |
@@ -3166,10 +3165,10 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev) | |||
3166 | return -EINVAL; | 3165 | return -EINVAL; |
3167 | } | 3166 | } |
3168 | 3167 | ||
3168 | pkt_dev->running = 0; | ||
3169 | kfree_skb(pkt_dev->skb); | 3169 | kfree_skb(pkt_dev->skb); |
3170 | pkt_dev->skb = NULL; | 3170 | pkt_dev->skb = NULL; |
3171 | pkt_dev->stopped_at = ktime_get(); | 3171 | pkt_dev->stopped_at = ktime_get(); |
3172 | pkt_dev->running = 0; | ||
3173 | 3172 | ||
3174 | show_results(pkt_dev, nr_frags); | 3173 | show_results(pkt_dev, nr_frags); |
3175 | 3174 | ||
@@ -3180,9 +3179,8 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t) | |||
3180 | { | 3179 | { |
3181 | struct pktgen_dev *pkt_dev, *best = NULL; | 3180 | struct pktgen_dev *pkt_dev, *best = NULL; |
3182 | 3181 | ||
3183 | if_lock(t); | 3182 | rcu_read_lock(); |
3184 | 3183 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) { | |
3185 | list_for_each_entry(pkt_dev, &t->if_list, list) { | ||
3186 | if (!pkt_dev->running) | 3184 | if (!pkt_dev->running) |
3187 | continue; | 3185 | continue; |
3188 | if (best == NULL) | 3186 | if (best == NULL) |
@@ -3190,7 +3188,8 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t) | |||
3190 | else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0) | 3188 | else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0) |
3191 | best = pkt_dev; | 3189 | best = pkt_dev; |
3192 | } | 3190 | } |
3193 | if_unlock(t); | 3191 | rcu_read_unlock(); |
3192 | |||
3194 | return best; | 3193 | return best; |
3195 | } | 3194 | } |
3196 | 3195 | ||
@@ -3200,13 +3199,13 @@ static void pktgen_stop(struct pktgen_thread *t) | |||
3200 | 3199 | ||
3201 | func_enter(); | 3200 | func_enter(); |
3202 | 3201 | ||
3203 | if_lock(t); | 3202 | rcu_read_lock(); |
3204 | 3203 | ||
3205 | list_for_each_entry(pkt_dev, &t->if_list, list) { | 3204 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) { |
3206 | pktgen_stop_device(pkt_dev); | 3205 | pktgen_stop_device(pkt_dev); |
3207 | } | 3206 | } |
3208 | 3207 | ||
3209 | if_unlock(t); | 3208 | rcu_read_unlock(); |
3210 | } | 3209 | } |
3211 | 3210 | ||
3212 | /* | 3211 | /* |
@@ -3220,8 +3219,6 @@ static void pktgen_rem_one_if(struct pktgen_thread *t) | |||
3220 | 3219 | ||
3221 | func_enter(); | 3220 | func_enter(); |
3222 | 3221 | ||
3223 | if_lock(t); | ||
3224 | |||
3225 | list_for_each_safe(q, n, &t->if_list) { | 3222 | list_for_each_safe(q, n, &t->if_list) { |
3226 | cur = list_entry(q, struct pktgen_dev, list); | 3223 | cur = list_entry(q, struct pktgen_dev, list); |
3227 | 3224 | ||
@@ -3235,8 +3232,6 @@ static void pktgen_rem_one_if(struct pktgen_thread *t) | |||
3235 | 3232 | ||
3236 | break; | 3233 | break; |
3237 | } | 3234 | } |
3238 | |||
3239 | if_unlock(t); | ||
3240 | } | 3235 | } |
3241 | 3236 | ||
3242 | static void pktgen_rem_all_ifs(struct pktgen_thread *t) | 3237 | static void pktgen_rem_all_ifs(struct pktgen_thread *t) |
@@ -3248,8 +3243,6 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t) | |||
3248 | 3243 | ||
3249 | /* Remove all devices, free mem */ | 3244 | /* Remove all devices, free mem */ |
3250 | 3245 | ||
3251 | if_lock(t); | ||
3252 | |||
3253 | list_for_each_safe(q, n, &t->if_list) { | 3246 | list_for_each_safe(q, n, &t->if_list) { |
3254 | cur = list_entry(q, struct pktgen_dev, list); | 3247 | cur = list_entry(q, struct pktgen_dev, list); |
3255 | 3248 | ||
@@ -3258,8 +3251,6 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t) | |||
3258 | 3251 | ||
3259 | pktgen_remove_device(t, cur); | 3252 | pktgen_remove_device(t, cur); |
3260 | } | 3253 | } |
3261 | |||
3262 | if_unlock(t); | ||
3263 | } | 3254 | } |
3264 | 3255 | ||
3265 | static void pktgen_rem_thread(struct pktgen_thread *t) | 3256 | static void pktgen_rem_thread(struct pktgen_thread *t) |
@@ -3407,10 +3398,10 @@ static int pktgen_thread_worker(void *arg) | |||
3407 | 3398 | ||
3408 | pr_debug("starting pktgen/%d: pid=%d\n", cpu, task_pid_nr(current)); | 3399 | pr_debug("starting pktgen/%d: pid=%d\n", cpu, task_pid_nr(current)); |
3409 | 3400 | ||
3410 | set_current_state(TASK_INTERRUPTIBLE); | ||
3411 | |||
3412 | set_freezable(); | 3401 | set_freezable(); |
3413 | 3402 | ||
3403 | __set_current_state(TASK_RUNNING); | ||
3404 | |||
3414 | while (!kthread_should_stop()) { | 3405 | while (!kthread_should_stop()) { |
3415 | pkt_dev = next_to_run(t); | 3406 | pkt_dev = next_to_run(t); |
3416 | 3407 | ||
@@ -3424,8 +3415,6 @@ static int pktgen_thread_worker(void *arg) | |||
3424 | continue; | 3415 | continue; |
3425 | } | 3416 | } |
3426 | 3417 | ||
3427 | __set_current_state(TASK_RUNNING); | ||
3428 | |||
3429 | if (likely(pkt_dev)) { | 3418 | if (likely(pkt_dev)) { |
3430 | pktgen_xmit(pkt_dev); | 3419 | pktgen_xmit(pkt_dev); |
3431 | 3420 | ||
@@ -3456,9 +3445,8 @@ static int pktgen_thread_worker(void *arg) | |||
3456 | } | 3445 | } |
3457 | 3446 | ||
3458 | try_to_freeze(); | 3447 | try_to_freeze(); |
3459 | |||
3460 | set_current_state(TASK_INTERRUPTIBLE); | ||
3461 | } | 3448 | } |
3449 | set_current_state(TASK_INTERRUPTIBLE); | ||
3462 | 3450 | ||
3463 | pr_debug("%s stopping all device\n", t->tsk->comm); | 3451 | pr_debug("%s stopping all device\n", t->tsk->comm); |
3464 | pktgen_stop(t); | 3452 | pktgen_stop(t); |
@@ -3485,8 +3473,8 @@ static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t, | |||
3485 | struct pktgen_dev *p, *pkt_dev = NULL; | 3473 | struct pktgen_dev *p, *pkt_dev = NULL; |
3486 | size_t len = strlen(ifname); | 3474 | size_t len = strlen(ifname); |
3487 | 3475 | ||
3488 | if_lock(t); | 3476 | rcu_read_lock(); |
3489 | list_for_each_entry(p, &t->if_list, list) | 3477 | list_for_each_entry_rcu(p, &t->if_list, list) |
3490 | if (strncmp(p->odevname, ifname, len) == 0) { | 3478 | if (strncmp(p->odevname, ifname, len) == 0) { |
3491 | if (p->odevname[len]) { | 3479 | if (p->odevname[len]) { |
3492 | if (exact || p->odevname[len] != '@') | 3480 | if (exact || p->odevname[len] != '@') |
@@ -3496,7 +3484,7 @@ static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t, | |||
3496 | break; | 3484 | break; |
3497 | } | 3485 | } |
3498 | 3486 | ||
3499 | if_unlock(t); | 3487 | rcu_read_unlock(); |
3500 | pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev); | 3488 | pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev); |
3501 | return pkt_dev; | 3489 | return pkt_dev; |
3502 | } | 3490 | } |
@@ -3510,6 +3498,12 @@ static int add_dev_to_thread(struct pktgen_thread *t, | |||
3510 | { | 3498 | { |
3511 | int rv = 0; | 3499 | int rv = 0; |
3512 | 3500 | ||
3501 | /* This function cannot be called concurrently, as its called | ||
3502 | * under pktgen_thread_lock mutex, but it can run from | ||
3503 | * userspace on another CPU than the kthread. The if_lock() | ||
3504 | * is used here to sync with concurrent instances of | ||
3505 | * _rem_dev_from_if_list() invoked via kthread, which is also | ||
3506 | * updating the if_list */ | ||
3513 | if_lock(t); | 3507 | if_lock(t); |
3514 | 3508 | ||
3515 | if (pkt_dev->pg_thread) { | 3509 | if (pkt_dev->pg_thread) { |
@@ -3518,9 +3512,9 @@ static int add_dev_to_thread(struct pktgen_thread *t, | |||
3518 | goto out; | 3512 | goto out; |
3519 | } | 3513 | } |
3520 | 3514 | ||
3521 | list_add(&pkt_dev->list, &t->if_list); | ||
3522 | pkt_dev->pg_thread = t; | ||
3523 | pkt_dev->running = 0; | 3515 | pkt_dev->running = 0; |
3516 | pkt_dev->pg_thread = t; | ||
3517 | list_add_rcu(&pkt_dev->list, &t->if_list); | ||
3524 | 3518 | ||
3525 | out: | 3519 | out: |
3526 | if_unlock(t); | 3520 | if_unlock(t); |
@@ -3675,11 +3669,13 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t, | |||
3675 | struct list_head *q, *n; | 3669 | struct list_head *q, *n; |
3676 | struct pktgen_dev *p; | 3670 | struct pktgen_dev *p; |
3677 | 3671 | ||
3672 | if_lock(t); | ||
3678 | list_for_each_safe(q, n, &t->if_list) { | 3673 | list_for_each_safe(q, n, &t->if_list) { |
3679 | p = list_entry(q, struct pktgen_dev, list); | 3674 | p = list_entry(q, struct pktgen_dev, list); |
3680 | if (p == pkt_dev) | 3675 | if (p == pkt_dev) |
3681 | list_del(&p->list); | 3676 | list_del_rcu(&p->list); |
3682 | } | 3677 | } |
3678 | if_unlock(t); | ||
3683 | } | 3679 | } |
3684 | 3680 | ||
3685 | static int pktgen_remove_device(struct pktgen_thread *t, | 3681 | static int pktgen_remove_device(struct pktgen_thread *t, |
@@ -3699,20 +3695,22 @@ static int pktgen_remove_device(struct pktgen_thread *t, | |||
3699 | pkt_dev->odev = NULL; | 3695 | pkt_dev->odev = NULL; |
3700 | } | 3696 | } |
3701 | 3697 | ||
3702 | /* And update the thread if_list */ | 3698 | /* Remove proc before if_list entry, because add_device uses |
3703 | 3699 | * list to determine if interface already exist, avoid race | |
3704 | _rem_dev_from_if_list(t, pkt_dev); | 3700 | * with proc_create_data() */ |
3705 | |||
3706 | if (pkt_dev->entry) | 3701 | if (pkt_dev->entry) |
3707 | proc_remove(pkt_dev->entry); | 3702 | proc_remove(pkt_dev->entry); |
3708 | 3703 | ||
3704 | /* And update the thread if_list */ | ||
3705 | _rem_dev_from_if_list(t, pkt_dev); | ||
3706 | |||
3709 | #ifdef CONFIG_XFRM | 3707 | #ifdef CONFIG_XFRM |
3710 | free_SAs(pkt_dev); | 3708 | free_SAs(pkt_dev); |
3711 | #endif | 3709 | #endif |
3712 | vfree(pkt_dev->flows); | 3710 | vfree(pkt_dev->flows); |
3713 | if (pkt_dev->page) | 3711 | if (pkt_dev->page) |
3714 | put_page(pkt_dev->page); | 3712 | put_page(pkt_dev->page); |
3715 | kfree(pkt_dev); | 3713 | kfree_rcu(pkt_dev, rcu); |
3716 | return 0; | 3714 | return 0; |
3717 | } | 3715 | } |
3718 | 3716 | ||
@@ -3812,6 +3810,7 @@ static void __exit pg_cleanup(void) | |||
3812 | { | 3810 | { |
3813 | unregister_netdevice_notifier(&pktgen_notifier_block); | 3811 | unregister_netdevice_notifier(&pktgen_notifier_block); |
3814 | unregister_pernet_subsys(&pg_net_ops); | 3812 | unregister_pernet_subsys(&pg_net_ops); |
3813 | /* Don't need rcu_barrier() due to use of kfree_rcu() */ | ||
3815 | } | 3814 | } |
3816 | 3815 | ||
3817 | module_init(pg_init); | 3816 | module_init(pg_init); |