aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/ps3flash.c
diff options
context:
space:
mode:
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)