Home | History | Annotate | Download | only in fs
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2010 Intel Corporation; author: H. Peter Anvin
      4  *
      5  *   Permission is hereby granted, free of charge, to any person
      6  *   obtaining a copy of this software and associated documentation
      7  *   files (the "Software"), to deal in the Software without
      8  *   restriction, including without limitation the rights to use,
      9  *   copy, modify, merge, publish, distribute, sublicense, and/or
     10  *   sell copies of the Software, and to permit persons to whom
     11  *   the Software is furnished to do so, subject to the following
     12  *   conditions:
     13  *
     14  *   The above copyright notice and this permission notice shall
     15  *   be included in all copies or substantial portions of the Software.
     16  *
     17  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     19  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     21  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     22  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     23  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     24  *   OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  * ----------------------------------------------------------------------- */
     27 
     28 /*
     29  * getfssec.c
     30  *
     31  * Generic getfssec implementation for disk-based filesystems, which
     32  * support the next_extent() method.
     33  *
     34  * The expected semantics of next_extent are as follows:
     35  *
     36  * The second argument will contain the initial sector number to be
     37  * mapped.  The routine is expected to populate
     38  * inode->next_extent.pstart and inode->next_extent.len (the caller
     39  * will store the initial sector number into inode->next_extent.lstart
     40  * on return.)
     41  *
     42  * If inode->next_extent.len != 0 on entry then the routine is allowed
     43  * to assume inode->next_extent contains valid data from the previous
     44  * usage, which can be used for optimization purposes.
     45  *
     46  * If the filesystem can map the entire file as a single extent
     47  * (e.g. iso9660), then the filesystem can simply insert the extent
     48  * information into inode->next_extent at searchdir/iget time, and leave
     49  * next_extent() as NULL.
     50  *
     51  * Note: the filesystem driver is not required to do extent coalescing,
     52  * if that is difficult to do; this routine will perform extent lookahead
     53  * and coalescing.
     54  */
     55 
     56 #include <dprintf.h>
     57 #include <minmax.h>
     58 #include "fs.h"
     59 
     60 static inline sector_t next_psector(sector_t psector, uint32_t skip)
     61 {
     62     if (EXTENT_SPECIAL(psector))
     63 	return psector;
     64     else
     65 	return psector + skip;
     66 }
     67 
     68 static inline sector_t next_pstart(const struct extent *e)
     69 {
     70     return next_psector(e->pstart, e->len);
     71 }
     72 
     73 
     74 static void get_next_extent(struct inode *inode)
     75 {
     76     /* The logical start address that we care about... */
     77     uint32_t lstart = inode->this_extent.lstart + inode->this_extent.len;
     78 
     79     if (inode->fs->fs_ops->next_extent(inode, lstart))
     80 	inode->next_extent.len = 0; /* ERROR */
     81     inode->next_extent.lstart = lstart;
     82 
     83     dprintf("Extent: inode %p @ %u start %llu len %u\n",
     84 	    inode, inode->next_extent.lstart,
     85 	    inode->next_extent.pstart, inode->next_extent.len);
     86 }
     87 
     88 uint32_t generic_getfssec(struct file *file, char *buf,
     89 			  int sectors, bool *have_more)
     90 {
     91     struct inode *inode = file->inode;
     92     struct fs_info *fs = file->fs;
     93     struct disk *disk = fs->fs_dev->disk;
     94     uint32_t bytes_read = 0;
     95     uint32_t bytes_left = inode->size - file->offset;
     96     uint32_t sectors_left =
     97 	(bytes_left + SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs);
     98     uint32_t lsector;
     99 
    100     if (sectors > sectors_left)
    101 	sectors = sectors_left;
    102 
    103     if (!sectors)
    104 	return 0;
    105 
    106     lsector = file->offset >> SECTOR_SHIFT(fs);
    107     dprintf("Offset: %u  lsector: %u\n", file->offset, lsector);
    108 
    109     if (lsector < inode->this_extent.lstart ||
    110 	lsector >= inode->this_extent.lstart + inode->this_extent.len) {
    111 	/* inode->this_extent unusable, maybe next_extent is... */
    112 	inode->this_extent = inode->next_extent;
    113     }
    114 
    115     if (lsector < inode->this_extent.lstart ||
    116 	lsector >= inode->this_extent.lstart + inode->this_extent.len) {
    117 	/* Still nothing useful... */
    118 	inode->this_extent.lstart = lsector;
    119 	inode->this_extent.len = 0;
    120     } else {
    121 	/* We have some usable information */
    122 	uint32_t delta = lsector - inode->this_extent.lstart;
    123 	inode->this_extent.lstart = lsector;
    124 	inode->this_extent.len -= delta;
    125 	inode->this_extent.pstart
    126 	    = next_psector(inode->this_extent.pstart, delta);
    127     }
    128 
    129     dprintf("this_extent: lstart %u pstart %llu len %u\n",
    130 	    inode->this_extent.lstart,
    131 	    inode->this_extent.pstart,
    132 	    inode->this_extent.len);
    133 
    134     while (sectors) {
    135 	uint32_t chunk;
    136 	size_t len;
    137 
    138 	while (sectors > inode->this_extent.len) {
    139 	    if (!inode->next_extent.len ||
    140 		inode->next_extent.lstart !=
    141 		inode->this_extent.lstart + inode->this_extent.len)
    142 		get_next_extent(inode);
    143 
    144 	    if (!inode->this_extent.len) {
    145 		/* Doesn't matter if it's contiguous... */
    146 		inode->this_extent = inode->next_extent;
    147 		if (!inode->next_extent.len) {
    148 		    sectors = 0; /* Failed to get anything... we're dead */
    149 		    break;
    150 		}
    151 	    } else if (inode->next_extent.len &&
    152 		inode->next_extent.pstart == next_pstart(&inode->this_extent)) {
    153 		/* Coalesce extents and loop */
    154 		inode->this_extent.len += inode->next_extent.len;
    155 	    } else {
    156 		/* Discontiguous extents */
    157 		break;
    158 	    }
    159 	}
    160 
    161 	dprintf("this_extent: lstart %u pstart %llu len %u\n",
    162 		inode->this_extent.lstart,
    163 		inode->this_extent.pstart,
    164 		inode->this_extent.len);
    165 
    166 	chunk = min(sectors, inode->this_extent.len);
    167 	len = chunk << SECTOR_SHIFT(fs);
    168 
    169 	dprintf("   I/O: inode %p @ %u start %llu len %u\n",
    170 		inode, inode->this_extent.lstart,
    171 		inode->this_extent.pstart, chunk);
    172 
    173 	if (inode->this_extent.pstart == EXTENT_ZERO) {
    174 	    memset(buf, 0, len);
    175 	} else {
    176 	    disk->rdwr_sectors(disk, buf, inode->this_extent.pstart, chunk, 0);
    177 	    inode->this_extent.pstart += chunk;
    178 	}
    179 
    180 	buf += len;
    181 	sectors -= chunk;
    182 	bytes_read += len;
    183 	inode->this_extent.lstart += chunk;
    184 	inode->this_extent.len -= chunk;
    185     }
    186 
    187     bytes_read = min(bytes_read, bytes_left);
    188     file->offset += bytes_read;
    189 
    190     if (have_more)
    191 	*have_more = bytes_read < bytes_left;
    192 
    193     return bytes_read;
    194 }
    195