diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/mtd/ubi/cdev.c | 72 | ||||
| -rw-r--r-- | drivers/mtd/ubi/ubi.h | 27 | ||||
| -rw-r--r-- | drivers/mtd/ubi/upd.c | 112 |
3 files changed, 183 insertions, 28 deletions
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 0c4044d6cae0..9d6aae5449b6 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c | |||
| @@ -132,8 +132,15 @@ static int vol_cdev_release(struct inode *inode, struct file *file) | |||
| 132 | if (vol->updating) { | 132 | if (vol->updating) { |
| 133 | ubi_warn("update of volume %d not finished, volume is damaged", | 133 | ubi_warn("update of volume %d not finished, volume is damaged", |
| 134 | vol->vol_id); | 134 | vol->vol_id); |
| 135 | ubi_assert(!vol->changing_leb); | ||
| 135 | vol->updating = 0; | 136 | vol->updating = 0; |
| 136 | vfree(vol->upd_buf); | 137 | vfree(vol->upd_buf); |
| 138 | } else if (vol->changing_leb) { | ||
| 139 | dbg_msg("only %lld of %lld bytes received for atomic LEB change" | ||
| 140 | " for volume %d:%d, cancel", vol->upd_received, | ||
| 141 | vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); | ||
| 142 | vol->changing_leb = 0; | ||
| 143 | vfree(vol->upd_buf); | ||
| 137 | } | 144 | } |
| 138 | 145 | ||
| 139 | ubi_close_volume(desc); | 146 | ubi_close_volume(desc); |
| @@ -351,24 +358,32 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, | |||
| 351 | struct ubi_volume *vol = desc->vol; | 358 | struct ubi_volume *vol = desc->vol; |
| 352 | struct ubi_device *ubi = vol->ubi; | 359 | struct ubi_device *ubi = vol->ubi; |
| 353 | 360 | ||
| 354 | if (!vol->updating) | 361 | if (!vol->updating && !vol->changing_leb) |
| 355 | return vol_cdev_direct_write(file, buf, count, offp); | 362 | return vol_cdev_direct_write(file, buf, count, offp); |
| 356 | 363 | ||
| 357 | err = ubi_more_update_data(ubi, vol, buf, count); | 364 | if (vol->updating) |
| 365 | err = ubi_more_update_data(ubi, vol, buf, count); | ||
| 366 | else | ||
| 367 | err = ubi_more_leb_change_data(ubi, vol, buf, count); | ||
| 368 | |||
| 358 | if (err < 0) { | 369 | if (err < 0) { |
| 359 | ubi_err("cannot write %zd bytes of update data, error %d", | 370 | ubi_err("cannot accept more %zd bytes of data, error %d", |
| 360 | count, err); | 371 | count, err); |
| 361 | return err; | 372 | return err; |
| 362 | } | 373 | } |
| 363 | 374 | ||
| 364 | if (err) { | 375 | if (err) { |
| 365 | /* | 376 | /* |
| 366 | * Update is finished, @err contains number of actually written | 377 | * The operation is finished, @err contains number of actually |
| 367 | * bytes now. | 378 | * written bytes. |
| 368 | */ | 379 | */ |
| 369 | count = err; | 380 | count = err; |
| 370 | 381 | ||
| 371 | vol->updating = 0; | 382 | if (vol->changing_leb) { |
| 383 | revoke_exclusive(desc, UBI_READWRITE); | ||
| 384 | return count; | ||
| 385 | } | ||
| 386 | |||
| 372 | err = ubi_check_volume(ubi, vol->vol_id); | 387 | err = ubi_check_volume(ubi, vol->vol_id); |
| 373 | if (err < 0) | 388 | if (err < 0) |
| 374 | return err; | 389 | return err; |
| @@ -433,6 +448,43 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 433 | break; | 448 | break; |
| 434 | } | 449 | } |
| 435 | 450 | ||
| 451 | /* Atomic logical eraseblock change command */ | ||
| 452 | case UBI_IOCEBCH: | ||
| 453 | { | ||
| 454 | struct ubi_leb_change_req req; | ||
| 455 | |||
| 456 | err = copy_from_user(&req, argp, | ||
| 457 | sizeof(struct ubi_leb_change_req)); | ||
| 458 | if (err) { | ||
| 459 | err = -EFAULT; | ||
| 460 | break; | ||
| 461 | } | ||
| 462 | |||
| 463 | if (desc->mode == UBI_READONLY || | ||
| 464 | vol->vol_type == UBI_STATIC_VOLUME) { | ||
| 465 | err = -EROFS; | ||
| 466 | break; | ||
| 467 | } | ||
| 468 | |||
| 469 | /* Validate the request */ | ||
| 470 | err = -EINVAL; | ||
| 471 | if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || | ||
| 472 | req.bytes < 0 || req.lnum >= vol->usable_leb_size) | ||
| 473 | break; | ||
| 474 | if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM && | ||
| 475 | req.dtype != UBI_UNKNOWN) | ||
| 476 | break; | ||
| 477 | |||
| 478 | err = get_exclusive(desc); | ||
| 479 | if (err < 0) | ||
| 480 | break; | ||
| 481 | |||
| 482 | err = ubi_start_leb_change(ubi, vol, &req); | ||
| 483 | if (req.bytes == 0) | ||
| 484 | revoke_exclusive(desc, UBI_READWRITE); | ||
| 485 | break; | ||
| 486 | } | ||
| 487 | |||
| 436 | #ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO | 488 | #ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO |
| 437 | /* Logical eraseblock erasure command */ | 489 | /* Logical eraseblock erasure command */ |
| 438 | case UBI_IOCEBER: | 490 | case UBI_IOCEBER: |
| @@ -445,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 445 | break; | 497 | break; |
| 446 | } | 498 | } |
| 447 | 499 | ||
| 448 | if (desc->mode == UBI_READONLY) { | 500 | if (desc->mode == UBI_READONLY || |
| 501 | vol->vol_type == UBI_STATIC_VOLUME) { | ||
| 449 | err = -EROFS; | 502 | err = -EROFS; |
| 450 | break; | 503 | break; |
| 451 | } | 504 | } |
| @@ -455,11 +508,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 455 | break; | 508 | break; |
| 456 | } | 509 | } |
| 457 | 510 | ||
| 458 | if (vol->vol_type != UBI_DYNAMIC_VOLUME) { | ||
| 459 | err = -EROFS; | ||
| 460 | break; | ||
| 461 | } | ||
| 462 | |||
| 463 | dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); | 511 | dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); |
| 464 | err = ubi_eba_unmap_leb(ubi, vol, lnum); | 512 | err = ubi_eba_unmap_leb(ubi, vol, lnum); |
| 465 | if (err) | 513 | if (err) |
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 3a88cf1eaaa8..457710615261 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h | |||
| @@ -158,15 +158,23 @@ struct ubi_volume_desc; | |||
| 158 | * @name: volume name | 158 | * @name: volume name |
| 159 | * | 159 | * |
| 160 | * @upd_ebs: how many eraseblocks are expected to be updated | 160 | * @upd_ebs: how many eraseblocks are expected to be updated |
| 161 | * @upd_bytes: how many bytes are expected to be received | 161 | * @ch_lnum: LEB number which is being changing by the atomic LEB change |
| 162 | * @upd_received: how many update bytes were already received | 162 | * operation |
| 163 | * @upd_buf: update buffer which is used to collect update data | 163 | * @ch_dtype: data persistency type which is being changing by the atomic LEB |
| 164 | * change operation | ||
| 165 | * @upd_bytes: how many bytes are expected to be received for volume update or | ||
| 166 | * atomic LEB change | ||
| 167 | * @upd_received: how many bytes were already received for volume update or | ||
| 168 | * atomic LEB change | ||
| 169 | * @upd_buf: update buffer which is used to collect update data or data for | ||
| 170 | * atomic LEB change | ||
| 164 | * | 171 | * |
| 165 | * @eba_tbl: EBA table of this volume (LEB->PEB mapping) | 172 | * @eba_tbl: EBA table of this volume (LEB->PEB mapping) |
| 166 | * @checked: %1 if this static volume was checked | 173 | * @checked: %1 if this static volume was checked |
| 167 | * @corrupted: %1 if the volume is corrupted (static volumes only) | 174 | * @corrupted: %1 if the volume is corrupted (static volumes only) |
| 168 | * @upd_marker: %1 if the update marker is set for this volume | 175 | * @upd_marker: %1 if the update marker is set for this volume |
| 169 | * @updating: %1 if the volume is being updated | 176 | * @updating: %1 if the volume is being updated |
| 177 | * @changing_leb: %1 if the atomic LEB change ioctl command is in progress | ||
| 170 | * | 178 | * |
| 171 | * @gluebi_desc: gluebi UBI volume descriptor | 179 | * @gluebi_desc: gluebi UBI volume descriptor |
| 172 | * @gluebi_refcount: reference count of the gluebi MTD device | 180 | * @gluebi_refcount: reference count of the gluebi MTD device |
| @@ -202,6 +210,8 @@ struct ubi_volume { | |||
| 202 | char name[UBI_VOL_NAME_MAX+1]; | 210 | char name[UBI_VOL_NAME_MAX+1]; |
| 203 | 211 | ||
| 204 | int upd_ebs; | 212 | int upd_ebs; |
| 213 | int ch_lnum; | ||
| 214 | int ch_dtype; | ||
| 205 | long long upd_bytes; | 215 | long long upd_bytes; |
| 206 | long long upd_received; | 216 | long long upd_received; |
| 207 | void *upd_buf; | 217 | void *upd_buf; |
| @@ -211,9 +221,14 @@ struct ubi_volume { | |||
| 211 | int corrupted:1; | 221 | int corrupted:1; |
| 212 | int upd_marker:1; | 222 | int upd_marker:1; |
| 213 | int updating:1; | 223 | int updating:1; |
| 224 | int changing_leb:1; | ||
| 214 | 225 | ||
| 215 | #ifdef CONFIG_MTD_UBI_GLUEBI | 226 | #ifdef CONFIG_MTD_UBI_GLUEBI |
| 216 | /* Gluebi-related stuff may be compiled out */ | 227 | /* |
| 228 | * Gluebi-related stuff may be compiled out. | ||
| 229 | * TODO: this should not be built into UBI but should be a separate | ||
| 230 | * ubimtd driver which works on top of UBI and emulates MTD devices. | ||
| 231 | */ | ||
| 217 | struct ubi_volume_desc *gluebi_desc; | 232 | struct ubi_volume_desc *gluebi_desc; |
| 218 | int gluebi_refcount; | 233 | int gluebi_refcount; |
| 219 | struct mtd_info gluebi_mtd; | 234 | struct mtd_info gluebi_mtd; |
| @@ -427,6 +442,10 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, | |||
| 427 | long long bytes); | 442 | long long bytes); |
| 428 | int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, | 443 | int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, |
| 429 | const void __user *buf, int count); | 444 | const void __user *buf, int count); |
| 445 | int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, | ||
| 446 | const struct ubi_leb_change_req *req); | ||
| 447 | int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, | ||
| 448 | const void __user *buf, int count); | ||
| 430 | 449 | ||
| 431 | /* misc.c */ | 450 | /* misc.c */ |
| 432 | int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); | 451 | int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); |
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 | } | ||
