linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* SMP read() stopping at memory page boundaries
@ 2007-06-20 14:52 Timo Sirainen
  2007-06-20 15:48 ` Timo Sirainen
  0 siblings, 1 reply; 7+ messages in thread
From: Timo Sirainen @ 2007-06-20 14:52 UTC (permalink / raw)
  To: linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 464 bytes --]

Tested with various 2.6.x i386/x86-64 SMP kernels and CPUs, for example
2.6.21.3/x86-64.

Process 1:

 - lock file
 - write(4096 + 16 bytes)
 - unlock file

Process 2:

 - lock file
 - read(8192 bytes)
 - unlock file

Sometimes read() returns only 4096 bytes. I'm locking the file, so I
don't think this should ever happen, right?

Attached a test program. Takes from a few seconds to half a minute with
my computer to print "page size cut".


[-- Attachment #1.2: concurrency2.c --]
[-- Type: text/x-csrc, Size: 1556 bytes --]

/*
   gcc concurrency2.c -o concurrency -Wall

   start two both a reader and a writer:

   ./concurrency
   ./concurrency 1
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/file.h>

#define MAX_PAGESIZE 8192

int main(int argc, char *argv[])
{
	char buf[MAX_PAGESIZE*2];
	int fd, ret, pagesize;

	memset(buf, 0, sizeof(buf));

	pagesize = getpagesize();
	assert(pagesize <= MAX_PAGESIZE);

	buf[pagesize] = 'h';
	if (argc == 1) {
		printf("writing, page size = %d\n", pagesize);
		for (;;) {
			fd = open("foo", O_RDWR | O_CREAT | O_TRUNC, 0600);
			if (fd == -1) {
				perror("open()");
				return 1;
			}

			if (flock(fd, LOCK_EX) < 0)
				perror("flock()");
			write(fd, buf, pagesize+16);
			if (flock(fd, LOCK_UN) < 0)
				perror("flock()");
			usleep(rand() % 1000);
			close(fd);
		}
	} else {
		printf("reading, page size = %d\n", pagesize);
		fd = open("foo", O_RDWR, 0600);
		if (fd == -1) {
			perror("open()");
			return 1;
		}
		for (;;) {
			usleep(rand() % 1000);
			if (flock(fd, LOCK_SH) < 0)
				perror("flock()");
			lseek(fd, 0, SEEK_SET);
			ret = read(fd, buf, sizeof(buf));
			if (flock(fd, LOCK_UN) < 0)
				perror("flock()");

			if (ret < pagesize) {
				if (ret > 0)
					printf("less than a page: %d\n", ret);
			} else if (ret == pagesize) {
				printf("page size cut\n");
			} else if (buf[pagesize] != 'h') {
				printf("broken data\n");
			}
		}
	}
	return 0;
}

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: SMP read() stopping at memory page boundaries
  2007-06-20 14:52 SMP read() stopping at memory page boundaries Timo Sirainen
@ 2007-06-20 15:48 ` Timo Sirainen
  2007-06-20 16:22   ` Ray Lee
  0 siblings, 1 reply; 7+ messages in thread
From: Timo Sirainen @ 2007-06-20 15:48 UTC (permalink / raw)
  To: linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1018 bytes --]

On Wed, 2007-06-20 at 17:52 +0300, Timo Sirainen wrote:
> Sometimes read() returns only 4096 bytes. I'm locking the file, so I
> don't think this should ever happen, right?

Sorry, the problem was with file truncating so there's no bug with
locking. I didn't notice it first because it happened to work with
FreeBSD and Solaris.

Without locking there's a problem that I'd want to avoid, but I guess
it's not necessarily a bug (although it doesn't seem to happen with
FreeBSD or Solaris):

Process 1:

 - create "foo2"
 - rename() to "foo"
 - lock
 - write(4096 + 16 bytes)
 - [fsync() here doesn't change anything]
 - pwrite("1111" to offset=4096-4)
 - unlock

Process 2:

 - open("foo")
 - read(8192 bytes)

read() sometimes returns 4096 bytes but with the "1111" already included
in the data. Is there a way to avoid this without locking the file while
reading? The "1111" tries to act as a kind of a lock.

Again attached a test program, which should really do what I
intended. :)

[-- Attachment #1.2: concurrency.c --]
[-- Type: text/x-csrc, Size: 1718 bytes --]

/*
   gcc concurrency.c -o concurrency -Wall

   start two both a reader and a writer:

   ./concurrency
   ./concurrency 1
*/
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/file.h>

#define MAX_PAGESIZE 8192

int main(int argc, char *argv[])
{
	char buf[MAX_PAGESIZE*2], ones[4] = { 1, 1, 1, 1 };
	int fd, ret, pagesize;

	memset(buf, 0, sizeof(buf));

	pagesize = getpagesize();
	assert(pagesize <= MAX_PAGESIZE);

	buf[pagesize] = 'h';
	if (argc == 1) {
		printf("writing, page size = %d\n", pagesize);
		for (;;) {
			fd = open("foo2", O_RDWR | O_CREAT | O_TRUNC, 0600);
			if (fd == -1) {
				perror("open()");
				return 1;
			}
			if (rename("foo2", "foo") < 0)
				perror("rename()");
			usleep(rand() % 1000);

			if (flock(fd, LOCK_EX) < 0)
				perror("flock()");
			pwrite(fd, buf, pagesize+16, 0);
			//usleep(rand() % 1000);
			//fdatasync(fd);
			pwrite(fd, ones, 4, pagesize-4);
			if (flock(fd, LOCK_UN) < 0)
				perror("flock()");
			usleep(rand() % 1000);
			close(fd);
		}
	} else {
		printf("reading, page size = %d\n", pagesize);
		for (;; close(fd)) {
			fd = open("foo", O_RDWR, 0600);
			if (fd == -1) {
				perror("open()");
				return 1;
			}

			ret = pread(fd, buf, sizeof(buf), 0);
			if (ret < pagesize) {
				if (ret > 0)
					printf("less than a page: %d\n", ret);
				continue;
			}

			if (memcmp(buf + pagesize - 4, ones, 4) == 0) {
				if (ret == pagesize) {
					printf("page size cut\n");
				} else if (buf[pagesize] != 'h') {
					printf("broken data\n");
				}
			}
		}
	}
	return 0;
}

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: SMP read() stopping at memory page boundaries
  2007-06-20 15:48 ` Timo Sirainen
