| 1 | |
| 2 | |
| 3 | * This file contains the jiffies based clocksource.
|
| 4 | *
|
| 5 | * Copyright (C) 2004, 2005 IBM, John Stultz ([email protected])
|
| 6 |
|
| 7 | #include <linux/clocksource.h>
|
| 8 | #include <linux/jiffies.h>
|
| 9 | #include <linux/module.h>
|
| 10 | #include <linux/init.h>
|
| 11 |
|
| 12 | #include "timekeeping.h"
|
| 13 | #include "tick-internal.h"
|
| 14 |
|
| 15 |
|
| 16 | static u64 jiffies_read(struct clocksource *cs)
|
| 17 | {
|
| 18 | return (u64) jiffies;
|
| 19 | }
|
| 20 |
|
| 21 | |
| 22 | * The Jiffies based clocksource is the lowest common
|
| 23 | * denominator clock source which should function on
|
| 24 | * all systems. It has the same coarse resolution as
|
| 25 | * the timer interrupt frequency HZ and it suffers
|
| 26 | * inaccuracies caused by missed or lost timer
|
| 27 | * interrupts and the inability for the timer
|
| 28 | * interrupt hardware to accurately tick at the
|
| 29 | * requested HZ value. It is also not recommended
|
| 30 | * for "tick-less" systems.
|
| 31 |
|
| 32 | static struct clocksource clocksource_jiffies = {
|
| 33 | .name = "jiffies",
|
| 34 | .rating = 1,
|
| 35 | .read = jiffies_read,
|
| 36 | .mask = CLOCKSOURCE_MASK(32),
|
| 37 | .mult = TICK_NSEC << JIFFIES_SHIFT,
|
| 38 | .shift = JIFFIES_SHIFT,
|
| 39 | .max_cycles = 10,
|
| 40 | };
|
| 41 |
|
| 42 | __cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(jiffies_lock);
|
| 43 | __cacheline_aligned_in_smp seqcount_raw_spinlock_t jiffies_seq =
|
| 44 | SEQCNT_RAW_SPINLOCK_ZERO(jiffies_seq, &jiffies_lock);
|
| 45 |
|
| 46 | #if (BITS_PER_LONG < 64)
|
| 47 | u64 get_jiffies_64(void)
|
| 48 | {
|
| 49 | unsigned int seq;
|
| 50 | u64 ret;
|
| 51 |
|
| 52 | do {
|
| 53 | seq = read_seqcount_begin(&jiffies_seq);
|
| 54 | ret = jiffies_64;
|
| 55 | } while (read_seqcount_retry(&jiffies_seq, seq));
|
| 56 | return ret;
|
| 57 | }
|
| 58 | EXPORT_SYMBOL(get_jiffies_64);
|
| 59 | #endif
|
| 60 |
|
| 61 | EXPORT_SYMBOL(jiffies);
|
| 62 |
|
| 63 | static bool cs_jiffies_registered __initdata;
|
| 64 |
|
| 65 | struct clocksource * __init __weak clocksource_default_clock(void)
|
| 66 | {
|
| 67 | if (!cs_jiffies_registered) {
|
| 68 | __clocksource_register(&clocksource_jiffies);
|
| 69 | cs_jiffies_registered = true;
|
| 70 | }
|
| 71 | return &clocksource_jiffies;
|
| 72 | }
|
| 73 |
|
| 74 | static struct clocksource refined_jiffies;
|
| 75 |
|
| 76 | void __init register_refined_jiffies(long cycles_per_second)
|
| 77 | {
|
| 78 | u64 nsec_per_tick, shift_hz;
|
| 79 | long cycles_per_tick;
|
| 80 |
|
| 81 | refined_jiffies = clocksource_jiffies;
|
| 82 | refined_jiffies.name = "refined-jiffies";
|
| 83 | refined_jiffies.rating++;
|
| 84 |
|
| 85 |
|
| 86 | cycles_per_tick = (cycles_per_second + HZ/2)/HZ;
|
| 87 |
|
| 88 | shift_hz = (u64)cycles_per_second << 8;
|
| 89 | shift_hz += cycles_per_tick/2;
|
| 90 | do_div(shift_hz, cycles_per_tick);
|
| 91 |
|
| 92 | nsec_per_tick = (u64)NSEC_PER_SEC << 8;
|
| 93 | nsec_per_tick += (u32)shift_hz/2;
|
| 94 | do_div(nsec_per_tick, (u32)shift_hz);
|
| 95 |
|
| 96 | refined_jiffies.mult = ((u32)nsec_per_tick) << JIFFIES_SHIFT;
|
| 97 |
|
| 98 | __clocksource_register(&refined_jiffies);
|
| 99 | }
|
| 100 |
|
| 101 | #ifdef CONFIG_SYSCTL
|
| 102 | static ulong mult_hz(const ulong val)
|
| 103 | {
|
| 104 | if (val >= ULONG_MAX / HZ)
|
| 105 | return ULONG_MAX;
|
| 106 | return val * HZ;
|
| 107 | }
|
| 108 |
|
| 109 | static ulong div_hz(const ulong val)
|
| 110 | {
|
| 111 | return val / HZ;
|
| 112 | }
|
| 113 |
|
| 114 | static int sysctl_u2k_int_conv_hz(const bool *negp, const ulong *u_ptr, int *k_ptr)
|
| 115 | {
|
| 116 | return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, mult_hz);
|
| 117 | }
|
| 118 |
|
| 119 | static int sysctl_k2u_int_conv_hz(bool *negp, ulong *u_ptr, const int *k_ptr)
|
| 120 | {
|
| 121 | return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, div_hz);
|
| 122 | }
|
| 123 |
|
| 124 | static int sysctl_u2k_int_conv_userhz(const bool *negp, const ulong *u_ptr, int *k_ptr)
|
| 125 | {
|
| 126 | return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, clock_t_to_jiffies);
|
| 127 | }
|
| 128 |
|
| 129 | static ulong sysctl_jiffies_to_clock_t(const ulong val)
|
| 130 | {
|
| 131 | return jiffies_to_clock_t(val);
|
| 132 | }
|
| 133 |
|
| 134 | static int sysctl_k2u_int_conv_userhz(bool *negp, ulong *u_ptr, const int *k_ptr)
|
| 135 | {
|
| 136 | return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_clock_t);
|
| 137 | }
|
| 138 |
|
| 139 | static ulong sysctl_msecs_to_jiffies(const ulong val)
|
| 140 | {
|
| 141 | if (val > jiffies_to_msecs(MAX_JIFFY_OFFSET))
|
| 142 | return MAX_JIFFY_OFFSET;
|
| 143 | return msecs_to_jiffies(val);
|
| 144 | }
|
| 145 |
|
| 146 | static int sysctl_u2k_int_conv_ms(const bool *negp, const ulong *u_ptr, int *k_ptr)
|
| 147 | {
|
| 148 | return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, sysctl_msecs_to_jiffies);
|
| 149 | }
|
| 150 |
|
| 151 | static ulong sysctl_jiffies_to_msecs(const ulong val)
|
| 152 | {
|
| 153 | return jiffies_to_msecs(val);
|
| 154 | }
|
| 155 |
|
| 156 | static int sysctl_k2u_int_conv_ms(bool *negp, ulong *u_ptr, const int *k_ptr)
|
| 157 | {
|
| 158 | return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_msecs);
|
| 159 | }
|
| 160 |
|
| 161 | static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
|
| 162 | int dir, const struct ctl_table *tbl)
|
| 163 | {
|
| 164 | return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
|
| 165 | sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz);
|
| 166 | }
|
| 167 |
|
| 168 | static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
|
| 169 | int *k_ptr, int dir,
|
| 170 | const struct ctl_table *tbl)
|
| 171 | {
|
| 172 | return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
|
| 173 | sysctl_u2k_int_conv_userhz,
|
| 174 | sysctl_k2u_int_conv_userhz);
|
| 175 | }
|
| 176 |
|
| 177 | static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
|
| 178 | int dir, const struct ctl_table *tbl)
|
| 179 | {
|
| 180 | return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
|
| 181 | sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
|
| 182 | }
|
| 183 |
|
| 184 | static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
|
| 185 | int *k_ptr, int dir,
|
| 186 | const struct ctl_table *tbl)
|
| 187 | {
|
| 188 | return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,
|
| 189 | sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
|
| 190 | }
|
| 191 |
|
| 192 | static int sysctl_u2k_ulong_conv_ms(const ulong *u_ptr, ulong *k_ptr)
|
| 193 | {
|
| 194 | return proc_ulong_u2k_conv_uop(u_ptr, k_ptr, sysctl_msecs_to_jiffies);
|
| 195 | }
|
| 196 |
|
| 197 | static int sysctl_k2u_ulong_conv_ms(ulong *u_ptr, const ulong *k_ptr)
|
| 198 | {
|
| 199 | return proc_ulong_k2u_conv_kop(u_ptr, k_ptr, sysctl_jiffies_to_msecs);
|
| 200 | }
|
| 201 |
|
| 202 | static int do_proc_ulong_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, ulong *k_ptr,
|
| 203 | int dir, const struct ctl_table *tbl)
|
| 204 | {
|
| 205 | return proc_ulong_conv(u_ptr, k_ptr, dir, tbl, true,
|
| 206 | sysctl_u2k_ulong_conv_ms, sysctl_k2u_ulong_conv_ms);
|
| 207 | }
|
| 208 |
|
| 209 | #else
|
| 210 | static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
|
| 211 | int dir, const struct ctl_table *tbl)
|
| 212 | {
|
| 213 | return -ENOSYS;
|
| 214 | }
|
| 215 |
|
| 216 | static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
|
| 217 | int *k_ptr, int dir,
|
| 218 | const struct ctl_table *tbl)
|
| 219 | {
|
| 220 | return -ENOSYS;
|
| 221 | }
|
| 222 |
|
| 223 | static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
|
| 224 | int dir, const struct ctl_table *tbl)
|
| 225 | {
|
| 226 | return -ENOSYS;
|
| 227 | }
|
| 228 |
|
| 229 | static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
|
| 230 | int *k_ptr, int dir,
|
| 231 | const struct ctl_table *tbl)
|
| 232 | {
|
| 233 | return -ENOSYS;
|
| 234 | }
|
| 235 |
|
| 236 | static int do_proc_ulong_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, ulong *k_ptr,
|
| 237 | int dir, const struct ctl_table *tbl)
|
| 238 | {
|
| 239 | return -ENOSYS;
|
| 240 | }
|
| 241 | #endif
|
| 242 |
|
| 243 | |
| 244 | * proc_dointvec_jiffies - read a vector of integers as seconds
|
| 245 | * @table: the sysctl table
|
| 246 | * @dir: %TRUE if this is a write to the sysctl file
|
| 247 | * @buffer: the user buffer
|
| 248 | * @lenp: the size of the user buffer
|
| 249 | * @ppos: file position
|
| 250 | *
|
| 251 | * Reads/writes up to table->maxlen/sizeof(unsigned int) integer
|
| 252 | * values from/to the user buffer, treated as an ASCII string.
|
| 253 | * The values read are assumed to be in seconds, and are converted into
|
| 254 | * jiffies.
|
| 255 | *
|
| 256 | * Returns 0 on success.
|
| 257 |
|
| 258 | int proc_dointvec_jiffies(const struct ctl_table *table, int dir,
|
| 259 | void *buffer, size_t *lenp, loff_t *ppos)
|
| 260 | {
|
| 261 | return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
|
| 262 | do_proc_int_conv_jiffies);
|
| 263 | }
|
| 264 | EXPORT_SYMBOL(proc_dointvec_jiffies);
|
| 265 |
|
| 266 | |
| 267 | * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds
|
| 268 | * @table: the sysctl table
|
| 269 | * @dir: %TRUE if this is a write to the sysctl file
|
| 270 | * @buffer: the user buffer
|
| 271 | * @lenp: the size of the user buffer
|
| 272 | * @ppos: pointer to the file position
|
| 273 | *
|
| 274 | * Reads/writes up to table->maxlen/sizeof(unsigned int) integer
|
| 275 | * values from/to the user buffer, treated as an ASCII string.
|
| 276 | * The values read are assumed to be in 1/USER_HZ seconds, and
|
| 277 | * are converted into jiffies.
|
| 278 | *
|
| 279 | * Returns 0 on success.
|
| 280 |
|
| 281 | int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir,
|
| 282 | void *buffer, size_t *lenp, loff_t *ppos)
|
| 283 | {
|
| 284 | return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
|
| 285 | do_proc_int_conv_userhz_jiffies);
|
| 286 | }
|
| 287 | EXPORT_SYMBOL(proc_dointvec_userhz_jiffies);
|
| 288 |
|
| 289 | |
| 290 | * proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds
|
| 291 | * @table: the sysctl table
|
| 292 | * @dir: %TRUE if this is a write to the sysctl file
|
| 293 | * @buffer: the user buffer
|
| 294 | * @lenp: the size of the user buffer
|
| 295 | * @ppos: the current position in the file
|
| 296 | *
|
| 297 | * Reads/writes up to table->maxlen/sizeof(unsigned int) integer
|
| 298 | * values from/to the user buffer, treated as an ASCII string.
|
| 299 | * The values read are assumed to be in 1/1000 seconds, and
|
| 300 | * are converted into jiffies.
|
| 301 | *
|
| 302 | * Returns 0 on success.
|
| 303 |
|
| 304 | int proc_dointvec_ms_jiffies(const struct ctl_table *table, int dir, void *buffer,
|
| 305 | size_t *lenp, loff_t *ppos)
|
| 306 | {
|
| 307 | return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
|
| 308 | do_proc_int_conv_ms_jiffies);
|
| 309 | }
|
| 310 | EXPORT_SYMBOL(proc_dointvec_ms_jiffies);
|
| 311 |
|
| 312 | int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
|
| 313 | void *buffer, size_t *lenp, loff_t *ppos)
|
| 314 | {
|
| 315 | return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
|
| 316 | do_proc_int_conv_ms_jiffies_minmax);
|
| 317 | }
|
| 318 |
|
| 319 | |
| 320 | * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values
|
| 321 | * @table: the sysctl table
|
| 322 | * @dir: %TRUE if this is a write to the sysctl file
|
| 323 | * @buffer: the user buffer
|
| 324 | * @lenp: the size of the user buffer
|
| 325 | * @ppos: file position
|
| 326 | *
|
| 327 | * Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long
|
| 328 | * values from/to the user buffer, treated as an ASCII string. The values
|
| 329 | * are treated as milliseconds, and converted to jiffies when they are stored.
|
| 330 | *
|
| 331 | * This routine will ensure the values are within the range specified by
|
| 332 | * table->extra1 (min) and table->extra2 (max).
|
| 333 | *
|
| 334 | * Returns 0 on success.
|
| 335 |
|
| 336 | int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
|
| 337 | void *buffer, size_t *lenp, loff_t *ppos)
|
| 338 | {
|
| 339 | return proc_doulongvec_conv(table, dir, buffer, lenp, ppos,
|
| 340 | do_proc_ulong_conv_ms_jiffies_minmax);
|
| 341 | }
|
| 342 | EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
|
| 343 |
|