diff options
-rw-r--r-- | net/core/pktgen.c | 101 |
1 files changed, 52 insertions, 49 deletions
diff --git a/net/core/pktgen.c b/net/core/pktgen.c index b61f553689b6..5b05e364b8ae 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 | ||
@@ -1737,14 +1739,14 @@ static int pktgen_thread_show(struct seq_file *seq, void *v) | |||
1737 | 1739 | ||
1738 | seq_puts(seq, "Running: "); | 1740 | seq_puts(seq, "Running: "); |
1739 | 1741 | ||
1740 | if_lock(t); | 1742 | rcu_read_lock(); |
1741 | list_for_each_entry(pkt_dev, &t->if_list, list) | 1743 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) |
1742 | if (pkt_dev->running) | 1744 | if (pkt_dev->running) |
1743 | seq_printf(seq, "%s ", pkt_dev->odevname); | 1745 | seq_printf(seq, "%s ", pkt_dev->odevname); |
1744 | 1746 | ||
1745 | seq_puts(seq, "\nStopped: "); | 1747 | seq_puts(seq, "\nStopped: "); |
1746 | 1748 | ||
1747 | list_for_each_entry(pkt_dev, &t->if_list, list) | 1749 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) |
1748 | if (!pkt_dev->running) | 1750 | if (!pkt_dev->running) |
1749 | seq_printf(seq, "%s ", pkt_dev->odevname); | 1751 | seq_printf(seq, "%s ", pkt_dev->odevname); |
1750 | 1752 | ||
@@ -1753,7 +1755,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v) | |||
1753 | else | 1755 | else |
1754 | seq_puts(seq, "\nResult: NA\n"); | 1756 | seq_puts(seq, "\nResult: NA\n"); |
1755 | 1757 | ||
1756 | if_unlock(t); | 1758 | rcu_read_unlock(); |
1757 | 1759 | ||
1758 | return 0; | 1760 | return 0; |
1759 | } | 1761 | } |
@@ -1878,10 +1880,8 @@ static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn, | |||
1878 | pkt_dev = pktgen_find_dev(t, ifname, exact); | 1880 | pkt_dev = pktgen_find_dev(t, ifname, exact); |
1879 | if (pkt_dev) { | 1881 | if (pkt_dev) { |
1880 | if (remove) { | 1882 | if (remove) { |
1881 | if_lock(t); | ||
1882 | pkt_dev->removal_mark = 1; | 1883 | pkt_dev->removal_mark = 1; |
1883 | t->control |= T_REMDEV; | 1884 | t->control |= T_REMDEV; |
1884 | if_unlock(t); | ||
1885 | } | 1885 | } |
1886 | break; | 1886 | break; |
1887 | } | 1887 | } |
@@ -1931,7 +1931,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) { | 1931 | list_for_each_entry(t, &pn->pktgen_threads, th_list) { |
1932 | struct pktgen_dev *pkt_dev; | 1932 | struct pktgen_dev *pkt_dev; |
1933 | 1933 | ||
1934 | list_for_each_entry(pkt_dev, &t->if_list, list) { | 1934 | rcu_read_lock(); |
1935 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) { | ||
1935 | if (pkt_dev->odev != dev) | 1936 | if (pkt_dev->odev != dev) |
1936 | continue; | 1937 | continue; |
1937 | 1938 | ||
@@ -1946,6 +1947,7 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d | |||
1946 | dev->name); | 1947 | dev->name); |
1947 | break; | 1948 | break; |
1948 | } | 1949 | } |
1950 | rcu_read_unlock(); | ||
1949 | } | 1951 | } |
1950 | } | 1952 | } |
1951 | 1953 | ||
@@ -2997,8 +2999,8 @@ static void pktgen_run(struct pktgen_thread *t) | |||
2997 | 2999 | ||
2998 | func_enter(); | 3000 | func_enter(); |
2999 | 3001 | ||
3000 | if_lock(t); | 3002 | rcu_read_lock(); |
3001 | list_for_each_entry(pkt_dev, &t->if_list, list) { | 3003 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) { |
3002 | 3004 | ||
3003 | /* | 3005 | /* |
3004 | * setup odev and create initial packet. | 3006 | * setup odev and create initial packet. |
@@ -3007,18 +3009,18 @@ static void pktgen_run(struct pktgen_thread *t) | |||
3007 | 3009 | ||
3008 | if (pkt_dev->odev) { | 3010 | if (pkt_dev->odev) { |
3009 | pktgen_clear_counters(pkt_dev); | 3011 | pktgen_clear_counters(pkt_dev); |
3010 | pkt_dev->running = 1; /* Cranke yeself! */ | ||
3011 | pkt_dev->skb = NULL; | 3012 | pkt_dev->skb = NULL; |
3012 | pkt_dev->started_at = pkt_dev->next_tx = ktime_get(); | 3013 | pkt_dev->started_at = pkt_dev->next_tx = ktime_get(); |
3013 | 3014 | ||
3014 | set_pkt_overhead(pkt_dev); | 3015 | set_pkt_overhead(pkt_dev); |
3015 | 3016 | ||
3016 | strcpy(pkt_dev->result, "Starting"); | 3017 | strcpy(pkt_dev->result, "Starting"); |
3018 | pkt_dev->running = 1; /* Cranke yeself! */ | ||
3017 | started++; | 3019 | started++; |
3018 | } else | 3020 | } else |
3019 | strcpy(pkt_dev->result, "Error starting"); | 3021 | strcpy(pkt_dev->result, "Error starting"); |
3020 | } | 3022 | } |
3021 | if_unlock(t); | 3023 | rcu_read_unlock(); |
3022 | if (started) | 3024 | if (started) |
3023 | t->control &= ~(T_STOP); | 3025 | t->control &= ~(T_STOP); |
3024 | } | 3026 | } |
@@ -3041,27 +3043,25 @@ static int thread_is_running(const struct pktgen_thread *t) | |||
3041 | { | 3043 | { |
3042 | const struct pktgen_dev *pkt_dev; | 3044 | const struct pktgen_dev *pkt_dev; |
3043 | 3045 | ||
3044 | list_for_each_entry(pkt_dev, &t->if_list, list) | 3046 | rcu_read_lock(); |
3045 | if (pkt_dev->running) | 3047 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) |
3048 | if (pkt_dev->running) { | ||
3049 | rcu_read_unlock(); | ||
3046 | return 1; | 3050 | return 1; |
3051 | } | ||
3052 | rcu_read_unlock(); | ||
3047 | return 0; | 3053 | return 0; |
3048 | } | 3054 | } |
3049 | 3055 | ||
3050 | static int pktgen_wait_thread_run(struct pktgen_thread *t) | 3056 | static int pktgen_wait_thread_run(struct pktgen_thread *t) |
3051 | { | 3057 | { |
3052 | if_lock(t); | ||
3053 | |||
3054 | while (thread_is_running(t)) { | 3058 | while (thread_is_running(t)) { |
3055 | 3059 | ||
3056 | if_unlock(t); | ||
3057 | |||
3058 | msleep_interruptible(100); | 3060 | msleep_interruptible(100); |
3059 | 3061 | ||
3060 | if (signal_pending(current)) | 3062 | if (signal_pending(current)) |
3061 | goto signal; | 3063 | goto signal; |
3062 | if_lock(t); | ||
3063 | } | 3064 | } |
3064 | if_unlock(t); | ||
3065 | return 1; | 3065 | return 1; |
3066 | signal: | 3066 | signal: |
3067 | return 0; | 3067 | return 0; |
@@ -3166,10 +3166,10 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev) | |||
3166 | return -EINVAL; | 3166 | return -EINVAL; |
3167 | } | 3167 | } |
3168 | 3168 | ||
3169 | pkt_dev->running = 0; | ||
3169 | kfree_skb(pkt_dev->skb); | 3170 | kfree_skb(pkt_dev->skb); |
3170 | pkt_dev->skb = NULL; | 3171 | pkt_dev->skb = NULL; |
3171 | pkt_dev->stopped_at = ktime_get(); | 3172 | pkt_dev->stopped_at = ktime_get(); |
3172 | pkt_dev->running = 0; | ||
3173 | 3173 | ||
3174 | show_results(pkt_dev, nr_frags); | 3174 | show_results(pkt_dev, nr_frags); |
3175 | 3175 | ||
@@ -3180,9 +3180,8 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t) | |||
3180 | { | 3180 | { |
3181 | struct pktgen_dev *pkt_dev, *best = NULL; | 3181 | struct pktgen_dev *pkt_dev, *best = NULL; |
3182 | 3182 | ||
3183 | if_lock(t); | 3183 | rcu_read_lock(); |
3184 | 3184 | 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) | 3185 | if (!pkt_dev->running) |
3187 | continue; | 3186 | continue; |
3188 | if (best == NULL) | 3187 | if (best == NULL) |
@@ -3190,7 +3189,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) | 3189 | else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0) |
3191 | best = pkt_dev; | 3190 | best = pkt_dev; |
3192 | } | 3191 | } |
3193 | if_unlock(t); | 3192 | rcu_read_unlock(); |
3193 | |||
3194 | return best; | 3194 | return best; |
3195 | } | 3195 | } |
3196 | 3196 | ||
@@ -3200,13 +3200,13 @@ static void pktgen_stop(struct pktgen_thread *t) | |||
3200 | 3200 | ||
3201 | func_enter(); | 3201 | func_enter(); |
3202 | 3202 | ||
3203 | if_lock(t); | 3203 | rcu_read_lock(); |
3204 | 3204 | ||
3205 | list_for_each_entry(pkt_dev, &t->if_list, list) { | 3205 | list_for_each_entry_rcu(pkt_dev, &t->if_list, list) { |
3206 | pktgen_stop_device(pkt_dev); | 3206 | pktgen_stop_device(pkt_dev); |
3207 | } | 3207 | } |
3208 | 3208 | ||
3209 | if_unlock(t); | 3209 | rcu_read_unlock(); |
3210 | } | 3210 | } |
3211 | 3211 | ||
3212 | /* | 3212 | /* |
@@ -3220,8 +3220,6 @@ static void pktgen_rem_one_if(struct pktgen_thread *t) | |||
3220 | 3220 | ||
3221 | func_enter(); | 3221 | func_enter(); |
3222 | 3222 | ||
3223 | if_lock(t); | ||
3224 | |||
3225 | list_for_each_safe(q, n, &t->if_list) { | 3223 | list_for_each_safe(q, n, &t->if_list) { |
3226 | cur = list_entry(q, struct pktgen_dev, list); | 3224 | cur = list_entry(q, struct pktgen_dev, list); |
3227 | 3225 | ||
@@ -3235,8 +3233,6 @@ static void pktgen_rem_one_if(struct pktgen_thread *t) | |||
3235 | 3233 | ||
3236 | break; | 3234 | break; |
3237 | } | 3235 | } |
3238 | |||
3239 | if_unlock(t); | ||
3240 | } | 3236 | } |
3241 | 3237 | ||
3242 | static void pktgen_rem_all_ifs(struct pktgen_thread *t) | 3238 | static void pktgen_rem_all_ifs(struct pktgen_thread *t) |
@@ -3248,8 +3244,6 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t) | |||
3248 | 3244 | ||
3249 | /* Remove all devices, free mem */ | 3245 | /* Remove all devices, free mem */ |
3250 | 3246 | ||
3251 | if_lock(t); | ||
3252 | |||
3253 | list_for_each_safe(q, n, &t->if_list) { | 3247 | list_for_each_safe(q, n, &t->if_list) { |
3254 | cur = list_entry(q, struct pktgen_dev, list); | 3248 | cur = list_entry(q, struct pktgen_dev, list); |
3255 | 3249 | ||
@@ -3258,8 +3252,6 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t) | |||
3258 | 3252 | ||
3259 | pktgen_remove_device(t, cur); | 3253 | pktgen_remove_device(t, cur); |
3260 | } | 3254 | } |
3261 | |||
3262 | if_unlock(t); | ||
3263 | } | 3255 | } |
3264 | 3256 | ||
3265 | static void pktgen_rem_thread(struct pktgen_thread *t) | 3257 | static void pktgen_rem_thread(struct pktgen_thread *t) |
@@ -3482,8 +3474,8 @@ static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t, | |||
3482 | struct pktgen_dev *p, *pkt_dev = NULL; | 3474 | struct pktgen_dev *p, *pkt_dev = NULL; |
3483 | size_t len = strlen(ifname); | 3475 | size_t len = strlen(ifname); |
3484 | 3476 | ||
3485 | if_lock(t); | 3477 | rcu_read_lock(); |
3486 | list_for_each_entry(p, &t->if_list, list) | 3478 | list_for_each_entry_rcu(p, &t->if_list, list) |
3487 | if (strncmp(p->odevname, ifname, len) == 0) { | 3479 | if (strncmp(p->odevname, ifname, len) == 0) { |
3488 | if (p->odevname[len]) { | 3480 | if (p->odevname[len]) { |
3489 | if (exact || p->odevname[len] != '@') | 3481 | if (exact || p->odevname[len] != '@') |
@@ -3493,7 +3485,7 @@ static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t, | |||
3493 | break; | 3485 | break; |
3494 | } | 3486 | } |
3495 | 3487 | ||
3496 | if_unlock(t); | 3488 | rcu_read_unlock(); |
3497 | pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev); | 3489 | pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev); |
3498 | return pkt_dev; | 3490 | return pkt_dev; |
3499 | } | 3491 | } |
@@ -3507,6 +3499,12 @@ static int add_dev_to_thread(struct pktgen_thread *t, | |||
3507 | { | 3499 | { |
3508 | int rv = 0; | 3500 | int rv = 0; |
3509 | 3501 | ||
3502 | /* This function cannot be called concurrently, as its called | ||
3503 | * under pktgen_thread_lock mutex, but it can run from | ||
3504 | * userspace on another CPU than the kthread. The if_lock() | ||
3505 | * is used here to sync with concurrent instances of | ||
3506 | * _rem_dev_from_if_list() invoked via kthread, which is also | ||
3507 | * updating the if_list */ | ||
3510 | if_lock(t); | 3508 | if_lock(t); |
3511 | 3509 | ||
3512 | if (pkt_dev->pg_thread) { | 3510 | if (pkt_dev->pg_thread) { |
@@ -3515,9 +3513,9 @@ static int add_dev_to_thread(struct pktgen_thread *t, | |||
3515 | goto out; | 3513 | goto out; |
3516 | } | 3514 | } |
3517 | 3515 | ||
3518 | list_add(&pkt_dev->list, &t->if_list); | ||
3519 | pkt_dev->pg_thread = t; | ||
3520 | pkt_dev->running = 0; | 3516 | pkt_dev->running = 0; |
3517 | pkt_dev->pg_thread = t; | ||
3518 | list_add_rcu(&pkt_dev->list, &t->if_list); | ||
3521 | 3519 | ||
3522 | out: | 3520 | out: |
3523 | if_unlock(t); | 3521 | if_unlock(t); |
@@ -3672,11 +3670,13 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t, | |||
3672 | struct list_head *q, *n; | 3670 | struct list_head *q, *n; |
3673 | struct pktgen_dev *p; | 3671 | struct pktgen_dev *p; |
3674 | 3672 | ||
3673 | if_lock(t); | ||
3675 | list_for_each_safe(q, n, &t->if_list) { | 3674 | list_for_each_safe(q, n, &t->if_list) { |
3676 | p = list_entry(q, struct pktgen_dev, list); | 3675 | p = list_entry(q, struct pktgen_dev, list); |
3677 | if (p == pkt_dev) | 3676 | if (p == pkt_dev) |
3678 | list_del(&p->list); | 3677 | list_del_rcu(&p->list); |
3679 | } | 3678 | } |
3679 | if_unlock(t); | ||
3680 | } | 3680 | } |
3681 | 3681 | ||
3682 | static int pktgen_remove_device(struct pktgen_thread *t, | 3682 | static int pktgen_remove_device(struct pktgen_thread *t, |
@@ -3696,20 +3696,22 @@ static int pktgen_remove_device(struct pktgen_thread *t, | |||
3696 | pkt_dev->odev = NULL; | 3696 | pkt_dev->odev = NULL; |
3697 | } | 3697 | } |
3698 | 3698 | ||
3699 | /* And update the thread if_list */ | 3699 | /* Remove proc before if_list entry, because add_device uses |
3700 | 3700 | * list to determine if interface already exist, avoid race | |
3701 | _rem_dev_from_if_list(t, pkt_dev); | 3701 | * with proc_create_data() */ |
3702 | |||
3703 | if (pkt_dev->entry) | 3702 | if (pkt_dev->entry) |
3704 | proc_remove(pkt_dev->entry); | 3703 | proc_remove(pkt_dev->entry); |
3705 | 3704 | ||
3705 | /* And update the thread if_list */ | ||
3706 | _rem_dev_from_if_list(t, pkt_dev); | ||
3707 | |||
3706 | #ifdef CONFIG_XFRM | 3708 | #ifdef CONFIG_XFRM |
3707 | free_SAs(pkt_dev); | 3709 | free_SAs(pkt_dev); |
3708 | #endif | 3710 | #endif |
3709 | vfree(pkt_dev->flows); | 3711 | vfree(pkt_dev->flows); |
3710 | if (pkt_dev->page) | 3712 | if (pkt_dev->page) |
3711 | put_page(pkt_dev->page); | 3713 | put_page(pkt_dev->page); |
3712 | kfree(pkt_dev); | 3714 | kfree_rcu(pkt_dev, rcu); |
3713 | return 0; | 3715 | return 0; |
3714 | } | 3716 | } |
3715 | 3717 | ||
@@ -3809,6 +3811,7 @@ static void __exit pg_cleanup(void) | |||
3809 | { | 3811 | { |
3810 | unregister_netdevice_notifier(&pktgen_notifier_block); | 3812 | unregister_netdevice_notifier(&pktgen_notifier_block); |
3811 | unregister_pernet_subsys(&pg_net_ops); | 3813 | unregister_pernet_subsys(&pg_net_ops); |
3814 | /* Don't need rcu_barrier() due to use of kfree_rcu() */ | ||
3812 | } | 3815 | } |
3813 | 3816 | ||
3814 | module_init(pg_init); | 3817 | module_init(pg_init); |