Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753446Ab1F0Sc1 (ORCPT ); Mon, 27 Jun 2011 14:32:27 -0400 Received: from idcmail-mo2no.shaw.ca ([64.59.134.9]:48538 "EHLO idcmail-mo2no.shaw.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753391Ab1F0ScV convert rfc822-to-8bit (ORCPT ); Mon, 27 Jun 2011 14:32:21 -0400 X-Cloudmark-SP-Filtered: true X-Cloudmark-SP-Result: v=1.1 cv=cUTwMia+6fMZF7bjUHU44PO5dfBASsAn9IQPzcMrg/g= c=1 sm=1 a=gC2PzEiSFgkA:10 a=BLceEmwcHowA:10 a=kj9zAlcOel0A:10 a=xqWC_Br6kY4A:10 a=c23vf5CSMVc0QQz9B4a6RA==:17 a=VwQbUJbxAAAA:8 a=aYvgaRvspPkMuA0tQpAA:9 a=ykXWStw2yB7pj69o0nAA:7 a=CjuIK1q_8ugA:10 a=x8gzFH9gYPwA:10 a=dCYqAk9ynNG6KrJT:21 a=tqnDQY3oleBIEB_O:21 a=HpAAvcLHHh0Zw7uRqdWCyQ==:117 Subject: Re: [PATCH] xfstests 255: add a seek_data/seek_hole tester Mime-Version: 1.0 (Apple Message framework v1082) Content-Type: text/plain; charset=us-ascii From: Andreas Dilger In-Reply-To: <1309197745-14107-5-git-send-email-josef@redhat.com> Date: Mon, 27 Jun 2011 12:32:17 -0600 Cc: linux-fsdevel@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, viro@ZenIV.linux.org.uk Content-Transfer-Encoding: 8BIT Message-Id: References: <1309197745-14107-1-git-send-email-josef@redhat.com> <1309197745-14107-5-git-send-email-josef@redhat.com> To: Josef Bacik X-Mailer: Apple Mail (2.1082) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15202 Lines: 524 On 2011-06-27, at 12:02 PM, Josef Bacik wrote: > This is a test to make sure seek_data/seek_hole is acting like it does on > Solaris. It will check to see if the fs supports finding a hole or not and will > adjust as necessary. > > diff --git a/src/seek-tester.c b/src/seek-tester.c > new file mode 100644 > index 0000000..2b8c957 > --- /dev/null > +++ b/src/seek-tester.c > @@ -0,0 +1,473 @@ > +/* > + * Copyright (C) 2011 Oracle. All rights reserved. > + * Copyright (C) 2011 Red Hat. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public > + * License v2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public > + * License along with this program; if not, write to the > + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > + * Boston, MA 021110-1307, USA. > + */ > + > +#define _XOPEN_SOURCE 500 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define SEEK_DATA 3 > +#define SEEK_HOLE 4 These should probably be "#ifndef SEEK_DATA" so that gcc doesn't complain in the future when these are added to a standard header. > +#define FS_NO_HOLES (1 << 0) > +#define QUIET (1 << 1) > + > +static blksize_t alloc_size; > +static unsigned flags = 0; > + > +static int get_io_sizes(int fd) > +{ > + struct stat buf; > + int ret; > + > + ret = fstat(fd, &buf); > + if (ret) > + fprintf(stderr, " ERROR %d: Failed to find io blocksize\n", > + errno); > + > + /* st_blksize is typically also the allocation size */ > + alloc_size = buf.st_blksize; > + > + if (!(flags & QUIET)) > + printf("Allocation size: %ld\n", alloc_size); > + > + return ret; > +} > + > +#define do_free(x) do { if(x) free(x); } while(0); > + > +static void *do_malloc(size_t size) > +{ > + void *buf; > + > + buf = malloc(size); > + if (!buf) > + fprintf(stderr, " ERROR: Unable to allocate %ld bytes\n", > + (long)size); > + > + return buf; > +} > + > +static int do_truncate(int fd, off_t length) > +{ > + int ret; > + > + ret = ftruncate(fd, length); > + if (ret) > + fprintf(stderr, " ERROR %d: Failed to extend file " > + "to %ld bytes\n", errno, (long)length); > + return ret; > +} > + > +static ssize_t do_pwrite(int fd, const void *buf, size_t count, off_t offset) > +{ > + ssize_t ret, written = 0; > + > + while (count > written) { > + ret = pwrite(fd, buf + written, count - written, offset + written); > + if (ret < 0) { > + fprintf(stderr, " ERROR %d: Failed to write %ld " > + "bytes\n", errno, (long)count); > + return ret; > + } > + written += ret; > + } > + > + return 0; > +} > + > +static int do_lseek(int testnum, int subtest, int fd, int origin, off_t set, > + off_t exp) > +{ > + off_t pos; > + int ret = -1; > + > + pos = lseek(fd, set, origin); > + > + if (pos != exp) { > + fprintf(stderr, " ERROR in Test %d.%d: POS expected %ld, " > + "got %ld\n", testnum, subtest, (long)exp, (long)pos); > + goto out; > + } > + > + if (pos == -1 && errno != ENXIO) { > + fprintf(stderr, " ERROR in Test %d.%d: ERRNO expected %d, " > + "got %d\n", testnum, subtest, ENXIO, errno); > + goto out; > + } > + > + ret = 0; > + > +out: > + return ret; > +} > + > +static int get_flags(int fd) > +{ > + const char *buf = "ABCDEF"; > + ssize_t written; > + off_t pos; > + int ret; > + > + ret = do_truncate(fd, alloc_size * 2); > + if (ret) > + return ret; > + > + written = do_pwrite(fd, buf, strlen(buf), 0); > + if (written) > + return -1; > + > + pos = lseek(fd, 0, SEEK_HOLE); > + if (pos == alloc_size * 2) { > + if (!(flags & QUIET)) > + printf("File system does not recognize holes, the only " > + "hole found will be at the end.\n"); > + flags |= FS_NO_HOLES; This is a question that I've also had about compatibility with older (well, every) Linux kernel that does not support SEEK_{HOLE,DATA} today. My reading of the existing generic_file_llseek() and default_llseek() code, along with most filesystem-specific llseek() implementations is that they will happily ignore the @whence parameter if it is not known, and pretend like it is 0 (SEEK_SET), so they will just set the position to the @offset parameter and return this value. In that case, the above "SEEK_HOLE" test would incorrectly fail on every Linux kernel in existence today because the returned pos == 0. Should applications call both SEEK_HOLE and SEEK_DATA with @offset=0, and if they return the same values (which is normally impossible, decide that the kernel does not support this SEEK_* functionality? > + } else if (pos == (off_t)-1) { > + fprintf(stderr, "SEEK_HOLE is not supported\n"); > + return -1; > + } > + > + return 0; > +} > + > +/* test hole data hole data */ > +static int test06(int fd, int testnum) > +{ > + int ret = 0; > + char *buf = NULL; > + int bufsz = alloc_size; > + int filsz = bufsz * 4; > + int off; > + > + if (flags & FS_NO_HOLES) > + return 1; > + > + /* HOLE - DATA - HOLE - DATA */ > + /* Each unit is bufsz */ > + > + buf = do_malloc(bufsz); > + if (!buf) > + goto out; > + memset(buf, 'a', bufsz); > + > + ret = do_pwrite(fd, buf, bufsz, bufsz); > + if (!ret) > + do_pwrite(fd, buf, bufsz, bufsz * 3); > + if (ret) > + goto out; > + > + /* offset at the beginning */ > + ret += do_lseek(testnum, 1, fd, SEEK_HOLE, 0, 0); > + ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 1, 1); > + ret += do_lseek(testnum, 3, fd, SEEK_DATA, 0, bufsz); > + ret += do_lseek(testnum, 4, fd, SEEK_DATA, 1, bufsz); > + > + /* offset around first hole-data boundary */ > + off = bufsz; > + ret += do_lseek(testnum, 5, fd, SEEK_HOLE, off - 1, off - 1); > + ret += do_lseek(testnum, 6, fd, SEEK_DATA, off - 1, off); > + ret += do_lseek(testnum, 7, fd, SEEK_HOLE, off, bufsz * 2); > + ret += do_lseek(testnum, 8, fd, SEEK_DATA, off, off); > + ret += do_lseek(testnum, 9, fd, SEEK_HOLE, off + 1, bufsz * 2); > + ret += do_lseek(testnum, 10, fd, SEEK_DATA, off + 1, off + 1); > + > + /* offset around data-hole boundary */ > + off = bufsz * 2; > + ret += do_lseek(testnum, 11, fd, SEEK_HOLE, off - 1, off); > + ret += do_lseek(testnum, 12, fd, SEEK_DATA, off - 1, off - 1); > + ret += do_lseek(testnum, 13, fd, SEEK_HOLE, off, off); > + ret += do_lseek(testnum, 14, fd, SEEK_DATA, off, bufsz * 3); > + ret += do_lseek(testnum, 15, fd, SEEK_HOLE, off + 1, off + 1); > + ret += do_lseek(testnum, 16, fd, SEEK_DATA, off + 1, bufsz * 3); > + > + /* offset around second hole-data boundary */ > + off = bufsz * 3; > + ret += do_lseek(testnum, 17, fd, SEEK_HOLE, off - 1, off - 1); > + ret += do_lseek(testnum, 18, fd, SEEK_DATA, off - 1, off); > + ret += do_lseek(testnum, 19, fd, SEEK_HOLE, off, filsz); > + ret += do_lseek(testnum, 20, fd, SEEK_DATA, off, off); > + ret += do_lseek(testnum, 21, fd, SEEK_HOLE, off + 1, filsz); > + ret += do_lseek(testnum, 22, fd, SEEK_DATA, off + 1, off + 1); > + > + /* offset around the end of file */ > + off = filsz; > + ret += do_lseek(testnum, 23, fd, SEEK_HOLE, off - 1, filsz); > + ret += do_lseek(testnum, 24, fd, SEEK_DATA, off - 1, filsz - 1); > + ret += do_lseek(testnum, 25, fd, SEEK_HOLE, off, -1); > + ret += do_lseek(testnum, 26, fd, SEEK_DATA, off, -1); > + ret += do_lseek(testnum, 27, fd, SEEK_HOLE, off + 1, -1); > + ret += do_lseek(testnum, 28, fd, SEEK_DATA, off + 1, -1); > + > +out: > + do_free(buf); > + return ret; > +} > + > +/* test file with data at the beginning and a hole at the end */ > +static int test05(int fd, int testnum) > +{ > + int ret = -1; > + char *buf = NULL; > + int bufsz = alloc_size; > + int filsz = bufsz * 4; > + > + if (flags & FS_NO_HOLES) > + return 1; > + > + /* DATA - HOLE */ > + /* Each unit is bufsz */ > + > + buf = do_malloc(bufsz); > + if (!buf) > + goto out; > + memset(buf, 'a', bufsz); > + > + ret = do_truncate(fd, filsz); > + if (!ret) > + ret = do_pwrite(fd, buf, bufsz, 0); > + if (ret) > + goto out; > + > + /* offset at the beginning */ > + ret += do_lseek(testnum, 1, fd, SEEK_HOLE, 0, bufsz); > + ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 1, bufsz); > + ret += do_lseek(testnum, 3, fd, SEEK_DATA, 0, 0); > + ret += do_lseek(testnum, 4, fd, SEEK_DATA, 1, 1); > + > + /* offset around data-hole boundary */ > + ret += do_lseek(testnum, 5, fd, SEEK_HOLE, bufsz - 1, bufsz); > + ret += do_lseek(testnum, 6, fd, SEEK_DATA, bufsz - 1, bufsz - 1); > + ret += do_lseek(testnum, 7, fd, SEEK_HOLE, bufsz, bufsz); > + ret += do_lseek(testnum, 8, fd, SEEK_DATA, bufsz, -1); > + ret += do_lseek(testnum, 9, fd, SEEK_HOLE, bufsz + 1, bufsz + 1); > + ret += do_lseek(testnum, 10, fd, SEEK_DATA, bufsz + 1, -1); > + > + /* offset around eof */ > + ret += do_lseek(testnum, 11, fd, SEEK_HOLE, filsz - 1, filsz - 1); > + ret += do_lseek(testnum, 12, fd, SEEK_DATA, filsz - 1, -1); > + ret += do_lseek(testnum, 13, fd, SEEK_HOLE, filsz, -1); > + ret += do_lseek(testnum, 14, fd, SEEK_DATA, filsz, -1); > + ret += do_lseek(testnum, 15, fd, SEEK_HOLE, filsz + 1, -1); > + ret += do_lseek(testnum, 16, fd, SEEK_DATA, filsz + 1, -1); > + > +out: > + do_free(buf); > + return ret; > +} > + > +/* test hole begin and data end */ > +static int test04(int fd, int testnum) > +{ > + int ret; > + char *buf = "ABCDEFGH"; > + int bufsz = sizeof(buf); > + int holsz = alloc_size * 2; > + int filsz = holsz + bufsz; > + > + if (flags & FS_NO_HOLES) > + return 1; > + > + /* HOLE - DATA */ > + > + ret = do_pwrite(fd, buf, bufsz, holsz); > + if (ret) > + goto out; > + > + /* offset at the beginning */ > + ret += do_lseek(testnum, 1, fd, SEEK_HOLE, 0, 0); > + ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 1, 1); > + ret += do_lseek(testnum, 3, fd, SEEK_DATA, 0, holsz); > + ret += do_lseek(testnum, 4, fd, SEEK_DATA, 1, holsz); > + > + /* offset around hole-data boundary */ > + ret += do_lseek(testnum, 5, fd, SEEK_HOLE, holsz - 1, holsz - 1); > + ret += do_lseek(testnum, 6, fd, SEEK_DATA, holsz - 1, holsz); > + ret += do_lseek(testnum, 7, fd, SEEK_HOLE, holsz, filsz); > + ret += do_lseek(testnum, 8, fd, SEEK_DATA, holsz, holsz); > + ret += do_lseek(testnum, 9, fd, SEEK_HOLE, holsz + 1, filsz); > + ret += do_lseek(testnum, 10, fd, SEEK_DATA, holsz + 1, holsz + 1); > + > + /* offset around eof */ > + ret += do_lseek(testnum, 11, fd, SEEK_HOLE, filsz - 1, filsz); > + ret += do_lseek(testnum, 12, fd, SEEK_DATA, filsz - 1, filsz - 1); > + ret += do_lseek(testnum, 13, fd, SEEK_HOLE, filsz, -1); > + ret += do_lseek(testnum, 14, fd, SEEK_DATA, filsz, -1); > + ret += do_lseek(testnum, 15, fd, SEEK_HOLE, filsz + 1, -1); > + ret += do_lseek(testnum, 16, fd, SEEK_DATA, filsz + 1, -1); > +out: > + return ret; > +} > + > +/* test full file */ > +static int test03(int fd, int testnum) > +{ > + char *buf = NULL; > + int bufsz = alloc_size + 100; > + int ret = -1; > + > + buf = do_malloc(bufsz); > + if (!buf) > + goto out; > + memset(buf, 'a', bufsz); > + > + ret = do_pwrite(fd, buf, bufsz, 0); > + if (ret) > + goto out; > + > + /* offset at the beginning */ > + ret += do_lseek(testnum, 1, fd, SEEK_HOLE, 0, bufsz); > + ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 1, bufsz); > + ret += do_lseek(testnum, 3, fd, SEEK_DATA, 0, 0); > + ret += do_lseek(testnum, 4, fd, SEEK_DATA, 1, 1); > + > + /* offset around eof */ > + ret += do_lseek(testnum, 5, fd, SEEK_HOLE, bufsz - 1, bufsz); > + ret += do_lseek(testnum, 6, fd, SEEK_DATA, bufsz - 1, bufsz - 1); > + ret += do_lseek(testnum, 7, fd, SEEK_HOLE, bufsz, -1); > + ret += do_lseek(testnum, 8, fd, SEEK_DATA, bufsz, -1); > + ret += do_lseek(testnum, 9, fd, SEEK_HOLE, bufsz + 1, -1); > + ret += do_lseek(testnum, 10, fd, SEEK_DATA, bufsz + 1, -1); > + > +out: > + do_free(buf); > + return ret; > +} > + > +/* test empty file */ > +static int test02(int fd, int testnum) > +{ > + int ret = 0; > + > + ret += do_lseek(testnum, 1, fd, SEEK_DATA, 0, -1); > + ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 0, -1); > + ret += do_lseek(testnum, 3, fd, SEEK_HOLE, 1, -1); > + > + return ret; > +} > + > +/* test feature support */ > +static int test01(int fd, int testnum) > +{ > + int ret; > + char buf[] = "ABCDEFGH"; > + int bufsz = sizeof(buf); > + > + ret = do_pwrite(fd, buf, bufsz, 0); > + if (ret) > + goto out; > + > + ret += do_lseek(testnum, 1, fd, SEEK_DATA, 0, 0); > + ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 0, bufsz); > + > +out: > + return ret; > +} > + > +struct testrec { > + int test_num; > + int (*test_func)(int fd, int testnum); > + char *test_desc; > +}; > + > +struct testrec seek_tests[] = { > + { 1, test01, "Test basic support" }, > + { 2, test02, "Test an empty file" }, > + { 3, test03, "Test a full file" }, > + { 4, test04, "Test file hole at beg, data at end" }, > + { 5, test05, "Test file data at beg, hole at end" }, > + { 6, test06, "Test file hole data hole data" }, > +}; > + > +static int run_test(int fd, struct testrec *tr) > +{ > + int ret; > + > + ret = tr->test_func(fd, tr->test_num); > + if (!(flags & QUIET)) > + printf("%02d. %-50s\t%s\n", tr->test_num, tr->test_desc, > + ret < 0 ? "FAIL" : (ret == 0 ? "SUCC" : "NOT RUN")); > + return ret; > +} > + > +void print_help() > +{ > + printf("seek-test [-h] [-q] filename\n"); > + printf("\t-h - this message\n"); > + printf("\t-q - quiet, no output\n"); > + printf("\tfilename - file to use for the test\n"); > +} > + > +int main(int argc, char **argv) > +{ > + int ret = -1; > + int i, fd = -1; > + int c; > + int numtests = sizeof(seek_tests) / sizeof(struct testrec); > + > + while ((c = getopt(argc, argv, "qh")) != -1) { > + switch (c) { > + case 'q': > + flags |= QUIET; > + break; > + case 'h': > + print_help(); > + exit(0); > + default: > + print_help(); > + exit(1); > + } > + } > + > + if (optind >= argc) { > + print_help(); > + exit(1); > + } > + > + fd = open(argv[optind], O_RDWR|O_CREAT|O_TRUNC, 0644); > + if (fd < 0) { > + fprintf(stderr, "Failed to open testfile: %d\n", errno); > + goto out; > + } > + > + ret = get_io_sizes(fd); > + if (ret) > + goto out; > + > + ret = get_flags(fd); > + if (ret) > + goto out; > + > + for (i = 0; i < numtests; ++i) { > + ret = do_truncate(fd, 0); > + if (ret) > + goto out; > + run_test(fd, &seek_tests[i]); > + } > + > +out: > + if (fd > -1) > + close(fd); > + return ret; > +} > -- > 1.7.5.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html Cheers, Andreas -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/