diff options
Diffstat (limited to 'kernel/smp.c')
-rw-r--r-- | kernel/smp.c | 83 |
1 files changed, 44 insertions, 39 deletions
diff --git a/kernel/smp.c b/kernel/smp.c index bbedbb7efe32..6ecf4b9895d4 100644 --- a/kernel/smp.c +++ b/kernel/smp.c | |||
@@ -74,9 +74,16 @@ static void generic_exec_single(int cpu, struct call_single_data *data) | |||
74 | spin_unlock_irqrestore(&dst->lock, flags); | 74 | spin_unlock_irqrestore(&dst->lock, flags); |
75 | 75 | ||
76 | /* | 76 | /* |
77 | * Make the list addition visible before sending the ipi. | 77 | * The list addition should be visible before sending the IPI |
78 | * handler locks the list to pull the entry off it because of | ||
79 | * normal cache coherency rules implied by spinlocks. | ||
80 | * | ||
81 | * If IPIs can go out of order to the cache coherency protocol | ||
82 | * in an architecture, sufficient synchronisation should be added | ||
83 | * to arch code to make it appear to obey cache coherency WRT | ||
84 | * locking and barrier primitives. Generic code isn't really equipped | ||
85 | * to do the right thing... | ||
78 | */ | 86 | */ |
79 | smp_mb(); | ||
80 | 87 | ||
81 | if (ipi) | 88 | if (ipi) |
82 | arch_send_call_function_single_ipi(cpu); | 89 | arch_send_call_function_single_ipi(cpu); |
@@ -104,6 +111,14 @@ void generic_smp_call_function_interrupt(void) | |||
104 | int cpu = get_cpu(); | 111 | int cpu = get_cpu(); |
105 | 112 | ||
106 | /* | 113 | /* |
114 | * Ensure entry is visible on call_function_queue after we have | ||
115 | * entered the IPI. See comment in smp_call_function_many. | ||
116 | * If we don't have this, then we may miss an entry on the list | ||
117 | * and never get another IPI to process it. | ||
118 | */ | ||
119 | smp_mb(); | ||
120 | |||
121 | /* | ||
107 | * It's ok to use list_for_each_rcu() here even though we may delete | 122 | * It's ok to use list_for_each_rcu() here even though we may delete |
108 | * 'pos', since list_del_rcu() doesn't clear ->next | 123 | * 'pos', since list_del_rcu() doesn't clear ->next |
109 | */ | 124 | */ |
@@ -154,49 +169,37 @@ void generic_smp_call_function_single_interrupt(void) | |||
154 | { | 169 | { |
155 | struct call_single_queue *q = &__get_cpu_var(call_single_queue); | 170 | struct call_single_queue *q = &__get_cpu_var(call_single_queue); |
156 | LIST_HEAD(list); | 171 | LIST_HEAD(list); |
172 | unsigned int data_flags; | ||
157 | 173 | ||
158 | /* | 174 | spin_lock(&q->lock); |
159 | * Need to see other stores to list head for checking whether | 175 | list_replace_init(&q->list, &list); |
160 | * list is empty without holding q->lock | 176 | spin_unlock(&q->lock); |
161 | */ | ||
162 | smp_read_barrier_depends(); | ||
163 | while (!list_empty(&q->list)) { | ||
164 | unsigned int data_flags; | ||
165 | |||
166 | spin_lock(&q->lock); | ||
167 | list_replace_init(&q->list, &list); | ||
168 | spin_unlock(&q->lock); | ||
169 | 177 | ||
170 | while (!list_empty(&list)) { | 178 | while (!list_empty(&list)) { |
171 | struct call_single_data *data; | 179 | struct call_single_data *data; |
172 | 180 | ||
173 | data = list_entry(list.next, struct call_single_data, | 181 | data = list_entry(list.next, struct call_single_data, |
174 | list); | 182 | list); |
175 | list_del(&data->list); | 183 | list_del(&data->list); |
176 | 184 | ||
177 | /* | ||
178 | * 'data' can be invalid after this call if | ||
179 | * flags == 0 (when called through | ||
180 | * generic_exec_single(), so save them away before | ||
181 | * making the call. | ||
182 | */ | ||
183 | data_flags = data->flags; | ||
184 | |||
185 | data->func(data->info); | ||
186 | |||
187 | if (data_flags & CSD_FLAG_WAIT) { | ||
188 | smp_wmb(); | ||
189 | data->flags &= ~CSD_FLAG_WAIT; | ||
190 | } else if (data_flags & CSD_FLAG_LOCK) { | ||
191 | smp_wmb(); | ||
192 | data->flags &= ~CSD_FLAG_LOCK; | ||
193 | } else if (data_flags & CSD_FLAG_ALLOC) | ||
194 | kfree(data); | ||
195 | } | ||
196 | /* | 185 | /* |
197 | * See comment on outer loop | 186 | * 'data' can be invalid after this call if |
187 | * flags == 0 (when called through | ||
188 | * generic_exec_single(), so save them away before | ||
189 | * making the call. | ||
198 | */ | 190 | */ |
199 | smp_read_barrier_depends(); | 191 | data_flags = data->flags; |
192 | |||
193 | data->func(data->info); | ||
194 | |||
195 | if (data_flags & CSD_FLAG_WAIT) { | ||
196 | smp_wmb(); | ||
197 | data->flags &= ~CSD_FLAG_WAIT; | ||
198 | } else if (data_flags & CSD_FLAG_LOCK) { | ||
199 | smp_wmb(); | ||
200 | data->flags &= ~CSD_FLAG_LOCK; | ||
201 | } else if (data_flags & CSD_FLAG_ALLOC) | ||
202 | kfree(data); | ||
200 | } | 203 | } |
201 | } | 204 | } |
202 | 205 | ||
@@ -375,6 +378,8 @@ void smp_call_function_many(const struct cpumask *mask, | |||
375 | 378 | ||
376 | /* | 379 | /* |
377 | * Make the list addition visible before sending the ipi. | 380 | * Make the list addition visible before sending the ipi. |
381 | * (IPIs must obey or appear to obey normal Linux cache coherency | ||
382 | * rules -- see comment in generic_exec_single). | ||
378 | */ | 383 | */ |
379 | smp_mb(); | 384 | smp_mb(); |
380 | 385 | ||