debug.c 14.0 KB
Newer Older
A
Artem B. Bityutskiy 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Copyright (c) International Business Machines Corp., 2006
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Author: Artem Bityutskiy (Битюцкий Артём)
 */

#include "ubi.h"
22 23
#include <linux/debugfs.h>
#include <linux/uaccess.h>
24
#include <linux/module.h>
A
Artem B. Bityutskiy 已提交
25

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

/**
 * ubi_dump_flash - dump a region of flash.
 * @ubi: UBI device description object
 * @pnum: the physical eraseblock number to dump
 * @offset: the starting offset within the physical eraseblock to dump
 * @len: the length of the region to dump
 */
void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len)
{
	int err;
	size_t read;
	void *buf;
	loff_t addr = (loff_t)pnum * ubi->peb_size + offset;

	buf = vmalloc(len);
	if (!buf)
		return;
	err = mtd_read(ubi->mtd, addr, len, &read, buf);
	if (err && err != -EUCLEAN) {
A
Artem Bityutskiy 已提交
46 47
		ubi_err("error %d while reading %d bytes from PEB %d:%d, read %zd bytes",
			err, len, pnum, offset, read);
48 49 50 51 52 53 54 55 56 57 58
		goto out;
	}

	ubi_msg("dumping %d bytes of data from PEB %d, offset %d",
		len, pnum, offset);
	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1);
out:
	vfree(buf);
	return;
}

A
Artem B. Bityutskiy 已提交
59
/**
60
 * ubi_dump_ec_hdr - dump an erase counter header.
A
Artem B. Bityutskiy 已提交
61 62
 * @ec_hdr: the erase counter header to dump
 */
63
void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
A
Artem B. Bityutskiy 已提交
64
{
65 66 67 68 69 70 71 72 73 74
	printk(KERN_DEBUG "Erase counter header dump:\n");
	printk(KERN_DEBUG "\tmagic          %#08x\n",
	       be32_to_cpu(ec_hdr->magic));
	printk(KERN_DEBUG "\tversion        %d\n", (int)ec_hdr->version);
	printk(KERN_DEBUG "\tec             %llu\n",
	       (long long)be64_to_cpu(ec_hdr->ec));
	printk(KERN_DEBUG "\tvid_hdr_offset %d\n",
	       be32_to_cpu(ec_hdr->vid_hdr_offset));
	printk(KERN_DEBUG "\tdata_offset    %d\n",
	       be32_to_cpu(ec_hdr->data_offset));
75 76
	printk(KERN_DEBUG "\timage_seq      %d\n",
	       be32_to_cpu(ec_hdr->image_seq));
77 78 79
	printk(KERN_DEBUG "\thdr_crc        %#08x\n",
	       be32_to_cpu(ec_hdr->hdr_crc));
	printk(KERN_DEBUG "erase counter header hexdump:\n");
A
Artem Bityutskiy 已提交
80 81
	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
		       ec_hdr, UBI_EC_HDR_SIZE, 1);
A
Artem B. Bityutskiy 已提交
82 83 84
}

/**
85
 * ubi_dump_vid_hdr - dump a volume identifier header.
A
Artem B. Bityutskiy 已提交
86 87
 * @vid_hdr: the volume identifier header to dump
 */
