diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-01-24 11:48:21 -0500 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-01-25 09:41:26 -0500 |
commit | e653879c269735c9ff6684e03edf1d4e041ff3d3 (patch) | |
tree | 2019a018e3fd809798dabfa92e356acc8030cdd4 /drivers/mtd/ubi/upd.c | |
parent | 866136827b9a71c39dcb06d23ce523f719eab74b (diff) |
UBI: implement atomic LEB change ioctl
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'drivers/mtd/ubi/upd.c')
-rw-r--r-- | drivers/mtd/ubi/upd.c | 112 |
1 files changed, 100 insertions, 12 deletions
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 59c61ab4f2aa..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 |
@@ -133,6 +134,7 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, | |||
133 | uint64_t tmp; | 134 | uint64_t tmp; |
134 | 135 | ||
135 | dbg_msg("start update of volume %d, %llu bytes", vol->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); | ||
136 | vol->updating = 1; | 138 | vol->updating = 1; |
137 | 139 | ||
138 | err = set_update_marker(ubi, vol); | 140 | err = set_update_marker(ubi, vol); |
@@ -168,6 +170,39 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, | |||
168 | } | 170 | } |
169 | 171 | ||
170 | /** | 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 | /** | ||
171 | * write_leb - write update data. | 206 | * write_leb - write update data. |
172 | * @ubi: UBI device description object | 207 | * @ubi: UBI device description object |
173 | * @vol: volume description object | 208 | * @vol: volume description object |
@@ -199,21 +234,19 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, | |||
199 | static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, | 234 | static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, |
200 | void *buf, int len, int used_ebs) | 235 | void *buf, int len, int used_ebs) |
201 | { | 236 | { |
202 | int err, l; | 237 | int err; |
203 | 238 | ||
204 | if (vol->vol_type == UBI_DYNAMIC_VOLUME) { | 239 | if (vol->vol_type == UBI_DYNAMIC_VOLUME) { |
205 | l = ALIGN(len, ubi->min_io_size); | 240 | len = ALIGN(len, ubi->min_io_size); |
206 | memset(buf + len, 0xFF, l - len); | 241 | memset(buf + len, 0xFF, len - len); |
207 | 242 | ||
208 | l = ubi_calc_data_len(ubi, buf, l); | 243 | len = ubi_calc_data_len(ubi, buf, len); |
209 | if (l == 0) { | 244 | if (len == 0) { |
210 | dbg_msg("all %d bytes contain 0xFF - skip", len); | 245 | dbg_msg("all %d bytes contain 0xFF - skip", len); |
211 | return 0; | 246 | return 0; |
212 | } | 247 | } |
213 | if (len != l) | ||
214 | dbg_msg("skip last %d bytes (0xFF)", len - l); | ||
215 | 248 | ||
216 | err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, l, UBI_UNKNOWN); | 249 | err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); |
217 | } else { | 250 | } else { |
218 | /* | 251 | /* |
219 | * When writing static volume, and this is the last logical | 252 | * When writing static volume, and this is the last logical |
@@ -239,9 +272,9 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, | |||
239 | * @count: how much bytes to write | 272 | * @count: how much bytes to write |
240 | * | 273 | * |
241 | * 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 |
242 | * 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 |
243 | * 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 |
244 | * 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 |
245 | * negative error code in case of failure. | 278 | * negative error code in case of failure. |
246 | */ | 279 | */ |
247 | int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, | 280 | int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, |
@@ -340,6 +373,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, | |||
340 | return err; | 373 | return err; |
341 | err = ubi_wl_flush(ubi); | 374 | err = ubi_wl_flush(ubi); |
342 | if (err == 0) { | 375 | if (err == 0) { |
376 | vol->updating = 0; | ||
343 | err = to_write; | 377 | err = to_write; |
344 | vfree(vol->upd_buf); | 378 | vfree(vol->upd_buf); |
345 | } | 379 | } |
@@ -347,3 +381,57 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, | |||
347 | 381 | ||
348 | return err; | 382 | return err; |
349 | } | 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 | } | ||