Commit 838035d2 authored by Alessandro Rubini's avatar Alessandro Rubini Committed by Adam Wujek

userspace/libwr: shmem init-time lock

Needed to ensure that writer is available before reader.
Note that data might not be available, when lock is released.
Read data with wrs_shm_seqbegin and wrs_shm_seqend to ensure proper data!
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 10531b6f
......@@ -1681,9 +1681,16 @@ for details about how they are used:
@itemx void wrs_shm_put(void *headptr);
Request access to a shared memory area, and stop using it.
Currently only @t{WRS_SHM_WRITE} and @t{WRS_SHM_READ} are defined as
a flag. If non-zero the head is properly initialized.
On error NULL is returned, and errno is set.
Flags are @t{WRS_SHM_WRITE}, @t{WRS_SHM_READ} and @t{WRS_SHM_LOCKED}.
If @t{WRS_SHM_WRITE} is set, head is properly initialized.
if @t{WRS_SHM_LOCKED} is set, a writer will mark the area as locked
before writing its own @t{pid}, and a reader will wait for the @t{pid}
to be valid (i.e. it waits for a writer to be there). A writer using
the ``locked'' flag must release the lock after initialization
by calling @t{wrs_shme_write(WRS_SHM_WRITE_END)}.
On error NULL is returned, and errno is set to @t{EINVAL},
@t{ETIMEDOUT} or to the error returned by underlying system calls
(for example, @t{EPERM} if the file cannot be mapped).
@item void *wrs_shm_alloc(void *headptr, size_t size);
......@@ -1703,13 +1710,15 @@ for details about how they are used:
to a pointer in the reader's address space, or NULL if on error.
Please see @t{tools/wrs_dump_shmem.c} about how this is used.
@item void wrs_shm_write(void *headptr, int begin);
@item void wrs_shm_write(void *headptr, int flags);
@t{flags} is @t{WRS_SHM_WRITE_BEGIN} or @t{WRS_SHM_WRITE_END}.
Whenever internal consistency of data structure is needed, the
writer should call this function before modifying shared structures,
with @t{begin} set to 1. It should also call the function again
when all modifications are done and data is internally consistent,
this time with @t{begin} set to zero.
and also
when all modifications are done and data is internally consistent.
A call with @t{WRS_SHM_WRITE_END} is mandatory for writers that
used @t{WRS_SHM_LOCKED} at @t{wrs_shm_get} time.
@item unsigned wrs_shm_seqbegin(void *headptr);
@itemx int wrs_shm_seqretry(void *headptr, unsigned start);
......
......@@ -36,8 +36,9 @@ struct wrs_shm_head {
};
/* flags */
#define WRS_SHM_READ 0x0000
#define WRS_SHM_WRITE 0x0001
#define WRS_SHM_READ 0x0000
#define WRS_SHM_WRITE 0x0001
#define WRS_SHM_LOCKED 0x0002 /* at init time: writers locks, readers wait */
/* get vs. put, like in the kernel. Errors are in errno (see source) */
void *wrs_shm_get(enum wrs_shm_name name_id, char *name, unsigned long flags);
......@@ -50,7 +51,9 @@ void *wrs_shm_alloc(void *headptr, size_t size);
void *wrs_shm_follow(void *headptr, void *ptr);
/* Before and after writing a chunk of data, act on sequence and stamp */
extern void wrs_shm_write(void *headptr, int begin);
#define WRS_SHM_WRITE_BEGIN 1
#define WRS_SHM_WRITE_END 0
extern void wrs_shm_write(void *headptr, int flags);
/* A reader can rely on the sequence number (in the <linux/seqlock.h> way) */
extern unsigned wrs_shm_seqbegin(void *headptr);
......
......@@ -11,13 +11,14 @@
#include <sys/mman.h>
#include <libwr/shmem.h>
#define SHM_LOCK_TIMEOUT 2
/* Get wrs shared memory */
/* return NULL and set errno on error */
void *wrs_shm_get(enum wrs_shm_name name_id, char *name, unsigned long flags)
{
struct wrs_shm_head *head;
struct stat stbuf;
struct timespec tv1, tv2;
void *map;
char fname[64];
int write_access = flags & WRS_SHM_WRITE;
......@@ -46,11 +47,32 @@ void *wrs_shm_get(enum wrs_shm_name name_id, char *name, unsigned long flags)
MAP_SHARED, fd, 0);
if (map == MAP_FAILED)
return NULL; /* keep errno */
if (!write_access)
return map;
/* Init the fields */
head = map;
if (!write_access) {
/* This is a reader: if locked, wait for a writer */
if (!(flags & WRS_SHM_LOCKED))
return map;
clock_gettime(CLOCK_MONOTONIC, &tv1);
while (1) {
/* Releasing does not mean initial data is in place! */
/* Read data with wrs_shm_seqbegin and
wrs_shm_seqend! */
if (head->pid && kill(head->pid, 0) == 0)
return map;
usleep(10 * 1000);
clock_gettime(CLOCK_MONOTONIC, &tv2);
if (tv2.tv_sec - tv1.tv_sec < SHM_LOCK_TIMEOUT)
continue;
errno = ETIMEDOUT;
return NULL;
}
}
/* Writer: init the fields */
if (head->pid && kill(head->pid, 0) == 0) {
munmap(map, WRS_SHM_MAX_SIZE);
errno = EBUSY;
......@@ -64,10 +86,14 @@ void *wrs_shm_get(enum wrs_shm_name name_id, char *name, unsigned long flags)
head->stamp = 0;
head->data_off = sizeof(*head);
head->data_size = 0;
head->pid = getpid();
if (flags & WRS_SHM_LOCKED)
head->sequence = 1; /* a sort of lock */
else
head->sequence = 0;
head->pid = getpid(); /* getpid() is a memory barrier, too */
head->pidsequence++;
/* version and size are up to the user (or to allocation) */
head->sequence = 0; /* a sort of unlock */
return map;
}
......@@ -119,12 +145,12 @@ void *wrs_shm_follow(void *headptr, void *ptr)
}
/* Before and after writing a chunk of data, act on sequence and stamp */
void wrs_shm_write(void *headptr, int begin)
void wrs_shm_write(void *headptr, int flags)
{
struct wrs_shm_head *head = headptr;
struct timespec tv;
if (!begin) {
if (flags == WRS_SHM_WRITE_END) {
/* At end-of-writing update the timestamp too */
clock_gettime(CLOCK_MONOTONIC, &tv);
head->stamp = tv.tv_sec;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment