Commit e88d6e10 authored by Artem Bityutskiy's avatar Artem Bityutskiy
Browse files

UBI: do not use vmalloc on I/O path



Similar reason as in case of the previous patch: it causes
deadlocks if a filesystem with writeback support works on top
of UBI. So pre-allocate needed buffers when attaching MTD device.
We also need mutexes to protect the buffers, but they do not
cause much contantion because they are used in recovery, torture,
and WL copy routines, which are called seldom.

Signed-off-by: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
parent 33818bbb
Loading
Loading
Loading
Loading
+27 −1
Original line number Diff line number Diff line
@@ -583,6 +583,22 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset,
	if (err)
		goto out_free;

	mutex_init(&ubi->buf_mutex);
	ubi->peb_buf1 = vmalloc(ubi->peb_size);
	if (!ubi->peb_buf1)
		goto out_free;

	ubi->peb_buf2 = vmalloc(ubi->peb_size);
	if (!ubi->peb_buf2)
		 goto out_free;

#ifdef CONFIG_MTD_UBI_DEBUG
	mutex_init(&ubi->dbg_buf_mutex);
	ubi->dbg_peb_buf = vmalloc(ubi->peb_size);
	if (!ubi->dbg_peb_buf)
		 goto out_free;
#endif

	err = attach_by_scanning(ubi);
	if (err) {
		dbg_err("failed to attach by scanning, error %d", err);
@@ -630,6 +646,11 @@ out_detach:
	ubi_wl_close(ubi);
	vfree(ubi->vtbl);
out_free:
	vfree(ubi->peb_buf1);
	vfree(ubi->peb_buf2);
#ifdef CONFIG_MTD_UBI_DEBUG
	vfree(ubi->dbg_peb_buf);
#endif
	kfree(ubi);
out_mtd:
	put_mtd_device(mtd);
@@ -651,6 +672,11 @@ static void detach_mtd_dev(struct ubi_device *ubi)
	ubi_wl_close(ubi);
	vfree(ubi->vtbl);
	put_mtd_device(ubi->mtd);
	vfree(ubi->peb_buf1);
	vfree(ubi->peb_buf2);
#ifdef CONFIG_MTD_UBI_DEBUG
	vfree(ubi->dbg_peb_buf);
#endif
	kfree(ubi_devices[ubi_num]);
	ubi_devices[ubi_num] = NULL;
	ubi_devices_cnt -= 1;
+24 −46
Original line number Diff line number Diff line
@@ -495,16 +495,18 @@ static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
	int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0;
	struct ubi_volume *vol = ubi->volumes[idx];
	struct ubi_vid_hdr *vid_hdr;
	unsigned char *new_buf;

	vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
	if (!vid_hdr) {
		return -ENOMEM;
	}

	mutex_lock(&ubi->buf_mutex);

retry:
	new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN);
	if (new_pnum < 0) {
		mutex_unlock(&ubi->buf_mutex);
		ubi_free_vid_hdr(ubi, vid_hdr);
		return new_pnum;
	}
@@ -524,31 +526,22 @@ retry:
		goto write_error;

	data_size = offset + len;
	new_buf = vmalloc(data_size);
	if (!new_buf) {
		err = -ENOMEM;
		goto out_put;
	}
	memset(new_buf + offset, 0xFF, len);
	memset(ubi->peb_buf1 + offset, 0xFF, len);

	/* Read everything before the area where the write failure happened */
	if (offset > 0) {
		err = ubi_io_read_data(ubi, new_buf, pnum, 0, offset);
		if (err && err != UBI_IO_BITFLIPS) {
			vfree(new_buf);
		err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset);
		if (err && err != UBI_IO_BITFLIPS)
			goto out_put;
	}
	}

	memcpy(new_buf + offset, buf, len);
	memcpy(ubi->peb_buf1 + offset, buf, len);

	err = ubi_io_write_data(ubi, new_buf, new_pnum, 0, data_size);
	if (err) {
		vfree(new_buf);
	err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size);
	if (err)
		goto write_error;
	}

	vfree(new_buf);
	mutex_unlock(&ubi->buf_mutex);
	ubi_free_vid_hdr(ubi, vid_hdr);

	vol->eba_tbl[lnum] = new_pnum;
@@ -558,6 +551,7 @@ retry:
	return 0;

