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.
*/
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);

View File

@ -53,6 +53,19 @@
# define get_real_lamport() CURRENT_TIME
#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,
* 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,
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,
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 int max_lamport_future;
extern bool _protect_lamport_time(struct lamport_clock *clock,
struct lamport_time *check);
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