diff options
| -rw-r--r-- | include/linux/rcuclassic.h | 26 | ||||
| -rw-r--r-- | kernel/rcuclassic.c | 157 |
2 files changed, 114 insertions, 69 deletions
diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h index c847e59c6006..04c728147be0 100644 --- a/include/linux/rcuclassic.h +++ b/include/linux/rcuclassic.h | |||
| @@ -66,11 +66,7 @@ static inline int rcu_batch_after(long a, long b) | |||
| 66 | return (a - b) > 0; | 66 | return (a - b) > 0; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | /* | 69 | /* Per-CPU data for Read-Copy UPdate. */ |
| 70 | * Per-CPU data for Read-Copy UPdate. | ||
| 71 | * nxtlist - new callbacks are added here | ||
| 72 | * curlist - current batch for which quiescent cycle started if any | ||
| 73 | */ | ||
| 74 | struct rcu_data { | 70 | struct rcu_data { |
| 75 | /* 1) quiescent state handling : */ | 71 | /* 1) quiescent state handling : */ |
| 76 | long quiescbatch; /* Batch # for grace period */ | 72 | long quiescbatch; /* Batch # for grace period */ |
| @@ -78,12 +74,24 @@ struct rcu_data { | |||
| 78 | int qs_pending; /* core waits for quiesc state */ | 74 | int qs_pending; /* core waits for quiesc state */ |
| 79 | 75 | ||
| 80 | /* 2) batch handling */ | 76 | /* 2) batch handling */ |
| 81 | long batch; /* Batch # for current RCU batch */ | 77 | /* |
| 78 | * if nxtlist is not NULL, then: | ||
| 79 | * batch: | ||
| 80 | * The batch # for the last entry of nxtlist | ||
| 81 | * [*nxttail[1], NULL = *nxttail[2]): | ||
| 82 | * Entries that batch # <= batch | ||
| 83 | * [*nxttail[0], *nxttail[1]): | ||
| 84 | * Entries that batch # <= batch - 1 | ||
| 85 | * [nxtlist, *nxttail[0]): | ||
| 86 | * Entries that batch # <= batch - 2 | ||
| 87 | * The grace period for these entries has completed, and | ||
| 88 | * the other grace-period-completed entries may be moved | ||
| 89 | * here temporarily in rcu_process_callbacks(). | ||
| 90 | */ | ||
| 91 | long batch; | ||
| 82 | struct rcu_head *nxtlist; | 92 | struct rcu_head *nxtlist; |
| 83 | struct rcu_head **nxttail; | 93 | struct rcu_head **nxttail[3]; |
| 84 | long qlen; /* # of queued callbacks */ | 94 | long qlen; /* # of queued callbacks */ |
| 85 | struct rcu_head *curlist; | ||
| 86 | struct rcu_head **curtail; | ||
| 87 | struct rcu_head *donelist; | 95 | struct rcu_head *donelist; |
| 88 | struct rcu_head **donetail; | 96 | struct rcu_head **donetail; |
| 89 | long blimit; /* Upper limit on a processed batch */ | 97 | long blimit; /* Upper limit on a processed batch */ |
diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index 03726eb95193..d3553ee55f64 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c | |||
| @@ -120,6 +120,43 @@ static inline void force_quiescent_state(struct rcu_data *rdp, | |||
| 120 | } | 120 | } |
| 121 | #endif | 121 | #endif |
| 122 | 122 | ||
| 123 | static void __call_rcu(struct rcu_head *head, struct rcu_ctrlblk *rcp, | ||
| 124 | struct rcu_data *rdp) | ||
| 125 | { | ||
| 126 | long batch; | ||
| 127 | smp_mb(); /* reads the most recently updated value of rcu->cur. */ | ||
| 128 | |||
| 129 | /* | ||
| 130 | * Determine the batch number of this callback. | ||
| 131 | * | ||
| 132 | * Using ACCESS_ONCE to avoid the following error when gcc eliminates | ||
| 133 | * local variable "batch" and emits codes like this: | ||
| 134 | * 1) rdp->batch = rcp->cur + 1 # gets old value | ||
| 135 | * ...... | ||
| 136 | * 2)rcu_batch_after(rcp->cur + 1, rdp->batch) # gets new value | ||
| 137 | * then [*nxttail[0], *nxttail[1]) may contain callbacks | ||
| 138 | * that batch# = rdp->batch, see the comment of struct rcu_data. | ||
| 139 | */ | ||
| 140 | batch = ACCESS_ONCE(rcp->cur) + 1; | ||
| 141 | |||
| 142 | if (rdp->nxtlist && rcu_batch_after(batch, rdp->batch)) { | ||
| 143 | /* process callbacks */ | ||
| 144 | rdp->nxttail[0] = rdp->nxttail[1]; | ||
| 145 | rdp->nxttail[1] = rdp->nxttail[2]; | ||
| 146 | if (rcu_batch_after(batch - 1, rdp->batch)) | ||
| 147 | rdp->nxttail[0] = rdp->nxttail[2]; | ||
| 148 | } | ||
| 149 | |||
| 150 | rdp->batch = batch; | ||
| 151 | *rdp->nxttail[2] = head; | ||
| 152 | rdp->nxttail[2] = &head->next; | ||
| 153 | |||
| 154 | if (unlikely(++rdp->qlen > qhimark)) { | ||
| 155 | rdp->blimit = INT_MAX; | ||
| 156 | force_quiescent_state(rdp, &rcu_ctrlblk); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 123 | /** | 160 | /** |
| 124 | * call_rcu - Queue an RCU callback for invocation after a grace period. | 161 | * call_rcu - Queue an RCU callback for invocation after a grace period. |
| 125 | * @head: structure to be used for queueing the RCU updates. | 162 | * @head: structure to be used for queueing the RCU updates. |
| @@ -135,18 +172,11 @@ void call_rcu(struct rcu_head *head, | |||
| 135 | void (*func)(struct rcu_head *rcu)) | 172 | void (*func)(struct rcu_head *rcu)) |
| 136 | { | 173 | { |
| 137 | unsigned long flags; | 174 | unsigned long flags; |
| 138 | struct rcu_data *rdp; | ||
| 139 | 175 | ||
| 140 | head->func = func; | 176 | head->func = func; |
| 141 | head->next = NULL; | 177 | head->next = NULL; |
| 142 | local_irq_save(flags); | 178 | local_irq_save(flags); |
| 143 | rdp = &__get_cpu_var(rcu_data); | 179 | __call_rcu(head, &rcu_ctrlblk, &__get_cpu_var(rcu_data)); |
| 144 | *rdp->nxttail = head; | ||
| 145 | rdp->nxttail = &head->next; | ||
| 146 | if (unlikely(++rdp->qlen > qhimark)) { | ||
| 147 | rdp->blimit = INT_MAX; | ||
| 148 | force_quiescent_state(rdp, &rcu_ctrlblk); | ||
| 149 | } | ||
| 150 | local_irq_restore(flags); | 180 | local_irq_restore(flags); |
| 151 | } | 181 | } |
| 152 | EXPORT_SYMBOL_GPL(call_rcu); | 182 | EXPORT_SYMBOL_GPL(call_rcu); |
| @@ -171,20 +201,11 @@ void call_rcu_bh(struct rcu_head *head, | |||
| 171 | void (*func)(struct rcu_head *rcu)) | 201 | void (*func)(struct rcu_head *rcu)) |
| 172 | { | 202 | { |
| 173 | unsigned long flags; | 203 | unsigned long flags; |
| 174 | struct rcu_data *rdp; | ||
| 175 | 204 | ||
| 176 | head->func = func; | 205 | head->func = func; |
| 177 | head->next = NULL; | 206 | head->next = NULL; |
| 178 | local_irq_save(flags); | 207 | local_irq_save(flags); |
| 179 | rdp = &__get_cpu_var(rcu_bh_data); | 208 | __call_rcu(head, &rcu_bh_ctrlblk, &__get_cpu_var(rcu_bh_data)); |
| 180 | *rdp->nxttail = head; | ||
| 181 | rdp->nxttail = &head->next; | ||
| 182 | |||
| 183 | if (unlikely(++rdp->qlen > qhimark)) { | ||
| 184 | rdp->blimit = INT_MAX; | ||
| 185 | force_quiescent_state(rdp, &rcu_bh_ctrlblk); | ||
| 186 | } | ||
| 187 | |||
| 188 | local_irq_restore(flags); | 209 | local_irq_restore(flags); |
| 189 | } | 210 | } |
| 190 | EXPORT_SYMBOL_GPL(call_rcu_bh); | 211 | EXPORT_SYMBOL_GPL(call_rcu_bh); |
| @@ -213,12 +234,6 @@ EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); | |||
| 213 | static inline void raise_rcu_softirq(void) | 234 | static inline void raise_rcu_softirq(void) |
| 214 | { | 235 | { |
| 215 | raise_softirq(RCU_SOFTIRQ); | 236 | raise_softirq(RCU_SOFTIRQ); |
| 216 | /* | ||
| 217 | * The smp_mb() here is required to ensure that this cpu's | ||
| 218 | * __rcu_process_callbacks() reads the most recently updated | ||
| 219 | * value of rcu->cur. | ||
| 220 | */ | ||
| 221 | smp_mb(); | ||
| 222 | } | 237 | } |
| 223 | 238 | ||
| 224 | /* | 239 | /* |
| @@ -360,13 +375,15 @@ static void rcu_check_quiescent_state(struct rcu_ctrlblk *rcp, | |||
| 360 | * which is dead and hence not processing interrupts. | 375 | * which is dead and hence not processing interrupts. |
| 361 | */ | 376 | */ |
| 362 | static void rcu_move_batch(struct rcu_data *this_rdp, struct rcu_head *list, | 377 | static void rcu_move_batch(struct rcu_data *this_rdp, struct rcu_head *list, |
| 363 | struct rcu_head **tail) | 378 | struct rcu_head **tail, long batch) |
| 364 | { | 379 | { |
| 365 | local_irq_disable(); | 380 | if (list) { |
| 366 | *this_rdp->nxttail = list; | 381 | local_irq_disable(); |
| 367 | if (list) | 382 | this_rdp->batch = batch; |
| 368 | this_rdp->nxttail = tail; | 383 | *this_rdp->nxttail[2] = list; |
| 369 | local_irq_enable(); | 384 | this_rdp->nxttail[2] = tail; |
| 385 | local_irq_enable(); | ||
| 386 | } | ||
| 370 | } | 387 | } |
| 371 | 388 | ||
| 372 | static void __rcu_offline_cpu(struct rcu_data *this_rdp, | 389 | static void __rcu_offline_cpu(struct rcu_data *this_rdp, |
| @@ -380,9 +397,9 @@ static void __rcu_offline_cpu(struct rcu_data *this_rdp, | |||
| 380 | if (rcp->cur != rcp->completed) | 397 | if (rcp->cur != rcp->completed) |
| 381 | cpu_quiet(rdp->cpu, rcp); | 398 | cpu_quiet(rdp->cpu, rcp); |
| 382 | spin_unlock_bh(&rcp->lock); | 399 | spin_unlock_bh(&rcp->lock); |
| 383 | rcu_move_batch(this_rdp, rdp->donelist, rdp->donetail); | 400 | /* spin_lock implies smp_mb() */ |
| 384 | rcu_move_batch(this_rdp, rdp->curlist, rdp->curtail); | 401 | rcu_move_batch(this_rdp, rdp->donelist, rdp->donetail, rcp->cur + 1); |
| 385 | rcu_move_batch(this_rdp, rdp->nxtlist, rdp->nxttail); | 402 | rcu_move_batch(this_rdp, rdp->nxtlist, rdp->nxttail[2], rcp->cur + 1); |
| 386 | 403 | ||
| 387 | local_irq_disable(); | 404 | local_irq_disable(); |
| 388 | this_rdp->qlen += rdp->qlen; | 405 | this_rdp->qlen += rdp->qlen; |
| @@ -416,27 +433,37 @@ static void rcu_offline_cpu(int cpu) | |||
| 416 | static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp, | 433 | static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp, |
| 417 | struct rcu_data *rdp) | 434 | struct rcu_data *rdp) |
| 418 | { | 435 | { |
| 419 | if (rdp->curlist && !rcu_batch_before(rcp->completed, rdp->batch)) { | 436 | if (rdp->nxtlist) { |
| 420 | *rdp->donetail = rdp->curlist; | ||
| 421 | rdp->donetail = rdp->curtail; | ||
| 422 | rdp->curlist = NULL; | ||
| 423 | rdp->curtail = &rdp->curlist; | ||
| 424 | } | ||
| 425 | |||
| 426 | if (rdp->nxtlist && !rdp->curlist) { | ||
| 427 | local_irq_disable(); | 437 | local_irq_disable(); |
| 428 | rdp->curlist = rdp->nxtlist; | ||
| 429 | rdp->curtail = rdp->nxttail; | ||
| 430 | rdp->nxtlist = NULL; | ||
| 431 | rdp->nxttail = &rdp->nxtlist; | ||
| 432 | local_irq_enable(); | ||
| 433 | 438 | ||
| 434 | /* | 439 | /* |
| 435 | * start the next batch of callbacks | 440 | * move the other grace-period-completed entries to |
| 441 | * [rdp->nxtlist, *rdp->nxttail[0]) temporarily | ||
| 442 | */ | ||
| 443 | if (!rcu_batch_before(rcp->completed, rdp->batch)) | ||
| 444 | rdp->nxttail[0] = rdp->nxttail[1] = rdp->nxttail[2]; | ||
| 445 | else if (!rcu_batch_before(rcp->completed, rdp->batch - 1)) | ||
| 446 | rdp->nxttail[0] = rdp->nxttail[1]; | ||
| 447 | |||
| 448 | /* | ||
| 449 | * the grace period for entries in | ||
| 450 | * [rdp->nxtlist, *rdp->nxttail[0]) has completed and | ||
| 451 | * move these entries to donelist | ||
| 436 | */ | 452 | */ |
| 453 | if (rdp->nxttail[0] != &rdp->nxtlist) { | ||
| 454 | *rdp->donetail = rdp->nxtlist; | ||
| 455 | rdp->donetail = rdp->nxttail[0]; | ||
| 456 | rdp->nxtlist = *rdp->nxttail[0]; | ||
| 457 | *rdp->donetail = NULL; | ||
| 458 | |||
| 459 | if (rdp->nxttail[1] == rdp->nxttail[0]) | ||
| 460 | rdp->nxttail[1] = &rdp->nxtlist; | ||
| 461 | if (rdp->nxttail[2] == rdp->nxttail[0]) | ||
| 462 | rdp->nxttail[2] = &rdp->nxtlist; | ||
| 463 | rdp->nxttail[0] = &rdp->nxtlist; | ||
| 464 | } | ||
| 437 | 465 | ||
| 438 | /* determine batch number */ | 466 | local_irq_enable(); |
| 439 | rdp->batch = rcp->cur + 1; | ||
| 440 | 467 | ||
| 441 | if (rcu_batch_after(rdp->batch, rcp->pending)) { | 468 | if (rcu_batch_after(rdp->batch, rcp->pending)) { |
| 442 | /* and start it/schedule start if it's a new batch */ | 469 | /* and start it/schedule start if it's a new batch */ |
| @@ -462,15 +489,26 @@ static void rcu_process_callbacks(struct softirq_action *unused) | |||
| 462 | 489 | ||
| 463 | static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) | 490 | static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) |
| 464 | { | 491 | { |
| 465 | /* This cpu has pending rcu entries and the grace period | 492 | if (rdp->nxtlist) { |
| 466 | * for them has completed. | 493 | /* |
| 467 | */ | 494 | * This cpu has pending rcu entries and the grace period |
| 468 | if (rdp->curlist && !rcu_batch_before(rcp->completed, rdp->batch)) | 495 | * for them has completed. |
| 469 | return 1; | 496 | */ |
| 497 | if (!rcu_batch_before(rcp->completed, rdp->batch)) | ||
| 498 | return 1; | ||
| 499 | if (!rcu_batch_before(rcp->completed, rdp->batch - 1) && | ||
| 500 | rdp->nxttail[0] != rdp->nxttail[1]) | ||
| 501 | return 1; | ||
| 502 | if (rdp->nxttail[0] != &rdp->nxtlist) | ||
| 503 | return 1; | ||
| 470 | 504 | ||
| 471 | /* This cpu has no pending entries, but there are new entries */ | 505 | /* |
| 472 | if (!rdp->curlist && rdp->nxtlist) | 506 | * This cpu has pending rcu entries and the new batch |
| 473 | return 1; | 507 | * for then hasn't been started nor scheduled start |
| 508 | */ | ||
| 509 | if (rcu_batch_after(rdp->batch, rcp->pending)) | ||
| 510 | return 1; | ||
| 511 | } | ||
| 474 | 512 | ||
| 475 | /* This cpu has finished callbacks to invoke */ | 513 | /* This cpu has finished callbacks to invoke */ |
| 476 | if (rdp->donelist) | 514 | if (rdp->donelist) |
| @@ -506,7 +544,7 @@ int rcu_needs_cpu(int cpu) | |||
| 506 | struct rcu_data *rdp = &per_cpu(rcu_data, cpu); | 544 | struct rcu_data *rdp = &per_cpu(rcu_data, cpu); |
| 507 | struct rcu_data *rdp_bh = &per_cpu(rcu_bh_data, cpu); | 545 | struct rcu_data *rdp_bh = &per_cpu(rcu_bh_data, cpu); |
| 508 | 546 | ||
| 509 | return (!!rdp->curlist || !!rdp_bh->curlist || rcu_pending(cpu)); | 547 | return !!rdp->nxtlist || !!rdp_bh->nxtlist || rcu_pending(cpu); |
| 510 | } | 548 | } |
| 511 | 549 | ||
| 512 | void rcu_check_callbacks(int cpu, int user) | 550 | void rcu_check_callbacks(int cpu, int user) |
| @@ -553,8 +591,7 @@ static void rcu_init_percpu_data(int cpu, struct rcu_ctrlblk *rcp, | |||
| 553 | struct rcu_data *rdp) | 591 | struct rcu_data *rdp) |
| 554 | { | 592 | { |
| 555 | memset(rdp, 0, sizeof(*rdp)); | 593 | memset(rdp, 0, sizeof(*rdp)); |
| 556 | rdp->curtail = &rdp->curlist; | 594 | rdp->nxttail[0] = rdp->nxttail[1] = rdp->nxttail[2] = &rdp->nxtlist; |
| 557 | rdp->nxttail = &rdp->nxtlist; | ||
| 558 | rdp->donetail = &rdp->donelist; | 595 | rdp->donetail = &rdp->donelist; |
| 559 | rdp->quiescbatch = rcp->completed; | 596 | rdp->quiescbatch = rcp->completed; |
| 560 | rdp->qs_pending = 0; | 597 | rdp->qs_pending = 0; |