@ 2007-06-20 16:22   ` Ray Lee
  2007-06-20 16:57     ` Timo Sirainen
  0 siblings, 1 reply; 7+ messages in thread
From: Ray Lee @ 2007-06-20 16:22 UTC (permalink / raw)
  To: Timo Sirainen; +Cc: linux-kernel

On 6/20/07, Timo Sirainen <tss@iki.fi> wrote:
> On Wed, 2007-06-20 at 17:52 +0300, Timo Sirainen wrote:
> > Sometimes read() returns only 4096 bytes. I'm locking the file, so I
> > don't think this should ever happen, right?

man 2 read

read() is always allowed to return less than you asked for. You need
to go back and ask for the rest. That's why people wrap read() and
write() in loops, and use those wrapped versions instead.

> read() sometimes returns 4096 bytes but with the "1111" already included
> in the data.

Uhm, do you mean 'without the 1111'? 4096 with the 1111 sounds
perfectly fine. (Though 4096 without the 1111 is also perfectly fine.)

> Is there a way to avoid this without locking the file while
> reading? The "1111" tries to act as a kind of a lock.

I think you've misunderstood how read and write work. pread and pwrite
are no different in this respect -- they just allow an atomic
seek+read/write.

Ray

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: SMP read() stopping at memory page boundaries
  2007-06-20 16:22   ` Ray Lee
@ 2007-06-20 16:57     ` Timo Sirainen
  2007-06-20 17:05       ` Alan Cox
  0 siblings, 1 reply; 7+ messages in thread
From: Timo Sirainen @ 2007-06-20 16:57 UTC (permalink / raw)
  To: Ray Lee; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 781 bytes --]

On Wed, 2007-06-20 at 09:22 -0700, Ray Lee wrote:
> On 6/20/07, Timo Sirainen <tss@iki.fi> wrote:
> > On Wed, 2007-06-20 at 17:52 +0300, Timo Sirainen wrote:
> > > Sometimes read() returns only 4096 bytes. I'm locking the file, so I
> > > don't think this should ever happen, right?
> 
> man 2 read
> 
> read() is always allowed to return less than you asked for. You need
> to go back and ask for the rest. That's why people wrap read() and
> write() in loops, and use those wrapped versions instead.

I thought this wouldn't happen when reading from local filesystems.
Anyway, my real program was doing that, and it was still seeing partial
data. But looks like I can't reproduce it in my test program with two
pread()s, so I'll have to do some more debugging.


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: SMP read() stopping at memory page boundaries
  2007-06-20 16:57     ` Timo Sirainen
