aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/ps3flash.c
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2009-06-10 00:39:07 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2009-06-15 02:47:27 -0400
commit6bd57f2e5db408e0dfdb3bf052d58c4e7b18ed3c (patch)
tree768510bab8b596ff499f796ea8c08077074eb5d6 /drivers/char/ps3flash.c
parenta4e623fbc9b201930abcf78df6db5e49aa8e00cb (diff)
ps3flash: Cache the last accessed FLASH chunk
Add support for caching, to reduce FLASH wear when writing using small blocksizes. As we also don't care anymore about heads and tails in case of partial writes, this greatly simplifies the code for handling writes. Note: We don't bother caching reads smaller than the FLASH chunk size (256 KiB). Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers/char/ps3flash.c')
-rw-r--r--drivers/char/ps3flash.c209
1 files changed, 104 insertions, 105 deletions
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index f7f21f47ea02..d884d31491d2 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -33,13 +33,16 @@
33 33
34struct ps3flash_private { 34struct ps3flash_private {
35 struct mutex mutex; /* Bounce buffer mutex */ 35 struct mutex mutex; /* Bounce buffer mutex */
36 u64 chunk_sectors;
37 int tag; /* Start sector of buffer, -1 if invalid */
38 bool dirty;
36}; 39};
37 40
38static struct ps3_storage_device *ps3flash_dev; 41static struct ps3_storage_device *ps3flash_dev;
39 42
40static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev, 43static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,
41 u64 lpar, u64 start_sector, 44 u64 lpar, u64 start_sector, u64 sectors,
42 u64 sectors, int write) 45 int write)
43{ 46{
44 u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors, 47 u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors,
45 write); 48 write);
@@ -48,33 +51,55 @@ static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev,
48 __LINE__, write ? "write" : "read", res); 51 __LINE__, write ? "write" : "read", res);
49 return -EIO; 52 return -EIO;
50 } 53 }
51 return sectors; 54 return 0;
52} 55}
53 56
54static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev, 57static int ps3flash_writeback(struct ps3_storage_device *dev)
55 u64 start_sector, u64 sectors,
56 unsigned int sector_offset)
57{ 58{
58 u64 max_sectors, lpar; 59 struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
60 int res;
59 61
60 max_sectors = dev->bounce_size / dev->blk_size; 62 if (!priv->dirty || priv->tag < 0)
61 if (sectors > max_sectors) { 63 return 0;
62 dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %llu\n", 64
63 __func__, __LINE__, max_sectors); 65 res = ps3flash_read_write_sectors(dev, dev->bounce_lpar, priv->tag,
64 sectors = max_sectors; 66 priv->chunk_sectors, 1);
65 } 67 if (res)
68 return res;
66 69
67 lpar = dev->bounce_lpar + sector_offset * dev->blk_size; 70 priv->dirty = false;
68 return ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, 71 return 0;
69 0);
70} 72}
71 73
72static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev, 74static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector,
73 u64 start_sector) 75 u64 sectors)
74{ 76{
75 u64 sectors = dev->bounce_size / dev->blk_size; 77 struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
76 return ps3flash_read_write_sectors(dev, dev->bounce_lpar, start_sector, 78 unsigned int tag, offset;
77 sectors, 1); 79 u64 lpar;
80 int res;
81
82 offset = start_sector % priv->chunk_sectors;
83 tag = start_sector - offset;
84 if (tag == priv->tag)
85 return 0;
86
87 res = ps3flash_writeback(dev);
88 if (res)
89 return res;
90
91 priv->tag = -1;
92
93 lpar = dev->bounce_lpar + offset * dev->blk_size;
94 res = ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, 0);
95 if (res)
96 return res;
97
98 /* We don't bother caching reads smaller than the chunk size */
99 if (sectors == priv->chunk_sectors)
100 priv->tag = tag;
101
102 return 0;
78} 103}
79 104
80static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) 105static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
@@ -109,8 +134,8 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
109{ 134{
110 struct ps3_storage_device *dev = ps3flash_dev; 135 struct ps3_storage_device *dev = ps3flash_dev;
111 struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); 136 struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
112 u64 size, start_sector, end_sector, offset; 137 u64 size, start_sector, end_sector, offset, sectors;
113 ssize_t sectors_read; 138 int res;
114 size_t remaining, n; 139 size_t remaining, n;
115 const void *src; 140 const void *src;
116 141
@@ -130,30 +155,29 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
130 } 155 }
131 156
132 start_sector = *pos / dev->blk_size; 157 start_sector = *pos / dev->blk_size;
133 offset = *pos % dev->blk_size; 158 offset = *pos % dev->bounce_size;
134 end_sector = DIV_ROUND_UP(*pos + count, dev->blk_size); 159 end_sector = DIV_ROUND_UP(*pos + count, dev->blk_size);
135 160
136 remaining = count; 161 remaining = count;
137 do { 162 do {
163 sectors = min(end_sector - start_sector,
164 priv->chunk_sectors -
165 start_sector % priv->chunk_sectors);
166
138 mutex_lock(&priv->mutex); 167 mutex_lock(&priv->mutex);
139 168
140 sectors_read = ps3flash_read_sectors(dev, start_sector, 169 res = ps3flash_fetch(dev, start_sector, sectors);
141 end_sector-start_sector, 170 if (res)
142 0);
143 if (sectors_read < 0) {
144 mutex_unlock(&priv->mutex);
145 goto fail; 171 goto fail;
146 }
147 172
148 n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); 173 n = min_t(u64, remaining, dev->bounce_size - offset);
149 src = dev->bounce_buf+offset; 174 src = dev->bounce_buf + offset;
150 dev_dbg(&dev->sbd.core, 175 dev_dbg(&dev->sbd.core,
151 "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n", 176 "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n",
152 __func__, __LINE__, n, src, userbuf, kernelbuf); 177 __func__, __LINE__, n, src, userbuf, kernelbuf);
153 if (userbuf) { 178 if (userbuf) {
154 if (copy_to_user(userbuf, src, n)) { 179 if (copy_to_user(userbuf, src, n)) {
155 mutex_unlock(&priv->mutex); 180 res = -EFAULT;
156 sectors_read = -EFAULT;
157 goto fail; 181 goto fail;
158 } 182 }
159 userbuf += n; 183 userbuf += n;
@@ -167,14 +191,15 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
167 191
168 *pos += n; 192 *pos += n;
169 remaining -= n; 193 remaining -= n;
170 start_sector += sectors_read; 194 start_sector += sectors;
171 offset = 0; 195 offset = 0;
172 } while (remaining > 0); 196 } while (remaining > 0);
173 197
174 return count; 198 return count;
175 199
176fail: 200fail:
177 return sectors_read; 201 mutex_unlock(&priv->mutex);
202 return res;
178} 203}
179 204
180static ssize_t ps3flash_write(const char __user *userbuf, 205static ssize_t ps3flash_write(const char __user *userbuf,
@@ -182,11 +207,9 @@ static ssize_t ps3flash_write(const char __user *userbuf,
182{ 207{
183 struct ps3_storage_device *dev = ps3flash_dev; 208 struct ps3_storage_device *dev = ps3flash_dev;
184 struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); 209 struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
185 u64 size, chunk_sectors, start_write_sector, end_write_sector, 210 u64 size, sector, offset;
186 end_read_sector, start_read_sector, head, tail, offset; 211 int res = 0;
187 ssize_t res;
188 size_t remaining, n; 212 size_t remaining, n;
189 unsigned int sec_off;
190 void *dst; 213 void *dst;
191 214
192 dev_dbg(&dev->sbd.core, 215 dev_dbg(&dev->sbd.core,
@@ -204,71 +227,23 @@ static ssize_t ps3flash_write(const char __user *userbuf,
204 count = size - *pos; 227 count = size - *pos;
205 } 228 }
206 229
207 chunk_sectors = dev->bounce_size / dev->blk_size; 230 sector = *pos / dev->bounce_size * priv->chunk_sectors;
208
209 start_write_sector = *pos / dev->bounce_size * chunk_sectors;
210 offset = *pos % dev->bounce_size; 231 offset = *pos % dev->bounce_size;
211 end_write_sector = DIV_ROUND_UP(*pos + count, dev->bounce_size) *
212 chunk_sectors;
213
214 end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size);
215 start_read_sector = (*pos + count) / dev->blk_size;
216
217 /*
218 * As we have to write in 256 KiB chunks, while we can read in blk_size
219 * (usually 512 bytes) chunks, we perform the following steps:
220 * 1. Read from start_write_sector to end_read_sector ("head")
221 * 2. Read from start_read_sector to end_write_sector ("tail")
222 * 3. Copy data to buffer
223 * 4. Write from start_write_sector to end_write_sector
224 * All of this is complicated by using only one 256 KiB bounce buffer.
225 */
226
227 head = end_read_sector - start_write_sector;
228 tail = end_write_sector - start_read_sector;
229 232
230 remaining = count; 233 remaining = count;
231 do { 234 do {
235 n = min_t(u64, remaining, dev->bounce_size - offset);
236
232 mutex_lock(&priv->mutex); 237 mutex_lock(&priv->mutex);
233 238
234 if (end_read_sector >= start_read_sector) { 239 if (n != dev->bounce_size)
235 /* Merge head and tail */ 240 res = ps3flash_fetch(dev, sector, priv->chunk_sectors);
236 dev_dbg(&dev->sbd.core, 241 else if (sector != priv->tag)
237 "Merged head and tail: %llu sectors at %llu\n", 242 res = ps3flash_writeback(dev);
238 chunk_sectors, start_write_sector); 243 if (res)
239 res = ps3flash_read_sectors(dev, start_write_sector, 244 goto fail;
240 chunk_sectors, 0);
241 if (res < 0)
242 goto fail;
243 } else {
244 if (head) {
245 /* Read head */
246 dev_dbg(&dev->sbd.core,
247 "head: %llu sectors at %llu\n", head,
248 start_write_sector);
249 res = ps3flash_read_sectors(dev,
250 start_write_sector,
251 head, 0);
252 if (res < 0)
253 goto fail;
254 }
255 if (start_read_sector <
256 start_write_sector+chunk_sectors) {
257 /* Read tail */
258 dev_dbg(&dev->sbd.core,
259 "tail: %llu sectors at %llu\n", tail,
260 start_read_sector);
261 sec_off = start_read_sector-start_write_sector;
262 res = ps3flash_read_sectors(dev,
263 start_read_sector,
264 tail, sec_off);
265 if (res < 0)
266 goto fail;
267 }
268 }
269 245
270 n = min_t(u64, remaining, dev->bounce_size-offset); 246 dst = dev->bounce_buf + offset;
271 dst = dev->bounce_buf+offset;
272 dev_dbg(&dev->sbd.core, 247 dev_dbg(&dev->sbd.core,
273 "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n", 248 "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n",
274 __func__, __LINE__, n, userbuf, kernelbuf, dst); 249 __func__, __LINE__, n, userbuf, kernelbuf, dst);
@@ -284,16 +259,14 @@ static ssize_t ps3flash_write(const char __user *userbuf,
284 kernelbuf += n; 259 kernelbuf += n;
285 } 260 }
286 261
287 res = ps3flash_write_chunk(dev, start_write_sector); 262 priv->tag = sector;
288 if (res < 0) 263 priv->dirty = true;
289 goto fail;
290 264
291 mutex_unlock(&priv->mutex); 265 mutex_unlock(&priv->mutex);
292 266
293 *pos += n; 267 *pos += n;
294 remaining -= n; 268 remaining -= n;
295 start_write_sector += chunk_sectors; 269 sector += priv->chunk_sectors;
296 head = 0;
297 offset = 0; 270 offset = 0;
298 } while (remaining > 0); 271 } while (remaining > 0);
299 272
@@ -324,9 +297,31 @@ static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos)
324static ssize_t ps3flash_kernel_write(const void *buf, size_t count, 297static ssize_t ps3flash_kernel_write(const void *buf, size_t count,
325 loff_t pos) 298 loff_t pos)
326{ 299{
327 return ps3flash_write(NULL, buf, count, &pos); 300 ssize_t res;
301 int wb;
302
303 res = ps3flash_write(NULL, buf, count, &pos);
304 if (res < 0)
305 return res;
306
307 /* Make kernel writes synchronous */
308 wb = ps3flash_writeback(ps3flash_dev);
309 if (wb)
310 return wb;
311
312 return res;
328} 313}
329 314
315static int ps3flash_flush(struct file *file, fl_owner_t id)
316{
317 return ps3flash_writeback(ps3flash_dev);
318}
319
320static int ps3flash_fsync(struct file *file, struct dentry *dentry,
321 int datasync)
322{
323 return ps3flash_writeback(ps3flash_dev);
324}
330 325
331static irqreturn_t ps3flash_interrupt(int irq, void *data) 326static irqreturn_t ps3flash_interrupt(int irq, void *data)
332{ 327{
@@ -356,6 +351,8 @@ static const struct file_operations ps3flash_fops = {
356 .llseek = ps3flash_llseek, 351 .llseek = ps3flash_llseek,
357 .read = ps3flash_user_read, 352 .read = ps3flash_user_read,
358 .write = ps3flash_user_write, 353 .write = ps3flash_user_write,
354 .flush = ps3flash_flush,
355 .fsync = ps3flash_fsync,
359}; 356};
360 357
361static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = { 358static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {
@@ -411,9 +408,11 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
411 408
412 ps3_system_bus_set_drvdata(&dev->sbd, priv); 409 ps3_system_bus_set_drvdata(&dev->sbd, priv);
413 mutex_init(&priv->mutex); 410 mutex_init(&priv->mutex);
411 priv->tag = -1;
414 412
415 dev->bounce_size = ps3flash_bounce_buffer.size; 413 dev->bounce_size = ps3flash_bounce_buffer.size;
416 dev->bounce_buf = ps3flash_bounce_buffer.address; 414 dev->bounce_buf = ps3flash_bounce_buffer.address;
415 priv->chunk_sectors = dev->bounce_size / dev->blk_size;
417 416
418 error = ps3stor_setup(dev, ps3flash_interrupt); 417 error = ps3stor_setup(dev, ps3flash_interrupt);
419 if (error) 418 if (error)