Fix race when checking i_size on direct i/o read

Following on from the "Re: [PATCH v3] vfs: fix a bug when we do some dio
reads with append dio writes" thread on linux-fsdevel, this patch is my
current version of the fix proposed as option (b) in that thread.

Removing the i_size test from the direct i/o read path at vfs level
means that filesystems now have to deal with requests which are beyond
i_size themselves. These I've divided into three sets:

 a) Those with "no op" ->direct_IO (9p, cifs, ceph)
These are obviously not going to be an issue

 b) Those with "home brew" ->direct_IO (nfs, fuse)
I've been told that NFS should not have any problem with the larger
i_size, however I've added an extra test to FUSE to duplicate the
original behaviour just to be on the safe side.

 c) Those using __blockdev_direct_IO()
These call through to ->get_block() which should deal with the EOF
condition correctly. I've verified that with GFS2 and I believe that
Zheng has verified it for ext4. I've also run the test on XFS and it
passes both before and after this change.

The part of the patch in filemap.c looks a lot larger than it really is
- there are only two lines of real change. The rest is just indentation
of the contained code.

There remains a test of i_size though, which was added for btrfs. It
doesn't cause the other filesystems a problem as the test is performed
after ->direct_IO has been called. It is possible that there is a race
that does matter to btrfs, however this patch doesn't change that, so
its still an overall improvement.

[NOTE from Zheng Liu]
I adjust the part of code in fuse_direct_IO because there are some
differences between 2.6.32-358 and upstream kernel (3.13).  I believe
it is ok for us.

Reference: 9fe55eea7e4b444bafc42fa0000cc2d1d2847275
Patch-mainline: v3.13-40-g9fe55eea7e4b
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Reported-by: Zheng Liu <gnehzuil.liu@gmail.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Dave Chinner <david@fromorbit.com>
Acked-by: Miklos Szeredi <miklos@szeredi.hu>
Cc: Chris Mason <clm@fb.com>
Cc: Josef Bacik <jbacik@fb.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
Merge pull request #48 from gnehzuil/dio-read-zero

Fix race when checking i_size on direct i/o read
vfs: fix O_DIRECT read past end of block device