88
void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
A
Artem B. Bityutskiy 已提交
89
{
90 91
	printk(KERN_DEBUG "Volume identifier header dump:\n");
	printk(KERN_DEBUG "\tmagic     %08x\n", be32_to_cpu(vid_hdr->magic));
92 93 94 95 96 97 98 99 100
	printk(KERN_DEBUG "\tversion   %d\n",  (int)vid_hdr->version);
	printk(KERN_DEBUG "\tvol_type  %d\n",  (int)vid_hdr->vol_type);
	printk(KERN_DEBUG "\tcopy_flag %d\n",  (int)vid_hdr->copy_flag);
	printk(KERN_DEBUG "\tcompat    %d\n",  (int)vid_hdr->compat);
	printk(KERN_DEBUG "\tvol_id    %d\n",  be32_to_cpu(vid_hdr->vol_id));
	printk(KERN_DEBUG "\tlnum      %d\n",  be32_to_cpu(vid_hdr->lnum));
	printk(KERN_DEBUG "\tdata_size %d\n",  be32_to_cpu(vid_hdr->data_size));
	printk(KERN_DEBUG "\tused_ebs  %d\n",  be32_to_cpu(vid_hdr->used_ebs));
	printk(KERN_DEBUG "\tdata_pad  %d\n",  be32_to_cpu(vid_hdr->data_pad));
101
	printk(KERN_DEBUG "\tsqnum     %llu\n",
102
		(unsigned long long)be64_to_cpu(vid_hdr->sqnum));
103 104 105 106
	printk(KERN_DEBUG "\thdr_crc   %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
	printk(KERN_DEBUG "Volume identifier header hexdump:\n");
	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
		       vid_hdr, UBI_VID_HDR_SIZE, 1);
A
Artem B. Bityutskiy 已提交
107 108 109
}

/**
110
 * ubi_dump_vol_info - dump volume information.
A
Artem B. Bityutskiy 已提交
111 112
 * @vol: UBI volume description object
 */
113
void ubi_dump_vol_info(const struct ubi_volume *vol)
A
Artem B. Bityutskiy 已提交
114
{
115 116 117 118 119 120 121 122 123 124 125 126 127
	printk(KERN_DEBUG "Volume information dump:\n");
	printk(KERN_DEBUG "\tvol_id          %d\n", vol->vol_id);
	printk(KERN_DEBUG "\treserved_pebs   %d\n", vol->reserved_pebs);
	printk(KERN_DEBUG "\talignment       %d\n", vol->alignment);
	printk(KERN_DEBUG "\tdata_pad        %d\n", vol->data_pad);
	printk(KERN_DEBUG "\tvol_type        %d\n", vol->vol_type);
	printk(KERN_DEBUG "\tname_len        %d\n", vol->name_len);
	printk(KERN_DEBUG "\tusable_leb_size %d\n", vol->usable_leb_size);
	printk(KERN_DEBUG "\tused_ebs        %d\n", vol->used_ebs);
	printk(KERN_DEBUG "\tused_bytes      %lld\n", vol->used_bytes);
	printk(KERN_DEBUG "\tlast_eb_bytes   %d\n", vol->last_eb_bytes);
	printk(KERN_DEBUG "\tcorrupted       %d\n", vol->corrupted);
	printk(KERN_DEBUG "\tupd_marker      %d\n", vol->upd_marker);
A
Artem B. Bityutskiy 已提交
128 129 130

	if (vol->name_len <= UBI_VOL_NAME_MAX &&
	    strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
131
		printk(KERN_DEBUG "\tname            %s\n", vol->name);
A
Artem B. Bityutskiy 已提交
132
	} else {
133 134 135
		printk(KERN_DEBUG "\t1st 5 characters of name: %c%c%c%c%c\n",
		       vol->name[0], vol->name[1], vol->name[2],
		       vol->name[3], vol->name[4]);
A
Artem B. Bityutskiy 已提交
136 137 138 139
	}
}

/**
140
 * ubi_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
A
Artem B. Bityutskiy 已提交
141 142 143
 * @r: the object to dump
 * @idx: volume table index
 */
