Commit 4c609922 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'upstream-4.9-rc1' of git://git.infradead.org/linux-ubifs

Pull UBI/UBIFS updates from Richard Weinberger:
 "This pull request contains:

   - Fixes for both UBI and UBIFS
   - overlayfs support (O_TMPFILE, RENAME_WHITEOUT/EXCHANGE)
   - Code refactoring for the upcoming MLC support"

[ Ugh, we just got rid of the "rename2()" naming for the extended rename
  functionality. And this re-introduces it in ubifs with the cross-
  renaming and whiteout support.

  But rather than do any re-organizations in the merge itself, the
  naming can be cleaned up later ]

* tag 'upstream-4.9-rc1' of git://git.infradead.org/linux-ubifs: (27 commits)
  UBIFS: improve function-level documentation
  ubifs: fix host xattr_len when changing xattr
  ubifs: Use move variable in ubifs_rename()
  ubifs: Implement RENAME_EXCHANGE
  ubifs: Implement RENAME_WHITEOUT
  ubifs: Implement O_TMPFILE
  ubi: Fix Fastmap's update_vol()
  ubi: Fix races around ubi_refill_pools()
  ubi: Deal with interrupted erasures in WL
  UBI: introduce the VID buffer concept
  UBI: hide EBA internals
  UBI: provide an helper to query LEB information
  UBI: provide an helper to check whether a LEB is mapped or not
  UBI: add an helper to check lnum validity
  UBI: simplify LEB write and atomic LEB change code
  UBI: simplify recover_peb() code
  UBI: move the global ech and vidh variables into struct ubi_attach_info
  UBI: provide helpers to allocate and free aeb elements
  UBI: fastmap: use ubi_io_{read, write}_data() instead of ubi_io_{read, write}()
  UBI: fastmap: use ubi_rb_for_each_entry() in unmap_peb()
  ...
