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 | } |
---|