The direct-IO write path already had the i_size checks in mm/filemap.c,
but it turns out the read path did not, and removing the block size
checks in fs/block_dev.c (commit bbec0270bdd8: "blkdev_max_block: make
private to fs/buffer.c") removed the magic "shrink IO to past the end of
the device" code there.

Fix it by truncating the IO to the size of the block device, like the
write path already does.

NOTE! I suspect the write path would be *much* better off doing it this
way in fs/block_dev.c, rather than hidden deep in mm/filemap.c.  The
mm/filemap.c code is extremely hard to follow, and has various
conditionals on the target being a block device (ie the flag passed in
to 'generic_write_checks()', along with a conditional update of the
inode timestamp etc).

It is also quite possible that we should treat this whole block device
size as a "s_maxbytes" issue, and try to make the logic even more
generic.  However, in the meantime this is the fairly minimal targeted
fix.

Noted by Milan Broz thanks to a regression test for the cryptsetup
reencrypt tool.

[Note from Zheng Liu]
We backport this patch because after applied this commit (f7b8a8d527:
Fix race when checking i_size on direct i/o read) the kernel will be
panic when the system boots with md device.

Reported-and-tested-by: Milan Broz <mbroz@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
Merge pull request #49 from gnehzuil/fix-dio-read-past-eof

vfs: fix O_DIRECT read past end of block device
io_latency: add io_latency.h 

include/linux/io_latency.h contains iolatency related structure/function
definitions.

Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
io_latency: add iolatency stats in block/scsi layer

1. Extend struct request and request_queue to contain iolatency related field
2. do iolatency stats in get_request_wait()/blk_finish_request()/scsi_dispatch_cmd()

Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
io_latency: export proc interface

Add proc interface for iolatency under /proc/io-latency/sdX. There're 3
entries controlling the behavior of iolatency:
1. enable_iolatency: disable/enable the RT stats for LLD/device
2. enable_soft_iolatency: disable/enable the RT stats for software
layer(mainly cost in block and scsi layer)
3. enable_use_us: diable/enable millisecond-level stats. Notice that it
can only be modified when enable_iolatency=0 and enable_soft_iolatency=0

Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
Merge pull request #50 from shazj99/iolatency

Add Iolatency

Thanks, Applied.
Merge pull request #47 from Rover-Yu/dev

two networking subsystem pull requests on dev branch
net: do some cleanups and backport tcp friends v4 patch

Now tcp friends patch has been applied into ali kernel.  But the version
of this patch is v3.  Later the patch has been updated and a newer patch
set has been sent out.  So this patch backport v4 patch.

Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
net: fix a panic in tcp_close()

For tcp friends data skb, it has no tcp header,
and its transport_header is NULL, so it will panic if we deference tcp_hdr(skb)
in tcp_close().

So I add a check before we use tcp_hdr().

Signed-off-by: Weiping Pan <wpan@redhat.com>
Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
net: delete request_sock->sk_friends

The sock pointed by request_sock->friend may be freed since it does not have a
lock to protect it.
I just delete request_sock->friend since I think it is useless.

For sk_buff->friend, it has the same problem, and we use
"atomic_add(skb->truesize, &sk->sk_wmem_alloc)" to guarantee that the sock can
not be freed before the skb is freed.

Then for 3-way handshake with tcp friends enabled,
SYN->friend is NULL, SYN/ACK->friend is set in tcp_make_synack(),
and ACK->friend is set in tcp_send_ack().

Signed-off-by: Weiping Pan <wpan@redhat.com>
Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
scsi: export request_queue retries via sysfs

The request's retries of scsi disk is default SD_MAX_RETRIES(5),
which is timeing consuming under some applications. Export it
to sysfs, which can be modified via:
	/sys/block/sdx/device/retries OR
	/sys/class/scsi_device/x:0:0:0/device/retries

Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
scsi: set request's retries to be the value of request_queue

This patch assigns request_queue's rq_retries to request's retries,
which will be further assigned to scsi command later. In scsi EH handler,
it will affect the total completion time.

Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
scsi: don't send timeout request to EH anymore

For timeout request, complete it directly without sending it to
LLD driver for processing. We increment the ref_count of request
which will prevent the request from releasing. Though this will
lead to memory leak to some extent, but can avoid system panic if
the LLD driver begin to handle the request again. Meanwhile, we
also don't free scsi_cmnd.

Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
scsi: add timeout_shortcut to enable/disable the hacky request timeout path

Default we enable the hacky request timeout path, but we can choose to
disable it in runtime. It can be modified via:
        /sys/block/sdx/device/timeout_shortcut OR
        /sys/class/scsi_device/x:0:0:0/device/timeout_shortcut

Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
Merge pull request #51 from shazj99/scsi_timeout

Thanks. Applied.
vfs: fix a bug when we do some buffered reads with append dio writes

Currently we check i_size in buffered read path after we know the page
is update.  But it could return some zero-filled pages to the userspace
when we do some append dio writes.  We could use the following code
snippet to reproduce this problem in a ext2/3/4 filesystem.

code snippet:
  #define _GNU_SOURCE

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <memory.h>

  #include <unistd.h>
  #include <fcntl.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <errno.h>

  #include <pthread.h>

  #define BUF_ALIGN	1024

  struct writer_data {
  	int fd;
  	size_t blksize;
  	char *buf;
  };

  static void *writer(void *arg)
  {
  	struct writer_data *data = (struct writer_data *)arg;
  	int ret;

  	ret = write(data->fd, data->buf, data->blksize);
  	if (ret < 0)
  		fprintf(stderr, "write file failed: %s\n", strerror(errno));

  	return NULL;
  }

  int main(int argc, char *argv[])
  {
  	pthread_t tid;
  	struct writer_data wdata;
  	size_t max_blocks = 10 * 1024;
  	size_t blksize = 1 * 1024 * 1024;
  	char *rbuf, *wbuf;
  	int readfd, writefd;
  	int i, j;

  	if (argc < 2) {
  		fprintf(stderr, "usage: %s [filename]\n", argv[0]);
  		exit(1);
  	}

  	writefd = open(argv[1], O_CREAT|O_DIRECT|O_WRONLY|O_APPEND|O_TRUNC, S_IRWXU);
  	if (writefd < 0) {
  		fprintf(stderr, "failed to open wfile: %s\n", strerror(errno));
  		exit(1);
  	}
  	readfd = open(argv[1], O_RDONLY, S_IRWXU);
  	if (readfd < 0) {
  		fprintf(stderr, "failed to open rfile: %s\n", strerror(errno));
  		exit(1);
  	}

  	if (posix_memalign((void **)&wbuf, BUF_ALIGN, blksize)) {
  		fprintf(stderr, "failed to alloc memory: %s\n", strerror(errno));
  		exit(1);
  	}

  	if (posix_memalign((void **)&rbuf, 4096, blksize)) {
  		fprintf(stderr, "failed to alloc memory: %s\n", strerror(errno));
  		exit(1);
  	}

  	memset(wbuf, 'a', blksize);

  	wdata.fd = writefd;
  	wdata.blksize = blksize;
  	wdata.buf = wbuf;

  	for (i = 0; i < max_blocks; i++) {
  		void *retval;
  		int ret;

  		ret = pthread_create(&tid, NULL, writer, &wdata);
  		if (ret) {
  			fprintf(stderr, "create thread failed: %s\n", strerror(errno));
  			exit(1);
  		}

  		memset(rbuf, 'b', blksize);
  		do {
  			ret = pread(readfd, rbuf, blksize, i * blksize);
  		} while (ret <= 0);

  		if (ret < 0) {
  			fprintf(stderr, "read file failed: %s\n", strerror(errno));
  			exit(1);
  		}

  		if (pthread_join(tid, &retval)) {
  			fprintf(stderr, "pthread join failed: %s\n", strerror(errno));
  			exit(1);
  		}

  		if (ret >= 0) {
  			for (j = 0; j < ret; j++) {
  				if (rbuf[j] != 'a') {
  					fprintf(stderr, "encounter an error: offset %ld\n",
  						i);
  					goto err;
  				}
  			}
  		}
  	}

  err:
  	free(wbuf);
  	free(rbuf);

  	return 0;
  }

build & run:
  $ gcc code.c -o code -lpthread # build
  $ ./code ${filename} # run

As we expected, we should read nothing or data with 'a'.  But now we
read data with '0'.  I take a closer look at the code and it seems that
there is a bug in vfs.  Let me describe my found here.

  reader					writer
                                                generic_file_aio_write()
                                                ->__generic_file_aio_write()
                                                  ->generic_file_direct_write()
  generic_file_aio_read()
  ->do_generic_file_read()
    [fallback to buffered read]

    ->find_get_page()
    ->page_cache_sync_readahead()
    ->find_get_page()
    [in find_page label, we couldn't find a
     page before and after calling
     page_cache_sync_readahead().  So go to
     no_cached_page label]

    ->page_cache_alloc_cold()
    ->add_to_page_cache_lru()
    [in no_cached_page label, we alloc a page
     and goto readpage label.]

    ->aops->readpage()
    [in readpage label, readpage() callback
     is called and mpage_readpage() return a
     zero-filled page (e.g. ext3/4), and go
     to page_ok label]

                                                  ->a_ops->direct_IO()
                                                  ->i_size_write()
                                                  [we enlarge the i_size]

    Here we check i_size
    [in page_ok label, we check i_size but
     it has been enlarged.  Thus, we pass
     the check and return a zero-filled page]

This commit first trims desc->count to avoid to read too much data if
(*ppos + desc->count) >= i_size.  Then we will return directly if
desc->count == 0.  After doing that, we could fix the above problem.
Meanwhile this also can fix the problem that the reader does some
buffered reads with append dio write.

[Note from Zheng Liu]
This patch doesn't be applied into upstream kernel because the folks
think that no one really wants to use buffered IO and direct IO at the
same time.  But at Taobao we do have an application that does it.  So
that is the reason why this patch is applied into ali-kernel.

Cc: Christoph Hellwig <hch@infradead.org>
Cc: Dmitry Monakhov <dmonakhov@openvz.org>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
Merge pull request #52 from gnehzuil/dio-bug-fix

vfs: fix a bug when we do some buffered reads with append dio writes
Merge pull request #53 from gnehzuil/tcp-friends-bug-fix

Tcp friends bug fix