parents 1689c73a ec037dfc
This diff is collapsed.
...@@ -574,7 +574,7 @@ void ubi_free_internal_volumes(struct ubi_device *ubi) ...@@ -574,7 +574,7 @@ void ubi_free_internal_volumes(struct ubi_device *ubi)
for (i = ubi->vtbl_slots; for (i = ubi->vtbl_slots;
i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
kfree(ubi->volumes[i]->eba_tbl); ubi_eba_replace_table(ubi->volumes[i], NULL);
kfree(ubi->volumes[i]); kfree(ubi->volumes[i]);
} }
} }
......
...@@ -416,7 +416,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -416,7 +416,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
} }
rsvd_bytes = (long long)vol->reserved_pebs * rsvd_bytes = (long long)vol->reserved_pebs *
ubi->leb_size-vol->data_pad; vol->usable_leb_size;
if (bytes < 0 || bytes > rsvd_bytes) { if (bytes < 0 || bytes > rsvd_bytes) {
err = -EINVAL; err = -EINVAL;
break; break;
...@@ -454,7 +454,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -454,7 +454,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
/* Validate the request */ /* Validate the request */
err = -EINVAL; err = -EINVAL;
if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || if (!ubi_leb_valid(vol, req.lnum) ||
req.bytes < 0 || req.bytes > vol->usable_leb_size) req.bytes < 0 || req.bytes > vol->usable_leb_size)
break; break;
...@@ -485,7 +485,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, ...@@ -485,7 +485,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
break; break;
} }
if (lnum < 0 || lnum >= vol->reserved_pebs) { if (!ubi_leb_valid(vol, lnum)) {
err = -EINVAL; err = -EINVAL;
break; break;
} }
......
This diff is collapsed.
...@@ -262,6 +262,8 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) ...@@ -262,6 +262,8 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi)
struct ubi_fm_pool *pool = &ubi->fm_wl_pool; struct ubi_fm_pool *pool = &ubi->fm_wl_pool;
int pnum; int pnum;
ubi_assert(rwsem_is_locked(&ubi->fm_eba_sem));
if (pool->used == pool->size) { if (pool->used == pool->size) {
/* We cannot update the fastmap here because this /* We cannot update the fastmap here because this
* function is called in atomic context. * function is called in atomic context.
...@@ -303,7 +305,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi) ...@@ -303,7 +305,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi)
wrk->anchor = 1; wrk->anchor = 1;
wrk->func = &wear_leveling_worker; wrk->func = &wear_leveling_worker;
schedule_ubi_work(ubi, wrk); __schedule_ubi_work(ubi, wrk);
return 0; return 0;
} }
...@@ -344,7 +346,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e, ...@@ -344,7 +346,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e,
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID; vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID;
return schedule_erase(ubi, e, vol_id, lnum, torture); return schedule_erase(ubi, e, vol_id, lnum, torture, true);
} }
/** /**
......
This diff is collapsed.
...@@ -502,6 +502,7 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum) ...@@ -502,6 +502,7 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
loff_t addr; loff_t addr;
uint32_t data = 0; uint32_t data = 0;
struct ubi_ec_hdr ec_hdr; struct ubi_ec_hdr ec_hdr;
struct ubi_vid_io_buf vidb;
/* /*
* Note, we cannot generally define VID header buffers on stack, * Note, we cannot generally define VID header buffers on stack,
...@@ -528,7 +529,10 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum) ...@@ -528,7 +529,10 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
goto error; goto error;
} }
err = ubi_io_read_vid_hdr(ubi, pnum, &vid_hdr, 0); ubi_init_vid_buf(ubi, &vidb, &vid_hdr);
ubi_assert(&vid_hdr == ubi_get_vid_hdr(&vidb));
err = ubi_io_read_vid_hdr(ubi, pnum, &vidb, 0);
if (err != UBI_IO_BAD_HDR_EBADMSG && err != UBI_IO_BAD_HDR && if (err != UBI_IO_BAD_HDR_EBADMSG && err != UBI_IO_BAD_HDR &&
err != UBI_IO_FF){ err != UBI_IO_FF){
addr += ubi->vid_hdr_aloffset; addr += ubi->vid_hdr_aloffset;
...@@ -995,12 +999,11 @@ static int validate_vid_hdr(const struct ubi_device *ubi, ...@@ -995,12 +999,11 @@ static int validate_vid_hdr(const struct ubi_device *ubi,
* ubi_io_read_vid_hdr - read and check a volume identifier header. * ubi_io_read_vid_hdr - read and check a volume identifier header.
* @ubi: UBI device description object * @ubi: UBI device description object
* @pnum: physical eraseblock number to read from * @pnum: physical eraseblock number to read from
* @vid_hdr: &struct ubi_vid_hdr object where to store the read volume * @vidb: the volume identifier buffer to store data in
* identifier header
* @verbose: be verbose if the header is corrupted or wasn't found * @verbose: be verbose if the header is corrupted or wasn't found
* *
* This function reads the volume identifier header from physical eraseblock * This function reads the volume identifier header from physical eraseblock
* @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read * @pnum and stores it in @vidb. It also checks CRC checksum of the read
* volume identifier header. The error codes are the same as in * volume identifier header. The error codes are the same as in
* 'ubi_io_read_ec_hdr()'. * 'ubi_io_read_ec_hdr()'.
* *
...@@ -1008,16 +1011,16 @@ static int validate_vid_hdr(const struct ubi_device *ubi, ...@@ -1008,16 +1011,16 @@ static int validate_vid_hdr(const struct ubi_device *ubi,
* 'ubi_io_read_ec_hdr()', so refer commentaries in 'ubi_io_read_ec_hdr()'. * 'ubi_io_read_ec_hdr()', so refer commentaries in 'ubi_io_read_ec_hdr()'.
*/ */
int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
struct ubi_vid_hdr *vid_hdr, int verbose) struct ubi_vid_io_buf *vidb, int verbose)
{ {
int err, read_err; int err, read_err;
uint32_t crc, magic, hdr_crc; uint32_t crc, magic, hdr_crc;
void *p; struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb);
void *p = vidb->buffer;
dbg_io("read VID header from PEB %d", pnum); dbg_io("read VID header from PEB %d", pnum);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count); ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
p = (char *)vid_hdr - ubi->vid_hdr_shift;
read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_shift + UBI_VID_HDR_SIZE); ubi->vid_hdr_shift + UBI_VID_HDR_SIZE);
if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err)) if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err))
...@@ -1080,23 +1083,24 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, ...@@ -1080,23 +1083,24 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
* ubi_io_write_vid_hdr - write a volume identifier header. * ubi_io_write_vid_hdr - write a volume identifier header.
* @ubi: UBI device description object * @ubi: UBI device description object
* @pnum: the physical eraseblock number to write to * @pnum: the physical eraseblock number to write to
* @vid_hdr: the volume identifier header to write * @vidb: the volume identifier buffer to write
* *
* This function writes the volume identifier header described by @vid_hdr to * This function writes the volume identifier header described by @vid_hdr to
* physical eraseblock @pnum. This function automatically fills the * physical eraseblock @pnum. This function automatically fills the
* @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates * @vidb->hdr->magic and the @vidb->hdr->version fields, as well as calculates
* header CRC checksum and stores it at vid_hdr->hdr_crc. * header CRC checksum and stores it at vidb->hdr->hdr_crc.
* *
* This function returns zero in case of success and a negative error code in * This function returns zero in case of success and a negative error code in
* case of failure. If %-EIO is returned, the physical eraseblock probably went * case of failure. If %-EIO is returned, the physical eraseblock probably went
* bad. * bad.
*/ */
int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
struct ubi_vid_hdr *vid_hdr) struct ubi_vid_io_buf *vidb)
{ {
struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb);
int err; int err;
uint32_t crc; uint32_t crc;
void *p; void *p = vidb->buffer;
dbg_io("write VID header to PEB %d", pnum); dbg_io("write VID header to PEB %d", pnum);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count); ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
...@@ -1117,7 +1121,6 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, ...@@ -1117,7 +1121,6 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE)) if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE))
return -EROFS; return -EROFS;
p = (char *)vid_hdr - ubi->vid_hdr_shift;
err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_alsize); ubi->vid_hdr_alsize);
return err; return err;
...@@ -1283,17 +1286,19 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) ...@@ -1283,17 +1286,19 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
{ {
int err; int err;
uint32_t crc, hdr_crc; uint32_t crc, hdr_crc;
struct ubi_vid_io_buf *vidb;
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
void *p; void *p;
if (!ubi_dbg_chk_io(ubi)) if (!ubi_dbg_chk_io(ubi))
return 0; return 0;
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS);
if (!vid_hdr) if (!vidb)
return -ENOMEM; return -ENOMEM;
p = (char *)vid_hdr - ubi->vid_hdr_shift; vid_hdr = ubi_get_vid_hdr(vidb);
p = vidb->buffer;
err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_alsize); ubi->vid_hdr_alsize);
if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err)) if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err))
...@@ -1314,7 +1319,7 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) ...@@ -1314,7 +1319,7 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
err = self_check_vid_hdr(ubi, pnum, vid_hdr); err = self_check_vid_hdr(ubi, pnum, vid_hdr);
exit: exit:
ubi_free_vid_hdr(ubi, vid_hdr); ubi_free_vid_buf(vidb);
return err; return err;
} }
......
...@@ -538,7 +538,7 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, ...@@ -538,7 +538,7 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS; return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 || if (!ubi_leb_valid(vol, lnum) || offset < 0 || len < 0 ||
offset + len > vol->usable_leb_size || offset + len > vol->usable_leb_size ||
offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1)) offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
return -EINVAL; return -EINVAL;
...@@ -583,7 +583,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, ...@@ -583,7 +583,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS; return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || if (!ubi_leb_valid(vol, lnum) || len < 0 ||
len > vol->usable_leb_size || len & (ubi->min_io_size - 1)) len > vol->usable_leb_size || len & (ubi->min_io_size - 1))
return -EINVAL; return -EINVAL;
...@@ -620,7 +620,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) ...@@ -620,7 +620,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS; return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs) if (!ubi_leb_valid(vol, lnum))
return -EINVAL; return -EINVAL;
if (vol->upd_marker) if (vol->upd_marker)
...@@ -680,7 +680,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) ...@@ -680,7 +680,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS; return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs) if (!ubi_leb_valid(vol, lnum))
return -EINVAL; return -EINVAL;
if (vol->upd_marker) if (vol->upd_marker)
...@@ -716,13 +716,13 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum) ...@@ -716,13 +716,13 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum)
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS; return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs) if (!ubi_leb_valid(vol, lnum))
return -EINVAL; return -EINVAL;
if (vol->upd_marker) if (vol->upd_marker)
return -EBADF; return -EBADF;
if (vol->eba_tbl[lnum] >= 0) if (ubi_eba_is_mapped(vol, lnum))
return -EBADMSG; return -EBADMSG;
return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0); return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0);
...@@ -751,13 +751,13 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) ...@@ -751,13 +751,13 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
dbg_gen("test LEB %d:%d", vol->vol_id, lnum); dbg_gen("test LEB %d:%d", vol->vol_id, lnum);
if (lnum < 0 || lnum >= vol->reserved_pebs) if (!ubi_leb_valid(vol, lnum))
return -EINVAL; return -EINVAL;
if (vol->upd_marker) if (vol->upd_marker)
return -EBADF; return -EBADF;
return vol->eba_tbl[lnum] >= 0; return ubi_eba_is_mapped(vol, lnum);
} }
EXPORT_SYMBOL_GPL(ubi_is_mapped); EXPORT_SYMBOL_GPL(ubi_is_mapped);
......
...@@ -166,6 +166,17 @@ enum { ...@@ -166,6 +166,17 @@ enum {
POWER_CUT_VID_WRITE = 0x02, POWER_CUT_VID_WRITE = 0x02,
}; };
/**
* struct ubi_vid_io_buf - VID buffer used to read/write VID info to/from the
* flash.
* @hdr: a pointer to the VID header stored in buffer
* @buffer: underlying buffer
*/
struct ubi_vid_io_buf {
struct ubi_vid_hdr *hdr;
void *buffer;
};
/** /**
* struct ubi_wl_entry - wear-leveling entry. * struct ubi_wl_entry - wear-leveling entry.
* @u.rb: link in the corresponding (free/used) RB-tree * @u.rb: link in the corresponding (free/used) RB-tree
...@@ -266,6 +277,21 @@ struct ubi_fm_pool { ...@@ -266,6 +277,21 @@ struct ubi_fm_pool {
int max_size; int max_size;
}; };
/**
* struct ubi_eba_leb_desc - EBA logical eraseblock descriptor
* @lnum: the logical eraseblock number
* @pnum: the physical eraseblock where the LEB can be found
*
* This structure is here to hide EBA's internal from other part of the
* UBI implementation.
*
* One can query the position of a LEB by calling ubi_eba_get_ldesc().
*/
struct ubi_eba_leb_desc {
int lnum;
int pnum;
};
/** /**
* struct ubi_volume - UBI volume description data structure. * struct ubi_volume - UBI volume description data structure.
* @dev: device object to make use of the the Linux device model * @dev: device object to make use of the the Linux device model
...@@ -344,7 +370,7 @@ struct ubi_volume { ...@@ -344,7 +370,7 @@ struct ubi_volume {
long long upd_received; long long upd_received;
void *upd_buf; void *upd_buf;
int *eba_tbl; struct ubi_eba_table *eba_tbl;
unsigned int checked:1; unsigned int checked:1;
unsigned int corrupted:1; unsigned int corrupted:1;
unsigned int upd_marker:1; unsigned int upd_marker:1;
...@@ -724,6 +750,8 @@ struct ubi_ainf_volume { ...@@ -724,6 +750,8 @@ struct ubi_ainf_volume {
* @ec_sum: a temporary variable used when calculating @mean_ec * @ec_sum: a temporary variable used when calculating @mean_ec
* @ec_count: a temporary variable used when calculating @mean_ec * @ec_count: a temporary variable used when calculating @mean_ec
* @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects * @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects
* @ech: temporary EC header. Only available during scan
* @vidh: temporary VID buffer. Only available during scan
* *
* This data structure contains the result of attaching an MTD device and may * This data structure contains the result of attaching an MTD device and may
* be used by other UBI sub-systems to build final UBI data structures, further * be used by other UBI sub-systems to build final UBI data structures, further
...@@ -752,6 +780,8 @@ struct ubi_attach_info { ...@@ -752,6 +780,8 @@ struct ubi_attach_info {
uint64_t ec_sum; uint64_t ec_sum;
int ec_count; int ec_count;
struct kmem_cache *aeb_slab_cache; struct kmem_cache *aeb_slab_cache;
struct ubi_ec_hdr *ech;
struct ubi_vid_io_buf *vidb;
}; };
/** /**
...@@ -792,8 +822,12 @@ extern struct mutex ubi_devices_mutex; ...@@ -792,8 +822,12 @@ extern struct mutex ubi_devices_mutex;
extern struct blocking_notifier_head ubi_notifiers; extern struct blocking_notifier_head ubi_notifiers;
/* attach.c */ /* attach.c */
struct ubi_ainf_peb *ubi_alloc_aeb(struct ubi_attach_info *ai, int pnum,
int ec);
void ubi_free_aeb(struct ubi_attach_info *ai, struct ubi_ainf_peb *aeb);
int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum,
int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips); int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips);
struct ubi_ainf_volume *ubi_add_av(struct ubi_attach_info *ai, int vol_id);
struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai, struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai,
int vol_id); int vol_id);
void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av); void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av);
...@@ -835,7 +869,21 @@ void ubi_update_reserved(struct ubi_device *ubi); ...@@ -835,7 +869,21 @@ void ubi_update_reserved(struct ubi_device *ubi);
void ubi_calculate_reserved(struct ubi_device *ubi); void ubi_calculate_reserved(struct ubi_device *ubi);
int ubi_check_pattern(const void *buf, uint8_t patt, int size); int ubi_check_pattern(const void *buf, uint8_t patt, int size);
static inline bool ubi_leb_valid(struct ubi_volume *vol, int lnum)
{
return lnum >= 0 && lnum < vol->reserved_pebs;
}
/* eba.c */ /* eba.c */
struct ubi_eba_table *ubi_eba_create_table(struct ubi_volume *vol,
int nentries);
void ubi_eba_destroy_table(struct ubi_eba_table *tbl);
void ubi_eba_copy_table(struct ubi_volume *vol, struct ubi_eba_table *dst,
int nentries);
void ubi_eba_replace_table(struct ubi_volume *vol, struct ubi_eba_table *tbl);
void ubi_eba_get_ldesc(struct ubi_volume *vol, int lnum,
struct ubi_eba_leb_desc *ldesc);
bool ubi_eba_is_mapped(struct ubi_volume *vol, int lnum);
int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
int lnum); int lnum);
int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
...@@ -850,7 +898,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, ...@@ -850,7 +898,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
int lnum, const void *buf, int len); int lnum, const void *buf, int len);
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
struct ubi_vid_hdr *vid_hdr); struct ubi_vid_io_buf *vidb);
int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai); int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai);
unsigned long long ubi_next_sqnum(struct ubi_device *ubi); unsigned long long ubi_next_sqnum(struct ubi_device *ubi);
int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap, int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap,
...@@ -885,9 +933,9 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, ...@@ -885,9 +933,9 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
struct ubi_ec_hdr *ec_hdr); struct ubi_ec_hdr *ec_hdr);
int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
struct ubi_vid_hdr *vid_hdr, int verbose); struct ubi_vid_io_buf *vidb, int verbose);
int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
struct ubi_vid_hdr *vid_hdr); struct ubi_vid_io_buf *vidb);
/* build.c */ /* build.c */
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
...@@ -1008,44 +1056,68 @@ static inline void ubi_move_aeb_to_list(struct ubi_ainf_volume *av, ...@@ -1008,44 +1056,68 @@ static inline void ubi_move_aeb_to_list(struct ubi_ainf_volume *av,
} }
/** /**
* ubi_zalloc_vid_hdr - allocate a volume identifier header object. * ubi_init_vid_buf - Initialize a VID buffer
* @ubi: UBI device description object * @ubi: the UBI device
* @gfp_flags: GFP flags to allocate with * @vidb: the VID buffer to initialize
* * @buf: the underlying buffer
* This function returns a pointer to the newly allocated and zero-filled
* volume identifier header object in case of success and %NULL in case of
* failure.
*/ */
static inline struct ubi_vid_hdr * static inline void ubi_init_vid_buf(const struct ubi_device *ubi,
ubi_zalloc_vid_hdr(const struct ubi_device *ubi, gfp_t gfp_flags) struct ubi_vid_io_buf *vidb,
void *buf)
{ {
void *vid_hdr; if (buf)
memset(buf, 0, ubi->vid_hdr_alsize);
vid_hdr = kzalloc(ubi->vid_hdr_alsize, gfp_flags); vidb->buffer = buf;
if (!vid_hdr) vidb->hdr = buf + ubi->vid_hdr_shift;
}
/**
* ubi_init_vid_buf - Allocate a VID buffer
* @ubi: the UBI device
* @gfp_flags: GFP flags to use for the allocation
*/
static inline struct ubi_vid_io_buf *
ubi_alloc_vid_buf(const struct ubi_device *ubi, gfp_t gfp_flags)
{
struct ubi_vid_io_buf *vidb;
void *buf;
vidb = kzalloc(sizeof(*vidb), gfp_flags);
if (!vidb)
return NULL;
buf = kmalloc(ubi->vid_hdr_alsize, gfp_flags);
if (!buf) {
kfree(vidb);
return NULL; return NULL;
}
/* ubi_init_vid_buf(ubi, vidb, buf);
* VID headers may be stored at un-aligned flash offsets, so we shift
* the pointer. return vidb;
*/
return vid_hdr + ubi->vid_hdr_shift;
} }
/** /**
* ubi_free_vid_hdr - free a volume identifier header object. * ubi_free_vid_buf - Free a VID buffer
* @ubi: UBI device description object * @vidb: the VID buffer to free
* @vid_hdr: the object to free
*/ */
static inline void ubi_free_vid_hdr(const struct ubi_device *ubi, static inline void ubi_free_vid_buf(struct ubi_vid_io_buf *vidb)
struct ubi_vid_hdr *vid_hdr)
{ {
void *p = vid_hdr; if (!vidb)
if (!p)
return; return;
kfree(p - ubi->vid_hdr_shift); kfree(vidb->buffer);
kfree(vidb);
}
/**
* ubi_get_vid_hdr - Get the VID header attached to a VID buffer
* @vidb: VID buffer
*/
static inline struct ubi_vid_hdr *ubi_get_vid_hdr(struct ubi_vid_io_buf *vidb)
{
return vidb->hdr;
} }
/* /*
......
...@@ -138,7 +138,7 @@ static void vol_release(struct device *dev) ...@@ -138,7 +138,7 @@ static void vol_release(struct device *dev)
{ {
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
kfree(vol->eba_tbl); ubi_eba_replace_table(vol, NULL);
kfree(vol); kfree(vol);
} }
...@@ -158,6 +158,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -158,6 +158,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
int i, err, vol_id = req->vol_id, do_free = 1; int i, err, vol_id = req->vol_id, do_free = 1;
struct ubi_volume *vol; struct ubi_volume *vol;
struct ubi_vtbl_record vtbl_rec; struct ubi_vtbl_record vtbl_rec;
struct ubi_eba_table *eba_tbl = NULL;
dev_t dev; dev_t dev;
if (ubi->ro_mode) if (ubi->ro_mode)
...@@ -241,14 +242,13 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -241,14 +242,13 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
if (err) if (err)
goto out_acc; goto out_acc;
vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL); eba_tbl = ubi_eba_create_table(vol, vol->reserved_pebs);
if (!vol->eba_tbl) { if (IS_ERR(eba_tbl)) {
err = -ENOMEM; err = PTR_ERR(eba_tbl);
goto out_acc; goto out_acc;
} }
for (i = 0; i < vol->reserved_pebs; i++) ubi_eba_replace_table(vol, eba_tbl);
vol->eba_tbl[i] = UBI_LEB_UNMAPPED;
if (vol->vol_type == UBI_DYNAMIC_VOLUME) { if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
vol->used_ebs = vol->reserved_pebs; vol->used_ebs = vol->reserved_pebs;
...@@ -329,7 +329,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ...@@ -329,7 +329,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
cdev_del(&vol->cdev); cdev_del(&vol->cdev);
out_mapping: out_mapping:
if (do_free) if (do_free)
kfree(vol->eba_tbl); ubi_eba_destroy_table(eba_tbl);
out_acc: out_acc:
spin_lock(&ubi->volumes_lock); spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs -= vol->reserved_pebs; ubi->rsvd_pebs -= vol->reserved_pebs;
...@@ -427,10 +427,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ...@@ -427,10 +427,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
*/ */
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
{ {
int i, err, pebs, *new_mapping; int i, err, pebs;
struct ubi_volume *vol = desc->vol; struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi; struct ubi_device *ubi = vol->ubi;
struct ubi_vtbl_record vtbl_rec; struct ubi_vtbl_record vtbl_rec;
struct ubi_eba_table *new_eba_tbl = NULL;
int vol_id = vol->vol_id; int vol_id = vol->vol_id;
if (ubi->ro_mode) if (ubi->ro_mode)
...@@ -450,12 +451,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ...@@ -450,12 +451,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
if (reserved_pebs == vol->reserved_pebs) if (reserved_pebs == vol->reserved_pebs)
return 0; return 0;
new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL); new_eba_tbl = ubi_eba_create_table(vol, reserved_pebs);
if (!new_mapping) if (IS_ERR(new_eba_tbl))
return -ENOMEM; return PTR_ERR(new_eba_tbl);
for (i = 0; i < reserved_pebs; i++)
new_mapping[i] = UBI_LEB_UNMAPPED;
spin_lock(&ubi->volumes_lock); spin_lock(&ubi->volumes_lock);
if (vol->ref_count > 1) { if (vol->ref_count > 1) {
...@@ -481,10 +479,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ...@@ -481,10 +479,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
} }
ubi->avail_pebs -= pebs; ubi->avail_pebs -= pebs;
ubi->rsvd_pebs += pebs; ubi->rsvd_pebs += pebs;
for (i = 0; i < vol->reserved_pebs; i++) ubi_eba_copy_table(vol, new_eba_tbl, vol->reserved_pebs);
new_mapping[i] = vol->eba_tbl[i]; ubi_eba_replace_table(vol, new_eba_tbl);
kfree(vol->eba_tbl);
vol->eba_tbl = new_mapping;
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
} }
...@@ -498,10 +494,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ...@@ -498,10 +494,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
ubi->rsvd_pebs += pebs; ubi->rsvd_pebs += pebs;
ubi->avail_pebs -= pebs; ubi->avail_pebs -= pebs;
ubi_update_reserved(ubi); ubi_update_reserved(ubi);
for (i = 0; i < reserved_pebs; i++) ubi_eba_copy_table(vol, new_eba_tbl, reserved_pebs);
new_mapping[i] = vol->eba_tbl[i]; ubi_eba_replace_table(vol, new_eba_tbl);
kfree(vol->eba_tbl);
vol->eba_tbl = new_mapping;
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
} }
...@@ -543,7 +537,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ...@@ -543,7 +537,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
spin_unlock(&ubi->volumes_lock); spin_unlock(&ubi->volumes_lock);
} }
out_free: out_free:
kfree(new_mapping); kfree(new_eba_tbl);
return err; return err;
} }
......
...@@ -299,15 +299,18 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai, ...@@ -299,15 +299,18 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai,
int copy, void *vtbl) int copy, void *vtbl)
{ {
int err, tries = 0; int err, tries = 0;
struct ubi_vid_io_buf *vidb;
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
struct ubi_ainf_peb *new_aeb; struct ubi_ainf_peb *new_aeb;
dbg_gen("create volume table (copy #%d)", copy + 1); dbg_gen("create volume table (copy #%d)", copy + 1);
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL);
if (!vid_hdr) if (!vidb)
return -ENOMEM; return -ENOMEM;
vid_hdr = ubi_get_vid_hdr(vidb);
retry: retry:
new_aeb = ubi_early_get_peb(ubi, ai); new_aeb = ubi_early_get_peb(ubi, ai);
if (IS_ERR(new_aeb)) { if (IS_ERR(new_aeb)) {
...@@ -324,7 +327,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai, ...@@ -324,7 +327,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai,
vid_hdr->sqnum = cpu_to_be64(++ai->max_sqnum); vid_hdr->sqnum = cpu_to_be64(++ai->max_sqnum);
/* The EC header is already there, write the VID header */ /* The EC header is already there, write the VID header */
err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vid_hdr); err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vidb);
if (err) if (err)
goto write_error; goto write_error;
...@@ -338,8 +341,8 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai, ...@@ -338,8 +341,8 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai,
* of this LEB as it will be deleted and freed in 'ubi_add_to_av()'. * of this LEB as it will be deleted and freed in 'ubi_add_to_av()'.
*/ */
err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0); err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0);
kmem_cache_free(ai->aeb_slab_cache, new_aeb); ubi_free_aeb(ai, new_aeb);
ubi_free_vid_hdr(ubi, vid_hdr); ubi_free_vid_buf(vidb);
return err; return err;
write_error: write_error:
...@@ -351,9 +354,9 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai, ...@@ -351,9 +354,9 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai,
list_add(&new_aeb->u.list, &ai->erase); list_add(&new_aeb->u.list, &ai->erase);
goto retry; goto retry;
} }
kmem_cache_free(ai->aeb_slab_cache, new_aeb); ubi_free_aeb(ai, new_aeb);
out_free: out_free:
ubi_free_vid_hdr(ubi, vid_hdr); ubi_free_vid_buf(vidb);
return err; return err;
} }
......
...@@ -580,7 +580,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ...@@ -580,7 +580,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
* failure. * failure.
*/ */
static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
int vol_id, int lnum, int torture) int vol_id, int lnum, int torture, bool nested)
{ {
struct ubi_work *wl_wrk; struct ubi_work *wl_wrk;
...@@ -599,7 +599,10 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, ...@@ -599,7 +599,10 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
wl_wrk->lnum = lnum; wl_wrk->lnum = lnum;
wl_wrk->torture = torture; wl_wrk->torture = torture;
schedule_ubi_work(ubi, wl_wrk); if (nested)
__schedule_ubi_work(ubi, wl_wrk);
else
schedule_ubi_work(ubi, wl_wrk);
return 0; return 0;
} }
...@@ -644,11 +647,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -644,11 +647,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
int shutdown) int shutdown)
{ {
int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0; int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
int vol_id = -1, lnum = -1; int erase = 0, keep = 0, vol_id = -1, lnum = -1;
#ifdef CONFIG_MTD_UBI_FASTMAP #ifdef CONFIG_MTD_UBI_FASTMAP
int anchor = wrk->anchor; int anchor = wrk->anchor;
#endif #endif
struct ubi_wl_entry *e1, *e2; struct ubi_wl_entry *e1, *e2;
struct ubi_vid_io_buf *vidb;
struct ubi_vid_hdr *vid_hdr; struct ubi_vid_hdr *vid_hdr;
int dst_leb_clean = 0; int dst_leb_clean = 0;
...@@ -656,10 +660,13 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -656,10 +660,13 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
if (shutdown) if (shutdown)
return 0; return 0;
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS);
if (!vid_hdr) if (!vidb)
return -ENOMEM; return -ENOMEM;
vid_hdr = ubi_get_vid_hdr(vidb);
down_read(&ubi->fm_eba_sem);
mutex_lock(&ubi->move_mutex); mutex_lock(&ubi->move_mutex);
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
ubi_assert(!ubi->move_from && !ubi->move_to); ubi_assert(!ubi->move_from && !ubi->move_to);
...@@ -753,7 +760,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -753,7 +760,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* which is being moved was unmapped. * which is being moved was unmapped.
*/ */
err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); err = ubi_io_read_vid_hdr(ubi, e1->pnum, vidb, 0);
if (err && err != UBI_IO_BITFLIPS) { if (err && err != UBI_IO_BITFLIPS) {
dst_leb_clean = 1; dst_leb_clean = 1;
if (err == UBI_IO_FF) { if (err == UBI_IO_FF) {
...@@ -780,6 +787,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -780,6 +787,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
e1->pnum); e1->pnum);
scrubbing = 1; scrubbing = 1;
goto out_not_moved; goto out_not_moved;
} else if (ubi->fast_attach && err == UBI_IO_BAD_HDR_EBADMSG) {
/*
* While a full scan would detect interrupted erasures
* at attach time we can face them here when attached from
* Fastmap.
*/
dbg_wl("PEB %d has ECC errors, maybe from an interrupted erasure",
e1->pnum);
erase = 1;
goto out_not_moved;
} }
ubi_err(ubi, "error %d while reading VID header from PEB %d", ubi_err(ubi, "error %d while reading VID header from PEB %d",
...@@ -790,7 +807,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -790,7 +807,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
vol_id = be32_to_cpu(vid_hdr->vol_id); vol_id = be32_to_cpu(vid_hdr->vol_id);
lnum = be32_to_cpu(vid_hdr->lnum); lnum = be32_to_cpu(vid_hdr->lnum);
err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vidb);
if (err) { if (err) {
if (err == MOVE_CANCEL_RACE) { if (err == MOVE_CANCEL_RACE) {
/* /*
...@@ -815,6 +832,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -815,6 +832,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* Target PEB had bit-flips or write error - torture it. * Target PEB had bit-flips or write error - torture it.
*/ */
torture = 1; torture = 1;
keep = 1;
goto out_not_moved; goto out_not_moved;
} }
...@@ -847,7 +865,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -847,7 +865,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
if (scrubbing) if (scrubbing)
ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d",
e1->pnum, vol_id, lnum, e2->pnum); e1->pnum, vol_id, lnum, e2->pnum);
ubi_free_vid_hdr(ubi, vid_hdr); ubi_free_vid_buf(vidb);
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
if (!ubi->move_to_put) { if (!ubi->move_to_put) {
...@@ -879,6 +897,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -879,6 +897,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
dbg_wl("done"); dbg_wl("done");
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
return 0; return 0;
/* /*
...@@ -901,7 +920,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -901,7 +920,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
ubi->erroneous_peb_count += 1; ubi->erroneous_peb_count += 1;
} else if (scrubbing) } else if (scrubbing)
wl_tree_add(e1, &ubi->scrub); wl_tree_add(e1, &ubi->scrub);
else else if (keep)
wl_tree_add(e1, &ubi->used); wl_tree_add(e1, &ubi->used);
if (dst_leb_clean) { if (dst_leb_clean) {
wl_tree_add(e2, &ubi->free); wl_tree_add(e2, &ubi->free);
...@@ -913,7 +932,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -913,7 +932,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
ubi->wl_scheduled = 0; ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
ubi_free_vid_hdr(ubi, vid_hdr); ubi_free_vid_buf(vidb);
if (dst_leb_clean) { if (dst_leb_clean) {
ensure_wear_leveling(ubi, 1); ensure_wear_leveling(ubi, 1);
} else { } else {
...@@ -922,7 +941,14 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -922,7 +941,14 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
goto out_ro; goto out_ro;
} }
if (erase) {
err = do_sync_erase(ubi, e1, vol_id, lnum, 1);
if (err)
goto out_ro;
}
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
return 0; return 0;
out_error: out_error:
...@@ -937,13 +963,14 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -937,13 +963,14 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
ubi->move_to_put = ubi->wl_scheduled = 0; ubi->move_to_put = ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
ubi_free_vid_hdr(ubi, vid_hdr); ubi_free_vid_buf(vidb);
wl_entry_destroy(ubi, e1); wl_entry_destroy(ubi, e1);
wl_entry_destroy(ubi, e2); wl_entry_destroy(ubi, e2);
out_ro: out_ro:
ubi_ro_mode(ubi); ubi_ro_mode(ubi);
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
up_read(&ubi->fm_eba_sem);
ubi_assert(err != 0); ubi_assert(err != 0);
return err < 0 ? err : -EIO; return err < 0 ? err : -EIO;
...@@ -951,7 +978,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ...@@ -951,7 +978,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
ubi->wl_scheduled = 0; ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
mutex_unlock(&ubi->move_mutex); mutex_unlock(&ubi->move_mutex);
ubi_free_vid_hdr(ubi, vid_hdr); up_read(&ubi->fm_eba_sem);
ubi_free_vid_buf(vidb);
return 0; return 0;
} }
...@@ -1073,7 +1101,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk) ...@@ -1073,7 +1101,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
int err1; int err1;
/* Re-schedule the LEB for erasure */ /* Re-schedule the LEB for erasure */
err1 = schedule_erase(ubi, e, vol_id, lnum, 0); err1 = schedule_erase(ubi, e, vol_id, lnum, 0, false);
if (err1) { if (err1) {
wl_entry_destroy(ubi, e); wl_entry_destroy(ubi, e);
err = err1; err = err1;
...@@ -1254,7 +1282,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, ...@@ -1254,7 +1282,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
} }
spin_unlock(&ubi->wl_lock); spin_unlock(&ubi->wl_lock);
err = schedule_erase(ubi, e, vol_id, lnum, torture); err = schedule_erase(ubi, e, vol_id, lnum, torture, false);
if (err) { if (err) {
spin_lock(&ubi->wl_lock); spin_lock(&ubi->wl_lock);
wl_tree_add(e, &ubi->used); wl_tree_add(e, &ubi->used);
...@@ -1545,7 +1573,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) ...@@ -1545,7 +1573,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
e->pnum = aeb->pnum; e->pnum = aeb->pnum;
e->ec = aeb->ec; e->ec = aeb->ec;
ubi->lookuptbl[e->pnum] = e; ubi->lookuptbl[e->pnum] = e;
if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0, false)) {
wl_entry_destroy(ubi, e); wl_entry_destroy(ubi, e);
goto out_free; goto out_free;
} }
...@@ -1624,7 +1652,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) ...@@ -1624,7 +1652,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
e->ec = aeb->ec; e->ec = aeb->ec;
ubi_assert(!ubi->lookuptbl[e->pnum]); ubi_assert(!ubi->lookuptbl[e->pnum]);
ubi->lookuptbl[e->pnum] = e; ubi->lookuptbl[e->pnum] = e;
if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0, false)) {
wl_entry_destroy(ubi, e); wl_entry_destroy(ubi, e);
goto out_free; goto out_free;
} }
......
...@@ -301,6 +301,95 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -301,6 +301,95 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
return err; return err;
} }
static int do_tmpfile(struct inode *dir, struct dentry *dentry,
umode_t mode, struct inode **whiteout)
{
struct inode *inode;
struct ubifs_info *c = dir->i_sb->s_fs_info;
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1};
struct ubifs_budget_req ino_req = { .dirtied_ino = 1 };
struct ubifs_inode *ui, *dir_ui = ubifs_inode(dir);
int err, instantiated = 0;
/*
* Budget request settings: new dirty inode, new direntry,
* budget for dirtied inode will be released via writeback.
*/
dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
dentry, mode, dir->i_ino);
err = ubifs_budget_space(c, &req);
if (err)
return err;
err = ubifs_budget_space(c, &ino_req);
if (err) {
ubifs_release_budget(c, &req);
return err;
}
inode = ubifs_new_inode(c, dir, mode);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto out_budg;
}
ui = ubifs_inode(inode);
if (whiteout) {
init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
ubifs_assert(inode->i_op == &ubifs_file_inode_operations);
}
err = ubifs_init_security(dir, inode, &dentry->d_name);
if (err)
goto out_inode;
mutex_lock(&ui->ui_mutex);
insert_inode_hash(inode);
if (whiteout) {
mark_inode_dirty(inode);
drop_nlink(inode);
*whiteout = inode;
} else {
d_tmpfile(dentry, inode);
}
ubifs_assert(ui->dirty);
instantiated = 1;
mutex_unlock(&ui->ui_mutex);
mutex_lock(&dir_ui->ui_mutex);
err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0);
if (err)
goto out_cancel;
mutex_unlock(&dir_ui->ui_mutex);
ubifs_release_budget(c, &req);
return 0;
out_cancel:
mutex_unlock(&dir_ui->ui_mutex);
out_inode:
make_bad_inode(inode);
if (!instantiated)
iput(inode);
out_budg:
ubifs_release_budget(c, &req);
if (!instantiated)
ubifs_release_budget(c, &ino_req);
ubifs_err(c, "cannot create temporary file, error %d", err);
return err;
}
static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry,
umode_t mode)
{
return do_tmpfile(dir, dentry, mode, NULL);
}
/** /**
* vfs_dent_type - get VFS directory entry type. * vfs_dent_type - get VFS directory entry type.
* @type: UBIFS directory entry type * @type: UBIFS directory entry type
...@@ -927,37 +1016,43 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -927,37 +1016,43 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
} }
/** /**
* lock_3_inodes - a wrapper for locking three UBIFS inodes. * lock_4_inodes - a wrapper for locking three UBIFS inodes.
* @inode1: first inode * @inode1: first inode
* @inode2: second inode * @inode2: second inode
* @inode3: third inode * @inode3: third inode
* @inode4: fouth inode
* *
* This function is used for 'ubifs_rename()' and @inode1 may be the same as * This function is used for 'ubifs_rename()' and @inode1 may be the same as
* @inode2 whereas @inode3 may be %NULL. * @inode2 whereas @inode3 and @inode4 may be %NULL.
* *
* We do not implement any tricks to guarantee strict lock ordering, because * We do not implement any tricks to guarantee strict lock ordering, because
* VFS has already done it for us on the @i_mutex. So this is just a simple * VFS has already done it for us on the @i_mutex. So this is just a simple
* wrapper function. * wrapper function.
*/ */
static void lock_3_inodes(struct inode *inode1, struct inode *inode2, static void lock_4_inodes(struct inode *inode1, struct inode *inode2,
struct inode *inode3) struct inode *inode3, struct inode *inode4)
{ {
mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
if (inode2 != inode1) if (inode2 != inode1)
mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
if (inode3) if (inode3)
mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3); mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3);
if (inode4)
mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4);
} }
/** /**
* unlock_3_inodes - a wrapper for unlocking three UBIFS inodes for rename. * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename.
* @inode1: first inode * @inode1: first inode
* @inode2: second inode * @inode2: second inode
* @inode3: third inode * @inode3: third inode
* @inode4: fouth inode
*/ */
static void unlock_3_inodes(struct inode *inode1, struct inode *inode2, static void unlock_4_inodes(struct inode *inode1, struct inode *inode2,
struct inode *inode3) struct inode *inode3, struct inode *inode4)
{ {
if (inode4)
mutex_unlock(&ubifs_inode(inode4)->ui_mutex);
if (inode3) if (inode3)
mutex_unlock(&ubifs_inode(inode3)->ui_mutex); mutex_unlock(&ubifs_inode(inode3)->ui_mutex);
if (inode1 != inode2) if (inode1 != inode2)
...@@ -972,7 +1067,9 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -972,7 +1067,9 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct ubifs_info *c = old_dir->i_sb->s_fs_info; struct ubifs_info *c = old_dir->i_sb->s_fs_info;
struct inode *old_inode = d_inode(old_dentry); struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry); struct inode *new_inode = d_inode(new_dentry);
struct inode *whiteout = NULL;
struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode); struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode);
struct ubifs_inode *whiteout_ui = NULL;
int err, release, sync = 0, move = (new_dir != old_dir); int err, release, sync = 0, move = (new_dir != old_dir);
int is_dir = S_ISDIR(old_inode->i_mode); int is_dir = S_ISDIR(old_inode->i_mode);
int unlink = !!new_inode; int unlink = !!new_inode;
...@@ -997,15 +1094,13 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -997,15 +1094,13 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
* separately. * separately.
*/ */
dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu", dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x",
old_dentry, old_inode->i_ino, old_dir->i_ino, old_dentry, old_inode->i_ino, old_dir->i_ino,
new_dentry, new_dir->i_ino); new_dentry, new_dir->i_ino, flags);
ubifs_assert(inode_is_locked(old_dir));
ubifs_assert(inode_is_locked(new_dir));
if (unlink) if (unlink)
ubifs_assert(inode_is_locked(new_inode)); ubifs_assert(inode_is_locked(new_inode));
if (unlink && is_dir) { if (unlink && is_dir) {
err = check_dir_empty(c, new_inode); err = check_dir_empty(c, new_inode);
if (err) if (err)
...@@ -1021,7 +1116,32 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1021,7 +1116,32 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
return err; return err;
} }
lock_3_inodes(old_dir, new_dir, new_inode); if (flags & RENAME_WHITEOUT) {
union ubifs_dev_desc *dev = NULL;
dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
if (!dev) {
ubifs_release_budget(c, &req);
ubifs_release_budget(c, &ino_req);
return -ENOMEM;
}
err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout);
if (err) {
ubifs_release_budget(c, &req);
ubifs_release_budget(c, &ino_req);
kfree(dev);
return err;
}
whiteout->i_state |= I_LINKABLE;
whiteout_ui = ubifs_inode(whiteout);
whiteout_ui->data = dev;
whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0));
ubifs_assert(!whiteout_ui->dirty);
}
lock_4_inodes(old_dir, new_dir, new_inode, whiteout);
/* /*
* Like most other Unix systems, set the @i_ctime for inodes on a * Like most other Unix systems, set the @i_ctime for inodes on a
...@@ -1091,12 +1211,34 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1091,12 +1211,34 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (unlink && IS_SYNC(new_inode)) if (unlink && IS_SYNC(new_inode))
sync = 1; sync = 1;
} }
err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry,
if (whiteout) {
struct ubifs_budget_req wht_req = { .dirtied_ino = 1,
.dirtied_ino_d = \
ALIGN(ubifs_inode(whiteout)->data_len, 8) };
err = ubifs_budget_space(c, &wht_req);
if (err) {
ubifs_release_budget(c, &req);
ubifs_release_budget(c, &ino_req);
kfree(whiteout_ui->data);
whiteout_ui->data_len = 0;
iput(whiteout);
return err;
}
inc_nlink(whiteout);
mark_inode_dirty(whiteout);
whiteout->i_state &= ~I_LINKABLE;
iput(whiteout);
}
err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, whiteout,
sync); sync);
if (err) if (err)
goto out_cancel; goto out_cancel;
unlock_3_inodes(old_dir, new_dir, new_inode); unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
ubifs_release_budget(c, &req); ubifs_release_budget(c, &req);
mutex_lock(&old_inode_ui->ui_mutex); mutex_lock(&old_inode_ui->ui_mutex);
...@@ -1129,12 +1271,74 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1129,12 +1271,74 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
inc_nlink(old_dir); inc_nlink(old_dir);
} }
} }
unlock_3_inodes(old_dir, new_dir, new_inode); if (whiteout) {
drop_nlink(whiteout);
iput(whiteout);
}
unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
ubifs_release_budget(c, &ino_req); ubifs_release_budget(c, &ino_req);
ubifs_release_budget(c, &req); ubifs_release_budget(c, &req);
return err; return err;
} }
static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
struct ubifs_info *c = old_dir->i_sb->s_fs_info;
struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
.dirtied_ino = 2 };
int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
struct inode *fst_inode = d_inode(old_dentry);
struct inode *snd_inode = d_inode(new_dentry);
struct timespec time;
int err;
ubifs_assert(fst_inode && snd_inode);
lock_4_inodes(old_dir, new_dir, NULL, NULL);
time = ubifs_current_time(old_dir);
fst_inode->i_ctime = time;
snd_inode->i_ctime = time;
old_dir->i_mtime = old_dir->i_ctime = time;
new_dir->i_mtime = new_dir->i_ctime = time;
if (old_dir != new_dir) {
if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
inc_nlink(new_dir);
drop_nlink(old_dir);
}
else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
drop_nlink(new_dir);
inc_nlink(old_dir);
}
}
err = ubifs_jnl_xrename(c, old_dir, old_dentry, new_dir, new_dentry,
sync);
unlock_4_inodes(old_dir, new_dir, NULL, NULL);
ubifs_release_budget(c, &req);
return err;
}
static int ubifs_rename2(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
{
if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE))
return -EINVAL;
ubifs_assert(inode_is_locked(old_dir));
ubifs_assert(inode_is_locked(new_dir));
if (flags & RENAME_EXCHANGE)
return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry);
return ubifs_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
}
int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry, int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat) struct kstat *stat)
{ {
...@@ -1183,13 +1387,14 @@ const struct inode_operations ubifs_dir_inode_operations = { ...@@ -1183,13 +1387,14 @@ const struct inode_operations ubifs_dir_inode_operations = {
.mkdir = ubifs_mkdir, .mkdir = ubifs_mkdir,
.rmdir = ubifs_rmdir, .rmdir = ubifs_rmdir,
.mknod = ubifs_mknod, .mknod = ubifs_mknod,
.rename = ubifs_rename, .rename = ubifs_rename2,
.setattr = ubifs_setattr, .setattr = ubifs_setattr,
.getattr = ubifs_getattr, .getattr = ubifs_getattr,
.listxattr = ubifs_listxattr, .listxattr = ubifs_listxattr,
#ifdef CONFIG_UBIFS_ATIME_SUPPORT #ifdef CONFIG_UBIFS_ATIME_SUPPORT
.update_time = ubifs_update_time, .update_time = ubifs_update_time,
#endif #endif
.tmpfile = ubifs_tmpfile,
}; };
const struct file_operations ubifs_dir_operations = { const struct file_operations ubifs_dir_operations = {
......
...@@ -1397,7 +1397,7 @@ int ubifs_update_time(struct inode *inode, struct timespec *time, ...@@ -1397,7 +1397,7 @@ int ubifs_update_time(struct inode *inode, struct timespec *time,
#endif #endif
/** /**
* update_ctime - update mtime and ctime of an inode. * update_mctime - update mtime and ctime of an inode.
* @inode: inode to update * @inode: inode to update
* *
* This function updates mtime and ctime of the inode if it is not equivalent to * This function updates mtime and ctime of the inode if it is not equivalent to
......
...@@ -113,7 +113,7 @@ static int switch_gc_head(struct ubifs_info *c) ...@@ -113,7 +113,7 @@ static int switch_gc_head(struct ubifs_info *c)
* data_nodes_cmp - compare 2 data nodes. * data_nodes_cmp - compare 2 data nodes.
* @priv: UBIFS file-system description object * @priv: UBIFS file-system description object
* @a: first data node * @a: first data node
* @a: second data node * @b: second data node
* *
* This function compares data nodes @a and @b. Returns %1 if @a has greater * This function compares data nodes @a and @b. Returns %1 if @a has greater
* inode or block number, and %-1 otherwise. * inode or block number, and %-1 otherwise.
......
...@@ -907,6 +907,147 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode) ...@@ -907,6 +907,147 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode)
return err; return err;
} }
/**
* ubifs_jnl_xrename - cross rename two directory entries.
* @c: UBIFS file-system description object
* @fst_dir: parent inode of 1st directory entry to exchange
* @fst_dentry: 1st directory entry to exchange
* @snd_dir: parent inode of 2nd directory entry to exchange
* @snd_dentry: 2nd directory entry to exchange
* @sync: non-zero if the write-buffer has to be synchronized
*
* This function implements the cross rename operation which may involve
* writing 2 inodes and 2 directory entries. It marks the written inodes as clean
* and returns zero on success. In case of failure, a negative error code is
* returned.
*/
int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
const struct dentry *fst_dentry,
const struct inode *snd_dir,
const struct dentry *snd_dentry, int sync)
{
union ubifs_key key;
struct ubifs_dent_node *dent1, *dent2;
int err, dlen1, dlen2, lnum, offs, len, plen = UBIFS_INO_NODE_SZ;
int aligned_dlen1, aligned_dlen2;
int twoparents = (fst_dir != snd_dir);
const struct inode *fst_inode = d_inode(fst_dentry);
const struct inode *snd_inode = d_inode(snd_dentry);
void *p;
dbg_jnl("dent '%pd' in dir ino %lu between dent '%pd' in dir ino %lu",
fst_dentry, fst_dir->i_ino, snd_dentry, snd_dir->i_ino);
ubifs_assert(ubifs_inode(fst_dir)->data_len == 0);
ubifs_assert(ubifs_inode(snd_dir)->data_len == 0);
ubifs_assert(mutex_is_locked(&ubifs_inode(fst_dir)->ui_mutex));
ubifs_assert(mutex_is_locked(&ubifs_inode(snd_dir)->ui_mutex));
dlen1 = UBIFS_DENT_NODE_SZ + snd_dentry->d_name.len + 1;
dlen2 = UBIFS_DENT_NODE_SZ + fst_dentry->d_name.len + 1;
aligned_dlen1 = ALIGN(dlen1, 8);
aligned_dlen2 = ALIGN(dlen2, 8);
len = aligned_dlen1 + aligned_dlen2 + ALIGN(plen, 8);
if (twoparents)
len += plen;
dent1 = kmalloc(len, GFP_NOFS);
if (!dent1)
return -ENOMEM;
/* Make reservation before allocating sequence numbers */
err = make_reservation(c, BASEHD, len);
if (err)
goto out_free;
/* Make new dent for 1st entry */
dent1->ch.node_type = UBIFS_DENT_NODE;
dent_key_init_flash(c, &dent1->key, snd_dir->i_ino, &snd_dentry->d_name);
dent1->inum = cpu_to_le64(fst_inode->i_ino);
dent1->type = get_dent_type(fst_inode->i_mode);
dent1->nlen = cpu_to_le16(snd_dentry->d_name.len);
memcpy(dent1->name, snd_dentry->d_name.name, snd_dentry->d_name.len);
dent1->name[snd_dentry->d_name.len] = '\0';
zero_dent_node_unused(dent1);
ubifs_prep_grp_node(c, dent1, dlen1, 0);
/* Make new dent for 2nd entry */
dent2 = (void *)dent1 + aligned_dlen1;
dent2->ch.node_type = UBIFS_DENT_NODE;
dent_key_init_flash(c, &dent2->key, fst_dir->i_ino, &fst_dentry->d_name);
dent2->inum = cpu_to_le64(snd_inode->i_ino);
dent2->type = get_dent_type(snd_inode->i_mode);
dent2->nlen = cpu_to_le16(fst_dentry->d_name.len);
memcpy(dent2->name, fst_dentry->d_name.name, fst_dentry->d_name.len);
dent2->name[fst_dentry->d_name.len] = '\0';
zero_dent_node_unused(dent2);
ubifs_prep_grp_node(c, dent2, dlen2, 0);
p = (void *)dent2 + aligned_dlen2;
if (!twoparents)
pack_inode(c, p, fst_dir, 1);
else {
pack_inode(c, p, fst_dir, 0);
p += ALIGN(plen, 8);
pack_inode(c, p, snd_dir, 1);
}
err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync);
if (err)
goto out_release;
if (!sync) {
struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
ubifs_wbuf_add_ino_nolock(wbuf, fst_dir->i_ino);
ubifs_wbuf_add_ino_nolock(wbuf, snd_dir->i_ino);
}
release_head(c, BASEHD);
dent_key_init(c, &key, snd_dir->i_ino, &snd_dentry->d_name);
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, &snd_dentry->d_name);
if (err)
goto out_ro;
offs += aligned_dlen1;
dent_key_init(c, &key, fst_dir->i_ino, &fst_dentry->d_name);
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, &fst_dentry->d_name);
if (err)
goto out_ro;
offs += aligned_dlen2;
ino_key_init(c, &key, fst_dir->i_ino);
err = ubifs_tnc_add(c, &key, lnum, offs, plen);
if (err)
goto out_ro;
if (twoparents) {
offs += ALIGN(plen, 8);
ino_key_init(c, &key, snd_dir->i_ino);
err = ubifs_tnc_add(c, &key, lnum, offs, plen);
if (err)
goto out_ro;
}
finish_reservation(c);
mark_inode_clean(c, ubifs_inode(fst_dir));
if (twoparents)
mark_inode_clean(c, ubifs_inode(snd_dir));
kfree(dent1);
return 0;
out_release:
release_head(c, BASEHD);
out_ro:
ubifs_ro_mode(c, err);
finish_reservation(c);
out_free:
kfree(dent1);
return err;
}
/** /**
* ubifs_jnl_rename - rename a directory entry. * ubifs_jnl_rename - rename a directory entry.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
...@@ -917,14 +1058,15 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode) ...@@ -917,14 +1058,15 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode)
* @sync: non-zero if the write-buffer has to be synchronized * @sync: non-zero if the write-buffer has to be synchronized
* *
* This function implements the re-name operation which may involve writing up * This function implements the re-name operation which may involve writing up
* to 3 inodes and 2 directory entries. It marks the written inodes as clean * to 4 inodes and 2 directory entries. It marks the written inodes as clean
* and returns zero on success. In case of failure, a negative error code is * and returns zero on success. In case of failure, a negative error code is
* returned. * returned.
*/ */
int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
const struct dentry *old_dentry, const struct dentry *old_dentry,
const struct inode *new_dir, const struct inode *new_dir,
const struct dentry *new_dentry, int sync) const struct dentry *new_dentry,
const struct inode *whiteout, int sync)
{ {
void *p; void *p;
union ubifs_key key; union ubifs_key key;
...@@ -958,7 +1100,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, ...@@ -958,7 +1100,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
aligned_dlen1 = ALIGN(dlen1, 8); aligned_dlen1 = ALIGN(dlen1, 8);
aligned_dlen2 = ALIGN(dlen2, 8); aligned_dlen2 = ALIGN(dlen2, 8);
len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8); len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8);
if (old_dir != new_dir) if (move)
len += plen; len += plen;
dent = kmalloc(len, GFP_NOFS); dent = kmalloc(len, GFP_NOFS);
if (!dent) if (!dent)
...@@ -980,13 +1122,19 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, ...@@ -980,13 +1122,19 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
zero_dent_node_unused(dent); zero_dent_node_unused(dent);
ubifs_prep_grp_node(c, dent, dlen1, 0); ubifs_prep_grp_node(c, dent, dlen1, 0);
/* Make deletion dent */
dent2 = (void *)dent + aligned_dlen1; dent2 = (void *)dent + aligned_dlen1;
dent2->ch.node_type = UBIFS_DENT_NODE; dent2->ch.node_type = UBIFS_DENT_NODE;
dent_key_init_flash(c, &dent2->key, old_dir->i_ino, dent_key_init_flash(c, &dent2->key, old_dir->i_ino,
&old_dentry->d_name); &old_dentry->d_name);
dent2->inum = 0;
dent2->type = DT_UNKNOWN; if (whiteout) {
dent2->inum = cpu_to_le64(whiteout->i_ino);
dent2->type = get_dent_type(whiteout->i_mode);
} else {
/* Make deletion dent */
dent2->inum = 0;
dent2->type = DT_UNKNOWN;
}
dent2->nlen = cpu_to_le16(old_dentry->d_name.len); dent2->nlen = cpu_to_le16(old_dentry->d_name.len);
memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len); memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len);
dent2->name[old_dentry->d_name.len] = '\0'; dent2->name[old_dentry->d_name.len] = '\0';
...@@ -1035,16 +1183,26 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, ...@@ -1035,16 +1183,26 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
if (err) if (err)
goto out_ro; goto out_ro;
err = ubifs_add_dirt(c, lnum, dlen2); offs += aligned_dlen1;
if (err) if (whiteout) {
goto out_ro; dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name);
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, &old_dentry->d_name);
if (err)
goto out_ro;
dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); ubifs_delete_orphan(c, whiteout->i_ino);
err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); } else {
if (err) err = ubifs_add_dirt(c, lnum, dlen2);
goto out_ro; if (err)
goto out_ro;
dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name);
err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name);
if (err)
goto out_ro;
}
offs += aligned_dlen1 + aligned_dlen2; offs += aligned_dlen2;
if (new_inode) { if (new_inode) {
ino_key_init(c, &key, new_inode->i_ino); ino_key_init(c, &key, new_inode->i_ino);
err = ubifs_tnc_add(c, &key, lnum, offs, ilen); err = ubifs_tnc_add(c, &key, lnum, offs, ilen);
...@@ -1058,7 +1216,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, ...@@ -1058,7 +1216,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
if (err) if (err)
goto out_ro; goto out_ro;
if (old_dir != new_dir) { if (move) {
offs += ALIGN(plen, 8); offs += ALIGN(plen, 8);
ino_key_init(c, &key, new_dir->i_ino); ino_key_init(c, &key, new_dir->i_ino);
err = ubifs_tnc_add(c, &key, lnum, offs, plen); err = ubifs_tnc_add(c, &key, lnum, offs, plen);
......
...@@ -636,7 +636,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, ...@@ -636,7 +636,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
/** /**
* ubifs_get_lp_stats - get lprops statistics. * ubifs_get_lp_stats - get lprops statistics.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
* @st: return statistics * @lst: return statistics
*/ */
void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst) void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst)
{ {
......
...@@ -34,7 +34,6 @@ static int dbg_populate_lsave(struct ubifs_info *c); ...@@ -34,7 +34,6 @@ static int dbg_populate_lsave(struct ubifs_info *c);
/** /**
* first_dirty_cnode - find first dirty cnode. * first_dirty_cnode - find first dirty cnode.
* @c: UBIFS file-system description object
* @nnode: nnode at which to start * @nnode: nnode at which to start
* *
* This function returns the first dirty cnode or %NULL if there is not one. * This function returns the first dirty cnode or %NULL if there is not one.
...@@ -1623,7 +1622,6 @@ static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum, ...@@ -1623,7 +1622,6 @@ static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum,
* dbg_check_ltab_lnum - check the ltab for a LPT LEB number. * dbg_check_ltab_lnum - check the ltab for a LPT LEB number.
* @c: the UBIFS file-system description object * @c: the UBIFS file-system description object
* @lnum: LEB number where node was written * @lnum: LEB number where node was written
* @offs: offset where node was written
* *
* This function returns %0 on success and a negative error code on failure. * This function returns %0 on success and a negative error code on failure.
*/ */
...@@ -1870,7 +1868,7 @@ int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len) ...@@ -1870,7 +1868,7 @@ int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len)
} }
/** /**
* ubifs_dump_lpt_leb - dump an LPT LEB. * dump_lpt_leb - dump an LPT LEB.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
* @lnum: LEB number to dump * @lnum: LEB number to dump
* *
......
...@@ -267,7 +267,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) ...@@ -267,7 +267,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
* replay_entries_cmp - compare 2 replay entries. * replay_entries_cmp - compare 2 replay entries.
* @priv: UBIFS file-system description object * @priv: UBIFS file-system description object
* @a: first replay entry * @a: first replay entry
* @a: second replay entry * @b: second replay entry
* *
* This is a comparios function for 'list_sort()' which compares 2 replay * This is a comparios function for 'list_sort()' which compares 2 replay
* entries @a and @b by comparing their sequence numer. Returns %1 if @a has * entries @a and @b by comparing their sequence numer. Returns %1 if @a has
......
...@@ -157,6 +157,7 @@ enum { ...@@ -157,6 +157,7 @@ enum {
WB_MUTEX_1 = 0, WB_MUTEX_1 = 0,
WB_MUTEX_2 = 1, WB_MUTEX_2 = 1,
WB_MUTEX_3 = 2, WB_MUTEX_3 = 2,
WB_MUTEX_4 = 3,
}; };
/* /*
...@@ -1520,10 +1521,15 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode, ...@@ -1520,10 +1521,15 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
const union ubifs_key *key, const void *buf, int len); const union ubifs_key *key, const void *buf, int len);
int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode); int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);
int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode); int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode);
int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
const struct dentry *fst_dentry,
const struct inode *snd_dir,
const struct dentry *snd_dentry, int sync);
int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
const struct dentry *old_dentry, const struct dentry *old_dentry,
const struct inode *new_dir, const struct inode *new_dir,
const struct dentry *new_dentry, int sync); const struct dentry *new_dentry,
const struct inode *whiteout, int sync);
int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode, int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
loff_t old_size, loff_t new_size); loff_t old_size, loff_t new_size);
int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
......
...@@ -200,6 +200,7 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, ...@@ -200,6 +200,7 @@ static int change_xattr(struct ubifs_info *c, struct inode *host,
struct ubifs_inode *host_ui = ubifs_inode(host); struct ubifs_inode *host_ui = ubifs_inode(host);
struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_inode *ui = ubifs_inode(inode);
void *buf = NULL; void *buf = NULL;
int old_size;
struct ubifs_budget_req req = { .dirtied_ino = 2, struct ubifs_budget_req req = { .dirtied_ino = 2,
.dirtied_ino_d = ALIGN(size, 8) + ALIGN(host_ui->data_len, 8) }; .dirtied_ino_d = ALIGN(size, 8) + ALIGN(host_ui->data_len, 8) };
...@@ -217,12 +218,13 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, ...@@ -217,12 +218,13 @@ static int change_xattr(struct ubifs_info *c, struct inode *host,
kfree(ui->data); kfree(ui->data);
ui->data = buf; ui->data = buf;
inode->i_size = ui->ui_size = size; inode->i_size = ui->ui_size = size;
old_size = ui->data_len;
ui->data_len = size; ui->data_len = size;
mutex_unlock(&ui->ui_mutex); mutex_unlock(&ui->ui_mutex);
mutex_lock(&host_ui->ui_mutex); mutex_lock(&host_ui->ui_mutex);
host->i_ctime = ubifs_current_time(host); host->i_ctime = ubifs_current_time(host);
host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len); host_ui->xattr_size -= CALC_XATTR_BYTES(old_size);
host_ui->xattr_size += CALC_XATTR_BYTES(size); host_ui->xattr_size += CALC_XATTR_BYTES(size);
/* /*
...@@ -241,7 +243,7 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, ...@@ -241,7 +243,7 @@ static int change_xattr(struct ubifs_info *c, struct inode *host,
out_cancel: out_cancel:
host_ui->xattr_size -= CALC_XATTR_BYTES(size); host_ui->xattr_size -= CALC_XATTR_BYTES(size);
host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len); host_ui->xattr_size += CALC_XATTR_BYTES(old_size);
mutex_unlock(&host_ui->ui_mutex); mutex_unlock(&host_ui->ui_mutex);
make_bad_inode(inode); make_bad_inode(inode);
out_free: out_free:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment