diff --git a/kernel/lamport.c b/kernel/lamport.c index 4bd026eb..d4d0e190 100644 --- a/kernel/lamport.c +++ b/kernel/lamport.c @@ -92,18 +92,21 @@ * * Please improve this code, but please use the right optimisation goal. */ -struct rw_semaphore lamport_sem = __RWSEM_INITIALIZER(lamport_sem); +struct lamport_clock global_lamport = { + .lamport_sem = __RWSEM_INITIALIZER(global_lamport.lamport_sem), +}; +EXPORT_SYMBOL_GPL(global_lamport); -struct lamport_time lamport_stamp = {}; - -void get_lamport(struct lamport_time *real_now, struct lamport_time *lamport_now) +void _get_lamport(struct lamport_clock *clock, + struct lamport_time *real_now, + struct lamport_time *lamport_now) { struct lamport_time _real_now; struct lamport_time _lamport_now; /* Get a consistent copy of _both_ clocks */ - down_read(&lamport_sem); - _lamport_now = lamport_stamp; + down_read(&clock->lamport_sem); + _lamport_now = clock->lamport_stamp; /* Theoretically, the next statement could be moved behind the unlock. * However, then we will loose strictness of real timestamps, * or even may produce contradictory orderings between real and @@ -111,7 +114,8 @@ void get_lamport(struct lamport_time *real_now, struct lamport_time *lamport_now * calls to get_lamport(). */ _real_now = get_real_lamport(); - up_read(&lamport_sem); + + up_read(&clock->lamport_sem); if (real_now) *real_now = _real_now; @@ -121,58 +125,63 @@ void get_lamport(struct lamport_time *real_now, struct lamport_time *lamport_now else *lamport_now = _lamport_now; } +EXPORT_SYMBOL_GPL(_get_lamport); -EXPORT_SYMBOL_GPL(get_lamport); - -void set_lamport(struct lamport_time *lamport_old) +void _set_lamport(struct lamport_clock *clock, + struct lamport_time *lamport_advance) { - protect_lamport_time(lamport_old); + protect_lamport_time(lamport_advance); /* Always advance the internal Lamport timestamp a little bit * in order to ensure strict monotonicity between set_lamport() calls. */ - down_write(&lamport_sem); - if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) - lamport_stamp = *lamport_old; + down_write(&clock->lamport_sem); + if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0) + clock->lamport_stamp = *lamport_advance; else - lamport_time_add_ns(&lamport_stamp, 1); - up_write(&lamport_sem); + lamport_time_add_ns(&clock->lamport_stamp, 1); + up_write(&clock->lamport_sem); } -EXPORT_SYMBOL_GPL(set_lamport); +EXPORT_SYMBOL_GPL(_set_lamport); -void set_lamport_nonstrict(struct lamport_time *lamport_old) +void _set_lamport_nonstrict(struct lamport_clock *clock, + struct lamport_time *lamport_advance) { - protect_lamport_time(lamport_old); + protect_lamport_time(lamport_advance); /* Speculate that advaning is not necessary, to avoid the lock */ - if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) { - down_write(&lamport_sem); - if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) - lamport_stamp = *lamport_old; - up_write(&lamport_sem); + if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0) { + down_write(&clock->lamport_sem); + if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0) + clock->lamport_stamp = *lamport_advance; + up_write(&clock->lamport_sem); } } -EXPORT_SYMBOL_GPL(set_lamport_nonstrict); +EXPORT_SYMBOL_GPL(_set_lamport_nonstrict); /* After advancing the Lamport time, re-get the new values. * This is almost equivalent to a sequence of set_lamport() ; get_lamport() * but more efficient because the lock is taken only once. */ -void set_get_lamport(struct lamport_time *lamport_old, struct lamport_time *real_now, struct lamport_time *lamport_now) +void _set_get_lamport(struct lamport_clock *clock, + struct lamport_time *lamport_advance, + struct lamport_time *real_now, + struct lamport_time *lamport_now) { struct lamport_time _real_now; - protect_lamport_time(lamport_old); + protect_lamport_time(lamport_advance); - down_write(&lamport_sem); - if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) - *lamport_now = *lamport_old; + down_write(&clock->lamport_sem); + if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0) + *lamport_now = *lamport_advance; else - *lamport_now = lamport_time_add(lamport_stamp, (struct lamport_time){0, 1}); - lamport_stamp = *lamport_now; + *lamport_now = lamport_time_add(clock->lamport_stamp, + (struct lamport_time){0, 1}); + clock->lamport_stamp = *lamport_now; _real_now = get_real_lamport(); - up_write(&lamport_sem); + up_write(&clock->lamport_sem); if (real_now) *real_now = _real_now; @@ -180,27 +189,30 @@ void set_get_lamport(struct lamport_time *lamport_old, struct lamport_time *real if (lamport_time_compare(&_real_now, lamport_now) > 0) *lamport_now = _real_now; } -EXPORT_SYMBOL_GPL(set_get_lamport); +EXPORT_SYMBOL_GPL(_set_get_lamport); /* Protect against illegal values, e.g. from currupt filesystems etc. */ int max_lamport_future = 30 * 24 * 3600; -bool protect_lamport_time(struct lamport_time *check) +bool _protect_lamport_time(struct lamport_clock *clock, + struct lamport_time *check) { struct lamport_time limit = get_real_lamport(); bool res = false; limit.tv_sec += max_lamport_future; if (unlikely(check->tv_sec >= limit.tv_sec)) { - down_write(&lamport_sem); - lamport_time_add_ns(&lamport_stamp, 1); - memcpy(check, &lamport_stamp, sizeof(*check)); + down_write(&clock->lamport_sem); + lamport_time_add_ns(&clock->lamport_stamp, 1); + lamport_time_add_ns(&clock->lamport_stamp, 1); + memcpy(check, &clock->lamport_stamp, sizeof(*check)); if (unlikely(check->tv_sec > limit.tv_sec)) max_lamport_future += check->tv_sec - limit.tv_sec; - up_write(&lamport_sem); + up_write(&clock->lamport_sem); res = true; } return res; } +EXPORT_SYMBOL_GPL(_protect_lamport_time); diff --git a/kernel/lamport.h b/kernel/lamport.h index 2532b049..29194ae8 100644 --- a/kernel/lamport.h +++ b/kernel/lamport.h @@ -53,6 +53,19 @@ # define get_real_lamport() CURRENT_TIME #endif +#include + +struct lamport_clock { + struct rw_semaphore lamport_sem; + struct lamport_time lamport_stamp; +}; + +extern struct lamport_clock global_lamport; + +/* Protect against illegal values, e.g. from currupt filesystems etc. + */ +extern int max_lamport_future; + /* * We always get both the local real time and the Lamport time in parallel, * consistently. @@ -62,30 +75,92 @@ * * When not interested in real time, you can simply leave real_now at NULL. */ -extern void get_lamport(struct lamport_time *real_now, - struct lamport_time *lamport_now); +extern void _get_lamport(struct lamport_clock *clock, + struct lamport_time *real_now, + struct lamport_time *lamport_now); /* This ensures _strict_ monotonicity of the Lamport clock */ -extern void set_lamport(struct lamport_time *lamport_old); +extern void _set_lamport(struct lamport_clock *clock, + struct lamport_time *lamport_advance); /* Non-strict version. * Use this for better performance when strictness is not needed. */ -extern void set_lamport_nonstrict(struct lamport_time *lamport_old); +extern void _set_lamport_nonstrict(struct lamport_clock *clock, + struct lamport_time *lamport_advance); /* After strictly advancing the Lamport time, re-get the new values. * This is almost equivalent to a sequence of set_lamport() ; get_lamport() * but (1) atomic and (2) more efficient * because the internal lock is taken only once. */ -extern void set_get_lamport(struct lamport_time *lamport_old, - struct lamport_time *real_now, - struct lamport_time *lamport_now); +extern void _set_get_lamport(struct lamport_clock *clock, + struct lamport_time *lamport_advance, + struct lamport_time *real_now, + struct lamport_time *lamport_now); -/* Protect against illegal values, e.g. from currupt filesystems etc. +extern bool _protect_lamport_time(struct lamport_clock *clock, + struct lamport_time *check); + + +/* Usually the following versions are the preferred ones for working + * on the single global lamport clock instance. */ -extern int max_lamport_future; -extern bool protect_lamport_time(struct lamport_time *check); +#define get_lamport(real_now,lamport_now) \ + _get_lamport(&global_lamport,real_now,lamport_now) + +#define set_lamport(lamport_advance) \ + _set_lamport(&global_lamport,lamport_advance) + +#define set_lamport_nonstrict(lamport_advance) \ + _set_lamport_nonstrict(&global_lamport,lamport_advance) + +#define set_get_lamport(lamport_advance,real_now,lamport_now) \ + _set_get_lamport(&global_lamport,lamport_advance,real_now,lamport_now) + +#define protect_lamport_time(check) \ + _protect_lamport_time(&global_lamport,check) + +/* + * Here is a very general lockless version for inlining. + * Typical usage is when no true concurrency can happen, or when + * some other embracing locks are already protecting the scene. + * All pointers except clock may be NULL, then no code + * will be generated. + */ +static inline +void __lamport_op(struct lamport_time *clock, + struct lamport_time *lamport_prev, + struct lamport_time *lamport_advance, + struct lamport_time *real_now, + struct lamport_time *lamport_now) +{ + /* Remember the old clock value. */ + if (lamport_prev) + *lamport_prev = *clock; + + /* Advance the clock when necessary. */ + if (lamport_advance) + if (lamport_time_compare(lamport_advance, clock) > 0) + *clock = *lamport_advance; + else + *clock = lamport_time_add(*clock, + (struct lamport_time){0, 1}); + else + *clock = lamport_time_add(*clock, + (struct lamport_time){0, 1}); + + /* Get and handle realtime */ + if (real_now) { + *real_now = get_real_lamport(); + if (lamport_time_compare(real_now, clock) > 0) + *clock = *real_now; + } + + /* Finally, retrieve new lamport stamp */ + if (lamport_now) + *lamport_now = *clock; +} #endif