1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include <linux/kthread.h>
26#include <linux/module.h>
27#include <linux/debugfs.h>
28#include <linux/seq_file.h>
29
30
31struct rcu_ctrlblk {
32 struct rcu_head *rcucblist;
33 struct rcu_head **donetail;
34 struct rcu_head **curtail;
35 RCU_TRACE(long qlen);
36 RCU_TRACE(char *name);
37};
38
39
40static struct rcu_ctrlblk rcu_sched_ctrlblk = {
41 .donetail = &rcu_sched_ctrlblk.rcucblist,
42 .curtail = &rcu_sched_ctrlblk.rcucblist,
43 RCU_TRACE(.name = "rcu_sched")
44};
45
46static struct rcu_ctrlblk rcu_bh_ctrlblk = {
47 .donetail = &rcu_bh_ctrlblk.rcucblist,
48 .curtail = &rcu_bh_ctrlblk.rcucblist,
49 RCU_TRACE(.name = "rcu_bh")
50};
51
52#ifdef CONFIG_DEBUG_LOCK_ALLOC
53int rcu_scheduler_active __read_mostly;
54EXPORT_SYMBOL_GPL(rcu_scheduler_active);
55#endif
56
57#ifdef CONFIG_TINY_PREEMPT_RCU
58
59#include <linux/delay.h>
60
61
62struct rcu_preempt_ctrlblk {
63 struct rcu_ctrlblk rcb;
64 struct rcu_head **nexttail;
65
66
67
68
69
70
71
72
73
74 struct list_head blkd_tasks;
75
76
77
78 struct list_head *gp_tasks;
79
80
81
82 struct list_head *exp_tasks;
83
84
85
86
87
88#ifdef CONFIG_RCU_BOOST
89 struct list_head *boost_tasks;
90
91
92
93
94
95#endif
96 u8 gpnum;
97 u8 gpcpu;
98 u8 completed;
99
100#ifdef CONFIG_RCU_BOOST
101 unsigned long boost_time;
102#endif
103#ifdef CONFIG_RCU_TRACE
104 unsigned long n_grace_periods;
105#ifdef CONFIG_RCU_BOOST
106 unsigned long n_tasks_boosted;
107
108 unsigned long n_exp_boosts;
109
110 unsigned long n_normal_boosts;
111
112 unsigned long n_balk_blkd_tasks;
113
114 unsigned long n_balk_exp_gp_tasks;
115
116 unsigned long n_balk_boost_tasks;
117
118 unsigned long n_balk_notyet;
119
120 unsigned long n_balk_nos;
121
122
123#endif
124#endif
125};
126
127static struct rcu_preempt_ctrlblk rcu_preempt_ctrlblk = {
128 .rcb.donetail = &rcu_preempt_ctrlblk.rcb.rcucblist,
129 .rcb.curtail = &rcu_preempt_ctrlblk.rcb.rcucblist,
130 .nexttail = &rcu_preempt_ctrlblk.rcb.rcucblist,
131 .blkd_tasks = LIST_HEAD_INIT(rcu_preempt_ctrlblk.blkd_tasks),
132 RCU_TRACE(.rcb.name = "rcu_preempt")
133};
134
135static int rcu_preempted_readers_exp(void);
136static void rcu_report_exp_done(void);
137
138
139
140
141static int rcu_cpu_blocking_cur_gp(void)
142{
143 return rcu_preempt_ctrlblk.gpcpu != rcu_preempt_ctrlblk.gpnum;
144}
145
146
147
148
149
150static int rcu_preempt_running_reader(void)
151{
152 return current->rcu_read_lock_nesting;
153}
154
155
156
157
158
159static int rcu_preempt_blocked_readers_any(void)
160{
161 return !list_empty(&rcu_preempt_ctrlblk.blkd_tasks);
162}
163
164
165
166
167
168static int rcu_preempt_blocked_readers_cgp(void)
169{
170 return rcu_preempt_ctrlblk.gp_tasks != NULL;
171}
172
173
174
175
176static int rcu_preempt_needs_another_gp(void)
177{
178 return *rcu_preempt_ctrlblk.rcb.curtail != NULL;
179}
180
181
182
183
184
185static int rcu_preempt_gp_in_progress(void)
186{
187 return rcu_preempt_ctrlblk.completed != rcu_preempt_ctrlblk.gpnum;
188}
189
190
191
192
193
194static struct list_head *rcu_next_node_entry(struct task_struct *t)
195{
196 struct list_head *np;
197
198 np = t->rcu_node_entry.next;
199 if (np == &rcu_preempt_ctrlblk.blkd_tasks)
200 np = NULL;
201 return np;
202}
203
204#ifdef CONFIG_RCU_TRACE
205
206#ifdef CONFIG_RCU_BOOST
207static void rcu_initiate_boost_trace(void);
208#endif
209
210
211
212
213static void show_tiny_preempt_stats(struct seq_file *m)
214{
215 seq_printf(m, "rcu_preempt: qlen=%ld gp=%lu g%u/p%u/c%u tasks=%c%c%c\n",
216 rcu_preempt_ctrlblk.rcb.qlen,
217 rcu_preempt_ctrlblk.n_grace_periods,
218 rcu_preempt_ctrlblk.gpnum,
219 rcu_preempt_ctrlblk.gpcpu,
220 rcu_preempt_ctrlblk.completed,
221 "T."[list_empty(&rcu_preempt_ctrlblk.blkd_tasks)],
222 "N."[!rcu_preempt_ctrlblk.gp_tasks],
223 "E."[!rcu_preempt_ctrlblk.exp_tasks]);
224#ifdef CONFIG_RCU_BOOST
225 seq_printf(m, "%sttb=%c ntb=%lu neb=%lu nnb=%lu j=%04x bt=%04x\n",
226 " ",
227 "B."[!rcu_preempt_ctrlblk.boost_tasks],
228 rcu_preempt_ctrlblk.n_tasks_boosted,
229 rcu_preempt_ctrlblk.n_exp_boosts,
230 rcu_preempt_ctrlblk.n_normal_boosts,
231 (int)(jiffies & 0xffff),
232 (int)(rcu_preempt_ctrlblk.boost_time & 0xffff));
233 seq_printf(m, "%s: nt=%lu egt=%lu bt=%lu ny=%lu nos=%lu\n",
234 " balk",
235 rcu_preempt_ctrlblk.n_balk_blkd_tasks,
236 rcu_preempt_ctrlblk.n_balk_exp_gp_tasks,
237 rcu_preempt_ctrlblk.n_balk_boost_tasks,
238 rcu_preempt_ctrlblk.n_balk_notyet,
239 rcu_preempt_ctrlblk.n_balk_nos);
240#endif
241}
242
243#endif
244
245#ifdef CONFIG_RCU_BOOST
246
247#include "rtmutex_common.h"
248
249#define RCU_BOOST_PRIO CONFIG_RCU_BOOST_PRIO
250
251
252static struct task_struct *rcu_kthread_task;
253static DECLARE_WAIT_QUEUE_HEAD(rcu_kthread_wq);
254static unsigned long have_rcu_kthread_work;
255
256
257
258
259
260static int rcu_boost(void)
261{
262 unsigned long flags;
263 struct rt_mutex mtx;
264 struct task_struct *t;
265 struct list_head *tb;
266
267 if (rcu_preempt_ctrlblk.boost_tasks == NULL &&
268 rcu_preempt_ctrlblk.exp_tasks == NULL)
269 return 0;
270
271 raw_local_irq_save(flags);
272
273
274
275
276
277
278 if (rcu_preempt_ctrlblk.boost_tasks == NULL &&
279 rcu_preempt_ctrlblk.exp_tasks == NULL) {
280 raw_local_irq_restore(flags);
281 return 0;
282 }
283
284
285
286
287
288
289
290 if (rcu_preempt_ctrlblk.exp_tasks != NULL) {
291 tb = rcu_preempt_ctrlblk.exp_tasks;
292 RCU_TRACE(rcu_preempt_ctrlblk.n_exp_boosts++);
293 } else {
294 tb = rcu_preempt_ctrlblk.boost_tasks;
295 RCU_TRACE(rcu_preempt_ctrlblk.n_normal_boosts++);
296 }
297 RCU_TRACE(rcu_preempt_ctrlblk.n_tasks_boosted++);
298
299
300
301
302
303
304
305
306
307 t = container_of(tb, struct task_struct, rcu_node_entry);
308 rt_mutex_init_proxy_locked(&mtx, t);
309 t->rcu_boost_mutex = &mtx;
310 t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BOOSTED;
311 raw_local_irq_restore(flags);
312 rt_mutex_lock(&mtx);
313 rt_mutex_unlock(&mtx);
314
315 return rcu_preempt_ctrlblk.boost_tasks != NULL ||
316 rcu_preempt_ctrlblk.exp_tasks != NULL;
317}
318
319
320
321
322
323
324
325
326
327
328
329static int rcu_initiate_boost(void)
330{
331 if (!rcu_preempt_blocked_readers_cgp() &&
332 rcu_preempt_ctrlblk.exp_tasks == NULL) {
333 RCU_TRACE(rcu_preempt_ctrlblk.n_balk_exp_gp_tasks++);
334 return 0;
335 }
336 if (rcu_preempt_ctrlblk.exp_tasks != NULL ||
337 (rcu_preempt_ctrlblk.gp_tasks != NULL &&
338 rcu_preempt_ctrlblk.boost_tasks == NULL &&
339 ULONG_CMP_GE(jiffies, rcu_preempt_ctrlblk.boost_time))) {
340 if (rcu_preempt_ctrlblk.exp_tasks == NULL)
341 rcu_preempt_ctrlblk.boost_tasks =
342 rcu_preempt_ctrlblk.gp_tasks;
343 invoke_rcu_callbacks();
344 } else
345 RCU_TRACE(rcu_initiate_boost_trace());
346 return 1;
347}
348
349#define RCU_BOOST_DELAY_JIFFIES DIV_ROUND_UP(CONFIG_RCU_BOOST_DELAY * HZ, 1000)
350
351
352
353
354static void rcu_preempt_boost_start_gp(void)
355{
356 rcu_preempt_ctrlblk.boost_time = jiffies + RCU_BOOST_DELAY_JIFFIES;
357}
358
359#else
360
361
362
363
364
365
366static int rcu_initiate_boost(void)
367{
368 return rcu_preempt_blocked_readers_cgp();
369}
370
371
372
373
374static void rcu_preempt_boost_start_gp(void)
375{
376}
377
378#endif
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401static void rcu_preempt_cpu_qs(void)
402{
403
404 rcu_preempt_ctrlblk.gpcpu = rcu_preempt_ctrlblk.gpnum;
405 current->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_NEED_QS;
406
407
408 if (!rcu_preempt_gp_in_progress())
409 return;
410
411
412
413
414 if (rcu_initiate_boost())
415 return;
416
417
418 rcu_preempt_ctrlblk.completed = rcu_preempt_ctrlblk.gpnum;
419 rcu_preempt_ctrlblk.rcb.donetail = rcu_preempt_ctrlblk.rcb.curtail;
420 rcu_preempt_ctrlblk.rcb.curtail = rcu_preempt_ctrlblk.nexttail;
421
422
423 if (!rcu_preempt_blocked_readers_any())
424 rcu_preempt_ctrlblk.rcb.donetail = rcu_preempt_ctrlblk.nexttail;
425
426
427 if (*rcu_preempt_ctrlblk.rcb.donetail != NULL)
428 invoke_rcu_callbacks();
429}
430
431
432
433
434static void rcu_preempt_start_gp(void)
435{
436 if (!rcu_preempt_gp_in_progress() && rcu_preempt_needs_another_gp()) {
437
438
439 rcu_preempt_ctrlblk.gpnum++;
440 RCU_TRACE(rcu_preempt_ctrlblk.n_grace_periods++);
441
442
443 if (rcu_preempt_blocked_readers_any())
444 rcu_preempt_ctrlblk.gp_tasks =
445 rcu_preempt_ctrlblk.blkd_tasks.next;
446
447
448 rcu_preempt_boost_start_gp();
449
450
451 if (!rcu_preempt_running_reader())
452 rcu_preempt_cpu_qs();
453 }
454}
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472void rcu_preempt_note_context_switch(void)
473{
474 struct task_struct *t = current;
475 unsigned long flags;
476
477 local_irq_save(flags);
478 if (rcu_preempt_running_reader() &&
479 (t->rcu_read_unlock_special & RCU_READ_UNLOCK_BLOCKED) == 0) {
480
481
482 t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BLOCKED;
483
484
485
486
487
488
489
490
491
492
493
494 list_add(&t->rcu_node_entry, &rcu_preempt_ctrlblk.blkd_tasks);
495 if (rcu_cpu_blocking_cur_gp())
496 rcu_preempt_ctrlblk.gp_tasks = &t->rcu_node_entry;
497 }
498
499
500
501
502
503
504
505
506
507
508 rcu_preempt_cpu_qs();
509 local_irq_restore(flags);
510}
511
512
513
514
515
516
517void __rcu_read_lock(void)
518{
519 current->rcu_read_lock_nesting++;
520 barrier();
521}
522EXPORT_SYMBOL_GPL(__rcu_read_lock);
523
524
525
526
527
528
529static void rcu_read_unlock_special(struct task_struct *t)
530{
531 int empty;
532 int empty_exp;
533 unsigned long flags;
534 struct list_head *np;
535 int special;
536
537
538
539
540
541 if (in_nmi())
542 return;
543
544 local_irq_save(flags);
545
546
547
548
549
550 special = t->rcu_read_unlock_special;
551 if (special & RCU_READ_UNLOCK_NEED_QS)
552 rcu_preempt_cpu_qs();
553
554
555 if (in_irq()) {
556 local_irq_restore(flags);
557 return;
558 }
559
560
561 if (special & RCU_READ_UNLOCK_BLOCKED) {
562 t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_BLOCKED;
563
564
565
566
567
568 empty = !rcu_preempt_blocked_readers_cgp();
569 empty_exp = rcu_preempt_ctrlblk.exp_tasks == NULL;
570 np = rcu_next_node_entry(t);
571 list_del_init(&t->rcu_node_entry);
572 if (&t->rcu_node_entry == rcu_preempt_ctrlblk.gp_tasks)
573 rcu_preempt_ctrlblk.gp_tasks = np;
574 if (&t->rcu_node_entry == rcu_preempt_ctrlblk.exp_tasks)
575 rcu_preempt_ctrlblk.exp_tasks = np;
576#ifdef CONFIG_RCU_BOOST
577 if (&t->rcu_node_entry == rcu_preempt_ctrlblk.boost_tasks)
578 rcu_preempt_ctrlblk.boost_tasks = np;
579#endif
580
581
582
583
584
585
586 if (!empty && !rcu_preempt_blocked_readers_cgp()) {
587 rcu_preempt_cpu_qs();
588 rcu_preempt_start_gp();
589 }
590
591
592
593
594
595 if (!empty_exp && rcu_preempt_ctrlblk.exp_tasks == NULL)
596 rcu_report_exp_done();
597 }
598#ifdef CONFIG_RCU_BOOST
599
600 if (special & RCU_READ_UNLOCK_BOOSTED) {
601 t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_BOOSTED;
602 rt_mutex_unlock(t->rcu_boost_mutex);
603 t->rcu_boost_mutex = NULL;
604 }
605#endif
606 local_irq_restore(flags);
607}
608
609
610
611
612
613
614
615
616void __rcu_read_unlock(void)
617{
618 struct task_struct *t = current;
619
620 barrier();
621 --t->rcu_read_lock_nesting;
622 barrier();
623 if (t->rcu_read_lock_nesting == 0 &&
624 unlikely(ACCESS_ONCE(t->rcu_read_unlock_special)))
625 rcu_read_unlock_special(t);
626#ifdef CONFIG_PROVE_LOCKING
627 WARN_ON_ONCE(t->rcu_read_lock_nesting < 0);
628#endif
629}
630EXPORT_SYMBOL_GPL(__rcu_read_unlock);
631
632
633
634
635
636
637
638
639static void rcu_preempt_check_callbacks(void)
640{
641 struct task_struct *t = current;
642
643 if (rcu_preempt_gp_in_progress() &&
644 (!rcu_preempt_running_reader() ||
645 !rcu_cpu_blocking_cur_gp()))
646 rcu_preempt_cpu_qs();
647 if (&rcu_preempt_ctrlblk.rcb.rcucblist !=
648 rcu_preempt_ctrlblk.rcb.donetail)
649 invoke_rcu_callbacks();
650 if (rcu_preempt_gp_in_progress() &&
651 rcu_cpu_blocking_cur_gp() &&
652 rcu_preempt_running_reader())
653 t->rcu_read_unlock_special |= RCU_READ_UNLOCK_NEED_QS;
654}
655
656
657
658
659
660
661
662
663
664static void rcu_preempt_remove_callbacks(struct rcu_ctrlblk *rcp)
665{
666 if (rcu_preempt_ctrlblk.nexttail == rcp->donetail)
667 rcu_preempt_ctrlblk.nexttail = &rcp->rcucblist;
668}
669
670
671
672
673static void rcu_preempt_process_callbacks(void)
674{
675 __rcu_process_callbacks(&rcu_preempt_ctrlblk.rcb);
676}
677
678
679
680
681void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
682{
683 unsigned long flags;
684
685 debug_rcu_head_queue(head);
686 head->func = func;
687 head->next = NULL;
688
689 local_irq_save(flags);
690 *rcu_preempt_ctrlblk.nexttail = head;
691 rcu_preempt_ctrlblk.nexttail = &head->next;
692 RCU_TRACE(rcu_preempt_ctrlblk.rcb.qlen++);
693 rcu_preempt_start_gp();
694 local_irq_restore(flags);
695}
696EXPORT_SYMBOL_GPL(call_rcu);
697
698
699
700
701
702
703
704
705
706
707void synchronize_rcu(void)
708{
709#ifdef CONFIG_DEBUG_LOCK_ALLOC
710 if (!rcu_scheduler_active)
711 return;
712#endif
713
714 WARN_ON_ONCE(rcu_preempt_running_reader());
715 if (!rcu_preempt_blocked_readers_any())
716 return;
717
718
719 rcu_barrier();
720}
721EXPORT_SYMBOL_GPL(synchronize_rcu);
722
723static DECLARE_WAIT_QUEUE_HEAD(sync_rcu_preempt_exp_wq);
724static unsigned long sync_rcu_preempt_exp_count;
725static DEFINE_MUTEX(sync_rcu_preempt_exp_mutex);
726
727
728
729
730
731
732
733static int rcu_preempted_readers_exp(void)
734{
735 return rcu_preempt_ctrlblk.exp_tasks != NULL;
736}
737
738
739
740
741
742
743static void rcu_report_exp_done(void)
744{
745 wake_up(&sync_rcu_preempt_exp_wq);
746}
747
748
749
750
751
752
753
754
755
756
757
758void synchronize_rcu_expedited(void)
759{
760 unsigned long flags;
761 struct rcu_preempt_ctrlblk *rpcp = &rcu_preempt_ctrlblk;
762 unsigned long snap;
763
764 barrier();
765
766 WARN_ON_ONCE(rcu_preempt_running_reader());
767
768
769
770
771
772
773 snap = sync_rcu_preempt_exp_count + 1;
774 mutex_lock(&sync_rcu_preempt_exp_mutex);
775 if (ULONG_CMP_LT(snap, sync_rcu_preempt_exp_count))
776 goto unlock_mb_ret;
777
778 local_irq_save(flags);
779
780
781
782
783
784
785
786
787 rpcp->exp_tasks = rpcp->blkd_tasks.next;
788 if (rpcp->exp_tasks == &rpcp->blkd_tasks)
789 rpcp->exp_tasks = NULL;
790
791
792 if (!rcu_preempted_readers_exp())
793 local_irq_restore(flags);
794 else {
795 rcu_initiate_boost();
796 local_irq_restore(flags);
797 wait_event(sync_rcu_preempt_exp_wq,
798 !rcu_preempted_readers_exp());
799 }
800
801
802 barrier();
803 sync_rcu_preempt_exp_count++;
804unlock_mb_ret:
805 mutex_unlock(&sync_rcu_preempt_exp_mutex);
806 barrier();
807}
808EXPORT_SYMBOL_GPL(synchronize_rcu_expedited);
809
810
811
812
813int rcu_preempt_needs_cpu(void)
814{
815 if (!rcu_preempt_running_reader())
816 rcu_preempt_cpu_qs();
817 return rcu_preempt_ctrlblk.rcb.rcucblist != NULL;
818}
819
820
821
822
823
824
825
826void exit_rcu(void)
827{
828 struct task_struct *t = current;
829
830 if (t->rcu_read_lock_nesting == 0)
831 return;
832 t->rcu_read_lock_nesting = 1;
833 __rcu_read_unlock();
834}
835
836#else
837
838#ifdef CONFIG_RCU_TRACE
839
840
841
842
843
844static void show_tiny_preempt_stats(struct seq_file *m)
845{
846}
847
848#endif
849
850
851
852
853
854static void rcu_preempt_check_callbacks(void)
855{
856}
857
858
859
860
861
862static void rcu_preempt_remove_callbacks(struct rcu_ctrlblk *rcp)
863{
864}
865
866
867
868
869
870static void rcu_preempt_process_callbacks(void)
871{
872}
873
874#endif
875
876#ifdef CONFIG_RCU_BOOST
877
878
879
880
881
882static void invoke_rcu_callbacks(void)
883{
884 have_rcu_kthread_work = 1;
885 wake_up(&rcu_kthread_wq);
886}
887
888
889
890
891
892
893
894
895static int rcu_kthread(void *arg)
896{
897 unsigned long work;
898 unsigned long morework;
899 unsigned long flags;
900
901 for (;;) {
902 wait_event_interruptible(rcu_kthread_wq,
903 have_rcu_kthread_work != 0);
904 morework = rcu_boost();
905 local_irq_save(flags);
906 work = have_rcu_kthread_work;
907 have_rcu_kthread_work = morework;
908 local_irq_restore(flags);
909 if (work)
910 rcu_process_callbacks(NULL);
911 schedule_timeout_interruptible(1);
912 }
913
914 return 0;
915}
916
917
918
919
920static int __init rcu_spawn_kthreads(void)
921{
922 struct sched_param sp;
923
924 rcu_kthread_task = kthread_run(rcu_kthread, NULL, "rcu_kthread");
925 sp.sched_priority = RCU_BOOST_PRIO;
926 sched_setscheduler_nocheck(rcu_kthread_task, SCHED_FIFO, &sp);
927 return 0;
928}
929early_initcall(rcu_spawn_kthreads);
930
931#else
932
933
934
935
936void invoke_rcu_callbacks(void)
937{
938 raise_softirq(RCU_SOFTIRQ);
939}
940
941void rcu_init(void)
942{
943 open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
944}
945
946#endif
947
948#ifdef CONFIG_DEBUG_LOCK_ALLOC
949#include <linux/kernel_stat.h>
950
951
952
953
954
955void __init rcu_scheduler_starting(void)
956{
957 WARN_ON(nr_context_switches() > 0);
958 rcu_scheduler_active = 1;
959}
960
961#endif
962
963#ifdef CONFIG_RCU_TRACE
964
965#ifdef CONFIG_RCU_BOOST
966
967static void rcu_initiate_boost_trace(void)
968{
969 if (list_empty(&rcu_preempt_ctrlblk.blkd_tasks))
970 rcu_preempt_ctrlblk.n_balk_blkd_tasks++;
971 else if (rcu_preempt_ctrlblk.gp_tasks == NULL &&
972 rcu_preempt_ctrlblk.exp_tasks == NULL)
973 rcu_preempt_ctrlblk.n_balk_exp_gp_tasks++;
974 else if (rcu_preempt_ctrlblk.boost_tasks != NULL)
975 rcu_preempt_ctrlblk.n_balk_boost_tasks++;
976 else if (!ULONG_CMP_GE(jiffies, rcu_preempt_ctrlblk.boost_time))
977 rcu_preempt_ctrlblk.n_balk_notyet++;
978 else
979 rcu_preempt_ctrlblk.n_balk_nos++;
980}
981
982#endif
983
984static void rcu_trace_sub_qlen(struct rcu_ctrlblk *rcp, int n)
985{
986 unsigned long flags;
987
988 raw_local_irq_save(flags);
989 rcp->qlen -= n;
990 raw_local_irq_restore(flags);
991}
992
993
994
995
996static int show_tiny_stats(struct seq_file *m, void *unused)
997{
998 show_tiny_preempt_stats(m);
999 seq_printf(m, "rcu_sched: qlen: %ld\n", rcu_sched_ctrlblk.qlen);
1000 seq_printf(m, "rcu_bh: qlen: %ld\n", rcu_bh_ctrlblk.qlen);
1001 return 0;
1002}
1003
1004static int show_tiny_stats_open(struct inode *inode, struct file *file)
1005{
1006 return single_open(file, show_tiny_stats, NULL);
1007}
1008
1009static const struct file_operations show_tiny_stats_fops = {
1010 .owner = THIS_MODULE,
1011 .open = show_tiny_stats_open,
1012 .read = seq_read,
1013 .llseek = seq_lseek,
1014 .release = single_release,
1015};
1016
1017static struct dentry *rcudir;
1018
1019static int __init rcutiny_trace_init(void)
1020{
1021 struct dentry *retval;
1022
1023 rcudir = debugfs_create_dir("rcu", NULL);
1024 if (!rcudir)
1025 goto free_out;
1026 retval = debugfs_create_file("rcudata", 0444, rcudir,
1027 NULL, &show_tiny_stats_fops);
1028 if (!retval)
1029 goto free_out;
1030 return 0;
1031free_out:
1032 debugfs_remove_recursive(rcudir);
1033 return 1;
1034}
1035
1036static void __exit rcutiny_trace_cleanup(void)
1037{
1038 debugfs_remove_recursive(rcudir);
1039}
1040
1041module_init(rcutiny_trace_init);
1042module_exit(rcutiny_trace_cleanup);
1043
1044MODULE_AUTHOR("Paul E. McKenney");
1045MODULE_DESCRIPTION("Read-Copy Update tracing for tiny implementation");
1046MODULE_LICENSE("GPL");
1047
1048#endif
1049