out_put:
	mutex_unlock(&ubi->buf_mutex);
	ubi_wl_put_peb(ubi, new_pnum, 1);
	ubi_free_vid_hdr(ubi, vid_hdr);
	return err;
@@ -570,6 +564,7 @@ write_error:
	ubi_warn("failed to write to PEB %d", new_pnum);
	ubi_wl_put_peb(ubi, new_pnum, 1);
	if (++tries > UBI_IO_RETRIES) {
		mutex_unlock(&ubi->buf_mutex);
		ubi_free_vid_hdr(ubi, vid_hdr);
		return err;
	}
@@ -965,7 +960,6 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
	int err, vol_id, lnum, data_size, aldata_size, pnum, idx;
	struct ubi_volume *vol;
	uint32_t crc;
	void *buf, *buf1 = NULL;

	vol_id = be32_to_cpu(vid_hdr->vol_id);
	lnum = be32_to_cpu(vid_hdr->lnum);
@@ -979,19 +973,15 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
		data_size = aldata_size =
			    ubi->leb_size - be32_to_cpu(vid_hdr->data_pad);

	buf = vmalloc(aldata_size);
	if (!buf)
		return -ENOMEM;

	/*
	 * We do not want anybody to write to this logical eraseblock while we
	 * are moving it, so we lock it.
	 */
	err = leb_write_lock(ubi, vol_id, lnum);
	if (err) {
		vfree(buf);
	if (err)
		return err;
	}

	mutex_lock(&ubi->buf_mutex);

	/*
	 * But the logical eraseblock might have been put by this time.
@@ -1023,7 +1013,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
	/* OK, now the LEB is locked and we can safely start moving it */

	dbg_eba("read %d bytes of data", aldata_size);
	err = ubi_io_read_data(ubi, buf, from, 0, aldata_size);
	err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size);
	if (err && err != UBI_IO_BITFLIPS) {
		ubi_warn("error %d while reading data from PEB %d",
			 err, from);
@@ -1042,10 +1032,10 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
	 */
	if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
		aldata_size = data_size =
				ubi_calc_data_len(ubi, buf, data_size);
			ubi_calc_data_len(ubi, ubi->peb_buf1, data_size);

	cond_resched();
	crc = crc32(UBI_CRC32_INIT, buf, data_size);
	crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size);
	cond_resched();

	/*
@@ -1076,23 +1066,18 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
	}

	if (data_size > 0) {
		err = ubi_io_write_data(ubi, buf, to, 0, aldata_size);
		err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
		if (err)
			goto out_unlock;

		cond_resched();

		/*
		 * We've written the data and are going to read it back to make
		 * sure it was written correctly.
		 */
		buf1 = vmalloc(aldata_size);
		if (!buf1) {
			err = -ENOMEM;
			goto out_unlock;
		}

		cond_resched();

		err = ubi_io_read_data(ubi, buf1, to, 0, aldata_size);
		err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size);
		if (err) {
			if (err != UBI_IO_BITFLIPS)
				ubi_warn("cannot read data back from PEB %d",
@@ -1102,7 +1087,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,

		cond_resched();

		if (memcmp(buf, buf1, aldata_size)) {
		if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) {
			ubi_warn("read data back from PEB %d - it is different",
				 to);
			goto out_unlock;
@@ -1112,16 +1097,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
	ubi_assert(vol->eba_tbl[lnum] == from);
	vol->eba_tbl[lnum] = to;

	leb_write_unlock(ubi, vol_id, lnum);
	vfree(buf);
	vfree(buf1);

	return 0;

out_unlock:
	mutex_unlock(&ubi->buf_mutex);
	leb_write_unlock(ubi, vol_id, lnum);
	vfree(buf);
	vfree(buf1);
	return err;
}

+29 −37
Original line number Diff line number Diff line
@@ -98,8 +98,8 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum);
static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
				  const struct ubi_vid_hdr *vid_hdr);
static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum,
				 int offset, int len);
static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
				 int len);
#else
#define paranoid_check_not_bad(ubi, pnum) 0
#define paranoid_check_peb_ec_hdr(ubi, pnum)  0
@@ -202,8 +202,8 @@ retry:
 * Note, in case of an error, it is possible that something was still written
 * to the flash media, but may be some garbage.
 */