144
void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
A
Artem B. Bityutskiy 已提交
145
{
146
	int name_len = be16_to_cpu(r->name_len);
A
Artem B. Bityutskiy 已提交
147

148 149 150 151 152 153 154 155
	printk(KERN_DEBUG "Volume table record %d dump:\n", idx);
	printk(KERN_DEBUG "\treserved_pebs   %d\n",
	       be32_to_cpu(r->reserved_pebs));
	printk(KERN_DEBUG "\talignment       %d\n", be32_to_cpu(r->alignment));
	printk(KERN_DEBUG "\tdata_pad        %d\n", be32_to_cpu(r->data_pad));
	printk(KERN_DEBUG "\tvol_type        %d\n", (int)r->vol_type);
	printk(KERN_DEBUG "\tupd_marker      %d\n", (int)r->upd_marker);
	printk(KERN_DEBUG "\tname_len        %d\n", name_len);
A
Artem B. Bityutskiy 已提交
156 157

	if (r->name[0] == '\0') {
158
		printk(KERN_DEBUG "\tname            NULL\n");
A
Artem B. Bityutskiy 已提交
159 160 161 162 163
		return;
	}

	if (name_len <= UBI_VOL_NAME_MAX &&
	    strnlen(&r->name[0], name_len + 1) == name_len) {
164
		printk(KERN_DEBUG "\tname            %s\n", &r->name[0]);
A
Artem B. Bityutskiy 已提交
165
	} else {
166
		printk(KERN_DEBUG "\t1st 5 characters of name: %c%c%c%c%c\n",
A
Artem B. Bityutskiy 已提交
167 168 169
			r->name[0], r->name[1], r->name[2], r->name[3],
			r->name[4]);
	}
170
	printk(KERN_DEBUG "\tcrc             %#08x\n", be32_to_cpu(r->crc));
A
Artem B. Bityutskiy 已提交
171 172 173
}

/**
A
Artem Bityutskiy 已提交
174 175
 * ubi_dump_av - dump a &struct ubi_ainf_volume object.
 * @av: the object to dump
A
Artem B. Bityutskiy 已提交
176
 */
A
Artem Bityutskiy 已提交
177
void ubi_dump_av(const struct ubi_ainf_volume *av)
A
Artem B. Bityutskiy 已提交
178
{
A
Artem Bityutskiy 已提交
179
	printk(KERN_DEBUG "Volume attaching information dump:\n");
A
Artem Bityutskiy 已提交
180 181 182 183 184 185 186 187
	printk(KERN_DEBUG "\tvol_id         %d\n", av->vol_id);
	printk(KERN_DEBUG "\thighest_lnum   %d\n", av->highest_lnum);
	printk(KERN_DEBUG "\tleb_count      %d\n", av->leb_count);
	printk(KERN_DEBUG "\tcompat         %d\n", av->compat);
	printk(KERN_DEBUG "\tvol_type       %d\n", av->vol_type);
	printk(KERN_DEBUG "\tused_ebs       %d\n", av->used_ebs);
	printk(KERN_DEBUG "\tlast_data_size %d\n", av->last_data_size);
	printk(KERN_DEBUG "\tdata_pad       %d\n", av->data_pad);
A
Artem B. Bityutskiy 已提交
188 189 190
}

/**
A
Artem Bityutskiy 已提交
191 192
 * ubi_dump_aeb - dump a &struct ubi_ainf_peb object.
 * @aeb: the object to dump
A
Artem B. Bityutskiy 已提交
193 194
 * @type: object type: 0 - not corrupted, 1 - corrupted
 */
A
Artem Bityutskiy 已提交
195
void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type)
A
Artem B. Bityutskiy 已提交
196
{
A
Artem Bityutskiy 已提交
197
	printk(KERN_DEBUG "eraseblock attaching information dump:\n");
A
Artem Bityutskiy 已提交
198 199
	printk(KERN_DEBUG "\tec       %d\n", aeb->ec);
	printk(KERN_DEBUG "\tpnum     %d\n", aeb->pnum);
A
Artem B. Bityutskiy 已提交
200
	if (type == 0) {
A
Artem Bityutskiy 已提交
201 202 203
		printk(KERN_DEBUG "\tlnum     %d\n", aeb->lnum);
		printk(KERN_DEBUG "\tscrub    %d\n", aeb->scrub);
		printk(KERN_DEBUG "\tsqnum    %llu\n", aeb->sqnum);
A
Artem B. Bityutskiy 已提交
204 205 206 207
	}
}

/**
208
 * ubi_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
A
Artem B. Bityutskiy 已提交
209 210
 * @req: the object to dump
 */
