• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
next_psector(sector_t psector,uint32_t skip)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 
next_pstart(const struct extent * e)68 static inline sector_t next_pstart(const struct extent *e)
69 {
70     return next_psector(e->pstart, e->len);
71 }
72 
73 
get_next_extent(struct inode * inode)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 
generic_getfssec(struct file * file,char * buf,int sectors,bool * have_more)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