0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/cpu.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/kernel.h>
0013 #include <linux/types.h>
0014
0015 #include "rcu_segcblist.h"
0016
0017
0018 void rcu_cblist_init(struct rcu_cblist *rclp)
0019 {
0020 rclp->head = NULL;
0021 rclp->tail = &rclp->head;
0022 rclp->len = 0;
0023 }
0024
0025
0026
0027
0028 void rcu_cblist_enqueue(struct rcu_cblist *rclp, struct rcu_head *rhp)
0029 {
0030 *rclp->tail = rhp;
0031 rclp->tail = &rhp->next;
0032 WRITE_ONCE(rclp->len, rclp->len + 1);
0033 }
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 void rcu_cblist_flush_enqueue(struct rcu_cblist *drclp,
0044 struct rcu_cblist *srclp,
0045 struct rcu_head *rhp)
0046 {
0047 drclp->head = srclp->head;
0048 if (drclp->head)
0049 drclp->tail = srclp->tail;
0050 else
0051 drclp->tail = &drclp->head;
0052 drclp->len = srclp->len;
0053 if (!rhp) {
0054 rcu_cblist_init(srclp);
0055 } else {
0056 rhp->next = NULL;
0057 srclp->head = rhp;
0058 srclp->tail = &rhp->next;
0059 WRITE_ONCE(srclp->len, 1);
0060 }
0061 }
0062
0063
0064
0065
0066
0067 struct rcu_head *rcu_cblist_dequeue(struct rcu_cblist *rclp)
0068 {
0069 struct rcu_head *rhp;
0070
0071 rhp = rclp->head;
0072 if (!rhp)
0073 return NULL;
0074 rclp->len--;
0075 rclp->head = rhp->next;
0076 if (!rclp->head)
0077 rclp->tail = &rclp->head;
0078 return rhp;
0079 }
0080
0081
0082 static void rcu_segcblist_set_len(struct rcu_segcblist *rsclp, long v)
0083 {
0084 #ifdef CONFIG_RCU_NOCB_CPU
0085 atomic_long_set(&rsclp->len, v);
0086 #else
0087 WRITE_ONCE(rsclp->len, v);
0088 #endif
0089 }
0090
0091
0092 static long rcu_segcblist_get_seglen(struct rcu_segcblist *rsclp, int seg)
0093 {
0094 return READ_ONCE(rsclp->seglen[seg]);
0095 }
0096
0097
0098 long rcu_segcblist_n_segment_cbs(struct rcu_segcblist *rsclp)
0099 {
0100 long len = 0;
0101 int i;
0102
0103 for (i = RCU_DONE_TAIL; i < RCU_CBLIST_NSEGS; i++)
0104 len += rcu_segcblist_get_seglen(rsclp, i);
0105
0106 return len;
0107 }
0108
0109
0110 static void rcu_segcblist_set_seglen(struct rcu_segcblist *rsclp, int seg, long v)
0111 {
0112 WRITE_ONCE(rsclp->seglen[seg], v);
0113 }
0114
0115
0116 static void rcu_segcblist_add_seglen(struct rcu_segcblist *rsclp, int seg, long v)
0117 {
0118 WRITE_ONCE(rsclp->seglen[seg], rsclp->seglen[seg] + v);
0119 }
0120
0121
0122 static void rcu_segcblist_move_seglen(struct rcu_segcblist *rsclp, int from, int to)
0123 {
0124 long len;
0125
0126 if (from == to)
0127 return;
0128
0129 len = rcu_segcblist_get_seglen(rsclp, from);
0130 if (!len)
0131 return;
0132
0133 rcu_segcblist_add_seglen(rsclp, to, len);
0134 rcu_segcblist_set_seglen(rsclp, from, 0);
0135 }
0136
0137
0138 static void rcu_segcblist_inc_seglen(struct rcu_segcblist *rsclp, int seg)
0139 {
0140 rcu_segcblist_add_seglen(rsclp, seg, 1);
0141 }
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210 void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v)
0211 {
0212 #ifdef CONFIG_RCU_NOCB_CPU
0213 smp_mb__before_atomic();
0214 atomic_long_add(v, &rsclp->len);
0215 smp_mb__after_atomic();
0216 #else
0217 smp_mb();
0218 WRITE_ONCE(rsclp->len, rsclp->len + v);
0219 smp_mb();
0220 #endif
0221 }
0222
0223
0224
0225
0226
0227
0228
0229 void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp)
0230 {
0231 rcu_segcblist_add_len(rsclp, 1);
0232 }
0233
0234
0235
0236
0237 void rcu_segcblist_init(struct rcu_segcblist *rsclp)
0238 {
0239 int i;
0240
0241 BUILD_BUG_ON(RCU_NEXT_TAIL + 1 != ARRAY_SIZE(rsclp->gp_seq));
0242 BUILD_BUG_ON(ARRAY_SIZE(rsclp->tails) != ARRAY_SIZE(rsclp->gp_seq));
0243 rsclp->head = NULL;
0244 for (i = 0; i < RCU_CBLIST_NSEGS; i++) {
0245 rsclp->tails[i] = &rsclp->head;
0246 rcu_segcblist_set_seglen(rsclp, i, 0);
0247 }
0248 rcu_segcblist_set_len(rsclp, 0);
0249 rcu_segcblist_set_flags(rsclp, SEGCBLIST_ENABLED);
0250 }
0251
0252
0253
0254
0255
0256 void rcu_segcblist_disable(struct rcu_segcblist *rsclp)
0257 {
0258 WARN_ON_ONCE(!rcu_segcblist_empty(rsclp));
0259 WARN_ON_ONCE(rcu_segcblist_n_cbs(rsclp));
0260 rcu_segcblist_clear_flags(rsclp, SEGCBLIST_ENABLED);
0261 }
0262
0263
0264
0265
0266 void rcu_segcblist_offload(struct rcu_segcblist *rsclp, bool offload)
0267 {
0268 if (offload)
0269 rcu_segcblist_set_flags(rsclp, SEGCBLIST_LOCKING | SEGCBLIST_OFFLOADED);
0270 else
0271 rcu_segcblist_clear_flags(rsclp, SEGCBLIST_OFFLOADED);
0272 }
0273
0274
0275
0276
0277
0278 bool rcu_segcblist_ready_cbs(struct rcu_segcblist *rsclp)
0279 {
0280 return rcu_segcblist_is_enabled(rsclp) &&
0281 &rsclp->head != READ_ONCE(rsclp->tails[RCU_DONE_TAIL]);
0282 }
0283
0284
0285
0286
0287
0288 bool rcu_segcblist_pend_cbs(struct rcu_segcblist *rsclp)
0289 {
0290 return rcu_segcblist_is_enabled(rsclp) &&
0291 !rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL);
0292 }
0293
0294
0295
0296
0297
0298 struct rcu_head *rcu_segcblist_first_cb(struct rcu_segcblist *rsclp)
0299 {
0300 if (rcu_segcblist_is_enabled(rsclp))
0301 return rsclp->head;
0302 return NULL;
0303 }
0304
0305
0306
0307
0308
0309
0310
0311
0312 struct rcu_head *rcu_segcblist_first_pend_cb(struct rcu_segcblist *rsclp)
0313 {
0314 if (rcu_segcblist_is_enabled(rsclp))
0315 return *rsclp->tails[RCU_DONE_TAIL];
0316 return NULL;
0317 }
0318
0319
0320
0321
0322
0323 bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, unsigned long *lp)
0324 {
0325 if (!rcu_segcblist_pend_cbs(rsclp))
0326 return false;
0327 *lp = rsclp->gp_seq[RCU_WAIT_TAIL];
0328 return true;
0329 }
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340 void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp,
0341 struct rcu_head *rhp)
0342 {
0343 rcu_segcblist_inc_len(rsclp);
0344 rcu_segcblist_inc_seglen(rsclp, RCU_NEXT_TAIL);
0345 rhp->next = NULL;
0346 WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rhp);
0347 WRITE_ONCE(rsclp->tails[RCU_NEXT_TAIL], &rhp->next);
0348 }
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360 bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
0361 struct rcu_head *rhp)
0362 {
0363 int i;
0364
0365 if (rcu_segcblist_n_cbs(rsclp) == 0)
0366 return false;
0367 rcu_segcblist_inc_len(rsclp);
0368 smp_mb();
0369 rhp->next = NULL;
0370 for (i = RCU_NEXT_TAIL; i > RCU_DONE_TAIL; i--)
0371 if (rsclp->tails[i] != rsclp->tails[i - 1])
0372 break;
0373 rcu_segcblist_inc_seglen(rsclp, i);
0374 WRITE_ONCE(*rsclp->tails[i], rhp);
0375 for (; i <= RCU_NEXT_TAIL; i++)
0376 WRITE_ONCE(rsclp->tails[i], &rhp->next);
0377 return true;
0378 }
0379
0380
0381
0382
0383
0384
0385 void rcu_segcblist_extract_done_cbs(struct rcu_segcblist *rsclp,
0386 struct rcu_cblist *rclp)
0387 {
0388 int i;
0389
0390 if (!rcu_segcblist_ready_cbs(rsclp))
0391 return;
0392 rclp->len = rcu_segcblist_get_seglen(rsclp, RCU_DONE_TAIL);
0393 *rclp->tail = rsclp->head;
0394 WRITE_ONCE(rsclp->head, *rsclp->tails[RCU_DONE_TAIL]);
0395 WRITE_ONCE(*rsclp->tails[RCU_DONE_TAIL], NULL);
0396 rclp->tail = rsclp->tails[RCU_DONE_TAIL];
0397 for (i = RCU_CBLIST_NSEGS - 1; i >= RCU_DONE_TAIL; i--)
0398 if (rsclp->tails[i] == rsclp->tails[RCU_DONE_TAIL])
0399 WRITE_ONCE(rsclp->tails[i], &rsclp->head);
0400 rcu_segcblist_set_seglen(rsclp, RCU_DONE_TAIL, 0);
0401 }
0402
0403
0404
0405
0406
0407
0408
0409
0410 void rcu_segcblist_extract_pend_cbs(struct rcu_segcblist *rsclp,
0411 struct rcu_cblist *rclp)
0412 {
0413 int i;
0414
0415 if (!rcu_segcblist_pend_cbs(rsclp))
0416 return;
0417 rclp->len = 0;
0418 *rclp->tail = *rsclp->tails[RCU_DONE_TAIL];
0419 rclp->tail = rsclp->tails[RCU_NEXT_TAIL];
0420 WRITE_ONCE(*rsclp->tails[RCU_DONE_TAIL], NULL);
0421 for (i = RCU_DONE_TAIL + 1; i < RCU_CBLIST_NSEGS; i++) {
0422 rclp->len += rcu_segcblist_get_seglen(rsclp, i);
0423 WRITE_ONCE(rsclp->tails[i], rsclp->tails[RCU_DONE_TAIL]);
0424 rcu_segcblist_set_seglen(rsclp, i, 0);
0425 }
0426 }
0427
0428
0429
0430
0431
0432 void rcu_segcblist_insert_count(struct rcu_segcblist *rsclp,
0433 struct rcu_cblist *rclp)
0434 {
0435 rcu_segcblist_add_len(rsclp, rclp->len);
0436 }
0437
0438
0439
0440
0441
0442 void rcu_segcblist_insert_done_cbs(struct rcu_segcblist *rsclp,
0443 struct rcu_cblist *rclp)
0444 {
0445 int i;
0446
0447 if (!rclp->head)
0448 return;
0449 rcu_segcblist_add_seglen(rsclp, RCU_DONE_TAIL, rclp->len);
0450 *rclp->tail = rsclp->head;
0451 WRITE_ONCE(rsclp->head, rclp->head);
0452 for (i = RCU_DONE_TAIL; i < RCU_CBLIST_NSEGS; i++)
0453 if (&rsclp->head == rsclp->tails[i])
0454 WRITE_ONCE(rsclp->tails[i], rclp->tail);
0455 else
0456 break;
0457 rclp->head = NULL;
0458 rclp->tail = &rclp->head;
0459 }
0460
0461
0462
0463
0464
0465 void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp,
0466 struct rcu_cblist *rclp)
0467 {
0468 if (!rclp->head)
0469 return;
0470
0471 rcu_segcblist_add_seglen(rsclp, RCU_NEXT_TAIL, rclp->len);
0472 WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rclp->head);
0473 WRITE_ONCE(rsclp->tails[RCU_NEXT_TAIL], rclp->tail);
0474 }
0475
0476
0477
0478
0479
0480 void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
0481 {
0482 int i, j;
0483
0484 WARN_ON_ONCE(!rcu_segcblist_is_enabled(rsclp));
0485 if (rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL))
0486 return;
0487
0488
0489
0490
0491
0492 for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) {
0493 if (ULONG_CMP_LT(seq, rsclp->gp_seq[i]))
0494 break;
0495 WRITE_ONCE(rsclp->tails[RCU_DONE_TAIL], rsclp->tails[i]);
0496 rcu_segcblist_move_seglen(rsclp, i, RCU_DONE_TAIL);
0497 }
0498
0499
0500 if (i == RCU_WAIT_TAIL)
0501 return;
0502
0503
0504 for (j = RCU_WAIT_TAIL; j < i; j++)
0505 WRITE_ONCE(rsclp->tails[j], rsclp->tails[RCU_DONE_TAIL]);
0506
0507
0508
0509
0510
0511
0512
0513 for (j = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++, j++) {
0514 if (rsclp->tails[j] == rsclp->tails[RCU_NEXT_TAIL])
0515 break;
0516 WRITE_ONCE(rsclp->tails[j], rsclp->tails[i]);
0517 rcu_segcblist_move_seglen(rsclp, i, j);
0518 rsclp->gp_seq[j] = rsclp->gp_seq[i];
0519 }
0520 }
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537 bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
0538 {
0539 int i, j;
0540
0541 WARN_ON_ONCE(!rcu_segcblist_is_enabled(rsclp));
0542 if (rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL))
0543 return false;
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553 for (i = RCU_NEXT_READY_TAIL; i > RCU_DONE_TAIL; i--)
0554 if (rsclp->tails[i] != rsclp->tails[i - 1] &&
0555 ULONG_CMP_LT(rsclp->gp_seq[i], seq))
0556 break;
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579 if (rcu_segcblist_restempty(rsclp, i) || ++i >= RCU_NEXT_TAIL)
0580 return false;
0581
0582
0583 for (j = i + 1; j <= RCU_NEXT_TAIL; j++)
0584 rcu_segcblist_move_seglen(rsclp, j, i);
0585
0586
0587
0588
0589
0590
0591
0592
0593 for (; i < RCU_NEXT_TAIL; i++) {
0594 WRITE_ONCE(rsclp->tails[i], rsclp->tails[RCU_NEXT_TAIL]);
0595 rsclp->gp_seq[i] = seq;
0596 }
0597 return true;
0598 }
0599
0600
0601
0602
0603
0604
0605
0606
0607 void rcu_segcblist_merge(struct rcu_segcblist *dst_rsclp,
0608 struct rcu_segcblist *src_rsclp)
0609 {
0610 struct rcu_cblist donecbs;
0611 struct rcu_cblist pendcbs;
0612
0613 lockdep_assert_cpus_held();
0614
0615 rcu_cblist_init(&donecbs);
0616 rcu_cblist_init(&pendcbs);
0617
0618 rcu_segcblist_extract_done_cbs(src_rsclp, &donecbs);
0619 rcu_segcblist_extract_pend_cbs(src_rsclp, &pendcbs);
0620
0621
0622
0623
0624
0625 rcu_segcblist_set_len(src_rsclp, 0);
0626
0627 rcu_segcblist_insert_count(dst_rsclp, &donecbs);
0628 rcu_segcblist_insert_count(dst_rsclp, &pendcbs);
0629 rcu_segcblist_insert_done_cbs(dst_rsclp, &donecbs);
0630 rcu_segcblist_insert_pend_cbs(dst_rsclp, &pendcbs);
0631
0632 rcu_segcblist_init(src_rsclp);
0633 }