diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 13:20:31 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 13:20:31 -0500 |
commit | a8e98d6d51a3eb7bb061b1625193a129c8bd094f (patch) | |
tree | 0fa58b6e11e37023b024e55b8f0e7e01438706d4 /drivers/mtd/ubi/upd.c | |
parent | f0f1b3364ae7f48084bdf2837fb979ff59622523 (diff) | |
parent | f9f7dd222364a6428d2ad99a515935dd1dd89d18 (diff) |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (120 commits)
[MTD] Fix mtdoops.c compilation
[MTD] [NOR] fix startup lock when using multiple nor flash chips
[MTD] [DOC200x] eccbuf is statically defined and always evaluate to true
[MTD] Fix maps/physmap.c compilation with CONFIG_PM
[MTD] onenand: Add panic_write function to the onenand driver
[MTD] mtdoops: Use the panic_write function when present
[MTD] Add mtd panic_write function pointer
[MTD] [NAND] Freescale enhanced Local Bus Controller FCM NAND support.
[MTD] physmap.c: Add support for multiple resources
[MTD] [NAND] Fix misparenthesization introduced by commit 78b65179...
[MTD] [NAND] Fix Blackfin NFC ECC calculating bug with page size 512 bytes
[MTD] [NAND] Remove wrong operation in PM function of the BF54x NFC driver
[MTD] [NAND] Remove unused variable in plat_nand_remove
[MTD] Unlocking all Intel flash that is locked on power up.
[MTD] [NAND] at91_nand: Make mtdparts option can override board info
[MTD] mtdoops: Various minor cleanups
[MTD] mtdoops: Ensure sequential write to the buffer
[MTD] mtdoops: Perform write operations in a workqueue
[MTD] mtdoops: Add further error return code checking
[MTD] [NOR] Test devtype, not definition in flash_probe(), drivers/mtd/devices/lart.c
...
Diffstat (limited to 'drivers/mtd/ubi/upd.c')
-rw-r--r-- | drivers/mtd/ubi/upd.c | 185 |
1 files changed, 137 insertions, 48 deletions
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 0efc586a8328..ddaa1a56cc69 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c | |||
@@ -22,7 +22,8 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | /* | 24 | /* |
25 | * This file contains implementation of the volume update functionality. | 25 | * This file contains implementation of the volume update and atomic LEB change |
26 | * functionality. | ||
26 | * | 27 | * |
27 | * The update operation is based on the per-volume update marker which is | 28 | * The update operation is based on the per-volume update marker which is |
28 | * stored in the volume table. The update marker is set before the update | 29 | * stored in the volume table. The update marker is set before the update |
@@ -45,29 +46,31 @@ | |||
45 | /** | 46 | /** |
46 | * set_update_marker - set update marker. | 47 | * set_update_marker - set update marker. |
47 | * @ubi: UBI device description object | 48 | * @ubi: UBI device description object |
48 | * @vol_id: volume ID | 49 | * @vol: volume description object |
49 | * | 50 | * |
50 | * This function sets the update marker flag for volume @vol_id. Returns zero | 51 | * This function sets the update marker flag for volume @vol. Returns zero |
51 | * in case of success and a negative error code in case of failure. | 52 | * in case of success and a negative error code in case of failure. |
52 | */ | 53 | */ |
53 | static int set_update_marker(struct ubi_device *ubi, int vol_id) | 54 | static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) |
54 | { | 55 | { |
55 | int err; | 56 | int err; |
56 | struct ubi_vtbl_record vtbl_rec; | 57 | struct ubi_vtbl_record vtbl_rec; |
57 | struct ubi_volume *vol = ubi->volumes[vol_id]; | ||
58 | 58 | ||
59 | dbg_msg("set update marker for volume %d", vol_id); | 59 | dbg_msg("set update marker for volume %d", vol->vol_id); |
60 | 60 | ||
61 | if (vol->upd_marker) { | 61 | if (vol->upd_marker) { |
62 | ubi_assert(ubi->vtbl[vol_id].upd_marker); | 62 | ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); |
63 | dbg_msg("already set"); | 63 | dbg_msg("already set"); |
64 | return 0; | 64 | return 0; |
65 | } | 65 | } |
66 | 66 | ||
67 | memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); | 67 | memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], |
68 | sizeof(struct ubi_vtbl_record)); | ||
68 | vtbl_rec.upd_marker = 1; | 69 | vtbl_rec.upd_marker = 1; |
69 | 70 | ||
70 | err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); | 71 | mutex_lock(&ubi->volumes_mutex); |
72 | err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); | ||
73 | mutex_unlock(&ubi->volumes_mutex); | ||
71 | vol->upd_marker = 1; | 74 | vol->upd_marker = 1; |
72 | return err; | 75 | return err; |
73 | } | 76 | } |
@@ -75,23 +78,24 @@ static int set_update_marker(struct ubi_device *ubi, int vol_id) | |||
75 | /** | 78 | /** |
76 | * clear_update_marker - clear update marker. | 79 | * clear_update_marker - clear update marker. |
77 | * @ubi: UBI device description object | 80 | * @ubi: UBI device description object |
78 | * @vol_id: volume ID | 81 | * @vol: volume description object |
79 | * @bytes: new data size in bytes | 82 | * @bytes: new data size in bytes |
80 | * | 83 | * |
81 | * This function clears the update marker for volume @vol_id, sets new volume | 84 | * This function clears the update marker for volume @vol, sets new volume |
82 | * data size and clears the "corrupted" flag (static volumes only). Returns | 85 | * data size and clears the "corrupted" flag (static volumes only). Returns |
83 | * zero in case of success and a negative error code in case of failure. | 86 | * zero in case of success and a negative error code in case of failure. |
84 | */ | 87 | */ |
85 | static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes) | 88 | static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, |
89 | long long bytes) | ||
86 | { | 90 | { |
87 | int err; | 91 | int err; |
88 | uint64_t tmp; | 92 | uint64_t tmp; |
89 | struct ubi_vtbl_record vtbl_rec; | 93 | struct ubi_vtbl_record vtbl_rec; |
90 | struct ubi_volume *vol = ubi->volumes[vol_id]; | ||
91 | 94 | ||
92 | dbg_msg("clear update marker for volume %d", vol_id); | 95 | dbg_msg("clear update marker for volume %d", vol->vol_id); |
93 | 96 | ||
94 | memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); | 97 | memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], |
98 | sizeof(struct ubi_vtbl_record)); | ||
95 | ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); | 99 | ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); |
96 | vtbl_rec.upd_marker = 0; | 100 | vtbl_rec.upd_marker = 0; |
97 | 101 | ||
@@ -106,7 +110,9 @@ static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long byt | |||
106 | vol->last_eb_bytes = vol->usable_leb_size; | 110 | vol->last_eb_bytes = vol->usable_leb_size; |
107 | } | 111 | } |
108 | 112 | ||
109 | err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); | 113 | mutex_lock(&ubi->volumes_mutex); |
114 | err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); | ||
115 | mutex_unlock(&ubi->volumes_mutex); | ||
110 | vol->upd_marker = 0; | 116 | vol->upd_marker = 0; |
111 | return err; | 117 | return err; |
112 | } | 118 | } |
@@ -114,35 +120,36 @@ static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long byt | |||
114 | /** | 120 | /** |
115 | * ubi_start_update - start volume update. | 121 | * ubi_start_update - start volume update. |
116 | * @ubi: UBI device description object | 122 | * @ubi: UBI device description object |
117 | * @vol_id: volume ID | 123 | * @vol: volume description object |
118 | * @bytes: update bytes | 124 | * @bytes: update bytes |
119 | * | 125 | * |
120 | * This function starts volume update operation. If @bytes is zero, the volume | 126 | * This function starts volume update operation. If @bytes is zero, the volume |
121 | * is just wiped out. Returns zero in case of success and a negative error code | 127 | * is just wiped out. Returns zero in case of success and a negative error code |
122 | * in case of failure. | 128 | * in case of failure. |
123 | */ | 129 | */ |
124 | int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) | 130 | int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, |
131 | long long bytes) | ||
125 | { | 132 | { |
126 | int i, err; | 133 | int i, err; |
127 | uint64_t tmp; | 134 | uint64_t tmp; |
128 | struct ubi_volume *vol = ubi->volumes[vol_id]; | ||
129 | 135 | ||
130 | dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes); | 136 | dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); |
137 | ubi_assert(!vol->updating && !vol->changing_leb); | ||
131 | vol->updating = 1; | 138 | vol->updating = 1; |
132 | 139 | ||
133 | err = set_update_marker(ubi, vol_id); | 140 | err = set_update_marker(ubi, vol); |
134 | if (err) | 141 | if (err) |
135 | return err; | 142 | return err; |
136 | 143 | ||
137 | /* Before updating - wipe out the volume */ | 144 | /* Before updating - wipe out the volume */ |
138 | for (i = 0; i < vol->reserved_pebs; i++) { | 145 | for (i = 0; i < vol->reserved_pebs; i++) { |
139 | err = ubi_eba_unmap_leb(ubi, vol_id, i); | 146 | err = ubi_eba_unmap_leb(ubi, vol, i); |
140 | if (err) | 147 | if (err) |
141 | return err; | 148 | return err; |
142 | } | 149 | } |
143 | 150 | ||
144 | if (bytes == 0) { | 151 | if (bytes == 0) { |
145 | err = clear_update_marker(ubi, vol_id, 0); | 152 | err = clear_update_marker(ubi, vol, 0); |
146 | if (err) | 153 | if (err) |
147 | return err; | 154 | return err; |
148 | err = ubi_wl_flush(ubi); | 155 | err = ubi_wl_flush(ubi); |
@@ -163,9 +170,42 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) | |||
163 | } | 170 | } |
164 | 171 | ||
165 | /** | 172 | /** |
173 | * ubi_start_leb_change - start atomic LEB change. | ||
174 | * @ubi: UBI device description object | ||
175 | * @vol: volume description object | ||
176 | * @req: operation request | ||
177 | * | ||
178 | * This function starts atomic LEB change operation. Returns zero in case of | ||
179 | * success and a negative error code in case of failure. | ||
180 | */ | ||
181 | int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, | ||
182 | const struct ubi_leb_change_req *req) | ||
183 | { | ||
184 | ubi_assert(!vol->updating && !vol->changing_leb); | ||
185 | |||
186 | dbg_msg("start changing LEB %d:%d, %u bytes", | ||
187 | vol->vol_id, req->lnum, req->bytes); | ||
188 | if (req->bytes == 0) | ||
189 | return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, | ||
190 | req->dtype); | ||
191 | |||
192 | vol->upd_bytes = req->bytes; | ||
193 | vol->upd_received = 0; | ||
194 | vol->changing_leb = 1; | ||
195 | vol->ch_lnum = req->lnum; | ||
196 | vol->ch_dtype = req->dtype; | ||
197 | |||
198 | vol->upd_buf = vmalloc(req->bytes); | ||
199 | if (!vol->upd_buf) | ||
200 | return -ENOMEM; | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | /** | ||
166 | * write_leb - write update data. | 206 | * write_leb - write update data. |
167 | * @ubi: UBI device description object | 207 | * @ubi: UBI device description object |
168 | * @vol_id: volume ID | 208 | * @vol: volume description object |
169 | * @lnum: logical eraseblock number | 209 | * @lnum: logical eraseblock number |
170 | * @buf: data to write | 210 | * @buf: data to write |
171 | * @len: data size | 211 | * @len: data size |
@@ -191,26 +231,22 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) | |||
191 | * This function returns zero in case of success and a negative error code in | 231 | * This function returns zero in case of success and a negative error code in |
192 | * case of failure. | 232 | * case of failure. |
193 | */ | 233 | */ |
194 | static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, | 234 | static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, |
195 | int len, int used_ebs) | 235 | void *buf, int len, int used_ebs) |
196 | { | 236 | { |
197 | int err, l; | 237 | int err; |
198 | struct ubi_volume *vol = ubi->volumes[vol_id]; | ||
199 | 238 | ||
200 | if (vol->vol_type == UBI_DYNAMIC_VOLUME) { | 239 | if (vol->vol_type == UBI_DYNAMIC_VOLUME) { |
201 | l = ALIGN(len, ubi->min_io_size); | 240 | len = ALIGN(len, ubi->min_io_size); |
202 | memset(buf + len, 0xFF, l - len); | 241 | memset(buf + len, 0xFF, len - len); |
203 | 242 | ||
204 | l = ubi_calc_data_len(ubi, buf, l); | 243 | len = ubi_calc_data_len(ubi, buf, len); |
205 | if (l == 0) { | 244 | if (len == 0) { |
206 | dbg_msg("all %d bytes contain 0xFF - skip", len); | 245 | dbg_msg("all %d bytes contain 0xFF - skip", len); |
207 | return 0; | 246 | return 0; |
208 | } | 247 | } |
209 | if (len != l) | ||
210 | dbg_msg("skip last %d bytes (0xFF)", len - l); | ||
211 | 248 | ||
212 | err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l, | 249 | err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); |
213 | UBI_UNKNOWN); | ||
214 | } else { | 250 | } else { |
215 | /* | 251 | /* |
216 | * When writing static volume, and this is the last logical | 252 | * When writing static volume, and this is the last logical |
@@ -222,7 +258,7 @@ static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, | |||
222 | * contain zeros, not random trash. | 258 | * contain zeros, not random trash. |
223 | */ | 259 | */ |
224 | memset(buf + len, 0, vol->usable_leb_size - len); | 260 | memset(buf + len, 0, vol->usable_leb_size - len); |
225 | err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len, | 261 | err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, |
226 | UBI_UNKNOWN, used_ebs); | 262 | UBI_UNKNOWN, used_ebs); |
227 | } | 263 | } |
228 | 264 | ||
@@ -236,16 +272,15 @@ static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, | |||
236 | * @count: how much bytes to write | 272 | * @count: how much bytes to write |
237 | * | 273 | * |
238 | * This function writes more data to the volume which is being updated. It may | 274 | * This function writes more data to the volume which is being updated. It may |
239 | * be called arbitrary number of times until all of the update data arrive. | 275 | * be called arbitrary number of times until all the update data arriveis. This |
240 | * This function returns %0 in case of success, number of bytes written during | 276 | * function returns %0 in case of success, number of bytes written during the |
241 | * the last call if the whole volume update was successfully finished, and a | 277 | * last call if the whole volume update has been successfully finished, and a |
242 | * negative error code in case of failure. | 278 | * negative error code in case of failure. |
243 | */ | 279 | */ |
244 | int ubi_more_update_data(struct ubi_device *ubi, int vol_id, | 280 | int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, |
245 | const void __user *buf, int count) | 281 | const void __user *buf, int count) |
246 | { | 282 | { |
247 | uint64_t tmp; | 283 | uint64_t tmp; |
248 | struct ubi_volume *vol = ubi->volumes[vol_id]; | ||
249 | int lnum, offs, err = 0, len, to_write = count; | 284 | int lnum, offs, err = 0, len, to_write = count; |
250 | 285 | ||
251 | dbg_msg("write %d of %lld bytes, %lld already passed", | 286 | dbg_msg("write %d of %lld bytes, %lld already passed", |
@@ -290,8 +325,8 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, | |||
290 | * is the last chunk, it's time to flush the buffer. | 325 | * is the last chunk, it's time to flush the buffer. |
291 | */ | 326 | */ |
292 | ubi_assert(flush_len <= vol->usable_leb_size); | 327 | ubi_assert(flush_len <= vol->usable_leb_size); |
293 | err = write_leb(ubi, vol_id, lnum, vol->upd_buf, | 328 | err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len, |
294 | flush_len, vol->upd_ebs); | 329 | vol->upd_ebs); |
295 | if (err) | 330 | if (err) |
296 | return err; | 331 | return err; |
297 | } | 332 | } |
@@ -318,8 +353,8 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, | |||
318 | 353 | ||
319 | if (len == vol->usable_leb_size || | 354 | if (len == vol->usable_leb_size || |
320 | vol->upd_received + len == vol->upd_bytes) { | 355 | vol->upd_received + len == vol->upd_bytes) { |
321 | err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len, | 356 | err = write_leb(ubi, vol, lnum, vol->upd_buf, |
322 | vol->upd_ebs); | 357 | len, vol->upd_ebs); |
323 | if (err) | 358 | if (err) |
324 | break; | 359 | break; |
325 | } | 360 | } |
@@ -333,16 +368,70 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, | |||
333 | ubi_assert(vol->upd_received <= vol->upd_bytes); | 368 | ubi_assert(vol->upd_received <= vol->upd_bytes); |
334 | if (vol->upd_received == vol->upd_bytes) { | 369 | if (vol->upd_received == vol->upd_bytes) { |
335 | /* The update is finished, clear the update marker */ | 370 | /* The update is finished, clear the update marker */ |
336 | err = clear_update_marker(ubi, vol_id, vol->upd_bytes); | 371 | err = clear_update_marker(ubi, vol, vol->upd_bytes); |
337 | if (err) | 372 | if (err) |
338 | return err; | 373 | return err; |
339 | err = ubi_wl_flush(ubi); | 374 | err = ubi_wl_flush(ubi); |
340 | if (err == 0) { | 375 | if (err == 0) { |
376 | vol->updating = 0; | ||
341 | err = to_write; | 377 | err = to_write; |
342 | vfree(vol->upd_buf); | 378 | vfree(vol->upd_buf); |
343 | vol->updating = 0; | ||
344 | } | 379 | } |
345 | } | 380 | } |
346 | 381 | ||
347 | return err; | 382 | return err; |
348 | } | 383 | } |
384 | |||
385 | /** | ||
386 | * ubi_more_leb_change_data - accept more data for atomic LEB change. | ||
387 | * @vol: volume description object | ||
388 | * @buf: write data (user-space memory buffer) | ||
389 | * @count: how much bytes to write | ||
390 | * | ||
391 | * This function accepts more data to the volume which is being under the | ||
392 | * "atomic LEB change" operation. It may be called arbitrary number of times | ||
393 | * until all data arrives. This function returns %0 in case of success, number | ||
394 | * of bytes written during the last call if the whole "atomic LEB change" | ||
395 | * operation has been successfully finished, and a negative error code in case | ||
396 | * of failure. | ||
397 | */ | ||
398 | int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, | ||
399 | const void __user *buf, int count) | ||
400 | { | ||
401 | int err; | ||
402 | |||
403 | dbg_msg("write %d of %lld bytes, %lld already passed", | ||
404 | count, vol->upd_bytes, vol->upd_received); | ||
405 | |||
406 | if (ubi->ro_mode) | ||
407 | return -EROFS; | ||
408 | |||
409 | if (vol->upd_received + count > vol->upd_bytes) | ||
410 | count = vol->upd_bytes - vol->upd_received; | ||
411 | |||
412 | err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); | ||
413 | if (err) | ||
414 | return -EFAULT; | ||
415 | |||
416 | vol->upd_received += count; | ||
417 | |||
418 | if (vol->upd_received == vol->upd_bytes) { | ||
419 | int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); | ||
420 | |||
421 | memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); | ||
422 | len = ubi_calc_data_len(ubi, vol->upd_buf, len); | ||
423 | err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, | ||
424 | vol->upd_buf, len, UBI_UNKNOWN); | ||
425 | if (err) | ||
426 | return err; | ||
427 | } | ||
428 | |||
429 | ubi_assert(vol->upd_received <= vol->upd_bytes); | ||
430 | if (vol->upd_received == vol->upd_bytes) { | ||
431 | vol->changing_leb = 0; | ||
432 | err = count; | ||
433 | vfree(vol->upd_buf); | ||
434 | } | ||
435 | |||
436 | return err; | ||
437 | } | ||