211
void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req)
A
Artem B. Bityutskiy 已提交
212 213 214
{
	char nm[17];

215 216 217 218 219 220
	printk(KERN_DEBUG "Volume creation request dump:\n");
	printk(KERN_DEBUG "\tvol_id    %d\n",   req->vol_id);
	printk(KERN_DEBUG "\talignment %d\n",   req->alignment);
	printk(KERN_DEBUG "\tbytes     %lld\n", (long long)req->bytes);
	printk(KERN_DEBUG "\tvol_type  %d\n",   req->vol_type);
	printk(KERN_DEBUG "\tname_len  %d\n",   req->name_len);
A
Artem B. Bityutskiy 已提交
221 222 223

	memcpy(nm, req->name, 16);
	nm[16] = 0;
224
	printk(KERN_DEBUG "\t1st 16 characters of name: %s\n", nm);
A
Artem B. Bityutskiy 已提交
225 226
}

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
/**
 * ubi_debugging_init_dev - initialize debugging for an UBI device.
 * @ubi: UBI device description object
 *
 * This function initializes debugging-related data for UBI device @ubi.
 * Returns zero in case of success and a negative error code in case of
 * failure.
 */
int ubi_debugging_init_dev(struct ubi_device *ubi)
{
	ubi->dbg = kzalloc(sizeof(struct ubi_debug_info), GFP_KERNEL);
	if (!ubi->dbg)
		return -ENOMEM;

	return 0;
}

/**
 * ubi_debugging_exit_dev - free debugging data for an UBI device.
 * @ubi: UBI device description object
 */
void ubi_debugging_exit_dev(struct ubi_device *ubi)
{
	kfree(ubi->dbg);
}

/*
 * Root directory for UBI stuff in debugfs. Contains sub-directories which
 * contain the stuff specific to particular UBI devices.
 */
static struct dentry *dfs_rootdir;

/**
 * ubi_debugfs_init - create UBI debugfs directory.
 *
 * Create UBI debugfs directory. Returns zero in case of success and a negative
 * error code in case of failure.
 */
int ubi_debugfs_init(void)
{
267
	if (!IS_ENABLED(CONFIG_DEBUG_FS))
268 269
		return 0;

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
	dfs_rootdir = debugfs_create_dir("ubi", NULL);
	if (IS_ERR_OR_NULL(dfs_rootdir)) {
		int err = dfs_rootdir ? -ENODEV : PTR_ERR(dfs_rootdir);

		ubi_err("cannot create \"ubi\" debugfs directory, error %d\n",
			err);
		return err;
	}

	return 0;
}

/**
 * ubi_debugfs_exit - remove UBI debugfs directory.
 */
void ubi_debugfs_exit(void)
{
287
	if (IS_ENABLED(CONFIG_DEBUG_FS))
288
		debugfs_remove(dfs_rootdir);
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
}

/* Read an UBI debugfs file */
static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	unsigned long ubi_num = (unsigned long)file->private_data;
	struct dentry *dent = file->f_path.dentry;
	struct ubi_device *ubi;
	struct ubi_debug_info *d;
	char buf[3];
	int val;

	ubi = ubi_get_device(ubi_num);
	if (!ubi)
		return -ENODEV;
	d = ubi->dbg;

	if (dent == d->dfs_chk_gen)
		val = d->chk_gen;
	else if (dent == d->dfs_chk_io)
		val = d->chk_io;
311 312 313 314 315 316
	else if (dent == d->dfs_disable_bgt)
		val = d->disable_bgt;
	else if (dent == d->dfs_emulate_bitflips)
		val = d->emulate_bitflips;
	else if (dent == d->dfs_emulate_io_failures)
		val = d->emulate_io_failures;
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	else {
		count = -EINVAL;
		goto out;
	}

	if (val)
		buf[0] = '1';
	else
		buf[0] = '0';
	buf[1] = '\n';
	buf[2] = 0x00;

	count = simple_read_from_buffer(user_buf, count, ppos, buf, 2);

out:
	ubi_put_device(ubi);
	return count;
}

