infra: allow multiple instances of lamport clock

This commit is contained in:
Thomas Schoebel-Theuer 2019-03-15 13:53:10 +01:00 committed by Thomas Schoebel-Theuer
parent fddc6e491b
commit 763f17a7d8
2 changed files with 136 additions and 49 deletions

View File

@ -92,18 +92,21 @@
* *
* Please improve this code, but please use the right optimisation goal. * 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_clock *clock,
struct lamport_time *real_now,
void get_lamport(struct lamport_time *real_now, struct lamport_time *lamport_now) struct lamport_time *lamport_now)
{ {
struct lamport_time _real_now; struct lamport_time _real_now;
struct lamport_time _lamport_now; struct lamport_time _lamport_now;
/* Get a consistent copy of _both_ clocks */ /* Get a consistent copy of _both_ clocks */
down_read(&lamport_sem); down_read(&clock->lamport_sem);
_lamport_now = lamport_stamp; _lamport_now = clock->lamport_stamp;
/* Theoretically, the next statement could be moved behind the unlock. /* Theoretically, the next statement could be moved behind the unlock.
* However, then we will loose strictness of real timestamps, * However, then we will loose strictness of real timestamps,
* or even may produce contradictory orderings between real and * 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(). * calls to get_lamport().
*/ */
_real_now = get_real_lamport(); _real_now = get_real_lamport();
up_read(&lamport_sem);
up_read(&clock->lamport_sem);
if (real_now) if (real_now)
*real_now = _real_now; *real_now = _real_now;
@ -121,58 +125,63 @@ void get_lamport(struct lamport_time *real_now, struct lamport_time *lamport_now
else else
*lamport_now = _lamport_now; *lamport_now = _lamport_now;
} }
EXPORT_SYMBOL_GPL(_get_lamport);
EXPORT_SYMBOL_GPL(get_lamport); void _set_lamport(struct lamport_clock *clock,
struct lamport_time *lamport_advance)
void set_lamport(struct lamport_time *lamport_old)
{ {
protect_lamport_time(lamport_old); protect_lamport_time(lamport_advance);
/* Always advance the internal Lamport timestamp a little bit /* Always advance the internal Lamport timestamp a little bit
* in order to ensure strict monotonicity between set_lamport() calls. * in order to ensure strict monotonicity between set_lamport() calls.
*/ */
down_write(&lamport_sem); down_write(&clock->lamport_sem);
if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0)
lamport_stamp = *lamport_old; clock->lamport_stamp = *lamport_advance;
else else
lamport_time_add_ns(&lamport_stamp, 1); lamport_time_add_ns(&clock->lamport_stamp, 1);
up_write(&lamport_sem); 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 /* Speculate that advaning is not necessary, to avoid the lock
*/ */
if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) { if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0) {
down_write(&lamport_sem); down_write(&clock->lamport_sem);
if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0)
lamport_stamp = *lamport_old; clock->lamport_stamp = *lamport_advance;
up_write(&lamport_sem); 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. /* After advancing the Lamport time, re-get the new values.
* This is almost equivalent to a sequence of set_lamport() ; get_lamport() * This is almost equivalent to a sequence of set_lamport() ; get_lamport()
* but more efficient because the lock is taken only once. * 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; struct lamport_time _real_now;
protect_lamport_time(lamport_old); protect_lamport_time(lamport_advance);
down_write(&lamport_sem); down_write(&clock->lamport_sem);
if (lamport_time_compare(lamport_old, &lamport_stamp) > 0) if (lamport_time_compare(lamport_advance, &clock->lamport_stamp) > 0)
*lamport_now = *lamport_old; *lamport_now = *lamport_advance;
else else
*lamport_now = lamport_time_add(lamport_stamp, (struct lamport_time){0, 1}); *lamport_now = lamport_time_add(clock->lamport_stamp,
lamport_stamp = *lamport_now; (struct lamport_time){0, 1});
clock->lamport_stamp = *lamport_now;
_real_now = get_real_lamport(); _real_now = get_real_lamport();
up_write(&lamport_sem); up_write(&clock->lamport_sem);
if (real_now) if (real_now)
*real_now = _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) if (lamport_time_compare(&_real_now, lamport_now) > 0)
*lamport_now = _real_now; *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. /* Protect against illegal values, e.g. from currupt filesystems etc.
*/ */
int max_lamport_future = 30 * 24 * 3600; 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(); struct lamport_time limit = get_real_lamport();
bool res = false; bool res = false;
limit.tv_sec += max_lamport_future; limit.tv_sec += max_lamport_future;
if (unlikely(check->tv_sec >= limit.tv_sec)) { if (unlikely(check->tv_sec >= limit.tv_sec)) {
down_write(&lamport_sem); down_write(&clock->lamport_sem);
lamport_time_add_ns(&lamport_stamp, 1); lamport_time_add_ns(&clock->lamport_stamp, 1);
memcpy(check, &lamport_stamp, sizeof(*check)); lamport_time_add_ns(&clock->lamport_stamp, 1);
memcpy(check, &clock->lamport_stamp, sizeof(*check));
if (unlikely(check->tv_sec > limit.tv_sec)) if (unlikely(check->tv_sec > limit.tv_sec))
max_lamport_future += 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; res = true;
} }
return res; return res;
} }
EXPORT_SYMBOL_GPL(_protect_lamport_time);

View File

@ -53,6 +53,19 @@
# define get_real_lamport() CURRENT_TIME # define get_real_lamport() CURRENT_TIME
#endif #endif
#include <linux/rwsem.h>
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, * We always get both the local real time and the Lamport time in parallel,
* consistently. * consistently.
@ -62,30 +75,92 @@
* *
* When not interested in real time, you can simply leave real_now at NULL. * When not interested in real time, you can simply leave real_now at NULL.
*/ */
extern void get_lamport(struct lamport_time *real_now, extern void _get_lamport(struct lamport_clock *clock,
struct lamport_time *real_now,
struct lamport_time *lamport_now); struct lamport_time *lamport_now);
/* This ensures _strict_ monotonicity of the Lamport clock */ /* 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. /* Non-strict version.
* Use this for better performance when strictness is not needed. * 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. /* After strictly advancing the Lamport time, re-get the new values.
* This is almost equivalent to a sequence of set_lamport() ; get_lamport() * This is almost equivalent to a sequence of set_lamport() ; get_lamport()
* but (1) atomic and (2) more efficient * but (1) atomic and (2) more efficient
* because the internal lock is taken only once. * because the internal lock is taken only once.
*/ */
extern void set_get_lamport(struct lamport_time *lamport_old, extern void _set_get_lamport(struct lamport_clock *clock,
struct lamport_time *lamport_advance,
struct lamport_time *real_now, struct lamport_time *real_now,
struct lamport_time *lamport_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);
extern int max_lamport_future;
extern bool protect_lamport_time(struct lamport_time *check);
/* Usually the following versions are the preferred ones for working
* on the single global lamport clock instance.
*/
#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 #endif