int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum,
		 int offset, int len)
int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
		 int len)
{
	int err;
	size_t written;
@@ -285,7 +285,7 @@ static void erase_callback(struct erase_info *ei)
 * zero in case of success and a negative error code in case of failure. If
 * %-EIO is returned, the physical eraseblock most probably went bad.
 */
static int do_sync_erase(const struct ubi_device *ubi, int pnum)
static int do_sync_erase(struct ubi_device *ubi, int pnum)
{
	int err, retries = 0;
	struct erase_info ei;
@@ -377,29 +377,25 @@ static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
 * test, a positive number of erase operations done if the test was
 * successfully passed, and other negative error codes in case of other errors.
 */
static int torture_peb(const struct ubi_device *ubi, int pnum)
static int torture_peb(struct ubi_device *ubi, int pnum)
{
	void *buf;
	int err, i, patt_count;

	buf = vmalloc(ubi->peb_size);
	if (!buf)
		return -ENOMEM;

	patt_count = ARRAY_SIZE(patterns);
	ubi_assert(patt_count > 0);

	mutex_lock(&ubi->buf_mutex);
	for (i = 0; i < patt_count; i++) {
		err = do_sync_erase(ubi, pnum);
		if (err)
			goto out;

		/* Make sure the PEB contains only 0xFF bytes */
		err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size);
		err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
		if (err)
			goto out;

		err = check_pattern(buf, 0xFF, ubi->peb_size);
		err = check_pattern(ubi->peb_buf1, 0xFF, ubi->peb_size);
		if (err == 0) {
			ubi_err("erased PEB %d, but a non-0xFF byte found",
				pnum);
@@ -408,17 +404,17 @@ static int torture_peb(const struct ubi_device *ubi, int pnum)
		}

		/* Write a pattern and check it */
		memset(buf, patterns[i], ubi->peb_size);
		err = ubi_io_write(ubi, buf, pnum, 0, ubi->peb_size);
		memset(ubi->peb_buf1, patterns[i], ubi->peb_size);
		err = ubi_io_write(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
		if (err)
			goto out;

		memset(buf, ~patterns[i], ubi->peb_size);
		err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size);
		memset(ubi->peb_buf1, ~patterns[i], ubi->peb_size);
		err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
		if (err)
			goto out;

		err = check_pattern(buf, patterns[i], ubi->peb_size);
		err = check_pattern(ubi->peb_buf1, patterns[i], ubi->peb_size);
		if (err == 0) {
			ubi_err("pattern %x checking failed for PEB %d",
				patterns[i], pnum);
@@ -430,6 +426,7 @@ static int torture_peb(const struct ubi_device *ubi, int pnum)
	err = patt_count;

out:
	mutex_unlock(&ubi->buf_mutex);
	if (err == UBI_IO_BITFLIPS || err == -EBADMSG) {
		/*
		 * If a bit-flip or data integrity error was detected, the test
@@ -440,7 +437,6 @@ out:
			pnum);
		err = -EIO;
	}
	vfree(buf);
	return err;
}

@@ -460,7 +456,7 @@ out:
 * codes in case of other errors. Note, %-EIO means that the physical
 * eraseblock is bad.
 */
int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture)
int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
{
	int err, ret = 0;

@@ -617,7 +613,7 @@ bad:
 * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty;
 * o a negative error code in case of failure.
 */
int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum,
int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
		       struct ubi_ec_hdr *ec_hdr, int verbose)
{
	int err, read_err = 0;
@@ -723,7 +719,7 @@ int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum,
 * case of failure. If %-EIO is returned, the physical eraseblock most probably
 * went bad.
 */
int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum,
int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
			struct ubi_ec_hdr *ec_hdr)
{
	int err;
@@ -889,7 +885,7 @@ bad:
 *   header there);
 * o a negative error code in case of failure.
 */
int ubi_io_read_vid_hdr(const 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)
{
	int err, read_err = 0;
@@ -996,7 +992,7 @@ int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum,
 * case of failure. If %-EIO is returned, the physical eraseblock probably went
 * bad.
 */
int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum,
int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
			 struct ubi_vid_hdr *vid_hdr)
{
	int err;
@@ -1219,44 +1215,40 @@ exit:
 * @offset of the physical eraseblock @pnum, %1 if not, and a negative error
 * code if an error occurred.
 */
static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum,
				 int offset, int len)
static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
				 int len)
{
	size_t read;
	int err;
	void *buf;
	loff_t addr = (loff_t)pnum * ubi->peb_size + offset;

	buf = vmalloc(len);
	if (!buf)
		return -ENOMEM;
	memset(buf, 0, len);

	err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
	mutex_lock(&ubi->dbg_buf_mutex);
	err = ubi->mtd->read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf);
	if (err && err != -EUCLEAN) {
		ubi_err("error %d while reading %d bytes from PEB %d:%d, "
			"read %zd bytes", err, len, pnum, offset, read);
		goto error;
	}

	err = check_pattern(buf, 0xFF, len);
	err = check_pattern(ubi->dbg_peb_buf, 0xFF, len);
	if (err == 0) {
		ubi_err("flash region at PEB %d:%d, length %d does not "
			"contain all 0xFF bytes", pnum, offset, len);
		goto fail;
	}
	mutex_unlock(&ubi->dbg_buf_mutex);

	vfree(buf);
	return 0;

fail:
	ubi_err("paranoid check failed for PEB %d", pnum);
	dbg_msg("hex dump of the %d-%d region", offset, offset + len);
	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1);
	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4,
		       ubi->dbg_peb_buf, len, 1);
	err = 1;
error:
	ubi_dbg_dump_stack();
	vfree(buf);
	mutex_unlock(&ubi->dbg_buf_mutex);
	return err;
}