/* Write an UBI debugfs file */
static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
			      size_t count, loff_t *ppos)
{
	unsigned long ubi_num = (unsigned long)file->private_data;
	struct dentry *dent = file->f_path.dentry;
	struct ubi_device *ubi;
	struct ubi_debug_info *d;
	size_t buf_size;
	char buf[8];
	int val;

	ubi = ubi_get_device(ubi_num);
	if (!ubi)
		return -ENODEV;
	d = ubi->dbg;

	buf_size = min_t(size_t, count, (sizeof(buf) - 1));
	if (copy_from_user(buf, user_buf, buf_size)) {
		count = -EFAULT;
		goto out;
	}

	if (buf[0] == '1')
		val = 1;
	else if (buf[0] == '0')
		val = 0;
	else {
		count = -EINVAL;
		goto out;
	}

	if (dent == d->dfs_chk_gen)
		d->chk_gen = val;
	else if (dent == d->dfs_chk_io)
		d->chk_io = val;
372 373 374 375 376 377
	else if (dent == d->dfs_disable_bgt)
		d->disable_bgt = val;
	else if (dent == d->dfs_emulate_bitflips)
		d->emulate_bitflips = val;
	else if (dent == d->dfs_emulate_io_failures)
		d->emulate_io_failures = val;
378 379 380 381 382 383 384 385 386 387 388 389
	else
		count = -EINVAL;

out:
	ubi_put_device(ubi);
	return count;
}

/* File operations for all UBI debugfs files */
static const struct file_operations dfs_fops = {
	.read   = dfs_file_read,
	.write  = dfs_file_write,
390
	.open	= simple_open,
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
	.llseek = no_llseek,
	.owner  = THIS_MODULE,
};

/**
 * ubi_debugfs_init_dev - initialize debugfs for an UBI device.
 * @ubi: UBI device description object
 *
 * This function creates all debugfs files for UBI device @ubi. Returns zero in
 * case of success and a negative error code in case of failure.
 */
int ubi_debugfs_init_dev(struct ubi_device *ubi)
{
	int err, n;
	unsigned long ubi_num = ubi->ubi_num;
	const char *fname;
	struct dentry *dent;
	struct ubi_debug_info *d = ubi->dbg;

410
	if (!IS_ENABLED(CONFIG_DEBUG_FS))
411 412
		return 0;

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
	n = snprintf(d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME,
		     ubi->ubi_num);
	if (n == UBI_DFS_DIR_LEN) {
		/* The array size is too small */
		fname = UBI_DFS_DIR_NAME;
		dent = ERR_PTR(-EINVAL);
		goto out;
	}

	fname = d->dfs_dir_name;
	dent = debugfs_create_dir(fname, dfs_rootdir);
	if (IS_ERR_OR_NULL(dent))
		goto out;
	d->dfs_dir = dent;

	fname = "chk_gen";
	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
				   &dfs_fops);
	if (IS_ERR_OR_NULL(dent))
		goto out_remove;
	d->dfs_chk_gen = dent;

	fname = "chk_io";
	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
				   &dfs_fops);
	if (IS_ERR_OR_NULL(dent))
		goto out_remove;
	d->dfs_chk_io = dent;

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
	fname = "tst_disable_bgt";
	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
				   &dfs_fops);
	if (IS_ERR_OR_NULL(dent))
		goto out_remove;
	d->dfs_disable_bgt = dent;

	fname = "tst_emulate_bitflips";
	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
				   &dfs_fops);
	if (IS_ERR_OR_NULL(dent))
		goto out_remove;
	d->dfs_emulate_bitflips = dent;

	fname = "tst_emulate_io_failures";
	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
				   &dfs_fops);
	if (IS_ERR_OR_NULL(dent))
		goto out_remove;
	d->dfs_emulate_io_failures = dent;

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
	return 0;

out_remove:
	debugfs_remove_recursive(d->dfs_dir);
out:
	err = dent ? PTR_ERR(dent) : -ENODEV;
	ubi_err("cannot create \"%s\" debugfs file or directory, error %d\n",
		fname, err);
	return err;
}

/**
 * dbg_debug_exit_dev - free all debugfs files corresponding to device @ubi
 * @ubi: UBI device description object
 */
void ubi_debugfs_exit_dev(struct ubi_device *ubi)
{
480
	if (IS_ENABLED(CONFIG_DEBUG_FS))
481
		debugfs_remove_recursive(ubi->dbg->dfs_dir);
482
}