diff options
Diffstat (limited to 'drivers/mtd/ubi/cdev.c')
| -rw-r--r-- | drivers/mtd/ubi/cdev.c | 82 |
1 files changed, 64 insertions, 18 deletions
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 5ec13dc4705b..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); |
| @@ -184,13 +191,13 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
| 184 | struct ubi_volume_desc *desc = file->private_data; | 191 | struct ubi_volume_desc *desc = file->private_data; |
| 185 | struct ubi_volume *vol = desc->vol; | 192 | struct ubi_volume *vol = desc->vol; |
| 186 | struct ubi_device *ubi = vol->ubi; | 193 | struct ubi_device *ubi = vol->ubi; |
| 187 | int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size; | 194 | int err, lnum, off, len, tbuf_size; |
| 188 | size_t count_save = count; | 195 | size_t count_save = count; |
| 189 | void *tbuf; | 196 | void *tbuf; |
| 190 | uint64_t tmp; | 197 | uint64_t tmp; |
| 191 | 198 | ||
| 192 | dbg_msg("read %zd bytes from offset %lld of volume %d", | 199 | dbg_msg("read %zd bytes from offset %lld of volume %d", |
| 193 | count, *offp, vol_id); | 200 | count, *offp, vol->vol_id); |
| 194 | 201 | ||
| 195 | if (vol->updating) { | 202 | if (vol->updating) { |
| 196 | dbg_err("updating"); | 203 | dbg_err("updating"); |
| @@ -204,7 +211,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
| 204 | return 0; | 211 | return 0; |
| 205 | 212 | ||
| 206 | if (vol->corrupted) | 213 | if (vol->corrupted) |
| 207 | dbg_msg("read from corrupted volume %d", vol_id); | 214 | dbg_msg("read from corrupted volume %d", vol->vol_id); |
| 208 | 215 | ||
| 209 | if (*offp + count > vol->used_bytes) | 216 | if (*offp + count > vol->used_bytes) |
| 210 | count_save = count = vol->used_bytes - *offp; | 217 | count_save = count = vol->used_bytes - *offp; |
| @@ -274,7 +281,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
| 274 | uint64_t tmp; | 281 | uint64_t tmp; |
| 275 | 282 | ||
| 276 | dbg_msg("requested: write %zd bytes to offset %lld of volume %u", | 283 | dbg_msg("requested: write %zd bytes to offset %lld of volume %u", |
| 277 | count, *offp, desc->vol->vol_id); | 284 | count, *offp, vol->vol_id); |
| 278 | 285 | ||
| 279 | if (vol->vol_type == UBI_STATIC_VOLUME) | 286 | if (vol->vol_type == UBI_STATIC_VOLUME) |
| 280 | return -EROFS; | 287 | return -EROFS; |
| @@ -351,23 +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->vol_id, 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 | ||
| 382 | if (vol->changing_leb) { | ||
| 383 | revoke_exclusive(desc, UBI_READWRITE); | ||
| 384 | return count; | ||
| 385 | } | ||
| 386 | |||
| 371 | err = ubi_check_volume(ubi, vol->vol_id); | 387 | err = ubi_check_volume(ubi, vol->vol_id); |
| 372 | if (err < 0) | 388 | if (err < 0) |
| 373 | return err; | 389 | return err; |
| @@ -382,7 +398,6 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, | |||
| 382 | revoke_exclusive(desc, UBI_READWRITE); | 398 | revoke_exclusive(desc, UBI_READWRITE); |
| 383 | } | 399 | } |
| 384 | 400 | ||
| 385 | *offp += count; | ||
| 386 | return count; | 401 | return count; |
| 387 | } | 402 | } |
| 388 | 403 | ||
| @@ -427,11 +442,46 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 427 | if (err < 0) | 442 | if (err < 0) |
| 428 | break; | 443 | break; |
| 429 | 444 | ||
| 430 | err = ubi_start_update(ubi, vol->vol_id, bytes); | 445 | err = ubi_start_update(ubi, vol, bytes); |
| 431 | if (bytes == 0) | 446 | if (bytes == 0) |
| 432 | revoke_exclusive(desc, UBI_READWRITE); | 447 | revoke_exclusive(desc, UBI_READWRITE); |
| 448 | break; | ||
| 449 | } | ||
| 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; | ||
| 433 | 481 | ||
| 434 | file->f_pos = 0; | 482 | err = ubi_start_leb_change(ubi, vol, &req); |
| 483 | if (req.bytes == 0) | ||
| 484 | revoke_exclusive(desc, UBI_READWRITE); | ||
| 435 | break; | 485 | break; |
| 436 | } | 486 | } |
| 437 | 487 | ||
| @@ -447,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 447 | break; | 497 | break; |
| 448 | } | 498 | } |
| 449 | 499 | ||
| 450 | if (desc->mode == UBI_READONLY) { | 500 | if (desc->mode == UBI_READONLY || |
| 501 | vol->vol_type == UBI_STATIC_VOLUME) { | ||
| 451 | err = -EROFS; | 502 | err = -EROFS; |
| 452 | break; | 503 | break; |
| 453 | } | 504 | } |
| @@ -457,11 +508,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 457 | break; | 508 | break; |
| 458 | } | 509 | } |
| 459 | 510 | ||
| 460 | if (vol->vol_type != UBI_DYNAMIC_VOLUME) { | ||
| 461 | err = -EROFS; | ||
| 462 | break; | ||
| 463 | } | ||
| 464 | |||
| 465 | dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); | 511 | dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); |
| 466 | err = ubi_eba_unmap_leb(ubi, vol, lnum); | 512 | err = ubi_eba_unmap_leb(ubi, vol, lnum); |
| 467 | if (err) | 513 | if (err) |