@ 2007-06-20 17:05       ` Alan Cox
  2007-06-20 17:48         ` Timo Sirainen
  0 siblings, 1 reply; 7+ messages in thread
From: Alan Cox @ 2007-06-20 17:05 UTC (permalink / raw)
  To: Timo Sirainen; +Cc: Ray Lee, linux-kernel

> I thought this wouldn't happen when reading from local filesystems.
> Anyway, my real program was doing that, and it was still seeing partial
> data. But looks like I can't reproduce it in my test program with two
> pread()s, so I'll have to do some more debugging.

It will occur if you are reading as someone else changes the file size.
Use file locking, it exists for a reason ;)

Alan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: SMP read() stopping at memory page boundaries
  2007-06-20 17:05       ` Alan Cox
@ 2007-06-20 17:48         ` Timo Sirainen
  2007-06-20 23:22           ` Jiri Kosina
  0 siblings, 1 reply; 7+ messages in thread
From: Timo Sirainen @ 2007-06-20 17:48 UTC (permalink / raw)
  To: Alan Cox; +Cc: Ray Lee, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 719 bytes --]

On Wed, 2007-06-20 at 18:05 +0100, Alan Cox wrote:
> > I thought this wouldn't happen when reading from local filesystems.
> > Anyway, my real program was doing that, and it was still seeing partial
> > data. But looks like I can't reproduce it in my test program with two
> > pread()s, so I'll have to do some more debugging.

Found the problem in my code. A buggy program, buggy test program and a
buggy brain while debugging doesn't seem to give the best results. :)

> It will occur if you are reading as someone else changes the file size.
> Use file locking, it exists for a reason ;)

Annoying extra overhead. Especially with NFS, when nowadays you can't
even use flock() to create local locks..


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: SMP read() stopping at memory page boundaries
  2007-06-20 17:48         ` Timo Sirainen
@ 2007-06-20 23:22           ` Jiri Kosina
  0 siblings, 0 replies; 7+ messages in thread
From: Jiri Kosina @ 2007-06-20 23:22 UTC (permalink / raw)
  To: Timo Sirainen; +Cc: Alan Cox, Ray Lee, linux-kernel

On Wed, 20 Jun 2007, Timo Sirainen wrote:

> > It will occur if you are reading as someone else changes the file size.
> > Use file locking, it exists for a reason ;)
> Annoying extra overhead. Especially with NFS, when nowadays you can't
> even use flock() to create local locks..

It's not only "nowadays", it has been like that for quite a some time.

On the other hand you can use fcntl(F_SETLK) though, which works even 
through NFS (v3).

-- 
Jiri Kosina

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2007-06-20 23:22 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-20 14:52 SMP read() stopping at memory page boundaries Timo Sirainen
2007-06-20 15:48 ` Timo Sirainen
2007-06-20 16:22   ` Ray Lee
2007-06-20 16:57     ` Timo Sirainen
2007-06-20 17:05       ` Alan Cox
2007-06-20 17:48         ` Timo Sirainen
2007-06-20 23:22           ` Jiri Kosina

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).