-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathlinux-time.html
872 lines (609 loc) · 282 KB
/
linux-time.html
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
<!DOCTYPE html>
<html lang="en">
<!-- Head tag -->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
<meta name="baidu-site-verification" content="093lY4ziMu" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="description" content="一个有内涵的技术分享平台">
<meta name="keyword" content="meizu,kernel,魅族">
<link rel="shortcut icon" href="/img/ironman-draw.png">
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
<!--<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>-->
<title>
Linux Time - 魅族内核团队
</title>
<link rel="canonical" href="https://kernel.meizu.com//linux-time.html">
<!-- Bootstrap Core CSS -->
<link rel="stylesheet" href="css/bootstrap.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="css/dusign-light.css">
<link rel="stylesheet" href="css/dusign-common-light.css">
<link rel="stylesheet" href="css/font-awesome.css">
<link rel="stylesheet" href="css/toc.css">
<!-- background effects end -->
<!-- Pygments Highlight CSS -->
<link rel="stylesheet" href="css/highlight.css">
<link rel="stylesheet" href="css/widget.css">
<link rel="stylesheet" href="css/rocket.css">
<link rel="stylesheet" href="css/signature.css">
<link rel="stylesheet" href="css/fonts.googleapis.css">
<link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css">
<!-- photography -->
<link rel="stylesheet" href="css/photography.css">
<!-- ga & ba script hoook -->
<script></script>
<meta name="generator" content="Hexo 7.3.0"></head>
<!-- hack iOS CSS :active style -->
<body ontouchstart="">
<!-- background effects start -->
<!-- background effects end -->
<!-- Modified by Yu-Hsuan Yen -->
<!-- Post Header -->
<style type="text/css">
header.intro-header{
background-image: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), url('')
/*post*/
}
</style>
<header class="intro-header" >
<!-- Signature -->
<div id="signature">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="post-heading">
<div class="tags">
<a class="tag" href="/tags/#hrtimer" title="hrtimer">hrtimer</a>
<a class="tag" href="/tags/#tickless" title="tickless">tickless</a>
<a class="tag" href="/tags/#clocksource" title="clocksource">clocksource</a>
<a class="tag" href="/tags/#timekeeper" title="timekeeper">timekeeper</a>
<a class="tag" href="/tags/#clockevent" title="clockevent">clockevent</a>
<a class="tag" href="/tags/#noHZ" title="noHZ">noHZ</a>
<a class="tag" href="/tags/#lowres timer" title="lowres timer">lowres timer</a>
<a class="tag" href="/tags/#wall time" title="wall time">wall time</a>
<a class="tag" href="/tags/#xtime" title="xtime">xtime</a>
<a class="tag" href="/tags/#monotonic time" title="monotonic time">monotonic time</a>
<a class="tag" href="/tags/#boottime" title="boottime">boottime</a>
</div>
<h1>Linux Time</h1>
<h2 class="subheading"></h2>
<span class="meta">
Posted by Peng Weilin on
2018-07-12
</span>
</div>
</div>
</div>
</div>
</div>
<div class="waveWrapper">
<div class="wave wave_before" style="background-image: url('/img/wave-light.png')"></div>
<div class="wave wave_after" style="background-image: url('/img/wave-light.png')"></div>
</div>
</header>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header page-scroll">
<button type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">魅族内核团队</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<!-- Known Issue, found by Hux:
<nav>'s height woule be hold on by its content.
so, when navbar scale out, the <nav> will cover tags.
also mask any touch event of tags, unfortunately.
-->
<div id="huxblog_navbar">
<div class="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/about/">About</a>
</li>
<li>
<a href="/archive/">Archives</a>
</li>
<li>
<a href="/categories/">Categories</a>
</li>
<li>
<a href="/tags/">Tags</a>
</li>
</ul>
</div>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<script>
// Drop Bootstarp low-performance Navbar
// Use customize navbar with high-quality material design animation
// in high-perf jank-free CSS3 implementation
var $body = document.body;
var $toggle = document.querySelector('.navbar-toggle');
var $navbar = document.querySelector('#huxblog_navbar');
var $collapse = document.querySelector('.navbar-collapse');
$toggle.addEventListener('click', handleMagic)
function handleMagic(e){
if ($navbar.className.indexOf('in') > 0) {
// CLOSE
$navbar.className = " ";
// wait until animation end.
setTimeout(function(){
// prevent frequently toggle
if($navbar.className.indexOf('in') < 0) {
$collapse.style.height = "0px"
}
},400)
}else{
// OPEN
$collapse.style.height = "auto"
$navbar.className += " in";
}
}
</script>
<!-- Main Content -->
<!-- Post Content -->
<article>
<div class="container">
<div class="row">
<!-- Post Container -->
<div class="
col-lg-8 col-lg-offset-2
col-md-10 col-md-offset-1
post-container">
<h2 id="1、Linux时钟框架"><a href="#1、Linux时钟框架" class="headerlink" title="1、Linux时钟框架"></a>1、Linux时钟框架</h2><p><img src="linux_time_profile.png" alt="image">[^DroidPhoneo]</p>
<p>上图是linux时钟框架一个经典的描述。本质上linux各种时钟架构和服务是基于硬件提供的两种timer而构建的。</p>
<p>1、定时Timer</p>
<ul>
<li>这类timer每个cpu都有一个独立的,称为local timer。这类timer的中断一般都是PPI(Private Peripheral Interrupt)类型,即每个cpu都有独立一份中断。 与PPI对应的是SPI(Shared Peripheral Interrupt,即多个cpu共享同一个中断。</li>
<li>这类timer一般是32bit宽度count,最重要的它会频繁的溢出并产生timer到期中断。</li>
<li>这类timer服务于tick timer(低精度)或者hrtimer(高精度)。</li>
<li>低精度模式,local timer工作在PERIODIC模式。即timer以tick时间(1/HZ)周期性的产生中断。在tick timer中处理任务调度tick、低精度timer、其他时间更新和统计profile。在这种模式下,所有利用时间的进行的运算,精度都是以tick(1/HZ)为单位的,精度较低。比如HZ=1000,那么tick=1ms。</li>
<li>高精度模式,local timer工作在ONESHOT模式。即系统可以支持hrtimer(high resolution)高精度timer,精度为local timer的计数clk达到ns级别。这种情况下把tick timer也转换成一种hrtimer。</li>
</ul>
<p>2、时间戳Timer</p>
<ul>
<li>这类timer一个系统多个cpu共享一个,称为global timer。</li>
<li>这类timer一般是32bit/64bit宽度count,一般不会溢出产生中断,系统实时的去读取count的值来计算当前的时间戳。</li>
<li>这类timer服务于clocksource/timekeeper。</li>
</ul>
<blockquote>
<p>本文的代码分析基于linux kernel 4.4.22,最好的学习方法还是”RTFSC”</p>
</blockquote>
<h3 id="1-1、Exynos-MCT-Multi-Core-Timer"><a href="#1-1、Exynos-MCT-Multi-Core-Timer" class="headerlink" title="1.1、Exynos MCT(Multi-Core Timer)"></a>1.1、Exynos MCT(Multi-Core Timer)</h3><p>我们以samsung exynos架构为例来说明linux对timer的使用。</p>
<p><img src="exynos_timer.png" alt="image"></p>
<p>从上图可以看到,exynos有1个64bit global timer用来做时间戳timer,有8个31bit localtimer用来做定时timer,每个cpu拥有一个localtimer。</p>
<p><img src="exynos_mct_initflow.png" alt="image"></p>
<p>上图是exynos driver的初始化流程,mct_init_dt()中包含了主要的初始化流程:</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">static void __init <span class="built_in">mct_init_dt</span>(struct device_node *np, unsigned int int_type)</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">exynos4_timer_resources</span>(np, of_iomap(np, <span class="number">0</span>)); <span class="comment">//(1)初始化localtimer,并将其注册成clockevent</span></span><br><span class="line"> <span class="built_in">exynos4_clocksource_init</span>(); <span class="comment">//(2)初始化globaltimer,并将其注册成clocksource</span></span><br><span class="line"> <span class="built_in">exynos4_clockevent_init</span>(); <span class="comment">//(3)将globaltimer的comparator 0注册成一个clockevent,一般不会使用</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>后面结合clocksource和clockevent的子系统的解析,再来详细描述exynos系统的具体实现。</p>
<h2 id="2、clocksource-timekeeper"><a href="#2、clocksource-timekeeper" class="headerlink" title="2、clocksource & timekeeper"></a>2、clocksource & timekeeper</h2><p><img src="clocksource_timekeeper.png" alt="image"></p>
<p>上图描述的是clocksource和timekeeper的关系:</p>
<ul>
<li>一个global timer对应注册一个clocksource。</li>
<li>一个系统中可以有多个clocksource,timekeeper选择精度最高的那个来使用。</li>
<li>用户使用timekeeper提供的接口来获取系统的时间戳。</li>
<li>为了避免无人主动获取时间clocksource定时器的溢出,timekeeper需要定期的去获取clocksource的值来更新系统时间,一般是在tick处理中更新。</li>
</ul>
<h3 id="2-1、clocksource"><a href="#2-1、clocksource" class="headerlink" title="2.1、clocksource"></a>2.1、clocksource</h3><p>下面来看一看clocksource的定义:</p>
<figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">static struct clocksource mct_frc = {</span><br><span class="line"> <span class="string">.name</span> = <span class="string">"mct-frc"</span>,</span><br><span class="line"> /* <span class="params">(1)</span> <span class="string">.rating</span> = 精度,数值越大越好,</span><br><span class="line"> select_best会选择精度最大的clocksource给timekeeper使用 */</span><br><span class="line"> <span class="string">.rating</span> = 400, </span><br><span class="line"> /* <span class="params">(2)</span> <span class="string">.read</span> = 读取clocksource的timer当前计数 */</span><br><span class="line"> <span class="string">.read</span> = exynos4_frc_read,</span><br><span class="line"> /* <span class="params">(3)</span> <span class="string">.mask</span> = timer的位宽 */</span><br><span class="line"> <span class="string">.mask</span> = CLOCKSOURCE_MASK<span class="params">(32)</span>,</span><br><span class="line"> <span class="string">.flags</span> = CLOCK_SOURCE_IS_CONTINUOUS,</span><br><span class="line"> <span class="string">.resume</span> = exynos4_frc_resume,</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>看一下clocksource的注册过程:</p>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br></pre></td><td class="code"><pre><span class="line">static void __init exynos4_clocksource_init(void)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">// 启动global timer</span></span><br><span class="line"> exynos4_mct_frc_start();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 注册timer_delay</span></span><br><span class="line"> exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;</span><br><span class="line"> exynos4_delay_timer.freq = clk_rate;</span><br><span class="line"> register_current_timer_delay(&exynos4_delay_timer);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (1) 注册clocksource</span></span><br><span class="line"> <span class="keyword">if</span> (clocksource_register_hz(&mct_frc, clk_rate))</span><br><span class="line"> panic(<span class="string">"%s: can't register clocksource\n"</span>, mct_frc.<span class="keyword">name</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 注册sched_clock</span></span><br><span class="line"> sched_clock_register(exynos4_read_sched_clock, <span class="number">32</span>, clk_rate);</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)</span><br><span class="line">{</span><br><span class="line"> return __clocksource_register_scale(cs, <span class="number">1</span>, hz);</span><br><span class="line">}</span><br><span class="line">||→</span><br><span class="line">int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)</span><br><span class="line">{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Initialize mult/shift and max_idle_ns */</span></span><br><span class="line"> <span class="comment">/* (1.1) 根据timer的频率freq,计算cs->mult、cs->shift</span></span><br><span class="line"><span class="comment"> 这两个字段是用来把timer的计数转换成实际时间单位ns</span></span><br><span class="line"><span class="comment"> ns = (count * cs->mult) >> cs->shift */</span></span><br><span class="line"> __clocksource_update_freq_scale(cs, scale, freq);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Add clocksource to the clocksource list */</span></span><br><span class="line"> mutex_lock(&clocksource_mutex);</span><br><span class="line"> <span class="comment">/* (1.2) 将新的clocksource加入全局链表 */</span></span><br><span class="line"> clocksource_enqueue(cs);</span><br><span class="line"> clocksource_enqueue_watchdog(cs);</span><br><span class="line"> <span class="comment">/* (1.3) 从全局链表中重新选择一个best</span></span><br><span class="line"><span class="comment"> clocksource给timekeeper使用 */</span></span><br><span class="line"> clocksource_select();</span><br><span class="line"> clocksource_select_watchdog(<span class="literal">false</span>);</span><br><span class="line"> mutex_unlock(&clocksource_mutex);</span><br><span class="line"> return <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line">|||→</span><br><span class="line">void __clocksource_update_freq_scale(struct clocksource *cs, u32 scale, u32 freq)</span><br><span class="line">{</span><br><span class="line"> u64 sec;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Default clocksources are *special* and self-define their mult/shift.</span></span><br><span class="line"><span class="comment"> * But, you're not special, so you should specify a freq value.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (freq) {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Calc the maximum number of seconds which we can run before</span></span><br><span class="line"><span class="comment"> * wrapping around. For clocksources which have a mask > 32-bit</span></span><br><span class="line"><span class="comment"> * we need to limit the max sleep time to have a good</span></span><br><span class="line"><span class="comment"> * conversion precision. 10 minutes is still a reasonable</span></span><br><span class="line"><span class="comment"> * amount. That results in a shift value of 24 for a</span></span><br><span class="line"><span class="comment"> * clocksource with mask >= 40-bit and f >= 4GHz. That maps to</span></span><br><span class="line"><span class="comment"> * ~ 0.06ppm granularity for NTP.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (1.1.1) 计算timer计数器到溢出,</span></span><br><span class="line"><span class="comment"> 最大能计数多少秒 = sec */</span></span><br><span class="line"> <span class="function"><span class="title">sec</span> = cs-></span>mask;</span><br><span class="line"> do_div(sec, freq);</span><br><span class="line"> do_div(sec, scale);</span><br><span class="line"> <span class="keyword">if</span> (!sec)</span><br><span class="line"> sec = <span class="number">1</span>;</span><br><span class="line"> <span class="function"><span class="title">else</span> <span class="keyword">if</span> (sec > 600 && cs-></span>mask > UINT_MAX)</span><br><span class="line"> sec = <span class="number">600</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1.1.2) 根据1s内的频率数freq,和1s内的ns数NSEC_PER_SEC</span></span><br><span class="line"><span class="comment"> 计算freq和ns之间的转换公式:</span></span><br><span class="line"><span class="comment"> ns = (freq * cs->mult) >> cs->shift </span></span><br><span class="line"><span class="comment"> 目的是把mult和shift算到最大值,最大可能的保留精度 */</span></span><br><span class="line"> <span class="function"><span class="title">clocks_calc_mult_shift</span>(&cs-></span><span class="function"><span class="title">mult</span>, &cs-></span>shift, freq,</span><br><span class="line"> NSEC_PER_SEC / scale, sec * scale);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Ensure clocksources that have large 'mult' values don't overflow</span></span><br><span class="line"><span class="comment"> * when adjusted.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="title">cs</span>-></span>maxadj = clocksource_max_adjustment(cs);</span><br><span class="line"> <span class="function"><span class="title">while</span> (freq && ((cs-></span><span class="function"><span class="title">mult</span> + cs-></span><span class="function"><span class="title">maxadj</span> < cs-></span>mult)</span><br><span class="line"> || (<span class="function"><span class="title">cs</span>-></span><span class="function"><span class="title">mult</span> - cs-></span><span class="function"><span class="title">maxadj</span> > cs-></span>mult))) {</span><br><span class="line"> <span class="function"><span class="title">cs</span>-></span>mult >>= <span class="number">1</span>;</span><br><span class="line"> <span class="function"><span class="title">cs</span>-></span>shift--;</span><br><span class="line"> <span class="function"><span class="title">cs</span>-></span>maxadj = clocksource_max_adjustment(cs);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Only warn for *special* clocksources that self-define</span></span><br><span class="line"><span class="comment"> * their mult/shift values and don't specify a freq.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> WARN_ONCE(<span class="function"><span class="title">cs</span>-></span><span class="function"><span class="title">mult</span> + cs-></span><span class="function"><span class="title">maxadj</span> < cs-></span>mult,</span><br><span class="line"> <span class="string">"timekeeping: Clocksource %s might overflow on 11%% adjustment\n"</span>,</span><br><span class="line"> <span class="function"><span class="title">cs</span>-></span><span class="keyword">name</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1.1.3) 根据mult和shift的值,计算最大能进入idle的时间max_idle_ns</span></span><br><span class="line"><span class="comment"> 才能保证idle时timer不会溢出*/</span></span><br><span class="line"> clocksource_update_max_deferment(cs);</span><br><span class="line"></span><br><span class="line"> pr_info(<span class="string">"%s: mask: 0x%llx max_cycles: 0x%llx, max_idle_ns: %lld ns\n"</span>,</span><br><span class="line"> <span class="function"><span class="title">cs</span>-></span><span class="function"><span class="title">name</span>, cs-></span><span class="function"><span class="title">mask</span>, cs-></span><span class="function"><span class="title">max_cycles</span>, cs-></span>max_idle_ns);</span><br><span class="line">}</span><br><span class="line">|||→</span><br><span class="line">static void clocksource_select(void)</span><br><span class="line">{</span><br><span class="line"> __clocksource_select(<span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line">static void __clocksource_select(bool skipcur)</span><br><span class="line">{</span><br><span class="line"> bool oneshot = tick_oneshot_mode_active();</span><br><span class="line"> struct clocksource *best, *cs;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Find the best suitable clocksource */</span></span><br><span class="line"> <span class="comment">/* (1.3.1) 选择best clocksource */</span></span><br><span class="line"> best = clocksource_find_best(oneshot, skipcur);</span><br><span class="line"> <span class="keyword">if</span> (!best)</span><br><span class="line"> return;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Check for the override clocksource. */</span></span><br><span class="line"> list_for_each_entry(cs, &clocksource_list, list) {</span><br><span class="line"> <span class="keyword">if</span> (skipcur && cs == curr_clocksource)</span><br><span class="line"> continue;</span><br><span class="line"> <span class="function"><span class="title">if</span> (strcmp(cs-></span><span class="keyword">name</span>, override_name) != <span class="number">0</span>)</span><br><span class="line"> continue;</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Check to make sure we don't switch to a non-highres</span></span><br><span class="line"><span class="comment"> * capable clocksource if the tick code is in oneshot</span></span><br><span class="line"><span class="comment"> * mode (highres or nohz)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (!(cs-></span>flags & CLOCK_SOURCE_VALID_FOR_HRES) && oneshot) {</span><br><span class="line"> <span class="comment">/* Override clocksource cannot be used. */</span></span><br><span class="line"> pr_warn(<span class="string">"Override clocksource %s is not HRT compatible - cannot switch while in HRT/NOHZ mode\n"</span>,</span><br><span class="line"> <span class="function"><span class="title">cs</span>-></span><span class="keyword">name</span>);</span><br><span class="line"> override_name[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> } <span class="keyword">else</span></span><br><span class="line"> <span class="comment">/* Override clocksource can be used. */</span></span><br><span class="line"> best = cs;</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1.3.2) 通知timekeeper更新clocksource,tick-sched更新 */</span></span><br><span class="line"> <span class="keyword">if</span> (curr_clocksource != best && !timekeeping_notify(best)) {</span><br><span class="line"> <span class="function"><span class="title">pr_info</span>("Switched to clocksource %s\n", best-></span><span class="keyword">name</span>);</span><br><span class="line"> curr_clocksource = best;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">||||→</span><br><span class="line">int timekeeping_notify(struct clocksource *clock)</span><br><span class="line">{</span><br><span class="line"> struct timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (tk-></span>tkr_mono.clock == clock)</span><br><span class="line"> return <span class="number">0</span>;</span><br><span class="line"> stop_machine(change_clocksource, clock, NULL);</span><br><span class="line"> tick_clock_notify();</span><br><span class="line"> <span class="function"><span class="title">return</span> tk-></span>tkr_mono.clock == clock ? <span class="number">0</span> : -<span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-1-1、exynos4-clocksource-init"><a href="#2-1-1、exynos4-clocksource-init" class="headerlink" title="2.1.1、exynos4_clocksource_init()"></a>2.1.1、exynos4_clocksource_init()</h4><p>exynos将global timer注册成clocksource,虽然global timer拥有64bit的位宽,但是注册的时候把其当成32bit的clocksource注册。</p>
<figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> u32 notrace <span class="title">exynos4_read_count_32</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">readl_relaxed</span>(reg_base + EXYNOS4_MCT_G_CNT_L);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">cycle_t</span> <span class="title">exynos4_frc_read</span><span class="params">(<span class="keyword">struct</span> clocksource *cs)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">exynos4_read_count_32</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">struct</span> <span class="title class_">clocksource</span> mct_frc = {</span><br><span class="line"> .name = <span class="string">"mct-frc"</span>,</span><br><span class="line"> .rating = <span class="number">400</span>,</span><br><span class="line"> .read = exynos4_frc_read,</span><br><span class="line"> .mask = <span class="built_in">CLOCKSOURCE_MASK</span>(<span class="number">32</span>),</span><br><span class="line"> .flags = CLOCK_SOURCE_IS_CONTINUOUS,</span><br><span class="line"> .resume = exynos4_frc_resume,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> __init <span class="title">exynos4_clocksource_init</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">exynos4_mct_frc_start</span>();</span><br><span class="line"></span><br><span class="line"> exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;</span><br><span class="line"> exynos4_delay_timer.freq = clk_rate;</span><br><span class="line"> <span class="built_in">register_current_timer_delay</span>(&exynos4_delay_timer);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) exynos将global timer注册成clocksource */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">clocksource_register_hz</span>(&mct_frc, clk_rate))</span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"%s: can't register clocksource\n"</span>, mct_frc.name);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">sched_clock_register</span>(exynos4_read_sched_clock, <span class="number">32</span>, clk_rate);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="2-2、timekeeper"><a href="#2-2、timekeeper" class="headerlink" title="2.2、timekeeper"></a>2.2、timekeeper</h3><p>timerkeeper提供了几种时间:xtime、monotonic time、raw monotonic time、boot time。</p>
<ul>
<li><strong>xtime</strong> 即是wall time,和RTC时间一样可以表示当前的时刻,它的起始时间是公元0世纪0秒,精度大于RTC时间;</li>
<li><strong>monotonic time</strong> 从系统开机后到现在的累计时间,不过不计算系统休眠的时间;</li>
<li><strong>raw monotonic time</strong> 和monotonic time含义一样,不过更纯粹,不会受到NTP时间调整的影响;</li>
<li><strong>boot time</strong> 在monotonic time的基础上加上了系统休眠的时间,它代表着系统上电后的总时间。</li>
</ul>
<table>
<thead>
<tr>
<th>时间种类</th>
<th>精度(统计单位)</th>
<th>访问速度</th>
<th>累计休眠时间</th>
<th>受NTP调整的影响</th>
<th>获取函数</th>
</tr>
</thead>
<tbody><tr>
<td>RTC</td>
<td>低</td>
<td>慢</td>
<td>Yes</td>
<td>Yes</td>
<td></td>
</tr>
<tr>
<td>xtime</td>
<td>高</td>
<td>快</td>
<td>Yes</td>
<td>Yes</td>
<td>do_gettimeofday()、ktime_get_real_ts()、ktime_get_real()</td>
</tr>
<tr>
<td>monotonic</td>
<td>高</td>
<td>快</td>
<td>No</td>
<td>Yes</td>
<td>ktime_get()、ktime_get_ts64()</td>
</tr>
<tr>
<td>raw monotonic</td>
<td>高</td>
<td>快</td>
<td>No</td>
<td>No</td>
<td>ktime_get_raw()、getrawmonotonic64()</td>
</tr>
<tr>
<td>boot time</td>
<td>高</td>
<td>快</td>
<td>Yes</td>
<td>Yes</td>
<td>ktime_get_boottime()</td>
</tr>
</tbody></table>
<h4 id="2-2-1、timekeeper的定义"><a href="#2-2-1、timekeeper的定义" class="headerlink" title="2.2.1、timekeeper的定义"></a>2.2.1、timekeeper的定义</h4><p>虽然clocksource定时器只有一个,但是timekeeper提供了xtime、monotonic time、raw time、boot time等几种时间,所以timekeeper结构体中定义了多个变量来记住这些差值。</p>
<figure class="highlight zephir"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * struct timekeeper - Structure holding internal timekeeping values.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@tkr</span>_mono: The readout base structure for CLOCK_MONOTONIC</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@tkr</span>_raw: The readout base structure for CLOCK_MONOTONIC_RAW</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@xtime</span>_sec: Current CLOCK_REALTIME time in seconds</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ktime</span>_sec: Current CLOCK_MONOTONIC time in seconds</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@wall</span>_to_monotonic: CLOCK_REALTIME to CLOCK_MONOTONIC offset</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@offs</span>_real: Offset clock monotonic -> clock realtime</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@offs</span>_boot: Offset clock monotonic -> clock boottime</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@offs</span>_tai: Offset clock monotonic -> clock tai</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@tai</span>_offset: The current UTC to TAI offset in seconds</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@clock</span>_was_set_seq: The sequence number of clock was set events</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@next</span>_leap_ktime: CLOCK_MONOTONIC time value of a pending leap-second</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@raw</span>_time: Monotonic raw base time in timespec64 format</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@cycle</span>_interval: Number of clock cycles in one NTP interval</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@xtime</span>_interval: Number of clock shifted nano seconds in one NTP</span></span><br><span class="line"><span class="comment"> * interval.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@xtime</span>_remainder: Shifted nano seconds left over when rounding</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@cycle</span>_interval</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@raw</span>_interval: Raw nano seconds accumulated per NTP interval.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ntp</span>_error: Difference between accumulated time and NTP time in ntp</span></span><br><span class="line"><span class="comment"> * shifted nano seconds.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ntp</span>_error_shift: Shift conversion between clock shifted nano seconds and</span></span><br><span class="line"><span class="comment"> * ntp shifted nano seconds.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@last</span>_warning: Warning ratelimiter (DEBUG_TIMEKEEPING)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@underflow</span>_seen: Underflow warning flag (DEBUG_TIMEKEEPING)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@overflow</span>_seen: Overflow warning flag (DEBUG_TIMEKEEPING)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Note: For timespec(64) based interfaces wall_to_monotonic is what</span></span><br><span class="line"><span class="comment"> * we need to add to xtime (or xtime corrected for sub jiffie times)</span></span><br><span class="line"><span class="comment"> * to get to monotonic time. Monotonic is pegged at zero at system</span></span><br><span class="line"><span class="comment"> * boot time, so wall_to_monotonic will be negative, however, we will</span></span><br><span class="line"><span class="comment"> * ALWAYS keep the tv_nsec part positive so we can use the usual</span></span><br><span class="line"><span class="comment"> * normalization.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * wall_to_monotonic is moved after resume from suspend for the</span></span><br><span class="line"><span class="comment"> * monotonic time not to jump. We need to add total_sleep_time to</span></span><br><span class="line"><span class="comment"> * wall_to_monotonic to get the real boot based time offset.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * wall_to_monotonic is no longer the boot time, getboottime must be</span></span><br><span class="line"><span class="comment"> * used instead.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">struct timekeeper {</span><br><span class="line"> struct tk_read_base tkr_mono; </span><br><span class="line"> <span class="comment">// tkr_mono.xtime_nsec:xtime/monotonic time 的ns</span></span><br><span class="line"> <span class="comment">// tkr_mono.base:monotonic time的base部分</span></span><br><span class="line"> struct tk_read_base tkr_raw;</span><br><span class="line"> <span class="comment">// tkr_mono.base:raw time的base部分</span></span><br><span class="line"> u64 xtime_sec; <span class="comment">// xtime的sec</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> ktime_sec; <span class="comment">// monotonic time 的整sec</span></span><br><span class="line"> struct timespec64 wall_to_monotonic; <span class="comment">// xtime + wall_to_monotonic = monotonic time</span></span><br><span class="line"> ktime_t offs_real; <span class="comment">// monotonic time + offs_real = xtime,</span></span><br><span class="line"> <span class="comment">// 和wall_to_monotonic是相反的值</span></span><br><span class="line"> ktime_t offs_boot; <span class="comment">// monotonic time + offs_boot = boot time</span></span><br><span class="line"> ktime_t offs_tai;</span><br><span class="line"> s32 tai_offset;</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> clock_was_set_seq;</span><br><span class="line"> ktime_t next_leap_ktime;</span><br><span class="line"> struct timespec64 raw_time; <span class="comment">// raw time</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* The following members are for timekeeping internal use */</span></span><br><span class="line"> cycle_t cycle_interval;</span><br><span class="line"> u64 xtime_interval;</span><br><span class="line"> s64 xtime_remainder;</span><br><span class="line"> u32 raw_interval;</span><br><span class="line"> <span class="comment">/* The ntp_tick_length() value currently being used.</span></span><br><span class="line"><span class="comment"> * This cached copy ensures we consistently apply the tick</span></span><br><span class="line"><span class="comment"> * length for an entire tick, as ntp_tick_length may change</span></span><br><span class="line"><span class="comment"> * mid-tick, and we don't want to apply that new value to</span></span><br><span class="line"><span class="comment"> * the tick in progress.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> u64 ntp_tick;</span><br><span class="line"> <span class="comment">/* Difference between accumulated time and NTP time in ntp</span></span><br><span class="line"><span class="comment"> * shifted nano seconds. */</span></span><br><span class="line"> s64 ntp_error;</span><br><span class="line"> u32 ntp_error_shift;</span><br><span class="line"> u32 ntp_err_mult;</span><br><span class="line">#ifdef CONFIG_DEBUG_TIMEKEEPING</span><br><span class="line"> <span class="keyword">long</span> last_warning;</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * These simple flag variables are managed</span></span><br><span class="line"><span class="comment"> * without locks, which is racy, but they are</span></span><br><span class="line"><span class="comment"> * ok since we don't really care about being</span></span><br><span class="line"><span class="comment"> * super precise about how many events were</span></span><br><span class="line"><span class="comment"> * seen, just that a problem was observed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">int</span> underflow_seen;</span><br><span class="line"> <span class="keyword">int</span> overflow_seen;</span><br><span class="line">#endif</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h4 id="2-2-2、timekeeper的初始化"><a href="#2-2-2、timekeeper的初始化" class="headerlink" title="2.2.2、timekeeper的初始化"></a>2.2.2、timekeeper的初始化</h4><p>timekeeper在初始化的过程中,读取当前的RTC值和clocksource的值,来初始化xtime、monotonic time、raw time、boot time,以及各种offset。</p>
<figure class="highlight sas"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">void __init timekeeping_init(void)</span><br><span class="line">{</span><br><span class="line"> struct timekeeper <span class="comment">*tk = &tk_core.timekeeper;</span></span><br><span class="line"> struct clocksource <span class="comment">*clock;</span></span><br><span class="line"> unsigned long flags;</span><br><span class="line"> struct timespec64 now, boot, tmp;</span><br><span class="line"></span><br><span class="line"> read_persistent_clock64(<span class="variable">&now</span>);</span><br><span class="line"> <span class="keyword">if</span> (!timespec64_valid_strict(<span class="variable">&now</span>)) {</span><br><span class="line"> pr_war<span class="meta">n</span>(<span class="string">"WARNING: Persistent clock returned invalid value!\n"</span></span><br><span class="line"> <span class="string">" Check your CMOS/BIOS settings.\n"</span>);</span><br><span class="line"> now.tv_sec = 0;</span><br><span class="line"> now.tv_nsec = 0;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (now.tv_sec || now.tv_nsec)</span><br><span class="line"> persistent_clock_exists = true;</span><br><span class="line"></span><br><span class="line"> read_boot_clock64(<span class="variable">&boot</span>);</span><br><span class="line"> <span class="keyword">if</span> (!timespec64_valid_strict(<span class="variable">&boot</span>)) {</span><br><span class="line"> pr_war<span class="meta">n</span>(<span class="string">"WARNING: Boot clock returned invalid value!\n"</span></span><br><span class="line"> <span class="string">" Check your CMOS/BIOS settings.\n"</span>);</span><br><span class="line"> boot.tv_sec = 0;</span><br><span class="line"> boot.tv_nsec = 0;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> raw_spin_lock_irqsave(<span class="variable">&timekeeper_lock</span>, flags);</span><br><span class="line"> write_seqcount_begi<span class="meta">n</span>(<span class="variable">&tk_core.</span>seq);</span><br><span class="line"> ntp_init();</span><br><span class="line"></span><br><span class="line"> clock = clocksource_default_clock();</span><br><span class="line"> <span class="keyword">if</span> (clock->enable)</span><br><span class="line"> clock->enable(clock);</span><br><span class="line"> tk_setup_internals(tk, clock);</span><br><span class="line"></span><br><span class="line"> tk_set_x<span class="meta">time</span>(tk, <span class="variable">&now</span>);</span><br><span class="line"> tk->raw_time.tv_sec = 0;</span><br><span class="line"> tk->raw_time.tv_nsec = 0;</span><br><span class="line"> <span class="keyword">if</span> (boot.tv_sec == 0 <span class="variable">&&</span> boot.tv_nsec == 0)</span><br><span class="line"> boot = tk_x<span class="meta">time</span>(tk);</span><br><span class="line"></span><br><span class="line"> set_normalized_timespec64(<span class="variable">&tmp</span>, -boot.tv_sec, -boot.tv_nsec);</span><br><span class="line"> tk_set_wall_to_mono(tk, tmp);</span><br><span class="line"></span><br><span class="line"> timekeeping_up<span class="meta">date</span>(tk, TK_MIRROR | TK_CLOCK_WAS_SET);</span><br><span class="line"></span><br><span class="line"> write_seqcount_end(<span class="variable">&tk_core.</span>seq);</span><br><span class="line"> raw_spin_unlock_irqrestore(<span class="variable">&timekeeper_lock</span>, flags);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>timekeeper原理上的初始化是在timekeeping_init()函数中完成的,但是read_persistent_clock64()、read_boot_clock64()都是空函数,所以实际上的初始化是另外的路径:rtc_hctosys() -> do_settimeofday64(),rtc初始化的时候重新配置timekeeper。</p>
<figure class="highlight sas"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line">static int __init rtc_hctosys(void)</span><br><span class="line">{</span><br><span class="line"> int err = -ENODEV;</span><br><span class="line"> struct rtc_time tm;</span><br><span class="line"> struct timespec64 tv64 = {</span><br><span class="line"> .tv_nsec = NSEC_PER_SEC >> 1,</span><br><span class="line"> };</span><br><span class="line"> struct rtc_device <span class="comment">*rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rtc == <span class="keyword">NULL</span>) {</span><br><span class="line"> pr_info(<span class="string">"unable to open rtc device (%s)\n"</span>,</span><br><span class="line"> CONFIG_RTC_HCTOSYS_DEVICE);</span><br><span class="line"> <span class="keyword">goto</span> err_open;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) 读取当前的rtc时间 */</span></span><br><span class="line"> err = rtc_read_<span class="meta">time</span>(rtc, <span class="variable">&tm</span>);</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> dev_err(rtc->dev.parent,</span><br><span class="line"> <span class="string">"hctosys: unable to read the hardware clock\n"</span>);</span><br><span class="line"> <span class="keyword">goto</span> err_read;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> tv64.tv_sec = rtc_tm_to_time64(<span class="variable">&tm</span>);</span><br><span class="line"> tv64.tv_nsec = tm.tm_cnt <span class="comment">* (1000000000 / 32768);</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* (2) 根据rtc时间配置xtime */</span></span><br><span class="line"> err = do_settimeofday64(<span class="variable">&tv64</span>);</span><br><span class="line"></span><br><span class="line"> dev_info(rtc->dev.parent,</span><br><span class="line"> <span class="string">"setting system clock to "</span></span><br><span class="line"> <span class="string">"%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n"</span>,</span><br><span class="line"> tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,</span><br><span class="line"> tm.tm_hour, tm.tm_min, tm.tm_sec,</span><br><span class="line"> (long long) tv64.tv_sec);</span><br><span class="line"></span><br><span class="line">err_read:</span><br><span class="line"> rtc_class_<span class="meta">close</span>(rtc);</span><br><span class="line"></span><br><span class="line">err_open:</span><br><span class="line"> rtc_hctosys_ret = err;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> err;</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">int do_settimeofday64(const struct timespec64 <span class="comment">*ts)</span></span><br><span class="line"><span class="comment">{</span></span><br><span class="line"><span class="comment"> struct timekeeper *tk = &tk_core.timekeeper;</span></span><br><span class="line"> struct timespec64 ts_delta, xt;</span><br><span class="line"> unsigned long flags;</span><br><span class="line"> int ret = 0;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!timespec64_valid_strict(ts))</span><br><span class="line"> <span class="keyword">return</span> -EINVAL;</span><br><span class="line"></span><br><span class="line"> raw_spin_lock_irqsave(<span class="variable">&timekeeper_lock</span>, flags);</span><br><span class="line"> write_seqcount_begi<span class="meta">n</span>(<span class="variable">&tk_core.</span>seq);</span><br><span class="line"></span><br><span class="line"> timekeeping_forward_now(tk);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.1) 读取当前的xtime,计算rtc time和xtime之间的差值 */</span></span><br><span class="line"> xt = tk_x<span class="meta">time</span>(tk);</span><br><span class="line"> ts_delta.tv_sec = ts->tv_sec - xt.tv_sec;</span><br><span class="line"> ts_delta.tv_nsec = ts->tv_nsec - xt.tv_nsec;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (timespec64_compare(<span class="variable">&tk</span>->wall_to_monotonic, <span class="variable">&ts_delta</span>) > 0) {</span><br><span class="line"> ret = -EINVAL;</span><br><span class="line"> <span class="keyword">goto</span> <span class="keyword">out</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.2) 将差值追加到offset;tk->wall_to_monotonic、tk->offs_real */</span></span><br><span class="line"> tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts_delta));</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.3) 更新xtime */</span></span><br><span class="line"> tk_set_x<span class="meta">time</span>(tk, ts);</span><br><span class="line"><span class="keyword">out</span>:</span><br><span class="line"> timekeeping_up<span class="meta">date</span>(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);</span><br><span class="line"></span><br><span class="line"> write_seqcount_end(<span class="variable">&tk_core.</span>seq);</span><br><span class="line"> raw_spin_unlock_irqrestore(<span class="variable">&timekeeper_lock</span>, flags);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* signal hrtimers about time change */</span></span><br><span class="line"> clock_was_set();</span><br><span class="line"> notify_time_up<span class="meta">date</span>();</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-2-3、timekeeper的update"><a href="#2-2-3、timekeeper的update" class="headerlink" title="2.2.3、timekeeper的update"></a>2.2.3、timekeeper的update</h4><p>clocksource定时器的值要定时的读出来,并且把增量加到timekeeper中,不然clocksource定时器会溢出。这个定时更新的时间一般是1 tick,调用的函数是update_wall_time():</p>
<figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br></pre></td><td class="code"><pre><span class="line">void <span class="title function_ invoke__">update_wall_time</span>(void)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timekeeper</span> *real_tk = &tk_core.timekeeper;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timekeeper</span> *tk = &shadow_timekeeper;</span><br><span class="line"> cycle_t offset;</span><br><span class="line"> int shift = <span class="number">0</span>, maxshift;</span><br><span class="line"> unsigned int clock_set = <span class="number">0</span>;</span><br><span class="line"> unsigned long flags;</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">raw_spin_lock_irqsave</span>(&timekeeper_lock, flags);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Make sure we're fully resumed: */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_ invoke__">unlikely</span>(timekeeping_suspended))</span><br><span class="line"> goto out;</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET</span><br><span class="line"> offset = real_tk<span class="punctuation">-></span>cycle_interval;</span><br><span class="line">#<span class="keyword">else</span></span><br><span class="line"> <span class="comment">/* (1) 获取clocksource和上一次update之间的offset */</span></span><br><span class="line"> offset = <span class="title function_ invoke__">clocksource_delta</span>(tk<span class="punctuation">-></span>tkr_mono.<span class="title function_ invoke__">read</span>(tk<span class="punctuation">-></span>tkr_mono.clock),</span><br><span class="line"> tk<span class="punctuation">-></span>tkr_mono.cycle_last, tk<span class="punctuation">-></span>tkr_mono.mask);</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Check if there's really nothing to do */</span></span><br><span class="line"> <span class="keyword">if</span> (offset < real_tk<span class="punctuation">-></span>cycle_interval)</span><br><span class="line"> goto out;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Do some additional sanity checking */</span></span><br><span class="line"> <span class="title function_ invoke__">timekeeping_check_update</span>(real_tk, offset);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * With NO_HZ we may have to accumulate many cycle_intervals</span></span><br><span class="line"><span class="comment"> * (think "ticks") worth of time at once. To do this efficiently,</span></span><br><span class="line"><span class="comment"> * we calculate the largest doubling multiple of cycle_intervals</span></span><br><span class="line"><span class="comment"> * that is smaller than the offset. We then accumulate that</span></span><br><span class="line"><span class="comment"> * chunk in one go, and then try to consume the next smaller</span></span><br><span class="line"><span class="comment"> * doubled multiple.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> shift = <span class="title function_ invoke__">ilog2</span>(offset) - <span class="title function_ invoke__">ilog2</span>(tk<span class="punctuation">-></span>cycle_interval);</span><br><span class="line"> shift = <span class="title function_ invoke__">max</span>(<span class="number">0</span>, shift);</span><br><span class="line"> <span class="comment">/* Bound shift to one less than what overflows tick_length */</span></span><br><span class="line"> maxshift = (<span class="number">64</span> - (<span class="title function_ invoke__">ilog2</span>(<span class="title function_ invoke__">ntp_tick_length</span>())+<span class="number">1</span>)) - <span class="number">1</span>;</span><br><span class="line"> shift = <span class="title function_ invoke__">min</span>(shift, maxshift);</span><br><span class="line"> <span class="comment">/* (2) 如果offset的值是多个cycle_interval,</span></span><br><span class="line"><span class="comment"> 不要一次update,使用2的n次方cycle_interval的方式逐个update。</span></span><br><span class="line"><span class="comment"> tk->cycle_interval的值在tk_setup_internals()时被赋值,默认为1 tick */</span></span><br><span class="line"> <span class="keyword">while</span> (offset >= tk<span class="punctuation">-></span>cycle_interval) {</span><br><span class="line"> <span class="comment">/* (3) 将offset更新到timekeeper中 */</span></span><br><span class="line"> offset = <span class="title function_ invoke__">logarithmic_accumulation</span>(tk, offset, shift,</span><br><span class="line"> &clock_set);</span><br><span class="line"> <span class="keyword">if</span> (offset < tk<span class="punctuation">-></span>cycle_interval<<shift)</span><br><span class="line"> shift--;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* correct the clock when NTP error is too big */</span></span><br><span class="line"> <span class="title function_ invoke__">timekeeping_adjust</span>(tk, offset);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * XXX This can be killed once everyone converts</span></span><br><span class="line"><span class="comment"> * to the new update_vsyscall.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_ invoke__">old_vsyscall_fixup</span>(tk);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Finally, make sure that after the rounding</span></span><br><span class="line"><span class="comment"> * xtime_nsec isn't larger than NSEC_PER_SEC</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> clock_set |= <span class="title function_ invoke__">accumulate_nsecs_to_secs</span>(tk);</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">write_seqcount_begin</span>(&tk_core.seq);</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Update the real timekeeper.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * We could avoid this memcpy by switching pointers, but that</span></span><br><span class="line"><span class="comment"> * requires changes to all other timekeeper usage sites as</span></span><br><span class="line"><span class="comment"> * well, i.e. move the timekeeper pointer getter into the</span></span><br><span class="line"><span class="comment"> * spinlocked/seqcount protected sections. And we trade this</span></span><br><span class="line"><span class="comment"> * memcpy under the tk_core.seq against one before we start</span></span><br><span class="line"><span class="comment"> * updating.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (4) */</span></span><br><span class="line"> <span class="title function_ invoke__">timekeeping_update</span>(tk, clock_set);</span><br><span class="line"> <span class="title function_ invoke__">memcpy</span>(real_tk, tk, <span class="title function_ invoke__">sizeof</span>(*tk));</span><br><span class="line"> <span class="comment">/* The memcpy must come last. Do not put anything here! */</span></span><br><span class="line"> <span class="title function_ invoke__">write_seqcount_end</span>(&tk_core.seq);</span><br><span class="line">out:</span><br><span class="line"> <span class="title function_ invoke__">raw_spin_unlock_irqrestore</span>(&timekeeper_lock, flags);</span><br><span class="line"> <span class="keyword">if</span> (clock_set)</span><br><span class="line"> <span class="comment">/* Have to call _delayed version, since in irq context*/</span></span><br><span class="line"> <span class="title function_ invoke__">clock_was_set_delayed</span>();</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line"><span class="keyword">static</span> cycle_t <span class="title function_ invoke__">logarithmic_accumulation</span>(<span class="keyword">struct</span> <span class="title class_">timekeeper</span> *tk, cycle_t offset,</span><br><span class="line"> <span class="type">u32</span> shift,</span><br><span class="line"> unsigned int *clock_set)</span><br><span class="line">{</span><br><span class="line"> cycle_t interval = tk<span class="punctuation">-></span>cycle_interval << shift;</span><br><span class="line"> <span class="type">u64</span> raw_nsecs;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* If the offset is smaller than a shifted interval, do nothing */</span></span><br><span class="line"> <span class="keyword">if</span> (offset < interval)</span><br><span class="line"> <span class="keyword">return</span> offset;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Accumulate one shifted interval */</span></span><br><span class="line"> offset -= interval;</span><br><span class="line"> <span class="comment">/* (3.1) 更新cycle_last */</span></span><br><span class="line"> tk<span class="punctuation">-></span>tkr_mono.cycle_last += interval;</span><br><span class="line"> tk<span class="punctuation">-></span>tkr_raw.cycle_last += interval;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (3.2) 更新xtime:</span></span><br><span class="line"><span class="comment"> tk->tkr_mono.xtime_nsec</span></span><br><span class="line"><span class="comment"> tk->xtime_sec */</span></span><br><span class="line"> tk<span class="punctuation">-></span>tkr_mono.xtime_nsec += tk<span class="punctuation">-></span>xtime_interval << shift;</span><br><span class="line"> *clock_set |= <span class="title function_ invoke__">accumulate_nsecs_to_secs</span>(tk);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Accumulate raw time */</span></span><br><span class="line"> <span class="comment">/* (3.3) 更新raw time:</span></span><br><span class="line"><span class="comment"> tk->raw_time.tv_nsec</span></span><br><span class="line"><span class="comment"> tk->raw_time.tv_sec */</span></span><br><span class="line"> raw_nsecs = (<span class="type">u64</span>)tk<span class="punctuation">-></span>raw_interval << shift;</span><br><span class="line"> raw_nsecs += tk<span class="punctuation">-></span>raw_time.tv_nsec;</span><br><span class="line"> <span class="keyword">if</span> (raw_nsecs >= NSEC_PER_SEC) {</span><br><span class="line"> <span class="type">u64</span> raw_secs = raw_nsecs;</span><br><span class="line"> raw_nsecs = <span class="title function_ invoke__">do_div</span>(raw_secs, NSEC_PER_SEC);</span><br><span class="line"> tk<span class="punctuation">-></span>raw_time.tv_sec += raw_secs;</span><br><span class="line"> }</span><br><span class="line"> tk<span class="punctuation">-></span>raw_time.tv_nsec = raw_nsecs;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Accumulate error between NTP and clock interval */</span></span><br><span class="line"> tk<span class="punctuation">-></span>ntp_error += tk<span class="punctuation">-></span>ntp_tick << shift;</span><br><span class="line"> tk<span class="punctuation">-></span>ntp_error -= (tk<span class="punctuation">-></span>xtime_interval + tk<span class="punctuation">-></span>xtime_remainder) <<</span><br><span class="line"> (tk<span class="punctuation">-></span>ntp_error_shift + shift);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> offset;</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line"><span class="keyword">static</span> void <span class="title function_ invoke__">timekeeping_update</span>(<span class="keyword">struct</span> <span class="title class_">timekeeper</span> *tk, unsigned int action)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (action & TK_CLEAR_NTP) {</span><br><span class="line"> tk<span class="punctuation">-></span>ntp_error = <span class="number">0</span>;</span><br><span class="line"> <span class="title function_ invoke__">ntp_clear</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">tk_update_leap_state</span>(tk);</span><br><span class="line"> <span class="comment">/* (4.1) update monotonic time */</span></span><br><span class="line"> <span class="title function_ invoke__">tk_update_ktime_data</span>(tk);</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">update_vsyscall</span>(tk);</span><br><span class="line"> <span class="title function_ invoke__">update_pvclock_gtod</span>(tk, action & TK_CLOCK_WAS_SET);</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">update_fast_timekeeper</span>(&tk<span class="punctuation">-></span>tkr_mono, &tk_fast_mono);</span><br><span class="line"> <span class="title function_ invoke__">update_fast_timekeeper</span>(&tk<span class="punctuation">-></span>tkr_raw, &tk_fast_raw);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (action & TK_CLOCK_WAS_SET)</span><br><span class="line"> tk<span class="punctuation">-></span>clock_was_set_seq++;</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * The mirroring of the data to the shadow-timekeeper needs</span></span><br><span class="line"><span class="comment"> * to happen last here to ensure we don't over-write the</span></span><br><span class="line"><span class="comment"> * timekeeper structure on the next update with stale data</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (action & TK_MIRROR)</span><br><span class="line"> <span class="title function_ invoke__">memcpy</span>(&shadow_timekeeper, &tk_core.timekeeper,</span><br><span class="line"> <span class="title function_ invoke__">sizeof</span>(tk_core.timekeeper));</span><br><span class="line">}</span><br><span class="line">||→</span><br><span class="line"><span class="keyword">static</span> inline void <span class="title function_ invoke__">tk_update_ktime_data</span>(<span class="keyword">struct</span> <span class="title class_">timekeeper</span> *tk)</span><br><span class="line">{</span><br><span class="line"> <span class="type">u64</span> seconds;</span><br><span class="line"> <span class="type">u32</span> nsec;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * The xtime based monotonic readout is:</span></span><br><span class="line"><span class="comment"> * nsec = (xtime_sec + wtm_sec) * 1e9 + wtm_nsec + now();</span></span><br><span class="line"><span class="comment"> * The ktime based monotonic readout is:</span></span><br><span class="line"><span class="comment"> * nsec = base_mono + now();</span></span><br><span class="line"><span class="comment"> * ==> base_mono = (xtime_sec + wtm_sec) * 1e9 + wtm_nsec</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (4.1.1) update tk->tkr_mono.base的值,</span></span><br><span class="line"><span class="comment"> = tk->xtime_sec + tk->wall_to_monotonic,</span></span><br><span class="line"><span class="comment"> tk->tkr_mono.xtime_nsec 没有计算到base中 */</span></span><br><span class="line"> seconds = (<span class="type">u64</span>)(tk<span class="punctuation">-></span>xtime_sec + tk<span class="punctuation">-></span>wall_to_monotonic.tv_sec);</span><br><span class="line"> nsec = (<span class="type">u32</span>) tk<span class="punctuation">-></span>wall_to_monotonic.tv_nsec;</span><br><span class="line"> tk<span class="punctuation">-></span>tkr_mono.base = <span class="title function_ invoke__">ns_to_ktime</span>(seconds * NSEC_PER_SEC + nsec);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Update the monotonic raw base */</span></span><br><span class="line"> <span class="comment">/* (4.1.2) update tk->tkr_raw.base的值,</span></span><br><span class="line"><span class="comment"> 直接转换tk->raw_time */</span></span><br><span class="line"> tk<span class="punctuation">-></span>tkr_raw.base = <span class="title function_ invoke__">timespec64_to_ktime</span>(tk<span class="punctuation">-></span>raw_time);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * The sum of the nanoseconds portions of xtime and</span></span><br><span class="line"><span class="comment"> * wall_to_monotonic can be greater/equal one second. Take</span></span><br><span class="line"><span class="comment"> * this into account before updating tk->ktime_sec.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (4.1.3) update tk->ktime_sec的值</span></span><br><span class="line"><span class="comment"> nsec += (u32)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);</span></span><br><span class="line"><span class="comment"> if (nsec >= NSEC_PER_SEC)</span></span><br><span class="line"><span class="comment"> seconds++;</span></span><br><span class="line"><span class="comment"> tk->ktime_sec = seconds;</span></span><br><span class="line"><span class="comment">}</span></span><br></pre></td></tr></table></figure>
<h4 id="2-2-4、timekeeper的获取"><a href="#2-2-4、timekeeper的获取" class="headerlink" title="2.2.4、timekeeper的获取"></a>2.2.4、timekeeper的获取</h4><ul>
<li><strong>xtime/wall time 的获取:</strong></li>
</ul>
<p>do_gettimeofday()、ktime_get_real_ts()最后调用的getnstimeofday64() -> __getnstimeofday64()获取到xtime:</p>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line">int __getnstimeofday64(struct timespec64 *ts)</span><br><span class="line">{</span><br><span class="line"> struct timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"> unsigned long seq;</span><br><span class="line"> s64 nsecs = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqcount_begin(&tk_core.seq);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) sec直接从变量tk->xtime_sec获取到,</span></span><br><span class="line"><span class="comment"> 即上一tick更新的值 */</span></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span><span class="function"><span class="title">tv_sec</span> = tk-></span>xtime_sec;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* (2) nsec需要更新最新的值:tk->tkr_mono.xtime_nsec + delta</span></span><br><span class="line"><span class="comment"> delta是距离上一次tick更新的差值 */</span></span><br><span class="line"> <span class="function"><span class="title">nsecs</span> = timekeeping_get_ns(&tk-></span>tkr_mono);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">while</span> (read_seqcount_retry(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>tv_nsec = <span class="number">0</span>;</span><br><span class="line"> timespec64_add_ns(ts, nsecs);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Do not bail out early, in case there were callers still using</span></span><br><span class="line"><span class="comment"> * the value, even in the face of the WARN_ON.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (unlikely(timekeeping_suspended))</span><br><span class="line"> return -EAGAIN;</span><br><span class="line"> return <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">static inline s64 timekeeping_get_ns(struct tk_read_base *tkr)</span><br><span class="line">{</span><br><span class="line"> cycle_t delta;</span><br><span class="line"> s64 nsec;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.1) 获取距离上一次tick更新,timer的delta值 */</span></span><br><span class="line"> delta = timekeeping_get_delta(tkr);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.2) delta加上上一次的nsec tkr->xtime_nsec,</span></span><br><span class="line"><span class="comment"> 即为最新的ns值 */</span></span><br><span class="line"> <span class="function"><span class="title">nsec</span> = (delta * tkr-></span><span class="function"><span class="title">mult</span> + tkr-></span><span class="function"><span class="title">xtime_nsec</span>) >> tkr-></span>shift;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* If arch requires, add in get_arch_timeoffset() */</span></span><br><span class="line"> return nsec + arch_gettimeoffset();</span><br><span class="line">}</span><br><span class="line">||→</span><br><span class="line">static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)</span><br><span class="line">{</span><br><span class="line"> struct timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"> cycle_t now, last, mask, max, delta;</span><br><span class="line"> unsigned int seq;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Since we're called holding a seqlock, the data may shift</span></span><br><span class="line"><span class="comment"> * under us while we're doing the calculation. This can cause</span></span><br><span class="line"><span class="comment"> * false positives, since we'd note a problem but throw the</span></span><br><span class="line"><span class="comment"> * results away. So nest another seqlock here to atomically</span></span><br><span class="line"><span class="comment"> * grab the points we are checking with.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqcount_begin(&tk_core.seq);</span><br><span class="line"> <span class="comment">/* (2.1.1) 使用read函数读取当前timer的计数 */</span></span><br><span class="line"> <span class="function"><span class="title">now</span> = tkr-></span><span class="function"><span class="title">read</span>(tkr-></span>clock);</span><br><span class="line"> <span class="function"><span class="title">last</span> = tkr-></span>cycle_last;</span><br><span class="line"> <span class="function"><span class="title">mask</span> = tkr-></span>mask;</span><br><span class="line"> <span class="function"><span class="title">max</span> = tkr-></span><span class="function"><span class="title">clock</span>-></span>max_cycles;</span><br><span class="line"> } <span class="keyword">while</span> (read_seqcount_retry(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.1.2) 使用公式:(now - last) & mask,</span></span><br><span class="line"><span class="comment"> 计算delta值 */</span></span><br><span class="line"> delta = clocksource_delta(now, last, mask);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Try to catch underflows by checking if we are seeing small</span></span><br><span class="line"><span class="comment"> * mask-relative negative values.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (unlikely((~delta & mask) < (mask >> <span class="number">3</span>))) {</span><br><span class="line"> <span class="function"><span class="title">tk</span>-></span>underflow_seen = <span class="number">1</span>;</span><br><span class="line"> delta = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Cap delta value to the max_cycles values to avoid mult overflows */</span></span><br><span class="line"> <span class="keyword">if</span> (unlikely(delta > max)) {</span><br><span class="line"> <span class="function"><span class="title">tk</span>-></span>overflow_seen = <span class="number">1</span>;</span><br><span class="line"> <span class="function"><span class="title">delta</span> = tkr-></span><span class="function"><span class="title">clock</span>-></span>max_cycles;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return delta;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>ktime_get_real()使用monotonic time再加上差值timekeeper.offs_real的方法来获取xtime:</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> inline ktime_t <span class="title">ktime_get_real</span>(<span class="params"><span class="keyword">void</span></span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">return</span> ktime_get_with_offset(TK_OFFS_REAL);</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line"><span class="keyword">static</span> ktime_t *offsets[TK_OFFS_MAX] = {</span><br><span class="line"> [<span class="meta">TK_OFFS_REAL</span>] = &tk_core.timekeeper.offs_real,</span><br><span class="line"> [<span class="meta">TK_OFFS_BOOT</span>] = &tk_core.timekeeper.offs_boot,</span><br><span class="line"> [<span class="meta">TK_OFFS_TAI</span>] = &tk_core.timekeeper.offs_tai,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function">ktime_t <span class="title">ktime_get_with_offset</span>(<span class="params"><span class="built_in">enum</span> tk_offsets offs</span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">struct</span> timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"> unsigned <span class="built_in">int</span> seq;</span><br><span class="line"> ktime_t <span class="keyword">base</span>, *offset = offsets[offs];</span><br><span class="line"> s64 nsecs;</span><br><span class="line"></span><br><span class="line"> WARN_ON(timekeeping_suspended);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqcount_begin(&tk_core.seq);</span><br><span class="line"> <span class="comment">/* (1) monotonic time = tk->tkr_mono.base,</span></span><br><span class="line"><span class="comment"> offset = timekeeper.offs_real */</span></span><br><span class="line"> <span class="keyword">base</span> = ktime_add(tk->tkr_mono.<span class="keyword">base</span>, *offset);</span><br><span class="line"> <span class="comment">/* (2) nsec需要更新最新的值:tk->tkr_mono.xtime_nsec + delta</span></span><br><span class="line"><span class="comment"> delta是距离上一次tick更新的差值 */</span></span><br><span class="line"> nsecs = timekeeping_get_ns(&tk->tkr_mono);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">while</span> (read_seqcount_retry(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ktime_add_ns(<span class="keyword">base</span>, nsecs);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li><strong>monotonic time 的获取;</strong></li>
</ul>
<p>ktime_get()直接获取monotonic time:</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">ktime_t <span class="title">ktime_get</span>(<span class="params"><span class="keyword">void</span></span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">struct</span> timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"> unsigned <span class="built_in">int</span> seq;</span><br><span class="line"> ktime_t <span class="keyword">base</span>;</span><br><span class="line"> s64 nsecs;</span><br><span class="line"></span><br><span class="line"> WARN_ON(timekeeping_suspended);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqcount_begin(&tk_core.seq);</span><br><span class="line"> <span class="comment">/* (1) monotonic time = tk->tkr_mono.base */</span></span><br><span class="line"> <span class="keyword">base</span> = tk->tkr_mono.<span class="keyword">base</span>;</span><br><span class="line"> <span class="comment">/* (2) nsec需要更新最新的值:tk->tkr_mono.xtime_nsec + delta</span></span><br><span class="line"><span class="comment"> delta是距离上一次tick更新的差值 */</span></span><br><span class="line"> nsecs = timekeeping_get_ns(&tk->tkr_mono);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">while</span> (read_seqcount_retry(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ktime_add_ns(<span class="keyword">base</span>, nsecs);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>ktime_get_ts64()通过xtime加上差值tk->wall_to_monotonic的方法来获取monotonic time:</p>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">void ktime_get_ts64(struct timespec64 *ts)</span><br><span class="line">{</span><br><span class="line"> struct timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"> struct timespec64 tomono;</span><br><span class="line"> s64 nsec;</span><br><span class="line"> unsigned int seq;</span><br><span class="line"></span><br><span class="line"> WARN_ON(timekeeping_suspended);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqcount_begin(&tk_core.seq);</span><br><span class="line"> <span class="comment">/* (1) 获取xtime */</span></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span><span class="function"><span class="title">tv_sec</span> = tk-></span>xtime_sec;</span><br><span class="line"> <span class="function"><span class="title">nsec</span> = timekeeping_get_ns(&tk-></span>tkr_mono);</span><br><span class="line"> <span class="comment">/* (2) 加上xtime和monotonic之间的差值tk->wall_to_monotonic */</span></span><br><span class="line"> <span class="function"><span class="title">tomono</span> = tk-></span>wall_to_monotonic;</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">while</span> (read_seqcount_retry(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>tv_sec += tomono.tv_sec;</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>tv_nsec = <span class="number">0</span>;</span><br><span class="line"> timespec64_add_ns(ts, nsec + tomono.tv_nsec);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li><strong>raw monotonic time 的获取;</strong></li>
</ul>
<p>ktime_get_raw()通过tk->tkr_raw.base获取raw monotonic time:</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">ktime_t <span class="title">ktime_get_raw</span>(<span class="params"><span class="keyword">void</span></span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">struct</span> timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"> unsigned <span class="built_in">int</span> seq;</span><br><span class="line"> ktime_t <span class="keyword">base</span>;</span><br><span class="line"> s64 nsecs;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqcount_begin(&tk_core.seq);</span><br><span class="line"> <span class="comment">/* (1) raw monotonic time = tk->tkr_raw.base */</span></span><br><span class="line"> <span class="keyword">base</span> = tk->tkr_raw.<span class="keyword">base</span>;</span><br><span class="line"> <span class="comment">/* (2) nsec需要更新最新的值:tk->tkr_raw.xtime_nsec + delta</span></span><br><span class="line"><span class="comment"> delta是距离上一次tick更新的差值 */</span></span><br><span class="line"> nsecs = timekeeping_get_ns(&tk->tkr_raw);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">while</span> (read_seqcount_retry(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ktime_add_ns(<span class="keyword">base</span>, nsecs);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>getrawmonotonic64()通过tk->raw_time获取raw monotonic time:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">getrawmonotonic64</span><span class="params">(<span class="keyword">struct</span> timespec64 *ts)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timekeeper</span> *tk = &tk_core.timekeeper;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timespec64</span> ts64;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> seq;</span><br><span class="line"> s64 nsecs;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = <span class="built_in">read_seqcount_begin</span>(&tk_core.seq);</span><br><span class="line"> nsecs = <span class="built_in">timekeeping_get_ns</span>(&tk->tkr_raw);</span><br><span class="line"> ts64 = tk->raw_time;</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">while</span> (<span class="built_in">read_seqcount_retry</span>(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">timespec64_add_ns</span>(&ts64, nsecs);</span><br><span class="line"> *ts = ts64;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li><strong>boot time 的获取;</strong></li>
</ul>
<p>ktime_get_boottime()使用monotonic time再加上差值timekeeper.offs_boot的方法来获取boot time:</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> inline ktime_t <span class="title">ktime_get_boottime</span>(<span class="params"><span class="keyword">void</span></span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">return</span> ktime_get_with_offset(TK_OFFS_BOOT);</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line"><span class="keyword">static</span> ktime_t *offsets[TK_OFFS_MAX] = {</span><br><span class="line"> [<span class="meta">TK_OFFS_REAL</span>] = &tk_core.timekeeper.offs_real,</span><br><span class="line"> [<span class="meta">TK_OFFS_BOOT</span>] = &tk_core.timekeeper.offs_boot,</span><br><span class="line"> [<span class="meta">TK_OFFS_TAI</span>] = &tk_core.timekeeper.offs_tai,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function">ktime_t <span class="title">ktime_get_with_offset</span>(<span class="params"><span class="built_in">enum</span> tk_offsets offs</span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">struct</span> timekeeper *tk = &tk_core.timekeeper;</span><br><span class="line"> unsigned <span class="built_in">int</span> seq;</span><br><span class="line"> ktime_t <span class="keyword">base</span>, *offset = offsets[offs];</span><br><span class="line"> s64 nsecs;</span><br><span class="line"></span><br><span class="line"> WARN_ON(timekeeping_suspended);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqcount_begin(&tk_core.seq);</span><br><span class="line"> <span class="comment">/* (1) monotonic time = tk->tkr_mono.base,</span></span><br><span class="line"><span class="comment"> offset = timekeeper.offs_boot */</span></span><br><span class="line"> <span class="keyword">base</span> = ktime_add(tk->tkr_mono.<span class="keyword">base</span>, *offset);</span><br><span class="line"> <span class="comment">/* (2) nsec需要更新最新的值:tk->tkr_mono.xtime_nsec + delta</span></span><br><span class="line"><span class="comment"> delta是距离上一次tick更新的差值 */</span></span><br><span class="line"> nsecs = timekeeping_get_ns(&tk->tkr_mono);</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">while</span> (read_seqcount_retry(&tk_core.seq, seq));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ktime_add_ns(<span class="keyword">base</span>, nsecs);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-2-5、timekeeper-suspend"><a href="#2-2-5、timekeeper-suspend" class="headerlink" title="2.2.5、timekeeper suspend"></a>2.2.5、timekeeper suspend</h4><p>系统在进入suspend以后,clocksource不会再工作,这部分时间会计入xtime和boot time,但是不会计入monotonic time。</p>
<figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">timekeeping_resume</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timekeeper</span> *tk = &tk_core.timekeeper;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">clocksource</span> *clock = tk->tkr_mono.clock;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> flags;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timespec64</span> ts_new, ts_delta;</span><br><span class="line"> <span class="type">cycle_t</span> cycle_now, cycle_delta;</span><br><span class="line"></span><br><span class="line"> sleeptime_injected = <span class="literal">false</span>;</span><br><span class="line"> <span class="built_in">read_persistent_clock64</span>(&ts_new);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">clockevents_resume</span>();</span><br><span class="line"> <span class="built_in">clocksource_resume</span>();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">raw_spin_lock_irqsave</span>(&timekeeper_lock, flags);</span><br><span class="line"> <span class="built_in">write_seqcount_begin</span>(&tk_core.seq);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * After system resumes, we need to calculate the suspended time and</span></span><br><span class="line"><span class="comment"> * compensate it for the OS time. There are 3 sources that could be</span></span><br><span class="line"><span class="comment"> * used: Nonstop clocksource during suspend, persistent clock and rtc</span></span><br><span class="line"><span class="comment"> * device.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * One specific platform may have 1 or 2 or all of them, and the</span></span><br><span class="line"><span class="comment"> * preference will be:</span></span><br><span class="line"><span class="comment"> * suspend-nonstop clocksource -> persistent clock -> rtc</span></span><br><span class="line"><span class="comment"> * The less preferred source will only be tried if there is no better</span></span><br><span class="line"><span class="comment"> * usable source. The rtc part is handled separately in rtc core code.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> cycle_now = tk->tkr_mono.<span class="built_in">read</span>(clock);</span><br><span class="line"> <span class="keyword">if</span> ((clock->flags & CLOCK_SOURCE_SUSPEND_NONSTOP) &&</span><br><span class="line"> cycle_now > tk->tkr_mono.cycle_last) {</span><br><span class="line"> u64 num, max = ULLONG_MAX;</span><br><span class="line"> u32 mult = clock->mult;</span><br><span class="line"> u32 shift = clock->shift;</span><br><span class="line"> s64 nsec = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> cycle_delta = <span class="built_in">clocksource_delta</span>(cycle_now, tk->tkr_mono.cycle_last,</span><br><span class="line"> tk->tkr_mono.mask);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * "cycle_delta * mutl" may cause 64 bits overflow, if the</span></span><br><span class="line"><span class="comment"> * suspended time is too long. In that case we need do the</span></span><br><span class="line"><span class="comment"> * 64 bits math carefully</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="built_in">do_div</span>(max, mult);</span><br><span class="line"> <span class="keyword">if</span> (cycle_delta > max) {</span><br><span class="line"> num = <span class="built_in">div64_u64</span>(cycle_delta, max);</span><br><span class="line"> nsec = (((u64) max * mult) >> shift) * num;</span><br><span class="line"> cycle_delta -= num * max;</span><br><span class="line"> }</span><br><span class="line"> nsec += ((u64) cycle_delta * mult) >> shift;</span><br><span class="line"></span><br><span class="line"> ts_delta = <span class="built_in">ns_to_timespec64</span>(nsec);</span><br><span class="line"> sleeptime_injected = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">timespec64_compare</span>(&ts_new, &timekeeping_suspend_time) > <span class="number">0</span>) {</span><br><span class="line"> ts_delta = <span class="built_in">timespec64_sub</span>(ts_new, timekeeping_suspend_time);</span><br><span class="line"> sleeptime_injected = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sleeptime_injected)</span><br><span class="line"> __timekeeping_inject_sleeptime(tk, &ts_delta);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Re-base the last cycle value */</span></span><br><span class="line"> tk->tkr_mono.cycle_last = cycle_now;</span><br><span class="line"> tk->tkr_raw.cycle_last = cycle_now;</span><br><span class="line"></span><br><span class="line"> tk->ntp_error = <span class="number">0</span>;</span><br><span class="line"> timekeeping_suspended = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">timekeeping_update</span>(tk, TK_MIRROR | TK_CLOCK_WAS_SET);</span><br><span class="line"> <span class="built_in">write_seqcount_end</span>(&tk_core.seq);</span><br><span class="line"> <span class="built_in">raw_spin_unlock_irqrestore</span>(&timekeeper_lock, flags);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">touch_softlockup_watchdog</span>();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">tick_resume</span>();</span><br><span class="line"> <span class="built_in">hrtimers_resume</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">timekeeping_suspend</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timekeeper</span> *tk = &tk_core.timekeeper;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> flags;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timespec64</span> delta, delta_delta;</span><br><span class="line"> <span class="type">static</span> <span class="keyword">struct</span> <span class="title class_">timespec64</span> old_delta;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">read_persistent_clock64</span>(&timekeeping_suspend_time);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * On some systems the persistent_clock can not be detected at</span></span><br><span class="line"><span class="comment"> * timekeeping_init by its return value, so if we see a valid</span></span><br><span class="line"><span class="comment"> * value returned, update the persistent_clock_exists flag.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (timekeeping_suspend_time.tv_sec || timekeeping_suspend_time.tv_nsec)</span><br><span class="line"> persistent_clock_exists = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">raw_spin_lock_irqsave</span>(&timekeeper_lock, flags);</span><br><span class="line"> <span class="built_in">write_seqcount_begin</span>(&tk_core.seq);</span><br><span class="line"> <span class="built_in">timekeeping_forward_now</span>(tk);</span><br><span class="line"> timekeeping_suspended = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (persistent_clock_exists) {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * To avoid drift caused by repeated suspend/resumes,</span></span><br><span class="line"><span class="comment"> * which each can add ~1 second drift error,</span></span><br><span class="line"><span class="comment"> * try to compensate so the difference in system time</span></span><br><span class="line"><span class="comment"> * and persistent_clock time stays close to constant.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> delta = <span class="built_in">timespec64_sub</span>(<span class="built_in">tk_xtime</span>(tk), timekeeping_suspend_time);</span><br><span class="line"> delta_delta = <span class="built_in">timespec64_sub</span>(delta, old_delta);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">abs</span>(delta_delta.tv_sec) >= <span class="number">2</span>) {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * if delta_delta is too large, assume time correction</span></span><br><span class="line"><span class="comment"> * has occurred and set old_delta to the current delta.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> old_delta = delta;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">/* Otherwise try to adjust old_system to compensate */</span></span><br><span class="line"> timekeeping_suspend_time =</span><br><span class="line"> <span class="built_in">timespec64_add</span>(timekeeping_suspend_time, delta_delta);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">timekeeping_update</span>(tk, TK_MIRROR);</span><br><span class="line"> <span class="built_in">halt_fast_timekeeper</span>(tk);</span><br><span class="line"> <span class="built_in">write_seqcount_end</span>(&tk_core.seq);</span><br><span class="line"> <span class="built_in">raw_spin_unlock_irqrestore</span>(&timekeeper_lock, flags);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">tick_suspend</span>();</span><br><span class="line"> <span class="built_in">clocksource_suspend</span>();</span><br><span class="line"> <span class="built_in">clockevents_suspend</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* sysfs resume/suspend bits for timekeeping */</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">struct</span> <span class="title class_">syscore_ops</span> timekeeping_syscore_ops = {</span><br><span class="line"> .resume = timekeeping_resume,</span><br><span class="line"> .suspend = timekeeping_suspend,</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>和初始化一样的原因,理论上timekeeper的操作在timekeeping_resume()、timekeeping_suspend(),但是实际上在rtc的操作中执行rtc_suspend()、rtc_resume()。</p>
<figure class="highlight sas"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br></pre></td><td class="code"><pre><span class="line">static int rtc_suspend(struct device <span class="comment">*dev)</span></span><br><span class="line"><span class="comment">{</span></span><br><span class="line"><span class="comment"> struct rtc_device *rtc = to_rtc_device(dev);</span></span><br><span class="line"> struct rtc_time tm;</span><br><span class="line"> struct timespec64 delta, delta_delta;</span><br><span class="line"> int err;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (timekeeping_rtc_skipsuspend())</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (strcmp(dev_name(<span class="variable">&rtc</span>->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* snapshot the current RTC and system time at suspend*/</span></span><br><span class="line"> <span class="comment">/* (1.1) 读取suspend时候的rtc时间 */</span></span><br><span class="line"> err = rtc_read_<span class="meta">time</span>(rtc, <span class="variable">&tm</span>);</span><br><span class="line"> <span class="keyword">if</span> (err < 0) {</span><br><span class="line"> pr_debug(<span class="string">"%s: fail to read rtc time\n"</span>, dev_name(<span class="variable">&rtc</span>->dev));</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1.2) 读取当前xtime */</span></span><br><span class="line"> getnstimeofday64(<span class="variable">&old_system</span>);</span><br><span class="line"> old_rtc.tv_sec = rtc_tm_to_time64(<span class="variable">&tm</span>);</span><br><span class="line"> old_rtc.tv_nsec = tm.tm_cnt<span class="comment">*(1000000000/32768);</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * To avoid drift caused by repeated suspend/resumes,</span></span><br><span class="line"><span class="comment"> * which each can add ~1 second drift error,</span></span><br><span class="line"><span class="comment"> * try to compensate so the difference in system time</span></span><br><span class="line"><span class="comment"> * and rtc time stays close to constant.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (1.3) 如果rtc时间和xtime有偏差,尝试纠正xtime */</span></span><br><span class="line"> delta = timespec64_sub(old_system, old_rtc);</span><br><span class="line"> delta_delta = timespec64_sub(delta, old_delta);</span><br><span class="line"> <span class="keyword">if</span> (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * if delta_delta is too large, assume time correction</span></span><br><span class="line"><span class="comment"> * has occured and set old_delta to the current delta.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> old_delta = delta;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">/* Otherwise try to adjust old_system to compensate */</span></span><br><span class="line"> old_system = timespec64_sub(old_system, delta_delta);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">static int rtc_resume(struct device <span class="comment">*dev)</span></span><br><span class="line"><span class="comment">{</span></span><br><span class="line"><span class="comment"> struct rtc_device *rtc = to_rtc_device(dev);</span></span><br><span class="line"> struct rtc_time tm;</span><br><span class="line"> struct timespec64 new_system, new_rtc;</span><br><span class="line"> struct timespec64 sleep_time;</span><br><span class="line"> int err;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (timekeeping_rtc_skipresume())</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line"></span><br><span class="line"> rtc_hctosys_ret = -ENODEV;</span><br><span class="line"> <span class="keyword">if</span> (strcmp(dev_name(<span class="variable">&rtc</span>->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* snapshot the current rtc and system time at resume */</span></span><br><span class="line"> <span class="comment">/* (2.1) 读取resume后的rtc时间和xtime */</span></span><br><span class="line"> getnstimeofday64(<span class="variable">&new_system</span>);</span><br><span class="line"> err = rtc_read_<span class="meta">time</span>(rtc, <span class="variable">&tm</span>);</span><br><span class="line"> <span class="keyword">if</span> (err < 0) {</span><br><span class="line"> pr_debug(<span class="string">"%s: fail to read rtc time\n"</span>, dev_name(<span class="variable">&rtc</span>->dev));</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> new_rtc.tv_sec = rtc_tm_to_time64(<span class="variable">&tm</span>);</span><br><span class="line"> new_rtc.tv_nsec = tm.tm_cnt<span class="comment">*(1000000000/32768);</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (new_rtc.tv_sec < old_rtc.tv_sec) {</span><br><span class="line"> pr_debug(<span class="string">"%s: time travel!\n"</span>, dev_name(<span class="variable">&rtc</span>->dev));</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* calculate the RTC time delta (sleep time)*/</span></span><br><span class="line"> <span class="comment">/* (2.2) 计算suspend和resume之间rtc的差值 */</span></span><br><span class="line"> sleep_time = timespec64_sub(new_rtc, old_rtc);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Since these RTC suspend/resume handlers are not called</span></span><br><span class="line"><span class="comment"> * at the very end of suspend or the start of resume,</span></span><br><span class="line"><span class="comment"> * some run-time may pass on either sides of the sleep time</span></span><br><span class="line"><span class="comment"> * so subtract kernel run-time between rtc_suspend to rtc_resume</span></span><br><span class="line"><span class="comment"> * to keep things accurate.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (2.3) 使用上一步的差值,再减去,suspend和resume之间xtime的差值</span></span><br><span class="line"><span class="comment"> 得到实际的sleep时间*/</span></span><br><span class="line"> sleep_time = timespec64_sub(sleep_time,</span><br><span class="line"> timespec64_sub(new_system, old_system));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sleep_time.tv_sec >= 0)</span><br><span class="line"> <span class="comment">/* (2.4) 将计算得到的sleep时间,加入到timekeeper中 */</span></span><br><span class="line"> timekeeping_inject_sleeptime64(<span class="variable">&sleep_time</span>);</span><br><span class="line"> rtc_hctosys_ret = 0;</span><br><span class="line"> <span class="keyword">return</span> 0;</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">void timekeeping_inject_sleeptime64(struct timespec64 <span class="comment">*delta)</span></span><br><span class="line"><span class="comment">{</span></span><br><span class="line"><span class="comment"> struct timekeeper *tk = &tk_core.timekeeper;</span></span><br><span class="line"> unsigned long flags;</span><br><span class="line"></span><br><span class="line"> raw_spin_lock_irqsave(<span class="variable">&timekeeper_lock</span>, flags);</span><br><span class="line"> write_seqcount_begi<span class="meta">n</span>(<span class="variable">&tk_core.</span>seq);</span><br><span class="line"></span><br><span class="line"> timekeeping_forward_now(tk);</span><br><span class="line"></span><br><span class="line"> __timekeeping_inject_sleep<span class="meta">time</span>(tk, delta);</span><br><span class="line"></span><br><span class="line"> timekeeping_up<span class="meta">date</span>(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);</span><br><span class="line"></span><br><span class="line"> write_seqcount_end(<span class="variable">&tk_core.</span>seq);</span><br><span class="line"> raw_spin_unlock_irqrestore(<span class="variable">&timekeeper_lock</span>, flags);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* signal hrtimers about time change */</span></span><br><span class="line"> clock_was_set();</span><br><span class="line">}</span><br><span class="line">||→</span><br><span class="line">static void __timekeeping_inject_sleep<span class="meta">time</span>(struct timekeeper <span class="comment">*tk,</span></span><br><span class="line"><span class="comment"> struct timespec64 *delta)</span></span><br><span class="line"><span class="comment">{</span></span><br><span class="line"><span class="comment"> if (!timespec64_valid_strict(delta)) {</span></span><br><span class="line"><span class="comment"> printk_deferred(KERN_WARNING</span></span><br><span class="line"><span class="comment"> "__timekeeping_inject_sleeptime: Invalid "</span></span><br><span class="line"><span class="comment"> "sleep delta value!\n");</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* (2.4.1) 更新xtime */</span></span><br><span class="line"> tk_xtime_add(tk, delta);</span><br><span class="line"> <span class="comment">/* (2.4.2) 更新tk->wall_to_monotonic、tk->offs_real */</span></span><br><span class="line"> tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, <span class="comment">*delta));</span></span><br><span class="line"> <span class="comment">/* (2.4.3) 更新tk->offs_boot */</span></span><br><span class="line"> tk_update_sleep_<span class="meta">time</span>(tk, timespec64_to_k<span class="meta">time</span>(<span class="comment">*delta));</span></span><br><span class="line"> tk_debug_account_sleep_<span class="meta">time</span>(delta);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="3、clock-event"><a href="#3、clock-event" class="headerlink" title="3、clock_event"></a>3、clock_event</h2><p><img src="clockevent.png" alt="image"></p>
<p>clock_event其实就是对local timer的使用,每个cpu对应一个本地local timer。global timer启动后不需要主动做任何事情,只需要等待timekepper的读取就可以了。而local timer需要触发中断,它的主要价值就体现在定时中断处理了,中断的时间可以是固定的(period mode)也或者是不固定的(oneshot mode)。</p>
<h3 id="3-1、clock-event的注册"><a href="#3-1、clock-event的注册" class="headerlink" title="3.1、clock_event的注册"></a>3.1、clock_event的注册</h3><h4 id="3-1-1、exynos-clock-event的注册"><a href="#3-1-1、exynos-clock-event的注册" class="headerlink" title="3.1.1、exynos clock_event的注册"></a>3.1.1、exynos clock_event的注册</h4><p>exynos clock_event的注册分为两部分:</p>
<ul>
<li>第一部分:localtimer中断的注册:</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> __init <span class="title">exynos4_timer_resources</span><span class="params">(<span class="keyword">struct</span> device_node *np, <span class="type">void</span> __iomem *base)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="type">int</span> err, cpu;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">mct_clock_event_device</span> *mevt = <span class="built_in">this_cpu_ptr</span>(&percpu_mct_tick);</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">clk</span> *mct_clk, *tick_clk;</span><br><span class="line"></span><br><span class="line"> tick_clk = np ? <span class="built_in">of_clk_get_by_name</span>(np, <span class="string">"fin_pll"</span>) :</span><br><span class="line"> <span class="built_in">clk_get</span>(<span class="literal">NULL</span>, <span class="string">"fin_pll"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">IS_ERR</span>(tick_clk))</span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"%s: unable to determine tick clock rate\n"</span>, __func__);</span><br><span class="line"> clk_rate = <span class="built_in">clk_get_rate</span>(tick_clk);</span><br><span class="line"></span><br><span class="line"> mct_clk = np ? <span class="built_in">of_clk_get_by_name</span>(np, <span class="string">"mct"</span>) : <span class="built_in">clk_get</span>(<span class="literal">NULL</span>, <span class="string">"mct"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">IS_ERR</span>(mct_clk))</span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"%s: unable to retrieve mct clock instance\n"</span>, __func__);</span><br><span class="line"> <span class="built_in">clk_prepare_enable</span>(mct_clk);</span><br><span class="line"></span><br><span class="line"> reg_base = base;</span><br><span class="line"> <span class="keyword">if</span> (!reg_base)</span><br><span class="line"> <span class="built_in">panic</span>(<span class="string">"%s: unable to ioremap mct address space\n"</span>, __func__);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (mct_int_type == MCT_INT_PPI) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) 大部分的localtimer是PPI模式,</span></span><br><span class="line"><span class="comment"> 注册中断处理函数:exynos4_mct_tick_isr() */</span></span><br><span class="line"> err = <span class="built_in">request_percpu_irq</span>(mct_irqs[MCT_L0_IRQ],</span><br><span class="line"> exynos4_mct_tick_isr, <span class="string">"MCT"</span>,</span><br><span class="line"> &percpu_mct_tick);</span><br><span class="line"> <span class="built_in">WARN</span>(err, <span class="string">"MCT: can't request IRQ %d (%d)\n"</span>,</span><br><span class="line"> mct_irqs[MCT_L0_IRQ], err);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> for_each_possible_cpu(cpu) {</span><br><span class="line"> <span class="type">int</span> mct_irq = mct_irqs[MCT_L0_IRQ + cpu];</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">mct_clock_event_device</span> *pcpu_mevt =</span><br><span class="line"> <span class="built_in">per_cpu_ptr</span>(&percpu_mct_tick, cpu);</span><br><span class="line"></span><br><span class="line"> pcpu_mevt->evt.irq = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">irq_set_status_flags</span>(mct_irq, IRQ_NOAUTOEN);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">request_irq</span>(mct_irq,</span><br><span class="line"> exynos4_mct_tick_isr,</span><br><span class="line"> IRQF_TIMER | IRQF_NOBALANCING,</span><br><span class="line"> pcpu_mevt->name, pcpu_mevt)) {</span><br><span class="line"> <span class="built_in">pr_err</span>(<span class="string">"exynos-mct: cannot register IRQ (cpu%d)\n"</span>,</span><br><span class="line"> cpu);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> pcpu_mevt->evt.irq = mct_irq;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2) 注册cpu hotplug的notifier,</span></span><br><span class="line"><span class="comment"> 在其他cpu up时调用exynos4_local_timer_setup()注册clock_event */</span></span><br><span class="line"> err = <span class="built_in">register_cpu_notifier</span>(&exynos4_mct_cpu_nb);</span><br><span class="line"> <span class="keyword">if</span> (err)</span><br><span class="line"> <span class="keyword">goto</span> out_irq;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Immediately configure the timer on the boot CPU */</span></span><br><span class="line"> <span class="comment">/* (3) 注册本cpu的clock_event */</span></span><br><span class="line"> <span class="built_in">exynos4_local_timer_setup</span>(mevt);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">out_irq:</span><br><span class="line"> <span class="built_in">free_percpu_irq</span>(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">irqreturn_t</span> <span class="title">exynos4_mct_tick_isr</span><span class="params">(<span class="type">int</span> irq, <span class="type">void</span> *dev_id)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">mct_clock_event_device</span> *mevt = dev_id;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">clock_event_device</span> *evt = &mevt->evt;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">exynos4_mct_tick_clear</span>(mevt);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (4) localtimer中断处理函数是固定的也是非常简单的,</span></span><br><span class="line"><span class="comment"> 调用本cpu clock_event_device的handler函数:evt->event_handler(evt) */</span></span><br><span class="line"> evt-><span class="built_in">event_handler</span>(evt);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> IRQ_HANDLED;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<ul>
<li>第二部分:clock_event_device注册:</li>
</ul>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">static int exynos4_local_timer_setup(struct mct_clock_event_device *mevt)</span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="title">struct</span> clock_event_device *evt = &mevt-></span>evt;</span><br><span class="line"> unsigned int cpu = smp_processor_id();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">mevt</span>-></span>base = EXYNOS4_MCT_L_BASE(cpu);</span><br><span class="line"> <span class="function"><span class="title">snprintf</span>(mevt-></span><span class="function"><span class="title">name</span>, sizeof(mevt-></span><span class="keyword">name</span>), <span class="string">"mct_tick%d"</span>, cpu);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) 初始化clock_event_device */</span></span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span><span class="function"><span class="title">name</span> = mevt-></span><span class="keyword">name</span>;</span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>cpumask = cpumask_of(cpu); <span class="comment">// 本clock_event_device只服务于一个cpu</span></span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>set_next_event = exynos4_tick_set_next_event;</span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>set_state_periodic = set_state_periodic;</span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>set_state_shutdown = set_state_shutdown;</span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>set_state_oneshot = set_state_shutdown;</span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>tick_resume = set_state_shutdown;</span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;</span><br><span class="line"> <span class="function"><span class="title">evt</span>-></span>rating = <span class="number">450</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">exynos4_mct_write</span>(TICK_BASE_CNT, mevt-></span>base + MCT_L_TCNTB_OFFSET);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (mct_int_type == MCT_INT_SPI) {</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (evt-></span>irq == -<span class="number">1</span>)</span><br><span class="line"> return -EIO;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">irq_force_affinity</span>(evt-></span>irq, cpumask_of(cpu));</span><br><span class="line"> <span class="function"><span class="title">enable_irq</span>(evt-></span>irq);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> enable_percpu_irq(mct_irqs[MCT_L0_IRQ], <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* (2) 配置并注册clockevent */</span></span><br><span class="line"> clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + <span class="number">1</span>),</span><br><span class="line"> <span class="number">0</span>xf, <span class="number">0</span>x7fffffff);</span><br><span class="line"></span><br><span class="line"> return <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h4 id="3-1-2、clock-event-device的注册"><a href="#3-1-2、clock-event-device的注册" class="headerlink" title="3.1.2、clock_event_device的注册"></a>3.1.2、clock_event_device的注册</h4><p>我们来分析一下clock_event_device的注册过程。</p>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br></pre></td><td class="code"><pre><span class="line">void clockevents_config_and_register(struct clock_event_device *dev,</span><br><span class="line"> u32 freq, unsigned long min_delta,</span><br><span class="line"> unsigned long max_delta)</span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="title">dev</span>-></span>min_delta_ticks = min_delta; <span class="comment">// localtimer可配置的最小定时值</span></span><br><span class="line"> <span class="function"><span class="title">dev</span>-></span>max_delta_ticks = max_delta; <span class="comment">// localtimer可配置的最大定时值,</span></span><br><span class="line"> <span class="comment">// 比如exynos是31bit的localtimer,最大值就是0x7fffffff</span></span><br><span class="line"> <span class="comment">/* (1) 根据localtimer的freq,计算clock_event_device对应的mult、shift,</span></span><br><span class="line"><span class="comment"> mult、shift的作用是用来做ns到localtimer cycle之间的转换,</span></span><br><span class="line"><span class="comment"> 与之相反的是,在clocksource中mult、shift用来转换localtimer cycle到ns */</span></span><br><span class="line"> clockevents_config(dev, freq);</span><br><span class="line"> <span class="comment">/* (2) 继续注册clock_event_device */</span></span><br><span class="line"> clockevents_register_device(dev);</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">void clockevents_config(struct clock_event_device *dev, u32 freq)</span><br><span class="line">{</span><br><span class="line"> u64 sec;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1.1) 如果不支持oneshot模式,只是period模式,</span></span><br><span class="line"><span class="comment"> 定时周期是固定的,不需要动态计算ns到cycle的转换 */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (!(dev-></span>features & CLOCK_EVT_FEAT_ONESHOT))</span><br><span class="line"> return;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Calculate the maximum number of seconds we can sleep. Limit</span></span><br><span class="line"><span class="comment"> * to 10 minutes for hardware which can program more than</span></span><br><span class="line"><span class="comment"> * 32bit ticks so we still get reasonable conversion values.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="title">sec</span> = dev-></span>max_delta_ticks;</span><br><span class="line"> do_div(sec, freq);</span><br><span class="line"> <span class="keyword">if</span> (!sec)</span><br><span class="line"> sec = <span class="number">1</span>;</span><br><span class="line"> <span class="function"><span class="title">else</span> <span class="keyword">if</span> (sec > 600 && dev-></span>max_delta_ticks > UINT_MAX)</span><br><span class="line"> sec = <span class="number">600</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1.2) 根据localtimer的freq,计算clock_event_device对应的mult、shift */</span></span><br><span class="line"> clockevents_calc_mult_shift(dev, freq, sec);</span><br><span class="line"> <span class="comment">/* (1.3) 转换min、max的cycle到ns */</span></span><br><span class="line"> <span class="function"><span class="title">dev</span>-></span><span class="function"><span class="title">min_delta_ns</span> = cev_delta2ns(dev-></span>min_delta_ticks, dev, <span class="literal">false</span>);</span><br><span class="line"> <span class="function"><span class="title">dev</span>-></span><span class="function"><span class="title">max_delta_ns</span> = cev_delta2ns(dev-></span>max_delta_ticks, dev, <span class="literal">true</span>);</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">void clockevents_register_device(struct clock_event_device *dev)</span><br><span class="line">{</span><br><span class="line"> unsigned long flags;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Initialize state to DETACHED */</span></span><br><span class="line"> clockevent_set_state(dev, CLOCK_EVT_STATE_DETACHED);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (!dev-></span>cpumask) {</span><br><span class="line"> WARN_ON(num_possible_cpus() > <span class="number">1</span>);</span><br><span class="line"> <span class="function"><span class="title">dev</span>-></span>cpumask = cpumask_of(smp_processor_id());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> raw_spin_lock_irqsave(&clockevents_lock, flags);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.1) 将clock_event_device加入到全局链表clockevent_devices中 */</span></span><br><span class="line"> <span class="function"><span class="title">list_add</span>(&dev-></span>list, &clockevent_devices);</span><br><span class="line"> <span class="comment">/* (2.2) 继续尝试向本cpu的tick_device中注册clock_event_device */</span></span><br><span class="line"> tick_check_new_device(dev);</span><br><span class="line"> clockevents_notify_released();</span><br><span class="line"></span><br><span class="line"> raw_spin_unlock_irqrestore(&clockevents_lock, flags);</span><br><span class="line">}</span><br><span class="line">||→</span><br><span class="line">void tick_check_new_device(struct clock_event_device *newdev)</span><br><span class="line">{</span><br><span class="line"> struct clock_event_device *curdev;</span><br><span class="line"> struct tick_device *td;</span><br><span class="line"> int cpu;</span><br><span class="line"></span><br><span class="line"> cpu = smp_processor_id();</span><br><span class="line"> td = &per_cpu(tick_cpu_device, cpu);</span><br><span class="line"> <span class="function"><span class="title">curdev</span> = td-></span>evtdev;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* cpu local device ? */</span></span><br><span class="line"> <span class="comment">/* (2.2.1) 新的clock_event_device是否支持本cpu? */</span></span><br><span class="line"> <span class="keyword">if</span> (!tick_check_percpu(curdev, newdev, cpu))</span><br><span class="line"> goto out_bc;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Preference decision */</span></span><br><span class="line"> <span class="comment">/* (2.2.2) 新的clock_event_device是否比当前clock_event_device更适合?</span></span><br><span class="line"><span class="comment"> 1.如果curdev已经是oneshot模式,而newdev不支持oneshot,则切换</span></span><br><span class="line"><span class="comment"> 2.newdev的精度要大于curdev,精度 = dev->rating */</span></span><br><span class="line"> <span class="keyword">if</span> (!tick_check_preferred(curdev, newdev))</span><br><span class="line"> goto out_bc;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (!try_module_get(newdev-></span>owner))</span><br><span class="line"> return;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Replace the eventually existing device by the new</span></span><br><span class="line"><span class="comment"> * device. If the current device is the broadcast device, do</span></span><br><span class="line"><span class="comment"> * not give it back to the clockevents layer !</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (tick_is_broadcast_device(curdev)) {</span><br><span class="line"> clockevents_shutdown(curdev);</span><br><span class="line"> curdev = NULL;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* (2.2.3) 关闭curdev、newdev */</span></span><br><span class="line"> clockevents_exchange_device(curdev, newdev);</span><br><span class="line"> <span class="comment">/* (2.2.4) 继续clock_event_device注册 */</span></span><br><span class="line"> tick_setup_device(td, newdev, cpu, cpumask_of(cpu));</span><br><span class="line"> <span class="function"><span class="title">if</span> (newdev-></span>features & CLOCK_EVT_FEAT_ONESHOT)</span><br><span class="line"> tick_oneshot_notify();</span><br><span class="line"> return;</span><br><span class="line"></span><br><span class="line">out_bc:</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Can the new device be used as a broadcast device ?</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (2.2.5) 如果newdev不适合注册成本cpu的td->evtdev,</span></span><br><span class="line"><span class="comment"> 尝试将其注册成broadcast clockevent */</span></span><br><span class="line"> tick_install_broadcast_device(newdev);</span><br><span class="line">}</span><br><span class="line">|||→</span><br><span class="line">static void tick_setup_device(struct tick_device *td,</span><br><span class="line"> struct clock_event_device *newdev, int cpu,</span><br><span class="line"> const struct cpumask *cpumask)</span><br><span class="line">{</span><br><span class="line"> ktime_t next_event;</span><br><span class="line"> void (*handler)(struct clock_event_device *) = NULL;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * First device setup ?</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (!td-></span>evtdev) {</span><br><span class="line"> <span class="comment">/* (2.2.4.1) 如果是tick_do_timer_cpu没有被设置,且没有使能tick_nohz_full_cpu</span></span><br><span class="line"><span class="comment"> 把tick_do_timer_cpu设置成本cpu,</span></span><br><span class="line"><span class="comment"> tick_do_timer_cpu负责在tick中update jiffies、update_wall_time */</span></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * If no cpu took the do_timer update, assign it to</span></span><br><span class="line"><span class="comment"> * this cpu:</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {</span><br><span class="line"> <span class="keyword">if</span> (!tick_nohz_full_cpu(cpu))</span><br><span class="line"> tick_do_timer_cpu = cpu;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> tick_do_timer_cpu = TICK_DO_TIMER_NONE;</span><br><span class="line"> tick_next_period = ktime_get();</span><br><span class="line"> tick_period = ktime_set(<span class="number">0</span>, NSEC_PER_SEC / HZ);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Startup in periodic mode first.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (2.2.4.2) 如果tick_device是第一次设置clock_event_device,</span></span><br><span class="line"><span class="comment"> 把tick_device设置成period模式 */</span></span><br><span class="line"> <span class="function"><span class="title">td</span>-></span>mode = TICKDEV_MODE_PERIODIC;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">/* (2.2.4.3) 如果tick_device不是第一次设置clock_event_device,</span></span><br><span class="line"><span class="comment"> 备份原clock_event_deviced的event_handler和next_event */</span></span><br><span class="line"> <span class="function"><span class="title">handler</span> = td-></span><span class="function"><span class="title">evtdev</span>-></span>event_handler;</span><br><span class="line"> <span class="function"><span class="title">next_event</span> = td-></span><span class="function"><span class="title">evtdev</span>-></span>next_event;</span><br><span class="line"> <span class="function"><span class="title">td</span>-></span><span class="function"><span class="title">evtdev</span>-></span>event_handler = clockevents_handle_noop;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.2.4.4) 更新tick_device->evtdev到new clock_event_deviced */</span></span><br><span class="line"> <span class="function"><span class="title">td</span>-></span>evtdev = newdev;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * When the device is not per cpu, pin the interrupt to the</span></span><br><span class="line"><span class="comment"> * current cpu:</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (!cpumask_equal(newdev-></span>cpumask, cpumask))</span><br><span class="line"> <span class="function"><span class="title">irq_set_affinity</span>(newdev-></span>irq, cpumask);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * When global broadcasting is active, check if the current</span></span><br><span class="line"><span class="comment"> * device is registered as a placeholder for broadcast mode.</span></span><br><span class="line"><span class="comment"> * This allows us to handle this x86 misfeature in a generic</span></span><br><span class="line"><span class="comment"> * way. This function also returns !=0 when we keep the</span></span><br><span class="line"><span class="comment"> * current active broadcast state for this CPU.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (2.2.4.5) 如果全局的brodcast clockevent服务已经启动,</span></span><br><span class="line"><span class="comment"> 本cpu的clockevent注册需要向brodcas服务,</span></span><br><span class="line"><span class="comment"> 这是为了解决x86的一个失误(misfeature),其他架构不需要? */</span></span><br><span class="line"> <span class="keyword">if</span> (tick_device_uses_broadcast(newdev, cpu))</span><br><span class="line"> return;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.2.4.6) 根据td->mode安装clock_event_deviced的event_handler,并启动 */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (td-></span>mode == TICKDEV_MODE_PERIODIC)</span><br><span class="line"> <span class="comment">/* (2.2.4.7) period模式 */</span></span><br><span class="line"> tick_setup_periodic(newdev, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="comment">/* (2.2.4.8) oneshot模式 */</span></span><br><span class="line"> tick_setup_oneshot(newdev, handler, next_event);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="3-2、tick-device的period-mode"><a href="#3-2、tick-device的period-mode" class="headerlink" title="3.2、tick_device的period mode"></a>3.2、tick_device的period mode</h3><p>接上节,在cpu第一次注册clock_event_deviced的时候,td->mode默认被设置成period模式。event_handler会被初始化成tick_handle_periodic:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">tick_setup_periodic</span><span class="params">(<span class="keyword">struct</span> clock_event_device *dev, <span class="type">int</span> broadcast)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">/* (1) 设置period模式下的event_handler */</span></span><br><span class="line"> <span class="built_in">tick_set_periodic_handler</span>(dev, broadcast);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Broadcast setup ? */</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">tick_device_is_functional</span>(dev))</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2) 如果dev支持period模式,则硬件上启动period模式:</span></span><br><span class="line"><span class="comment"> tick_device->mode = TICKDEV_MODE_PERIODIC</span></span><br><span class="line"><span class="comment"> clock_event_device->state_use_accessors = CLOCK_EVT_STATE_PERIODIC */</span></span><br><span class="line"> <span class="keyword">if</span> ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&</span><br><span class="line"> !<span class="built_in">tick_broadcast_oneshot_active</span>()) {</span><br><span class="line"> <span class="built_in">clockevents_switch_state</span>(dev, CLOCK_EVT_STATE_PERIODIC);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> seq;</span><br><span class="line"> <span class="type">ktime_t</span> next;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = <span class="built_in">read_seqbegin</span>(&jiffies_lock);</span><br><span class="line"> next = tick_next_period;</span><br><span class="line"> } <span class="keyword">while</span> (<span class="built_in">read_seqretry</span>(&jiffies_lock, seq));</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (3) 如果dev不支持period模式只支持oneshot模式,则硬件上启动one shot模式,</span></span><br><span class="line"><span class="comment"> 使用oneshot模式来模拟period模式:</span></span><br><span class="line"><span class="comment"> tick_device->mode = TICKDEV_MODE_PERIODIC</span></span><br><span class="line"><span class="comment"> clock_event_device->state_use_accessors = CLOCK_EVT_STATE_ONESHOT */</span></span><br><span class="line"> <span class="built_in">clockevents_switch_state</span>(dev, CLOCK_EVT_STATE_ONESHOT);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (;;) {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">clockevents_program_event</span>(dev, next, <span class="literal">false</span>))</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> next = <span class="built_in">ktime_add</span>(next, tick_period);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">tick_set_periodic_handler</span><span class="params">(<span class="keyword">struct</span> clock_event_device *dev, <span class="type">int</span> broadcast)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (!broadcast)</span><br><span class="line"> <span class="comment">/* (1.1) 设置period模式下的event_handler */</span></span><br><span class="line"> dev->event_handler = tick_handle_periodic;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> dev->event_handler = tick_handle_periodic_broadcast;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>仔细分析一下tick_handle_periodic:</p>
<figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line">void tick_handle_periodic(<span class="name">struct</span> clock_event_device *dev)</span><br><span class="line">{</span><br><span class="line"> int cpu = smp_processor_id();</span><br><span class="line"> ktime_t next = dev->next_event;</span><br><span class="line"></span><br><span class="line"> /* (<span class="number">1</span>) 周期性的tick任务 */</span><br><span class="line"> tick_periodic(cpu);</span><br><span class="line"></span><br><span class="line">#if defined(CONFIG_HIGH_RES_TIMERS) || defined(CONFIG_NO_HZ_COMMON)</span><br><span class="line"> /*</span><br><span class="line"> * The cpu might have transitioned to HIGHRES or NOHZ mode via</span><br><span class="line"> * update_process_times() -> run_local_timers() -></span><br><span class="line"> * hrtimer_run_queues().</span><br><span class="line"> */</span><br><span class="line"> if (<span class="name">dev->event_handler</span> != tick_handle_periodic)</span><br><span class="line"> return<span class="comment">;</span></span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line"> if (!clockevent_state_oneshot(<span class="name">dev</span>))</span><br><span class="line"> return<span class="comment">;</span></span><br><span class="line"> /* (<span class="number">2</span>) 如果tick_device是period mode,而clockevent是oneshot模式,</span><br><span class="line"> 编程oneshot模式clockevent在下一周期触发:</span><br><span class="line"> tick_device->mode = TICKDEV_MODE_PERIODIC</span><br><span class="line"> clock_event_device->state_use_accessors = CLOCK_EVT_STATE_ONESHOT */</span><br><span class="line"> for (;;) {</span><br><span class="line"> /*</span><br><span class="line"> * Setup the next period for devices, which do not have</span><br><span class="line"> * periodic mode:</span><br><span class="line"> */</span><br><span class="line"> next = ktime_add(next, tick_period);</span><br><span class="line"></span><br><span class="line"> if (!clockevents_program_event(dev, next, false))</span><br><span class="line"> return;</span><br><span class="line"> /*</span><br><span class="line"> * Have to be careful here. If we're in oneshot mode,</span><br><span class="line"> * before we call tick_periodic() in a loop, we need</span><br><span class="line"> * to be sure we're using a real hardware clocksource.</span><br><span class="line"> * Otherwise we could get trapped in an infinite</span><br><span class="line"> * loop, as the tick_periodic() increments jiffies,</span><br><span class="line"> * which then will increment time, possibly causing</span><br><span class="line"> * the loop to trigger again and again.</span><br><span class="line"> */</span><br><span class="line"> if (<span class="name">timekeeping_valid_for_hres</span>())</span><br><span class="line"> tick_periodic(<span class="name">cpu</span>)<span class="comment">;</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">static void tick_periodic(<span class="name">int</span> cpu)</span><br><span class="line">{</span><br><span class="line"> /* (<span class="number">1.1</span>) 如果本cpu是tick_do_timer_cpu,更新全局时间戳类型的任务,</span><br><span class="line"> 包括update jiffies、update_wall_time */</span><br><span class="line"> if (tick_do_timer_cpu == cpu) {</span><br><span class="line"> write_seqlock(&jiffies_lock);</span><br><span class="line"></span><br><span class="line"> /* Keep track of the next tick event */</span><br><span class="line"> tick_next_period = ktime_add(tick_next_period, tick_period);</span><br><span class="line"></span><br><span class="line"> /* (<span class="number">1.1</span>.<span class="number">1</span>) 更新jiffies */</span><br><span class="line"> do_timer(1);</span><br><span class="line"> write_sequnlock(&jiffies_lock);</span><br><span class="line"> /* (<span class="number">1.1</span>.<span class="number">2</span>) 读取clocksource来更新timekeeper */</span><br><span class="line"> update_wall_time();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /* (<span class="number">1.2</span>) 运行软件timer(<span class="name">run_local_timers</span>())和运行调度tick任务(<span class="name">scheduler_tick</span>()) */</span><br><span class="line"> update_process_times(user_mode(get_irq_regs()));</span><br><span class="line"> profile_tick(CPU_PROFILING);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="3-3、运行Mode"><a href="#3-3、运行Mode" class="headerlink" title="3.3、运行Mode"></a>3.3、运行Mode</h3><p>关于mode,有几个结构涉及到:tick_device、clock_event_device、tick_sched、hrtimer_cpu_base、。组合起来有以下几种情况:</p>
<p><img src="clockevent_mode.png" alt="image"></p>
<p>其实归结起来就3种mode:NOHZ_MODE_INACTIVE、NOHZ_MODE_LOWRES、NOHZ_MODE_HIGHRES。下面来逐个解析一下。</p>
<h4 id="3-3-1、NOHZ-MODE-INACTIVE"><a href="#3-3-1、NOHZ-MODE-INACTIVE" class="headerlink" title="3.3.1、NOHZ_MODE_INACTIVE"></a>3.3.1、NOHZ_MODE_INACTIVE</h4><p>NOHZ_MODE_INACTIVE就是系统初始化时的状态:“td=period模式, dev=period/oneshot模式, hrtimer=low res, noHz=dis”。</p>
<p><img src="clockevent_NOHZ_MODE_INACTIVE.png" alt="image"></p>
<p>NOHZ_MODE_INACTIVE模式:</p>
<ul>
<li>tick_device工作在period模式,HW local timer工作在period/oneshot模式;</li>
<li>noHZ没有使能,进入idle会被tick timer中断打断;</li>
<li>hrtimer工作在低精度模式,和低精度定时器(SW local timer)的精度一样,都是基于tick的;</li>
</ul>
<h4 id="3-3-2、NOHZ-MODE-LOWRES"><a href="#3-3-2、NOHZ-MODE-LOWRES" class="headerlink" title="3.3.2、NOHZ_MODE_LOWRES"></a>3.3.2、NOHZ_MODE_LOWRES</h4><p>在系统的运行过程中系统尝试进入精度更高的模式,如果noHZ可以使能,但是hrtimer高精度不能使能,即进入NOHZ_MODE_LOWRES模式:“td=period模式, dev=oneshot模式, hrtimer=low res, noHz=en”。</p>
<p><img src="clockevent_NOHZ_MODE_LOWRES.png" alt="image"></p>
<p>NOHZ_MODE_LOWRES模式:</p>
<ul>
<li>tick_device工作在oneshot模式,HW local timer工作在oneshot模式;</li>
<li>noHZ使能,进入idle不会被tick timer中断打断;</li>
<li>hrtimer工作在低精度模式,和低精度定时器(SW local timer)的精度一样,都是基于tick的;</li>
</ul>
<p>为了支持noHZ,tick_device必须切换成oneshot模式,在进入idle时停掉tick timer(tick_nohz_idle_enter() -> __tick_nohz_idle_enter() -> tick_nohz_stop_sched_tick()),在离开idle时恢复tick timer(tick_nohz_idle_exit() -> tick_nohz_restart_sched_tick()),这样idle过程就不会被tick中断。就实现了noHZ模式(tickless)。</p>
<p>NOHZ_MODE_LOWRES模式下,没有进入idle时tick_device还是以固定周期工作的:</p>
<figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> void <span class="title function_ invoke__">tick_nohz_handler</span>(<span class="keyword">struct</span> <span class="title class_">clock_event_device</span> *dev)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">tick_sched</span> *ts = <span class="title function_ invoke__">this_cpu_ptr</span>(&tick_cpu_sched);</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">pt_regs</span> *regs = <span class="title function_ invoke__">get_irq_regs</span>();</span><br><span class="line"> ktime_t now = <span class="title function_ invoke__">ktime_get</span>();</span><br><span class="line"></span><br><span class="line"> dev<span class="punctuation">-></span>next_event.tv64 = KTIME_MAX;</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">tick_sched_do_timer</span>(now);</span><br><span class="line"> <span class="title function_ invoke__">tick_sched_handle</span>(ts, regs);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* No need to reprogram if we are running tickless */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_ invoke__">unlikely</span>(ts<span class="punctuation">-></span>tick_stopped))</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) HW local timer还是以固定周期发生中断 */</span></span><br><span class="line"> <span class="title function_ invoke__">hrtimer_forward</span>(&ts<span class="punctuation">-></span>sched_timer, now, tick_period);</span><br><span class="line"> <span class="title function_ invoke__">tick_program_event</span>(<span class="title function_ invoke__">hrtimer_get_expires</span>(&ts<span class="punctuation">-></span>sched_timer), <span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h4 id="3-3-3、NOHZ-MODE-HIGHRES"><a href="#3-3-3、NOHZ-MODE-HIGHRES" class="headerlink" title="3.3.3、NOHZ_MODE_HIGHRES"></a>3.3.3、NOHZ_MODE_HIGHRES</h4><p>在系统的运行过程中系统尝试进入精度更高的模式,如果noHZ可以使能,hrtimer高精度可以使能,即进入NOHZ_MODE_HIGHRES模式:“td=period模式, dev=oneshot模式, hrtimer=high res, noHz=en”。</p>
<p><img src="clockevent_NOHZ_MODE_HIGHRES.png" alt="image"></p>
<p>NOHZ_MODE_HIGHRES:</p>
<ul>
<li>tick_device工作在oneshot模式,HW local timer工作在oneshot模式;</li>
<li>noHZ使能,进入idle不会被tick timer中断打断;</li>
<li>hrtimer工作在高精度模式,和硬件定时器(HWlocal timer)的精度一样,远大于低精度定时器tick精度;</li>
</ul>
<p>为了支持hrtimer的高精度模式,hrtimer必须直接使用tick_device的oneshot模式,而常规的tick timer转换成hrtimer的一个子timer。</p>
<p><img src="ftrace_NOHZ_MODE_HIGHRES.png" alt="image"></p>
<p>上图是NOHZ_MODE_HIGHRES模式下,用ftrace抓取HW timer硬件中断和tick任务的执行情况:</p>
<ul>
<li>tick任务是以固定周期4ms固定执行的;</li>
<li>遇到tick任务超过4ms的间隔,这时就是进入了idle状态,且发生了noHZ(tickless);</li>
<li>硬件timer中断的发生周期是不固定的,是和hrtimer绑定的;</li>
<li>发生tick的时候肯定发生了timer硬中断,因为tick是其中一个hrtimer;</li>
</ul>
<h4 id="3-3-4、Mode切换"><a href="#3-3-4、Mode切换" class="headerlink" title="3.3.4、Mode切换"></a>3.3.4、Mode切换</h4><p>系统初始状态工作在NOHZ_MODE_INACTIVE模式时,会动态检测是否可以进入更高级别的模式NOHZ_MODE_LOWRES、NOHZ_MODE_HIGHRES。这个检测工作是在这个路径中做的:tick_device工作在period模式:tick_handle_periodic() -> tick_periodic() -> update_process_times() -> run_local_timers() -> hrtimer_run_queues()</p>
<figure class="highlight smali"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">void hrtimer_run_queues(void)</span><br><span class="line">{</span><br><span class="line"> struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);</span><br><span class="line"> ktime_t now;</span><br><span class="line"></span><br><span class="line"> /* (3) 如果hrtimer已经切换到高精度模式,</span><br><span class="line"> 则不会从run_local_timers()低精度定时器路径来运行hrtimer */</span><br><span class="line"><span class="built_in"> if </span>(__hrtimer_hres_active(cpu_base))</span><br><span class="line"> return;</span><br><span class="line"></span><br><span class="line"> /*</span><br><span class="line"> * This _is_ ugly: We have to<span class="built_in"> check </span>periodically, whether we</span><br><span class="line"> * can switch to highres<span class="built_in"> and </span>/<span class="built_in"> or </span>nohz mode. The clocksource</span><br><span class="line"> * switch happens with xtime_lock held. Notification from</span><br><span class="line"> * there only sets the<span class="built_in"> check </span>bit in the tick_oneshot code,</span><br><span class="line"> * otherwise we might deadlock vs. xtime_lock.</span><br><span class="line"> */</span><br><span class="line"> </span><br><span class="line"> /* (1) 如果hrtimer没有使能、noHZ使能,</span><br><span class="line"> 则调用:tick_check_oneshot_change() -> tick_nohz_switch_to_nohz(),</span><br><span class="line"> 切换到NOHZ_MODE_LOWRES模式 */</span><br><span class="line"><span class="built_in"> if </span>(tick_check_oneshot_change(!hrtimer_is_hres_enabled())) {</span><br><span class="line"> </span><br><span class="line"> /* (2) 如果hrtimer使能、noHZ使能,</span><br><span class="line"> 则调用:hrtimer_switch_to_hres(),</span><br><span class="line"> 切换到NOHZ_MODE_HIGHRES模式 */</span><br><span class="line"> hrtimer_switch_to_hres();</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> raw_spin_lock(&cpu_base->lock);</span><br><span class="line"> now = hrtimer_update_base(cpu_base);</span><br><span class="line"> /* (4) 低精度hrtimer的运行函数 */</span><br><span class="line"> __hrtimer_run_queues(cpu_base, now);</span><br><span class="line"> raw_spin_unlock(&cpu_base->lock);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h2 id="4、noHZ"><a href="#4、noHZ" class="headerlink" title="4、noHZ"></a>4、noHZ</h2><p>系统在NOHZ_MODE_LOWRES、NOHZ_MODE_HIGHRES两种模式下支持noHZ。noHZ是一个功耗优化的feature,在系统负载比较轻的时候没有任务需要调度cpu会进入idle状态,但是系统的tick任务(update_process_times())默认会以固定周期执行,这种固定周期会打断idle状态让系统恢复成正常耗电状态。</p>
<p>tick任务这种不管有没有任务都是固定周期运行的特性是需要改进的,noHZ就是为了解决这一问题而产生的:如果在idle状态的过程中tick任务没有到期需要处理的低精度timer和高精度timer,tick任务可以继续保持睡眠,直到真正有timer到期。</p>
<p>idle进程的主要执行序列如下:</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">static void <span class="built_in">cpu_idle_loop</span>(void)</span><br><span class="line">{</span><br><span class="line"> while (<span class="number">1</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) 进入idle前,noHZ的处理 */</span></span><br><span class="line"> <span class="built_in">tick_nohz_idle_enter</span>();</span><br><span class="line"></span><br><span class="line"> while (!need_resched()) {</span><br><span class="line"> <span class="built_in">check_pgt_cache</span>();</span><br><span class="line"> <span class="built_in">rmb</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2) cpu hotplug之cpu_down()的处理 */</span></span><br><span class="line"> if (cpu_is_offline(smp_processor_id())) {</span><br><span class="line"></span><br><span class="line"> <span class="built_in">arch_cpu_idle_dead</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">local_irq_disable</span>();</span><br><span class="line"> <span class="built_in">arch_cpu_idle_enter</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (3) cpu idle的进入 */</span></span><br><span class="line"> if (cpu_idle_force_poll || tick_check_broadcast_expired())</span><br><span class="line"> <span class="built_in">cpu_idle_poll</span>();</span><br><span class="line"> else</span><br><span class="line"> <span class="built_in">cpuidle_idle_call</span>();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">arch_cpu_idle_exit</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (4) 退出idle后,noHZ的处理 */</span></span><br><span class="line"> <span class="built_in">tick_nohz_idle_exit</span>();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>可以看到,其中的关键在tick_nohz_idle_enter()/tick_nohz_idle_exit()函数。</p>
<h3 id="4-1、tick-nohz-idle-enter-exit"><a href="#4-1、tick-nohz-idle-enter-exit" class="headerlink" title="4.1、tick_nohz_idle_enter/exit()"></a>4.1、tick_nohz_idle_enter/exit()</h3><p>tick_nohz_idle_enter()的解析:</p>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br></pre></td><td class="code"><pre><span class="line">void tick_nohz_idle_enter(void)</span><br><span class="line">{</span><br><span class="line"> struct tick_sched *ts;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> ts = this_cpu_ptr(&tick_cpu_sched);</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>inidle = <span class="number">1</span>;</span><br><span class="line"> __tick_nohz_idle_enter(ts);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">static void __tick_nohz_idle_enter(struct tick_sched *ts)</span><br><span class="line">{</span><br><span class="line"> ktime_t now, expires;</span><br><span class="line"> int cpu = smp_processor_id();</span><br><span class="line"></span><br><span class="line"> now = tick_nohz_start_idle(ts);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) 判断当前能否stop tick任务 */</span></span><br><span class="line"> <span class="keyword">if</span> (can_stop_idle_tick(cpu, ts)) {</span><br><span class="line"> <span class="function"><span class="title">int</span> was_stopped = ts-></span>tick_stopped;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>idle_calls++;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2) 尝试stop tick任务 */</span></span><br><span class="line"> expires = tick_nohz_stop_sched_tick(ts, now, cpu);</span><br><span class="line"> <span class="keyword">if</span> (expires.tv64 > <span class="number">0</span>LL) {</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>idle_sleeps++;</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>idle_expires = expires;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (!was_stopped && ts-></span>tick_stopped)</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span><span class="function"><span class="title">idle_jiffies</span> = ts-></span>last_jiffies;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">||→</span><br><span class="line">static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,</span><br><span class="line"> ktime_t now, int cpu)</span><br><span class="line">{</span><br><span class="line"> struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);</span><br><span class="line"> u64 basemono, next_tick, next_tmr, next_rcu, delta, expires;</span><br><span class="line"> unsigned long seq, basejiff;</span><br><span class="line"> ktime_t tick;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Read jiffies and the time when jiffies were updated last */</span></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> seq = read_seqbegin(&jiffies_lock);</span><br><span class="line"> basemono = last_jiffies_update.tv64;</span><br><span class="line"> basejiff = jiffies;</span><br><span class="line"> } <span class="keyword">while</span> (read_seqretry(&jiffies_lock, seq));</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>last_jiffies = basejiff;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rcu_needs_cpu(basemono, &next_rcu) ||</span><br><span class="line"> arch_needs_cpu() || irq_work_needs_cpu()) {</span><br><span class="line"> next_tick = basemono + TICK_NSEC;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Get the next pending timer. If high resolution</span></span><br><span class="line"><span class="comment"> * timers are enabled this only takes the timer wheel</span></span><br><span class="line"><span class="comment"> * timers into account. If high resolution timers are</span></span><br><span class="line"><span class="comment"> * disabled this also looks at the next expiring</span></span><br><span class="line"><span class="comment"> * hrtimer.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (2.1) 获取下一个timer的到期时间(包括低精度和高精度timer) */</span></span><br><span class="line"> next_tmr = get_next_timer_interrupt(basejiff, basemono);</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>next_timer = next_tmr;</span><br><span class="line"> <span class="comment">/* Take the next rcu event into account */</span></span><br><span class="line"> next_tick = next_rcu < next_tmr ? next_rcu : next_tmr;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * If the tick is due in the next period, keep it ticking or</span></span><br><span class="line"><span class="comment"> * restart it proper.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (2.2) 如果差距小于一个tick,不需要进入noHZ模式 */</span></span><br><span class="line"> delta = next_tick - basemono;</span><br><span class="line"> <span class="keyword">if</span> (delta <= (u64)TICK_NSEC) {</span><br><span class="line"> tick.tv64 = <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="title">if</span> (!ts-></span>tick_stopped)</span><br><span class="line"> goto out;</span><br><span class="line"> <span class="keyword">if</span> (delta == <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">/* Tick is stopped, but required now. Enforce it */</span></span><br><span class="line"> tick_nohz_restart(ts, now);</span><br><span class="line"> goto out;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * If this cpu is the one which updates jiffies, then give up</span></span><br><span class="line"><span class="comment"> * the assignment and let it be taken by the cpu which runs</span></span><br><span class="line"><span class="comment"> * the tick timer next, which might be this cpu as well. If we</span></span><br><span class="line"><span class="comment"> * don't drop this here the jiffies might be stale and</span></span><br><span class="line"><span class="comment"> * do_timer() never invoked. Keep track of the fact that it</span></span><br><span class="line"><span class="comment"> * was the one which had the do_timer() duty last. If this cpu</span></span><br><span class="line"><span class="comment"> * is the one which had the do_timer() duty last, we limit the</span></span><br><span class="line"><span class="comment"> * sleep time to the timekeeping max_deferement value.</span></span><br><span class="line"><span class="comment"> * Otherwise we can sleep as long as we want.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/* (2.3) 根据timekeeper的可能溢出的位宽,得到的idle最大值 */</span></span><br><span class="line"> delta = timekeeping_max_deferment();</span><br><span class="line"> <span class="keyword">if</span> (cpu == tick_do_timer_cpu) {</span><br><span class="line"> tick_do_timer_cpu = TICK_DO_TIMER_NONE;</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>do_timer_last = <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {</span><br><span class="line"> delta = KTIME_MAX;</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>do_timer_last = <span class="number">0</span>;</span><br><span class="line"> } <span class="function"><span class="title">else</span> <span class="keyword">if</span> (!ts-></span>do_timer_last) {</span><br><span class="line"> delta = KTIME_MAX;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_NO_HZ_FULL</span><br><span class="line"> <span class="comment">/* Limit the tick delta to the maximum scheduler deferment */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (!ts-></span>inidle)</span><br><span class="line"> delta = min(delta, scheduler_tick_max_deferment());</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Calculate the next expiry time */</span></span><br><span class="line"> <span class="keyword">if</span> (delta < (KTIME_MAX - basemono))</span><br><span class="line"> expires = basemono + delta;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> expires = KTIME_MAX;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.4) 综合上面条件,得到合理的stop tick的时间 */</span></span><br><span class="line"> expires = min_t(u64, expires, next_tick);</span><br><span class="line"> tick.tv64 = expires;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Skip reprogram of event if its not changed */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (ts-></span><span class="function"><span class="title">tick_stopped</span> && (expires == dev-></span>next_event.tv64))</span><br><span class="line"> goto out;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * nohz_stop_sched_tick can be called several times before</span></span><br><span class="line"><span class="comment"> * the nohz_restart_sched_tick is called. This happens when</span></span><br><span class="line"><span class="comment"> * interrupts arrive which do not cause a reschedule. In the</span></span><br><span class="line"><span class="comment"> * first call we save the current tick time, so we can restart</span></span><br><span class="line"><span class="comment"> * the scheduler tick in nohz_restart_sched_tick.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (!ts-></span>tick_stopped) {</span><br><span class="line"> nohz_balance_enter_idle(cpu);</span><br><span class="line"> calc_load_enter_idle();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span><span class="function"><span class="title">last_tick</span> = hrtimer_get_expires(&ts-></span>sched_timer);</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>tick_stopped = <span class="number">1</span>;</span><br><span class="line"> trace_tick_stop(<span class="number">1</span>, <span class="string">" "</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * If the expiration time == KTIME_MAX, then we simply stop</span></span><br><span class="line"><span class="comment"> * the tick timer.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (unlikely(expires == KTIME_MAX)) {</span><br><span class="line"> <span class="function"><span class="title">if</span> (ts-></span>nohz_mode == NOHZ_MODE_HIGHRES)</span><br><span class="line"> <span class="function"><span class="title">hrtimer_cancel</span>(&ts-></span>sched_timer);</span><br><span class="line"> goto out;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2.5) 实际的stop tick动作:</span></span><br><span class="line"><span class="comment"> 将local timer的周期改为大于一个tick的时间,将idle时间延长 */</span></span><br><span class="line"> <span class="function"><span class="title">if</span> (ts-></span>nohz_mode == NOHZ_MODE_HIGHRES)</span><br><span class="line"> <span class="function"><span class="title">hrtimer_start</span>(&ts-></span>sched_timer, tick, HRTIMER_MODE_ABS_PINNED);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> tick_program_event(tick, <span class="number">1</span>);</span><br><span class="line">out:</span><br><span class="line"> <span class="comment">/* Update the estimated sleep length */</span></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span><span class="function"><span class="title">sleep_length</span> = ktime_sub(dev-></span>next_event, now);</span><br><span class="line"> return tick;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>tick_nohz_idle_exit()的解析:</p>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">void tick_nohz_idle_exit(void)</span><br><span class="line">{</span><br><span class="line"> struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);</span><br><span class="line"> ktime_t now;</span><br><span class="line"></span><br><span class="line"> local_irq_disable();</span><br><span class="line"></span><br><span class="line"> WARN_ON_ONCE(!<span class="function"><span class="title">ts</span>-></span>inidle);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>inidle = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (ts-></span><span class="function"><span class="title">idle_active</span> || ts-></span>tick_stopped)</span><br><span class="line"> now = ktime_get();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (ts-></span>idle_active)</span><br><span class="line"> tick_nohz_stop_idle(ts, now);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (ts-></span>tick_stopped) {</span><br><span class="line"> <span class="comment">/* (1) 重启tick任务 */</span></span><br><span class="line"> tick_nohz_restart_sched_tick(ts, now);</span><br><span class="line"> tick_nohz_account_idle_ticks(ts);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> local_irq_enable();</span><br><span class="line">}</span><br><span class="line">|→</span><br><span class="line">static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">/* Update jiffies first */</span></span><br><span class="line"> tick_do_update_jiffies64(now);</span><br><span class="line"> update_cpu_load_nohz();</span><br><span class="line"></span><br><span class="line"> calc_load_exit_idle();</span><br><span class="line"> touch_softlockup_watchdog();</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Cancel the scheduled timer and restore the tick</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>tick_stopped = <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="title">ts</span>-></span>idle_exittime = now;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1.1) 重启local timer */</span></span><br><span class="line"> tick_nohz_restart(ts, now);</span><br><span class="line">}</span><br><span class="line">||→</span><br><span class="line">static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)</span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="title">hrtimer_cancel</span>(&ts-></span>sched_timer);</span><br><span class="line"> <span class="function"><span class="title">hrtimer_set_expires</span>(&ts-></span><span class="function"><span class="title">sched_timer</span>, ts-></span>last_tick);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Forward the time to expire in the future */</span></span><br><span class="line"> <span class="function"><span class="title">hrtimer_forward</span>(&ts-></span>sched_timer, now, tick_period);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="title">if</span> (ts-></span>nohz_mode == NOHZ_MODE_HIGHRES)</span><br><span class="line"> <span class="function"><span class="title">hrtimer_start_expires</span>(&ts-></span>sched_timer, HRTIMER_MODE_ABS_PINNED);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="function"><span class="title">tick_program_event</span>(hrtimer_get_expires(&ts-></span>sched_timer), <span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="4-2-tick-nohz-irq-enter-exit"><a href="#4-2-tick-nohz-irq-enter-exit" class="headerlink" title="4.2 tick_nohz_irq_enter/exit()"></a>4.2 tick_nohz_irq_enter/exit()</h3><p>因为在idle退出执行完本tick需要处理的timer后又需要重新关闭tick,系统设计了tick_nohz_irq_enter()/tick_nohz_irq_exit()来处理这种操作。在本次中断处理完timer后,在tick_nohz_irq_exit()中判断是否重新关闭tick任务。</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">static void <span class="built_in">cpu_idle_loop</span>(void)</span><br><span class="line">{</span><br><span class="line"> while (<span class="number">1</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) 关闭tick */</span></span><br><span class="line"> <span class="built_in">tick_nohz_idle_enter</span>();</span><br><span class="line"></span><br><span class="line"> while (!need_resched()) {</span><br><span class="line"> <span class="built_in">check_pgt_cache</span>();</span><br><span class="line"> <span class="built_in">rmb</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2) cpu hotplug之cpu_down()的处理 */</span></span><br><span class="line"> if (cpu_is_offline(smp_processor_id())) {</span><br><span class="line"></span><br><span class="line"> <span class="built_in">arch_cpu_idle_dead</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (3) 关中断 */</span></span><br><span class="line"> <span class="built_in">local_irq_disable</span>();</span><br><span class="line"> <span class="built_in">arch_cpu_idle_enter</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (4) 进入idle,</span></span><br><span class="line"><span class="comment"> cpu进入暂停状态 */</span></span><br><span class="line"> if (cpu_idle_force_poll || tick_check_broadcast_expired())</span><br><span class="line"> <span class="built_in">cpu_idle_poll</span>();</span><br><span class="line"> else</span><br><span class="line"> <span class="built_in">cpuidle_idle_call</span>();</span><br><span class="line"> <span class="comment">/* (5) cpu被local timer中断唤醒退出idle状态,继续执行;</span></span><br><span class="line"><span class="comment"> 但是因为irq是disable状态,中断服务程序并不能马上得到执行*/</span></span><br><span class="line"> <span class="comment">/* (5.1) 退出idle,并且开中断 */</span> </span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* (6) 中断打开后,被阻塞的local timer中断服务得到执行,到期的软件timer得到执行;*/</span></span><br><span class="line"> <span class="comment">/* (6.1) 退出中断时调用tick_nohz_irq_exit(),重新计算一个tick可以被stop的值 */</span></span><br><span class="line"> </span><br><span class="line"> <span class="built_in">arch_cpu_idle_exit</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (7) 重启tick */</span></span><br><span class="line"> <span class="built_in">tick_nohz_idle_exit</span>();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>tick_nohz_irq_enter()/tick_nohz_irq_exit()的代码解析:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title">tick_nohz_irq_enter</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">tick_sched</span> *ts = <span class="built_in">this_cpu_ptr</span>(&tick_cpu_sched);</span><br><span class="line"> <span class="type">ktime_t</span> now;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!ts->idle_active && !ts->tick_stopped)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> now = <span class="built_in">ktime_get</span>();</span><br><span class="line"> <span class="keyword">if</span> (ts->idle_active)</span><br><span class="line"> <span class="built_in">tick_nohz_stop_idle</span>(ts, now);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* (1) 基本就是空操作 */</span></span><br><span class="line"> <span class="keyword">if</span> (ts->tick_stopped) {</span><br><span class="line"> <span class="built_in">tick_nohz_update_jiffies</span>(now);</span><br><span class="line"> <span class="built_in">tick_nohz_kick_tick</span>(ts, now);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">tick_nohz_irq_exit</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">tick_sched</span> *ts = <span class="built_in">this_cpu_ptr</span>(&tick_cpu_sched);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (ts->inidle)</span><br><span class="line"> <span class="comment">/* (2) 重新判断stop tick任务 */</span></span><br><span class="line"> __tick_nohz_idle_enter(ts);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">tick_nohz_full_update_tick</span>(ts);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="4-3、local-timer时钟被关闭时的处理"><a href="#4-3、local-timer时钟被关闭时的处理" class="headerlink" title="4.3、local timer时钟被关闭时的处理"></a>4.3、local timer时钟被关闭时的处理</h3><p>还有一种情况需要考虑,在系统进入深层次的idle状态时,local timer本身的时钟可能会被关闭。比如MTK平台进入soidle状态时,local timer本身会被停止,这时会用一个GPT timer来替代local timer继续工作。</p>
<p>核心函数是timer_setting_before_wfi()/timer_setting_after_wfi():</p>
<ul>
<li>timer_setting_before_wfi()在进入idle前被调用,读出local timer的剩余值并配置到GPT timer中;</li>
<li>timer_setting_after_wfi()在退出idle后被调用,读出GPT timer的值来重新恢复local timer;</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">timer_setting_before_wfi</span><span class="params">(<span class="type">bool</span> f26m_off)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> USING_STD_TIMER_OPS</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SMP</span></span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> timer_left = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (1) 读出local timer的剩余值 */</span></span><br><span class="line"> timer_left = <span class="built_in">localtimer_get_counter</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (2) 根据GPT timer在不同状态下的频率,把剩余值配置到GPT中 */</span></span><br><span class="line"> <span class="keyword">if</span> ((<span class="type">int</span>)timer_left <= <span class="number">0</span>)</span><br><span class="line"> <span class="built_in">gpt_set_cmp</span>(IDLE_GPT, <span class="number">1</span>); <span class="comment">/* Trigger idle_gpt Timeout imediately */</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (f26m_off)</span><br><span class="line"> <span class="built_in">gpt_set_cmp</span>(IDLE_GPT, <span class="built_in">div_u64</span>(timer_left, <span class="number">406.25</span>));</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">gpt_set_cmp</span>(IDLE_GPT, timer_left);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (f26m_off)</span><br><span class="line"> <span class="built_in">gpt_set_clk</span>(IDLE_GPT, GPT_CLK_SRC_RTC, GPT_CLK_DIV_1);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">start_gpt</span>(IDLE_GPT);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> <span class="built_in">gpt_get_cnt</span>(GPT1, &timer_left);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">timer_setting_after_wfi</span><span class="params">(<span class="type">bool</span> f26m_off)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> USING_STD_TIMER_OPS</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SMP</span></span><br><span class="line"> <span class="comment">/* (3) 判断当前退出idle状态是否是因为GPT到期引起的 */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">gpt_check_and_ack_irq</span>(IDLE_GPT)) {</span><br><span class="line"> <span class="comment">/* (3.1) 如果GPT时间已经到期,证明local timer也已经到期,</span></span><br><span class="line"><span class="comment"> 触发local timer在下一时钟执行 */</span></span><br><span class="line"> <span class="built_in">localtimer_set_next_event</span>(<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (f26m_off)</span><br><span class="line"> <span class="built_in">gpt_set_clk</span>(IDLE_GPT, GPT_CLK_SRC_SYS, GPT_CLK_DIV_1);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">/* (4) 退出idle是因为GPT以外的中断源唤醒的 */</span></span><br><span class="line"> <span class="comment">/* waked up by other wakeup source */</span></span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> cnt, cmp;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* (4.1) 读出GPT中的剩余到期值,重新配置到local timer中 */</span></span><br><span class="line"> <span class="built_in">idle_gpt_get_cnt</span>(IDLE_GPT, &cnt);</span><br><span class="line"> <span class="built_in">idle_gpt_get_cmp</span>(IDLE_GPT, &cmp);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">unlikely</span>(cmp < cnt)) {</span><br><span class="line"> <span class="built_in">idle_err</span>(<span class="string">"[%s]GPT%d: counter = %10u, compare = %10u\n"</span>,</span><br><span class="line"> __func__, IDLE_GPT + <span class="number">1</span>, cnt, cmp);</span><br><span class="line"> <span class="comment">/* BUG(); */</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (f26m_off) {</span><br><span class="line"> <span class="built_in">localtimer_set_next_event</span>((cmp - cnt) * <span class="number">1625</span> / <span class="number">4</span>);</span><br><span class="line"> <span class="built_in">gpt_set_clk</span>(IDLE_GPT, GPT_CLK_SRC_SYS, GPT_CLK_DIV_1);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">localtimer_set_next_event</span>(cmp - cnt);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">stop_gpt</span>(IDLE_GPT);</span><br><span class="line"> }</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>需要特别说明的是,这种GPT timer全局只有一个,进入soidle的状态时cpu也只有一个在线,所以能正常的工作。</p>
<h2 id="5、hrtimer"><a href="#5、hrtimer" class="headerlink" title="5、hrtimer"></a>5、hrtimer</h2><h3 id="5-1、hrtimer的组织"><a href="#5-1、hrtimer的组织" class="headerlink" title="5.1、hrtimer的组织"></a>5.1、hrtimer的组织</h3><p>hrtimer的组织相对来说还是比较简单的,每个cpu对应一个hrtimer_cpu_base,每个hrtimer_cpu_base中有4类clock_base代表4种时间类型(HRTIMER_BASE_REALTIME、HRTIMER_BASE_MONOTONIC、HRTIMER_BASE_BOOTTIME、HRTIMER_BASE_TAI)的hrtimer,每个clock_base是以红黑树来组织同一类型的hrtimer的:</p>
<p><img src="hrtimer.png" alt="image"></p>
<figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =</span><br><span class="line">{</span><br><span class="line"><span class="meta"> .lock</span> = __RAW_SPIN_LOCK_UNLOCKED(hrtimer_bases<span class="number">.</span><span class="keyword">lock</span>),</span><br><span class="line"><span class="meta"> .seq</span> = SEQCNT_ZERO(hrtimer_bases<span class="number">.</span>seq),</span><br><span class="line"><span class="meta"> .clock_base</span> =</span><br><span class="line"> {</span><br><span class="line"> {</span><br><span class="line"><span class="meta"> .index</span> = HRTIMER_BASE_MONOTONIC,</span><br><span class="line"><span class="meta"> .clockid</span> = CLOCK_MONOTONIC,</span><br><span class="line"><span class="meta"> .get_time</span> = &ktime_get,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"><span class="meta"> .index</span> = HRTIMER_BASE_REALTIME,</span><br><span class="line"><span class="meta"> .clockid</span> = CLOCK_REALTIME,</span><br><span class="line"><span class="meta"> .get_time</span> = &ktime_get_real,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"><span class="meta"> .index</span> = HRTIMER_BASE_BOOTTIME,</span><br><span class="line"><span class="meta"> .clockid</span> = CLOCK_BOOTTIME,</span><br><span class="line"><span class="meta"> .get_time</span> = &ktime_get_boottime,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"><span class="meta"> .index</span> = HRTIMER_BASE_TAI,</span><br><span class="line"><span class="meta"> .clockid</span> = CLOCK_TAI,</span><br><span class="line"><span class="meta"> .get_time</span> = &ktime_get_clocktai,</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line">}<span class="comment">;</span></span><br></pre></td></tr></table></figure>
<h3 id="5-2、低精度模式-NOHZ-MODE-INACTIVE-NOHZ-MODE-LOWRES"><a href="#5-2、低精度模式-NOHZ-MODE-INACTIVE-NOHZ-MODE-LOWRES" class="headerlink" title="5.2、低精度模式(NOHZ_MODE_INACTIVE/NOHZ_MODE_LOWRES)"></a>5.2、低精度模式(NOHZ_MODE_INACTIVE/NOHZ_MODE_LOWRES)</h3><p>前面几章已经详细描述了执行路径,在低精度模式下hrtimer的实际精度和低精度定时器是一样的,都是基于tick精度的。他的执行路径如下。</p>
<p>NOHZ_MODE_INACTIVE模式:</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">tick_handle_periodic</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">tick_periodic</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">update_process_times</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">run_local_timers</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">hrtimer_run_queues</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="built_in">__hrtimer_run_queues</span>()</span><br></pre></td></tr></table></figure>
<p>NOHZ_MODE_LOWRES模式:</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">tick_nohz_handler</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">tick_sched_handle</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">update_process_times</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">run_local_timers</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">hrtimer_run_queues</span><span class="params">()</span></span></span><br><span class="line"> ↓</span><br><span class="line"><span class="built_in">__hrtimer_run_queues</span>()</span><br></pre></td></tr></table></figure>
<h3 id="5-3、高精度模式-NOHZ-MODE-HIGHRES"><a href="#5-3、高精度模式-NOHZ-MODE-HIGHRES" class="headerlink" title="5.3、高精度模式(NOHZ_MODE_HIGHRES)"></a>5.3、高精度模式(NOHZ_MODE_HIGHRES)</h3><p>在高精度模式下hrtimer才能发挥出真正的精度,他的可以精确定时到小于一个tick,精度依赖于硬件local timer。</p>
<p>NOHZ_MODE_LOWRES模式:</p>
<figure class="highlight isbl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">hrtimer_interrupt</span>()</span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">__hrtimer_run_queues</span>()</span></span><br></pre></td></tr></table></figure>
<h2 id="6、低精度timer-lowres-timer"><a href="#6、低精度timer-lowres-timer" class="headerlink" title="6、低精度timer(lowres timer)"></a>6、低精度timer(lowres timer)</h2><p>低精度timer在系统中的应用范围更广,若非特别声明是hrtimer其他都是使用低精度timer,类如schedule_timeout()、msleep()。他有以下特点:</p>
<ul>
<li>精度低,以tick为单位计时;</li>
<li>执行上下文,低精度timer执行时是在softirq中,而hrtimer的实际执行是在中断当中。所以低精度的执行精度更小于hrtimer;</li>
<li>对系统的实时影响小,softirq比irq对系统的实时性影响更小;</li>
</ul>
<h3 id="6-1、低精度timer的组织"><a href="#6-1、低精度timer的组织" class="headerlink" title="6.1、低精度timer的组织"></a>6.1、低精度timer的组织</h3><p>低精度timer的组织形式和hrtimer类似,只是timer的链接不是采用红黑树,而是采用tv1 - tv5等一系列的链表。</p>
<p><img src="lowres_timer.png" alt="image"></p>
<p>tv1 - tv5中保留着一系列槽位,每个槽位代表一个超时时间,把相同超时时间的低精度timer链接到同一槽位当中。</p>
<h3 id="6-2、低精度timer的执行路径"><a href="#6-2、低精度timer的执行路径" class="headerlink" title="6.2、低精度timer的执行路径"></a>6.2、低精度timer的执行路径</h3><p>低精度timer的实际执行时在softirq中执行的,在中断中的动作只是简单触发softirq。</p>
<p>中断中:</p>
<figure class="highlight isbl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">tick_handle_periodic</span>()/<span class="title">tick_nohz_handler</span>()/<span class="title">hrtimer_interrupt</span>()</span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">run_local_timers</span>()</span></span><br><span class="line"> ↓</span><br><span class="line"><span class="function"><span class="title">raise_softirq</span>(<span class="variable">TIMER_SOFTIRQ</span>);</span></span><br></pre></td></tr></table></figure>
<p>软中断中:</p>
<figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">run_timer_softir<span class="string">q()</span></span><br><span class="line"> ↓</span><br><span class="line">__run_timers()</span><br></pre></td></tr></table></figure>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[^DroidPhoneo]: <a href="http://blog.csdn.net/DroidPhone/article/category/1263459">Linux 时间子系统</a></p>
<p>[^wowo]: <a href="http://www.wowotech.net/timer_subsystem/time_subsystem_index.html">wowotech time subsystem</a></p>
<hr>
<!-- Pager -->
<ul class="pager">
<li class="previous">
<a href="//android-p-fbe.html" data-toggle="tooltip" data-placement="top" title="Android FBE">← Previous Post</a>
</li>
<li class="next">
<a href="//device-tree.html" data-toggle="tooltip" data-placement="top" title="Device Tree 详解">Next Post →</a>
</li>
</ul>
<!-- tip start -->
<div class="comment_notes">
<p>
This is copyright.
</p>
</div>
<!-- tip end -->
<!-- Music start-->
<!-- Music end -->
<!-- Sharing -->
<div class="social-share" data-wechat-qrcode-helper="" align="center"></div>
<!-- css & js -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/css/share.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/js/social-share.min.js"></script>
<!-- Sharing -->
<!-- gitment start -->
<!-- gitment end -->
<!-- 来必力City版安装代码 -->
<!-- City版安装代码已完成 -->
<!-- disqus comment start -->
<!-- disqus comment end -->
</div>
<!-- Tabe of Content -->
<!-- Table of Contents -->
<!-- Sidebar Container -->
<div class="
col-lg-8 col-lg-offset-2
col-md-10 col-md-offset-1
sidebar-container">
<!-- Featured Tags -->
<section>
<!-- no hr -->
<h5><a href="/tags/">FEATURED TAGS</a></h5>
<div class="tags">
<a class="tag" href="/tags/#hrtimer" title="hrtimer">hrtimer</a>
<a class="tag" href="/tags/#tickless" title="tickless">tickless</a>
<a class="tag" href="/tags/#clocksource" title="clocksource">clocksource</a>
<a class="tag" href="/tags/#timekeeper" title="timekeeper">timekeeper</a>
<a class="tag" href="/tags/#clockevent" title="clockevent">clockevent</a>
<a class="tag" href="/tags/#noHZ" title="noHZ">noHZ</a>
<a class="tag" href="/tags/#lowres timer" title="lowres timer">lowres timer</a>
<a class="tag" href="/tags/#wall time" title="wall time">wall time</a>
<a class="tag" href="/tags/#xtime" title="xtime">xtime</a>
<a class="tag" href="/tags/#monotonic time" title="monotonic time">monotonic time</a>
<a class="tag" href="/tags/#boottime" title="boottime">boottime</a>
</div>
</section>
<!-- Friends Blog -->
<hr>
<h5>FRIENDS</h5>
<ul class="list-inline">
<li><a href="#" target="_blank">Other</a></li>
</ul>
</div>
</div>
</div>
</article>
<!-- async load function -->
<script>
function async(u, c) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = u;
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
s.parentNode.insertBefore(o, s);
}
</script>
<!-- anchor-js, Doc:http://bryanbraun.github.io/anchorjs/ -->
<script>
async("https://cdn.bootcss.com/anchor-js/1.1.1/anchor.min.js",function(){
anchors.options = {
visible: 'hover',
placement: 'left',
icon: 'ℬ'
};
anchors.add().remove('.intro-header h1').remove('.subheading').remove('.sidebar-container h5');
})
</script>
<style type="text/css">
/* place left on bigger screen */
@media all and (min-width: 800px) {
.anchorjs-link{
position: absolute;
left: -0.75em;
font-size: 1.1em;
margin-top : -0.1em;
}
}
</style>
<!-- Footer -->
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<ul class="list-inline text-center">
</ul>
<p class="copyright text-muted">
Copyright © meizu 2024
<br>
Powered by
<a href="https://github.com/dusign/hexo-theme-snail">
<i>hexo-theme-snail</i>
</a> |
<iframe name="star" style="margin-left: 2px; margin-bottom:-5px;" frameborder="0" scrolling="0"
width="100px" height="20px"
src="https://ghbtns.com/github-btn.html?user=dusign&repo=hexo-theme-snail&type=star&count=true">
</iframe>
</p>
</div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="js/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="js/bootstrap.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="js/hux-blog.min.js"></script>
<!-- Search -->
<script src="js/search.js"></script>
<!-- async load function -->
<script>
function async(u, c) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = u;
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
s.parentNode.insertBefore(o, s);
}
</script>
<!-- jquery.tagcloud.js -->
<script>
// only load tagcloud.js in tag.html
if($('#tag_cloud').length !== 0){
async("https://kernel.meizu.com/js/jquery.tagcloud.js",function(){
$.fn.tagcloud.defaults = {
//size: {start: 1, end: 1, unit: 'em'},
color: {start: '#bbbbee', end: '#0085a1'},
};
$('#tag_cloud a').tagcloud();
})
}
</script>
<!--fastClick.js -->
<script>
async("https://cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
var $nav = document.querySelector("nav");
if($nav) FastClick.attach($nav);
})
</script>
<!-- Google Analytics -->
<script>
// dynamic User by Hux
var _gaId = 'UA-XXXXXXXX-X';
var _gaDomain = 'yoursite';
// Originial
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', _gaId, _gaDomain);
ga('send', 'pageview');
</script>
<!-- Baidu Tongji -->
<!-- Search -->
<script type="text/javascript">
var search_path = "search.xml";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "/" + search_path;
searchFunc(path, 'local-search-input', 'local-search-result');
</script>
<!-- busuanzi -->
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<a id="rocket" href="#top" class=""></a>
<script type="text/javascript" src="/js/totop.js?v=1.0.0" async=""></script>
<script type="text/javascript" src="/js/toc.js?v=1.0.0" async=""></script>
<!-- background effects line -->
<script type="text/javascript" src="/js/mouse-click.js" content='["🌱","just do it","🍀"]' color='["rgb(121,93,179)" ,"rgb(76,180,231)" ,"rgb(184,90,154)"]'></script>
<!-- background effects end -->
<!--<script size="50" alpha='0.3' zIndex="-999" src="/js/ribbonStatic.js"></script>-->
<script src="/js/ribbonDynamic.js"></script>
</body>
</html>