fcntl spinlock in Linux?
Rusty Russell
rusty at samba.org
Thu Feb 14 02:42:59 MST 2013
Volker Lendecke <Volker.Lendecke at SerNet.DE> writes:
> Also, I am pretty certain that the fact that this happens in
> an otherwise overloaded system adds to that effect, during
> my tests here the systems were otherwise idle.
Oops, I dropped the list from my CC's somehow.
OK, I'll assume that improving this benchmark with 10000 or 20000 mean
we have a real improvement.
I haven't tested, but one thing I noticed looking at the code: we're
often deleted from the block list when we're woken, so we don't need
to re-grab the lock in that case:
diff --git a/fs/locks.c b/fs/locks.c
index a94e331..41de9eb 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -498,6 +498,9 @@ static void __locks_delete_block(struct file_lock *waiter)
*/
void locks_delete_block(struct file_lock *waiter)
{
+ /* We could already be deleted by the time this runs. */
+ if (list_empty(&waiter->fl_block))
+ return;
lock_flocks();
__locks_delete_block(waiter);
unlock_flocks();
Cheers,
Rusty.
// 2>/dev/null; set -e; OUT=/tmp/`basename $0 .c`; if [ ! -f "$OUT" ] || [ "$OUT" -ot "$0" ]; then gcc -Wall -g -o "$OUT".$$ $0 -lrt && mv "$OUT".$$ "$OUT"; fi; exec "$OUT" "$@"
#define HAVE_CLOCK_GETTIME_IN_LIBRT 1
#define HAVE_STRUCT_TIMESPEC 1
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
/* Taken from ccan/time: Licensed under BSD-MIT - see LICENSE file for details */
#if HAVE_STRUCT_TIMESPEC
#include <time.h>
#else
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
#endif
#if !HAVE_CLOCK_GETTIME && !HAVE_CLOCK_GETTIME_IN_LIBRT
#include <sys/time.h>
struct timespec time_now(void)
{
struct timeval now;
struct timespec ret;
gettimeofday(&now, NULL);
ret.tv_sec = now.tv_sec;
ret.tv_nsec = now.tv_usec * 1000;
return ret;
}
#else
#include <time.h>
struct timespec time_now(void)
{
struct timespec ret;
clock_gettime(CLOCK_REALTIME, &ret);
return ret;
}
#endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GETTIME_IN_LIBRT */
static struct timespec time_sub(struct timespec recent,
struct timespec old)
{
struct timespec diff;
diff.tv_sec = recent.tv_sec - old.tv_sec;
if (old.tv_nsec > recent.tv_nsec) {
diff.tv_sec--;
diff.tv_nsec = 1000000000 + recent.tv_nsec - old.tv_nsec;
} else
diff.tv_nsec = recent.tv_nsec - old.tv_nsec;
return diff;
}
/* Taken from TDB: LGPLv3 */
static int fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag)
{
struct flock fl;
fl.l_type = rw;
fl.l_whence = SEEK_SET;
fl.l_start = off;
fl.l_len = len;
fl.l_pid = 0;
if (waitflag)
return fcntl(fd, F_SETLKW, &fl);
else
return fcntl(fd, F_SETLK, &fl);
}
static int fcntl_unlock(int fd, int rw, off_t off, off_t len)
{
struct flock fl;
fl.l_type = F_UNLCK;
fl.l_whence = SEEK_SET;
fl.l_start = off;
fl.l_len = len;
fl.l_pid = 0;
return fcntl(fd, F_SETLKW, &fl);
}
int main(int argc, char *argv[])
{
int num = 5000, i;
const char *lockfile = argv[1];
char *fill;
int lockfd, to_lockers[2], from_lockers[2];
struct timespec start, diff;
if (argc > 2) {
num = atoi(argv[2]);
argv++;
argc--;
}
if (argc != 2)
errx(1, "Usage: lock-stampede <filename> [num]");
pipe(to_lockers);
pipe(from_lockers);
lockfd = open(lockfile, O_RDWR);
if (lockfd < 0)
err(1, "Opening %s", lockfile);
start = time_now();
for (i = 0; i < num; i++) {
char c;
switch (fork()) {
case 0:
close(from_lockers[0]);
close(to_lockers[1]);
/* Parent dies? */
if (read(to_lockers[0], &c, 1) != 1)
exit(0);
if (fcntl_lock(lockfd, F_WRLCK, i, 1, true) != 0)
err(1, "Locking %s", lockfile);
/* Tell parent we got it! */
if (write(from_lockers[1], &c, 1) != 1)
err(1, "Writing to parent");
/* If we exit, we'd release it, so wait. */
read(to_lockers[0], &c, 1);
exit(0);
case -1:
err(1, "fork failed");
}
}
close(to_lockers[0]);
close(from_lockers[1]);
diff = time_sub(time_now(), start);
printf("Children set up in %lu.%09u\n",
(long)diff.tv_sec, (int)diff.tv_nsec);
if (fcntl_lock(lockfd, F_WRLCK, 0, num, true) != 0)
err(1, "Locking %u bytes in %s", num, lockfile);
fill = calloc(num, 1);
/* OK, now wake all the kids: they block on lock. */
i = 0;
do {
int ret = write(to_lockers[1], fill + i, num - i);
if (ret < 0)
err(1, "writing to wake up locker children");
i += ret;
} while (i < num);
sleep(1);
start = time_now();
fcntl_unlock(lockfd, F_WRLCK, 0, num);
for (i = 0; i < num; i++)
if (read(from_lockers[0], fill, 1) != 1)
err(1, "Reading from locker children");
diff = time_sub(time_now(), start);
printf("Children got locks in %lu.%09u\n",
(long)diff.tv_sec, (int)diff.tv_nsec);
/* Children will now exit. */
exit(0);
}
More information about the samba-technical
mailing list