From: Eric Sandeen Subject: [PATCH 0/3] fiemap patches (RFC/testing) Date: Wed, 26 Mar 2008 10:37:13 -0500 Message-ID: <47EA6DA9.4010103@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit To: ext4 development Return-path: Received: from mx1.redhat.com ([66.187.233.31]:60619 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751873AbYCZPhT (ORCPT ); Wed, 26 Mar 2008 11:37:19 -0400 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id m2QFbJVA015396 for ; Wed, 26 Mar 2008 11:37:19 -0400 Received: from lacrosse.corp.redhat.com (lacrosse.corp.redhat.com [172.16.52.154]) by int-mx1.corp.redhat.com (8.13.1/8.13.1) with ESMTP id m2QFbIAR008704 for ; Wed, 26 Mar 2008 11:37:18 -0400 Received: from neon.msp.redhat.com (neon.msp.redhat.com [10.15.80.10]) by lacrosse.corp.redhat.com (8.12.11.20060308/8.11.6) with ESMTP id m2QFbDIl013996 for ; Wed, 26 Mar 2008 11:37:13 -0400 Sender: linux-ext4-owner@vger.kernel.org List-ID: Since Akira would like to use the fiemap ioctl for defrag, I thought I should put what I have so far out on the list, at least. This could go in the unstable part of the tree if you like, though I need to do more testing etc before it's really ready to go. Also, below is a quick test application I was using with the ioctl. -Eric #include #include #include #include #include #include #include #include #include #include /*************************************************/ /* All this should come from fiemap.h eventually */ struct fiemap_extent { __u64 fe_offset; /* offset in bytes for the start of the extent */ __u64 fe_length; /* length in bytes for the extent */ __u32 fe_flags; /* returned FIEMAP_EXTENT_* flags for the extent */ __u32 fe_lun; /* logical device number for extent (starting at 0) */ }; struct fiemap { __u64 fm_start; /* logical starting byte offset (in/out) */ __u64 fm_length; /* logical length of map (in/out) */ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ __u32 fm_extent_count; /* number of extents in fm_extents (in/out) */ __u64 fm_end_offset; /* logical offset of end of mapping in last ioctl */ struct fiemap_extent fm_extents[0]; }; #define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ #define FIEMAP_FLAG_HSM_READ 0x00000002 /* get data from HSM before map */ #define FIEMAP_FLAG_NUM_EXTENTS 0x00000004 /* return only number of extents */ #define FIEMAP_FLAG_INCOMPAT 0xff000000 /* error for unknown flags in here */ #define FIEMAP_FLAG_LUN_OFFSET 0x01000000 /* use lun offsets, instead of * logical file offsets */ #define FIEMAP_EXTENT_HOLE 0x00000001 /* has no data or space allocation */ #define FIEMAP_EXTENT_UNWRITTEN 0x00000002 /* space allocated, but no data */ #define FIEMAP_EXTENT_UNMAPPED 0x00000004 /* has data but no space allocation*/ #define FIEMAP_EXTENT_ERROR 0x00000008 /* mapping error, errno in fe_start*/ #define FIEMAP_EXTENT_NO_DIRECT 0x00000010 /* cannot access data directly */ #define FIEMAP_EXTENT_LAST 0x00000020 /* last extent in the file */ #define FIEMAP_EXTENT_DELALLOC 0x00000040 /* has data but not yet written, must have EXTENT_UNKNOWN set */ #define FIEMAP_EXTENT_SECONDARY 0x00000080 /* data (also) in secondary storage, not in primary if EXTENT_UNKNOWN*/ #define FIEMAP_EXTENT_EOF 0x00000100 /* if fm_start+fm_len is beyond EOF*/ #define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */ #define EXT4_IOC_FIEMAP _IOWR('f', 10, struct fiemap) /* get file extent info*/ /* End of what should be coming from fiemap.h */ /**********************************************/ void usage(void) { printf("Usage: fiemap [-vrSCL] [-s start] [-l length] [-c buf count] [-m max] filename\n"); printf(" -v : verbose mode\n"); printf(" -r : raw output: print raw ioctl structure values\n"); printf(" -S : set FIEMAP_FLAG_SYNC to sync before mapping\n"); printf(" -C : set FIEMAP_FLAG_NUM_EXTENTS to only get extent count, not mapping\n"); printf(" -L : set FIEMAP_FLAG_LUN_OFFSET to report extents in lun order\n"); printf(" -s start : start of mapping in bytes (default 0)\n"); printf(" -l length : length of mapping in bytes (default to end of file)\n"); printf(" -c count : count of extents in ioctl input structure (default 32)\n"); printf(" -m max : max nr of ioctls to call before exit (default 512)\n"); exit(EXIT_FAILURE); } #define EXABYTES(x) ((long long)(x) << 60) #define PETABYTES(x) ((long long)(x) << 50) #define TERABYTES(x) ((long long)(x) << 40) #define GIGABYTES(x) ((long long)(x) << 30) #define MEGABYTES(x) ((long long)(x) << 20) #define KILOBYTES(x) ((long long)(x) << 10) long long cvtnum(char *s) { long long i; char *sp; int c; i = strtoll(s, &sp, 0); if (i == 0 && sp == s) return -1LL; if (*sp == '\0') return i; if (sp[1] != '\0') return -1LL; c = tolower(*sp); switch (c) { case 'k': return KILOBYTES(i); case 'm': return MEGABYTES(i); case 'g': return GIGABYTES(i); case 't': return TERABYTES(i); case 'p': return PETABYTES(i); case 'e': return EXABYTES(i); } return -1LL; } void show_extents_table(struct fiemap *fiemap, int blocksize, int start_extent, int *is_last) { unsigned int i; __u64 lstart; lstart = fiemap->fm_start; for (i = 0; i < fiemap->fm_extent_count; i++) { __u64 length = fiemap->fm_extents[i].fe_length; __u64 phys = fiemap->fm_extents[i].fe_offset; int flags = fiemap->fm_extents[i].fe_flags; printf("ext: %3u logical: [%8llu..%8llu] phys: %8llu..%8llu flags: 0x%03X tot: %llu\n", i + start_extent, lstart, lstart + length - 1, (phys / blocksize), (flags & FIEMAP_EXTENT_HOLE) ? 0 : (phys + length - 1) / blocksize, flags, (length / blocksize)); lstart += length; if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) { *is_last = 1; return; /* XXX should we? or should look for exents filled in past last? */ } } } void show_extents_raw(struct fiemap *fiemap, int start_extent, int *is_last) { unsigned int i; for (i = 0; i < fiemap->fm_extent_count; i++) { printf("\tExtent %3u: start: %10lld length: %10lld flags 0x%03X lun %3u\n", i + start_extent, fiemap->fm_extents[i].fe_offset, fiemap->fm_extents[i].fe_length, fiemap->fm_extents[i].fe_flags, fiemap->fm_extents[i].fe_lun); if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) { *is_last = 1; return; /* XXX should we? or should look for exents filled in past last? */ } } } int main(int argc, char**argv) { int blocksize = 0; /* filesystem blocksize */ uint count = 32; /* extent count */ int countonly = 0; /* return only extent count? */ int fd; /* file descriptor */ int last = 0; /* last extent found */ int lunwise = 0; /* return extents lun-wise */ int maxioctls = 512; /* max ioctls to try */ int opt; int rc; int raw = 0; /* raw output format */ int sync = 0; /* sync file before mapping */ int verbose = 0; /* verbose output */ char *fname; /* filename to map */ char *fiebuf; /* fiemap buffer / ioctl argument */ __u64 lstart = 0; /* logical input mapping start */ __u64 llength = ~0ULL;/* logical input mapping length */ uint start_ext = 0; /* starting extent nr. for this batch */ struct fiemap *fiemap; while ((opt = getopt(argc, argv, "s:l:c:m:rSCLv")) != -1) { switch(opt) { /* logical mapping offset */ case 's': lstart = cvtnum(optarg); break; /* logical mapping length */ case 'l': llength = cvtnum(optarg); break; /* count of extent buffers to send */ case 'c': count = atoi(optarg); break; /* max nr. of ioctls to try (safety net) */ case 'm': maxioctls = atoi(optarg); break; /* raw format output */ case 'r': raw++; break; /* sync file before mapping */ case 'S': sync++; break; /* count extents only, no details */ case 'C': countonly++; break; /* return extents in lun order */ case 'L': lunwise++; break; /* be verbose */ case 'v': verbose++; break; default: usage(); } } fname = argv[optind++]; if (!fname) usage(); /* The whole buffer, extent maps and all */ fiebuf = malloc(sizeof (struct fiemap) + (count * sizeof(struct fiemap_extent))); if (!fiebuf) { perror("Could not allocate fiemap buffers"); exit(1); } /* Just the header */ fiemap = (struct fiemap *)fiebuf; fiemap->fm_start = lstart; fiemap->fm_length = llength; fiemap->fm_flags = 0; if (sync) fiemap->fm_flags |= FIEMAP_FLAG_SYNC; if (countonly) fiemap->fm_flags |= FIEMAP_FLAG_NUM_EXTENTS; if (lunwise) fiemap->fm_flags |= FIEMAP_FLAG_LUN_OFFSET; fiemap->fm_extent_count = count; fiemap->fm_end_offset = 0; /* output only */ fd = open(fname, O_RDONLY); if (fd < 0) { perror("Can't open file"); exit(1); } if (ioctl(fd, FIGETBSZ, &blocksize) < 0) { perror("Can't get block size"); close(fd); return; } do { if (verbose) printf("Input: start %llu length %llu flags 0x%X count %u end_offset %lld\n", fiemap->fm_start, fiemap->fm_length, fiemap->fm_flags, fiemap->fm_extent_count, fiemap->fm_end_offset); rc = ioctl(fd, EXT4_IOC_FIEMAP, (unsigned long)fiemap); if (rc < 0) { perror("FIEMAP ioctl failed"); close(fd); exit(1); } if (verbose) printf("Output: start %llu length %llu flags 0x%X count %u end_offset %lld\n", fiemap->fm_start, fiemap->fm_length, fiemap->fm_flags, fiemap->fm_extent_count, fiemap->fm_end_offset); if (raw) show_extents_raw(fiemap, start_ext, &last); else show_extents_table(fiemap, blocksize, start_ext, &last); start_ext += fiemap->fm_extent_count; /* Did we finish up the last of the reqest? */ if (fiemap->fm_length >= llength) break; /* Set up the next call arguments */ fiemap->fm_start += fiemap->fm_length; llength -= fiemap->fm_length; fiemap->fm_length = llength; fiemap->fm_extent_count = count; maxioctls--; } while (!last && maxioctls > 0); close(fd); return 0; }