Loading drivers/mtd/ubi/cdev.c +60 −12 Original line number Diff line number Diff line Loading @@ -132,8 +132,15 @@ static int vol_cdev_release(struct inode *inode, struct file *file) if (vol->updating) { ubi_warn("update of volume %d not finished, volume is damaged", vol->vol_id); ubi_assert(!vol->changing_leb); vol->updating = 0; vfree(vol->upd_buf); } else if (vol->changing_leb) { dbg_msg("only %lld of %lld bytes received for atomic LEB change" " for volume %d:%d, cancel", vol->upd_received, vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); vol->changing_leb = 0; vfree(vol->upd_buf); } ubi_close_volume(desc); Loading Loading @@ -351,24 +358,32 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; if (!vol->updating) if (!vol->updating && !vol->changing_leb) return vol_cdev_direct_write(file, buf, count, offp); if (vol->updating) err = ubi_more_update_data(ubi, vol, buf, count); else err = ubi_more_leb_change_data(ubi, vol, buf, count); if (err < 0) { ubi_err("cannot write %zd bytes of update data, error %d", ubi_err("cannot accept more %zd bytes of data, error %d", count, err); return err; } if (err) { /* * Update is finished, @err contains number of actually written * bytes now. * The operation is finished, @err contains number of actually * written bytes. */ count = err; vol->updating = 0; if (vol->changing_leb) { revoke_exclusive(desc, UBI_READWRITE); return count; } err = ubi_check_volume(ubi, vol->vol_id); if (err < 0) return err; Loading Loading @@ -433,6 +448,43 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } /* Atomic logical eraseblock change command */ case UBI_IOCEBCH: { struct ubi_leb_change_req req; err = copy_from_user(&req, argp, sizeof(struct ubi_leb_change_req)); if (err) { err = -EFAULT; break; } if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) { err = -EROFS; break; } /* Validate the request */ err = -EINVAL; if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || req.bytes < 0 || req.lnum >= vol->usable_leb_size) break; if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM && req.dtype != UBI_UNKNOWN) break; err = get_exclusive(desc); if (err < 0) break; err = ubi_start_leb_change(ubi, vol, &req); if (req.bytes == 0) revoke_exclusive(desc, UBI_READWRITE); break; } #ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO /* Logical eraseblock erasure command */ case UBI_IOCEBER: Loading @@ -445,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } if (desc->mode == UBI_READONLY) { if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) { err = -EROFS; break; } Loading @@ -455,11 +508,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } if (vol->vol_type != UBI_DYNAMIC_VOLUME) { err = -EROFS; break; } dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) Loading drivers/mtd/ubi/ubi.h +23 −4 Original line number Diff line number Diff line Loading @@ -158,15 +158,23 @@ struct ubi_volume_desc; * @name: volume name * * @upd_ebs: how many eraseblocks are expected to be updated * @upd_bytes: how many bytes are expected to be received * @upd_received: how many update bytes were already received * @upd_buf: update buffer which is used to collect update data * @ch_lnum: LEB number which is being changing by the atomic LEB change * operation * @ch_dtype: data persistency type which is being changing by the atomic LEB * change operation * @upd_bytes: how many bytes are expected to be received for volume update or * atomic LEB change * @upd_received: how many bytes were already received for volume update or * atomic LEB change * @upd_buf: update buffer which is used to collect update data or data for * atomic LEB change * * @eba_tbl: EBA table of this volume (LEB->PEB mapping) * @checked: %1 if this static volume was checked * @corrupted: %1 if the volume is corrupted (static volumes only) * @upd_marker: %1 if the update marker is set for this volume * @updating: %1 if the volume is being updated * @changing_leb: %1 if the atomic LEB change ioctl command is in progress * * @gluebi_desc: gluebi UBI volume descriptor * @gluebi_refcount: reference count of the gluebi MTD device Loading Loading @@ -202,6 +210,8 @@ struct ubi_volume { char name[UBI_VOL_NAME_MAX+1]; int upd_ebs; int ch_lnum; int ch_dtype; long long upd_bytes; long long upd_received; void *upd_buf; Loading @@ -211,9 +221,14 @@ struct ubi_volume { int corrupted:1; int upd_marker:1; int updating:1; int changing_leb:1; #ifdef CONFIG_MTD_UBI_GLUEBI /* Gluebi-related stuff may be compiled out */ /* * Gluebi-related stuff may be compiled out. * TODO: this should not be built into UBI but should be a separate * ubimtd driver which works on top of UBI and emulates MTD devices. */ struct ubi_volume_desc *gluebi_desc; int gluebi_refcount; struct mtd_info gluebi_mtd; Loading Loading @@ -427,6 +442,10 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, long long bytes); int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, const struct ubi_leb_change_req *req); int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); /* misc.c */ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); Loading drivers/mtd/ubi/upd.c +100 −12 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ */ /* * This file contains implementation of the volume update functionality. * This file contains implementation of the volume update and atomic LEB change * functionality. * * The update operation is based on the per-volume update marker which is * stored in the volume table. The update marker is set before the update Loading Loading @@ -133,6 +134,7 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, uint64_t tmp; dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); ubi_assert(!vol->updating && !vol->changing_leb); vol->updating = 1; err = set_update_marker(ubi, vol); Loading Loading @@ -167,6 +169,39 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, return 0; } /** * ubi_start_leb_change - start atomic LEB change. * @ubi: UBI device description object * @vol: volume description object * @req: operation request * * This function starts atomic LEB change operation. Returns zero in case of * success and a negative error code in case of failure. */ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, const struct ubi_leb_change_req *req) { ubi_assert(!vol->updating && !vol->changing_leb); dbg_msg("start changing LEB %d:%d, %u bytes", vol->vol_id, req->lnum, req->bytes); if (req->bytes == 0) return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, req->dtype); vol->upd_bytes = req->bytes; vol->upd_received = 0; vol->changing_leb = 1; vol->ch_lnum = req->lnum; vol->ch_dtype = req->dtype; vol->upd_buf = vmalloc(req->bytes); if (!vol->upd_buf) return -ENOMEM; return 0; } /** * write_leb - write update data. * @ubi: UBI device description object Loading Loading @@ -199,21 +234,19 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, void *buf, int len, int used_ebs) { int err, l; int err; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { l = ALIGN(len, ubi->min_io_size); memset(buf + len, 0xFF, l - len); len = ALIGN(len, ubi->min_io_size); memset(buf + len, 0xFF, len - len); l = ubi_calc_data_len(ubi, buf, l); if (l == 0) { len = ubi_calc_data_len(ubi, buf, len); if (len == 0) { dbg_msg("all %d bytes contain 0xFF - skip", len); return 0; } if (len != l) dbg_msg("skip last %d bytes (0xFF)", len - l); err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, l, UBI_UNKNOWN); err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); } else { /* * When writing static volume, and this is the last logical Loading @@ -239,9 +272,9 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, * @count: how much bytes to write * * This function writes more data to the volume which is being updated. It may * be called arbitrary number of times until all of the update data arrive. * This function returns %0 in case of success, number of bytes written during * the last call if the whole volume update was successfully finished, and a * be called arbitrary number of times until all the update data arriveis. This * function returns %0 in case of success, number of bytes written during the * last call if the whole volume update has been successfully finished, and a * negative error code in case of failure. */ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, Loading Loading @@ -340,6 +373,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, return err; err = ubi_wl_flush(ubi); if (err == 0) { vol->updating = 0; err = to_write; vfree(vol->upd_buf); } Loading @@ -347,3 +381,57 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, return err; } /** * ubi_more_leb_change_data - accept more data for atomic LEB change. * @vol: volume description object * @buf: write data (user-space memory buffer) * @count: how much bytes to write * * This function accepts more data to the volume which is being under the * "atomic LEB change" operation. It may be called arbitrary number of times * until all data arrives. This function returns %0 in case of success, number * of bytes written during the last call if the whole "atomic LEB change" * operation has been successfully finished, and a negative error code in case * of failure. */ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count) { int err; dbg_msg("write %d of %lld bytes, %lld already passed", count, vol->upd_bytes, vol->upd_received); if (ubi->ro_mode) return -EROFS; if (vol->upd_received + count > vol->upd_bytes) count = vol->upd_bytes - vol->upd_received; err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); if (err) return -EFAULT; vol->upd_received += count; if (vol->upd_received == vol->upd_bytes) { int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); len = ubi_calc_data_len(ubi, vol->upd_buf, len); err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, vol->upd_buf, len, UBI_UNKNOWN); if (err) return err; } ubi_assert(vol->upd_received <= vol->upd_bytes); if (vol->upd_received == vol->upd_bytes) { vol->changing_leb = 0; err = count; vfree(vol->upd_buf); } return err; } Loading
drivers/mtd/ubi/cdev.c +60 −12 Original line number Diff line number Diff line Loading @@ -132,8 +132,15 @@ static int vol_cdev_release(struct inode *inode, struct file *file) if (vol->updating) { ubi_warn("update of volume %d not finished, volume is damaged", vol->vol_id); ubi_assert(!vol->changing_leb); vol->updating = 0; vfree(vol->upd_buf); } else if (vol->changing_leb) { dbg_msg("only %lld of %lld bytes received for atomic LEB change" " for volume %d:%d, cancel", vol->upd_received, vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); vol->changing_leb = 0; vfree(vol->upd_buf); } ubi_close_volume(desc); Loading Loading @@ -351,24 +358,32 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; if (!vol->updating) if (!vol->updating && !vol->changing_leb) return vol_cdev_direct_write(file, buf, count, offp); if (vol->updating) err = ubi_more_update_data(ubi, vol, buf, count); else err = ubi_more_leb_change_data(ubi, vol, buf, count); if (err < 0) { ubi_err("cannot write %zd bytes of update data, error %d", ubi_err("cannot accept more %zd bytes of data, error %d", count, err); return err; } if (err) { /* * Update is finished, @err contains number of actually written * bytes now. * The operation is finished, @err contains number of actually * written bytes. */ count = err; vol->updating = 0; if (vol->changing_leb) { revoke_exclusive(desc, UBI_READWRITE); return count; } err = ubi_check_volume(ubi, vol->vol_id); if (err < 0) return err; Loading Loading @@ -433,6 +448,43 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } /* Atomic logical eraseblock change command */ case UBI_IOCEBCH: { struct ubi_leb_change_req req; err = copy_from_user(&req, argp, sizeof(struct ubi_leb_change_req)); if (err) { err = -EFAULT; break; } if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) { err = -EROFS; break; } /* Validate the request */ err = -EINVAL; if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || req.bytes < 0 || req.lnum >= vol->usable_leb_size) break; if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM && req.dtype != UBI_UNKNOWN) break; err = get_exclusive(desc); if (err < 0) break; err = ubi_start_leb_change(ubi, vol, &req); if (req.bytes == 0) revoke_exclusive(desc, UBI_READWRITE); break; } #ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO /* Logical eraseblock erasure command */ case UBI_IOCEBER: Loading @@ -445,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } if (desc->mode == UBI_READONLY) { if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) { err = -EROFS; break; } Loading @@ -455,11 +508,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } if (vol->vol_type != UBI_DYNAMIC_VOLUME) { err = -EROFS; break; } dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) Loading
drivers/mtd/ubi/ubi.h +23 −4 Original line number Diff line number Diff line Loading @@ -158,15 +158,23 @@ struct ubi_volume_desc; * @name: volume name * * @upd_ebs: how many eraseblocks are expected to be updated * @upd_bytes: how many bytes are expected to be received * @upd_received: how many update bytes were already received * @upd_buf: update buffer which is used to collect update data * @ch_lnum: LEB number which is being changing by the atomic LEB change * operation * @ch_dtype: data persistency type which is being changing by the atomic LEB * change operation * @upd_bytes: how many bytes are expected to be received for volume update or * atomic LEB change * @upd_received: how many bytes were already received for volume update or * atomic LEB change * @upd_buf: update buffer which is used to collect update data or data for * atomic LEB change * * @eba_tbl: EBA table of this volume (LEB->PEB mapping) * @checked: %1 if this static volume was checked * @corrupted: %1 if the volume is corrupted (static volumes only) * @upd_marker: %1 if the update marker is set for this volume * @updating: %1 if the volume is being updated * @changing_leb: %1 if the atomic LEB change ioctl command is in progress * * @gluebi_desc: gluebi UBI volume descriptor * @gluebi_refcount: reference count of the gluebi MTD device Loading Loading @@ -202,6 +210,8 @@ struct ubi_volume { char name[UBI_VOL_NAME_MAX+1]; int upd_ebs; int ch_lnum; int ch_dtype; long long upd_bytes; long long upd_received; void *upd_buf; Loading @@ -211,9 +221,14 @@ struct ubi_volume { int corrupted:1; int upd_marker:1; int updating:1; int changing_leb:1; #ifdef CONFIG_MTD_UBI_GLUEBI /* Gluebi-related stuff may be compiled out */ /* * Gluebi-related stuff may be compiled out. * TODO: this should not be built into UBI but should be a separate * ubimtd driver which works on top of UBI and emulates MTD devices. */ struct ubi_volume_desc *gluebi_desc; int gluebi_refcount; struct mtd_info gluebi_mtd; Loading Loading @@ -427,6 +442,10 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, long long bytes); int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, const struct ubi_leb_change_req *req); int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); /* misc.c */ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); Loading
drivers/mtd/ubi/upd.c +100 −12 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ */ /* * This file contains implementation of the volume update functionality. * This file contains implementation of the volume update and atomic LEB change * functionality. * * The update operation is based on the per-volume update marker which is * stored in the volume table. The update marker is set before the update Loading Loading @@ -133,6 +134,7 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, uint64_t tmp; dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); ubi_assert(!vol->updating && !vol->changing_leb); vol->updating = 1; err = set_update_marker(ubi, vol); Loading Loading @@ -167,6 +169,39 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, return 0; } /** * ubi_start_leb_change - start atomic LEB change. * @ubi: UBI device description object * @vol: volume description object * @req: operation request * * This function starts atomic LEB change operation. Returns zero in case of * success and a negative error code in case of failure. */ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, const struct ubi_leb_change_req *req) { ubi_assert(!vol->updating && !vol->changing_leb); dbg_msg("start changing LEB %d:%d, %u bytes", vol->vol_id, req->lnum, req->bytes); if (req->bytes == 0) return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, req->dtype); vol->upd_bytes = req->bytes; vol->upd_received = 0; vol->changing_leb = 1; vol->ch_lnum = req->lnum; vol->ch_dtype = req->dtype; vol->upd_buf = vmalloc(req->bytes); if (!vol->upd_buf) return -ENOMEM; return 0; } /** * write_leb - write update data. * @ubi: UBI device description object Loading Loading @@ -199,21 +234,19 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, void *buf, int len, int used_ebs) { int err, l; int err; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { l = ALIGN(len, ubi->min_io_size); memset(buf + len, 0xFF, l - len); len = ALIGN(len, ubi->min_io_size); memset(buf + len, 0xFF, len - len); l = ubi_calc_data_len(ubi, buf, l); if (l == 0) { len = ubi_calc_data_len(ubi, buf, len); if (len == 0) { dbg_msg("all %d bytes contain 0xFF - skip", len); return 0; } if (len != l) dbg_msg("skip last %d bytes (0xFF)", len - l); err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, l, UBI_UNKNOWN); err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); } else { /* * When writing static volume, and this is the last logical Loading @@ -239,9 +272,9 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, * @count: how much bytes to write * * This function writes more data to the volume which is being updated. It may * be called arbitrary number of times until all of the update data arrive. * This function returns %0 in case of success, number of bytes written during * the last call if the whole volume update was successfully finished, and a * be called arbitrary number of times until all the update data arriveis. This * function returns %0 in case of success, number of bytes written during the * last call if the whole volume update has been successfully finished, and a * negative error code in case of failure. */ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, Loading Loading @@ -340,6 +373,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, return err; err = ubi_wl_flush(ubi); if (err == 0) { vol->updating = 0; err = to_write; vfree(vol->upd_buf); } Loading @@ -347,3 +381,57 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, return err; } /** * ubi_more_leb_change_data - accept more data for atomic LEB change. * @vol: volume description object * @buf: write data (user-space memory buffer) * @count: how much bytes to write * * This function accepts more data to the volume which is being under the * "atomic LEB change" operation. It may be called arbitrary number of times * until all data arrives. This function returns %0 in case of success, number * of bytes written during the last call if the whole "atomic LEB change" * operation has been successfully finished, and a negative error code in case * of failure. */ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count) { int err; dbg_msg("write %d of %lld bytes, %lld already passed", count, vol->upd_bytes, vol->upd_received); if (ubi->ro_mode) return -EROFS; if (vol->upd_received + count > vol->upd_bytes) count = vol->upd_bytes - vol->upd_received; err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); if (err) return -EFAULT; vol->upd_received += count; if (vol->upd_received == vol->upd_bytes) { int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); len = ubi_calc_data_len(ubi, vol->upd_buf, len); err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, vol->upd_buf, len, UBI_UNKNOWN); if (err) return err; } ubi_assert(vol->upd_received <= vol->upd_bytes); if (vol->upd_received == vol->upd_bytes) { vol->changing_leb = 0; err = count; vfree(vol->upd_buf); } return err; }