+8 −11
Original line number Diff line number Diff line
@@ -45,8 +45,7 @@
#include "ubi.h"

#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
static int paranoid_check_si(const struct ubi_device *ubi,
			     struct ubi_scan_info *si);
static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si);
#else
#define paranoid_check_si(ubi, si) 0
#endif
@@ -259,9 +258,8 @@ static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id,
 *     o bit 2 is cleared: the older LEB is not corrupted;
 *     o bit 2 is set: the older LEB is corrupted.
 */
static int compare_lebs(const struct ubi_device *ubi,
			const struct ubi_scan_leb *seb, int pnum,
			const struct ubi_vid_hdr *vid_hdr)
static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb,
			int pnum, const struct ubi_vid_hdr *vid_hdr)
{
	void *buf;
	int len, err, second_is_newer, bitflips = 0, corrupted = 0;
@@ -413,7 +411,7 @@ out_free_vidh:
 * to be picked, while the older one has to be dropped. This function returns
 * zero in case of success and a negative error code in case of failure.
 */
int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si,
int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
		      int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
		      int bitflips)
{
@@ -667,8 +665,8 @@ void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv)
 * function returns zero in case of success and a negative error code in case
 * of failure.
 */
int ubi_scan_erase_peb(const struct ubi_device *ubi,
		       const struct ubi_scan_info *si, int pnum, int ec)
int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si,
		       int pnum, int ec)
{
	int err;
	struct ubi_ec_hdr *ec_hdr;
@@ -712,7 +710,7 @@ out_free:
 * This function returns scanning physical eraseblock information in case of
 * success and an error code in case of failure.
 */
struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi,
struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
					   struct ubi_scan_info *si)
{
	int err = 0, i;
@@ -1110,8 +1108,7 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si)
 * This function returns zero if the scanning information is all right, %1 if
 * not and a negative error code if an error occurred.
 */
static int paranoid_check_si(const struct ubi_device *ubi,
			     struct ubi_scan_info *si)
static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
{
	int pnum, err, vols_found = 0;
	struct rb_node *rb1, *rb2;
+4 −4
Original line number Diff line number Diff line
@@ -147,7 +147,7 @@ static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv,
		list_add_tail(&seb->u.list, list);
}

int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si,
int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
		      int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
		      int bitflips);
struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
@@ -155,10 +155,10 @@ struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
				       int lnum);
void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv);
struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi,
struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
					   struct ubi_scan_info *si);
int ubi_scan_erase_peb(const struct ubi_device *ubi,
		       const struct ubi_scan_info *si, int pnum, int ec);
int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si,
		       int pnum, int ec);
struct ubi_scan_info *ubi_scan(struct ubi_device *ubi);
void ubi_scan_destroy_si(struct ubi_scan_info *si);

Loading