1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) International Business Machines Corp., 2006 |
4 | * |
5 | * Author: Artem Bityutskiy (Битюцкий Артём) |
6 | */ |
7 | |
8 | #include "ubi.h" |
9 | #include <linux/debugfs.h> |
10 | #include <linux/uaccess.h> |
11 | #include <linux/module.h> |
12 | #include <linux/seq_file.h> |
13 | #include <linux/fault-inject.h> |
14 | |
15 | #ifdef CONFIG_MTD_UBI_FAULT_INJECTION |
16 | static DECLARE_FAULT_ATTR(fault_eccerr_attr); |
17 | static DECLARE_FAULT_ATTR(fault_bitflips_attr); |
18 | static DECLARE_FAULT_ATTR(fault_read_failure_attr); |
19 | static DECLARE_FAULT_ATTR(fault_write_failure_attr); |
20 | static DECLARE_FAULT_ATTR(fault_erase_failure_attr); |
21 | static DECLARE_FAULT_ATTR(fault_power_cut_attr); |
22 | static DECLARE_FAULT_ATTR(fault_io_ff_attr); |
23 | static DECLARE_FAULT_ATTR(fault_io_ff_bitflips_attr); |
24 | static DECLARE_FAULT_ATTR(fault_bad_hdr_attr); |
25 | static DECLARE_FAULT_ATTR(fault_bad_hdr_ebadmsg_attr); |
26 | |
27 | #define FAIL_ACTION(name, fault_attr) \ |
28 | bool should_fail_##name(void) \ |
29 | { \ |
30 | return should_fail(&fault_attr, 1); \ |
31 | } |
32 | |
33 | FAIL_ACTION(eccerr, fault_eccerr_attr) |
34 | FAIL_ACTION(bitflips, fault_bitflips_attr) |
35 | FAIL_ACTION(read_failure, fault_read_failure_attr) |
36 | FAIL_ACTION(write_failure, fault_write_failure_attr) |
37 | FAIL_ACTION(erase_failure, fault_erase_failure_attr) |
38 | FAIL_ACTION(power_cut, fault_power_cut_attr) |
39 | FAIL_ACTION(io_ff, fault_io_ff_attr) |
40 | FAIL_ACTION(io_ff_bitflips, fault_io_ff_bitflips_attr) |
41 | FAIL_ACTION(bad_hdr, fault_bad_hdr_attr) |
42 | FAIL_ACTION(bad_hdr_ebadmsg, fault_bad_hdr_ebadmsg_attr) |
43 | #endif |
44 | |
45 | /** |
46 | * ubi_dump_flash - dump a region of flash. |
47 | * @ubi: UBI device description object |
48 | * @pnum: the physical eraseblock number to dump |
49 | * @offset: the starting offset within the physical eraseblock to dump |
50 | * @len: the length of the region to dump |
51 | */ |
52 | void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len) |
53 | { |
54 | int err; |
55 | size_t read; |
56 | void *buf; |
57 | loff_t addr = (loff_t)pnum * ubi->peb_size + offset; |
58 | |
59 | buf = vmalloc(size: len); |
60 | if (!buf) |
61 | return; |
62 | err = mtd_read(mtd: ubi->mtd, from: addr, len, retlen: &read, buf); |
63 | if (err && err != -EUCLEAN) { |
64 | ubi_err(ubi, fmt: "err %d while reading %d bytes from PEB %d:%d, read %zd bytes" , |
65 | err, len, pnum, offset, read); |
66 | goto out; |
67 | } |
68 | |
69 | ubi_msg(ubi, fmt: "dumping %d bytes of data from PEB %d, offset %d" , |
70 | len, pnum, offset); |
71 | print_hex_dump(KERN_DEBUG, prefix_str: "" , prefix_type: DUMP_PREFIX_OFFSET, rowsize: 32, groupsize: 1, buf, len, ascii: 1); |
72 | out: |
73 | vfree(addr: buf); |
74 | return; |
75 | } |
76 | |
77 | /** |
78 | * ubi_dump_ec_hdr - dump an erase counter header. |
79 | * @ec_hdr: the erase counter header to dump |
80 | */ |
81 | void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) |
82 | { |
83 | pr_err("Erase counter header dump:\n" ); |
84 | pr_err("\tmagic %#08x\n" , be32_to_cpu(ec_hdr->magic)); |
85 | pr_err("\tversion %d\n" , (int)ec_hdr->version); |
86 | pr_err("\tec %llu\n" , (long long)be64_to_cpu(ec_hdr->ec)); |
87 | pr_err("\tvid_hdr_offset %d\n" , be32_to_cpu(ec_hdr->vid_hdr_offset)); |
88 | pr_err("\tdata_offset %d\n" , be32_to_cpu(ec_hdr->data_offset)); |
89 | pr_err("\timage_seq %d\n" , be32_to_cpu(ec_hdr->image_seq)); |
90 | pr_err("\thdr_crc %#08x\n" , be32_to_cpu(ec_hdr->hdr_crc)); |
91 | pr_err("erase counter header hexdump:\n" ); |
92 | print_hex_dump(KERN_DEBUG, prefix_str: "" , prefix_type: DUMP_PREFIX_OFFSET, rowsize: 32, groupsize: 1, |
93 | buf: ec_hdr, UBI_EC_HDR_SIZE, ascii: 1); |
94 | } |
95 | |
96 | /** |
97 | * ubi_dump_vid_hdr - dump a volume identifier header. |
98 | * @vid_hdr: the volume identifier header to dump |
99 | */ |
100 | void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) |
101 | { |
102 | pr_err("Volume identifier header dump:\n" ); |
103 | pr_err("\tmagic %08x\n" , be32_to_cpu(vid_hdr->magic)); |
104 | pr_err("\tversion %d\n" , (int)vid_hdr->version); |
105 | pr_err("\tvol_type %d\n" , (int)vid_hdr->vol_type); |
106 | pr_err("\tcopy_flag %d\n" , (int)vid_hdr->copy_flag); |
107 | pr_err("\tcompat %d\n" , (int)vid_hdr->compat); |
108 | pr_err("\tvol_id %d\n" , be32_to_cpu(vid_hdr->vol_id)); |
109 | pr_err("\tlnum %d\n" , be32_to_cpu(vid_hdr->lnum)); |
110 | pr_err("\tdata_size %d\n" , be32_to_cpu(vid_hdr->data_size)); |
111 | pr_err("\tused_ebs %d\n" , be32_to_cpu(vid_hdr->used_ebs)); |
112 | pr_err("\tdata_pad %d\n" , be32_to_cpu(vid_hdr->data_pad)); |
113 | pr_err("\tsqnum %llu\n" , |
114 | (unsigned long long)be64_to_cpu(vid_hdr->sqnum)); |
115 | pr_err("\thdr_crc %08x\n" , be32_to_cpu(vid_hdr->hdr_crc)); |
116 | pr_err("Volume identifier header hexdump:\n" ); |
117 | print_hex_dump(KERN_DEBUG, prefix_str: "" , prefix_type: DUMP_PREFIX_OFFSET, rowsize: 32, groupsize: 1, |
118 | buf: vid_hdr, UBI_VID_HDR_SIZE, ascii: 1); |
119 | } |
120 | |
121 | /** |
122 | * ubi_dump_vol_info - dump volume information. |
123 | * @vol: UBI volume description object |
124 | */ |
125 | void ubi_dump_vol_info(const struct ubi_volume *vol) |
126 | { |
127 | pr_err("Volume information dump:\n" ); |
128 | pr_err("\tvol_id %d\n" , vol->vol_id); |
129 | pr_err("\treserved_pebs %d\n" , vol->reserved_pebs); |
130 | pr_err("\talignment %d\n" , vol->alignment); |
131 | pr_err("\tdata_pad %d\n" , vol->data_pad); |
132 | pr_err("\tvol_type %d\n" , vol->vol_type); |
133 | pr_err("\tname_len %d\n" , vol->name_len); |
134 | pr_err("\tusable_leb_size %d\n" , vol->usable_leb_size); |
135 | pr_err("\tused_ebs %d\n" , vol->used_ebs); |
136 | pr_err("\tused_bytes %lld\n" , vol->used_bytes); |
137 | pr_err("\tlast_eb_bytes %d\n" , vol->last_eb_bytes); |
138 | pr_err("\tcorrupted %d\n" , vol->corrupted); |
139 | pr_err("\tupd_marker %d\n" , vol->upd_marker); |
140 | pr_err("\tskip_check %d\n" , vol->skip_check); |
141 | |
142 | if (vol->name_len <= UBI_VOL_NAME_MAX && |
143 | strnlen(p: vol->name, maxlen: vol->name_len + 1) == vol->name_len) { |
144 | pr_err("\tname %s\n" , vol->name); |
145 | } else { |
146 | pr_err("\t1st 5 characters of name: %c%c%c%c%c\n" , |
147 | vol->name[0], vol->name[1], vol->name[2], |
148 | vol->name[3], vol->name[4]); |
149 | } |
150 | } |
151 | |
152 | /** |
153 | * ubi_dump_vtbl_record - dump a &struct ubi_vtbl_record object. |
154 | * @r: the object to dump |
155 | * @idx: volume table index |
156 | */ |
157 | void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) |
158 | { |
159 | int name_len = be16_to_cpu(r->name_len); |
160 | |
161 | pr_err("Volume table record %d dump:\n" , idx); |
162 | pr_err("\treserved_pebs %d\n" , be32_to_cpu(r->reserved_pebs)); |
163 | pr_err("\talignment %d\n" , be32_to_cpu(r->alignment)); |
164 | pr_err("\tdata_pad %d\n" , be32_to_cpu(r->data_pad)); |
165 | pr_err("\tvol_type %d\n" , (int)r->vol_type); |
166 | pr_err("\tupd_marker %d\n" , (int)r->upd_marker); |
167 | pr_err("\tname_len %d\n" , name_len); |
168 | |
169 | if (r->name[0] == '\0') { |
170 | pr_err("\tname NULL\n" ); |
171 | return; |
172 | } |
173 | |
174 | if (name_len <= UBI_VOL_NAME_MAX && |
175 | strnlen(p: &r->name[0], maxlen: name_len + 1) == name_len) { |
176 | pr_err("\tname %s\n" , &r->name[0]); |
177 | } else { |
178 | pr_err("\t1st 5 characters of name: %c%c%c%c%c\n" , |
179 | r->name[0], r->name[1], r->name[2], r->name[3], |
180 | r->name[4]); |
181 | } |
182 | pr_err("\tcrc %#08x\n" , be32_to_cpu(r->crc)); |
183 | } |
184 | |
185 | /** |
186 | * ubi_dump_av - dump a &struct ubi_ainf_volume object. |
187 | * @av: the object to dump |
188 | */ |
189 | void ubi_dump_av(const struct ubi_ainf_volume *av) |
190 | { |
191 | pr_err("Volume attaching information dump:\n" ); |
192 | pr_err("\tvol_id %d\n" , av->vol_id); |
193 | pr_err("\thighest_lnum %d\n" , av->highest_lnum); |
194 | pr_err("\tleb_count %d\n" , av->leb_count); |
195 | pr_err("\tcompat %d\n" , av->compat); |
196 | pr_err("\tvol_type %d\n" , av->vol_type); |
197 | pr_err("\tused_ebs %d\n" , av->used_ebs); |
198 | pr_err("\tlast_data_size %d\n" , av->last_data_size); |
199 | pr_err("\tdata_pad %d\n" , av->data_pad); |
200 | } |
201 | |
202 | /** |
203 | * ubi_dump_aeb - dump a &struct ubi_ainf_peb object. |
204 | * @aeb: the object to dump |
205 | * @type: object type: 0 - not corrupted, 1 - corrupted |
206 | */ |
207 | void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type) |
208 | { |
209 | pr_err("eraseblock attaching information dump:\n" ); |
210 | pr_err("\tec %d\n" , aeb->ec); |
211 | pr_err("\tpnum %d\n" , aeb->pnum); |
212 | if (type == 0) { |
213 | pr_err("\tlnum %d\n" , aeb->lnum); |
214 | pr_err("\tscrub %d\n" , aeb->scrub); |
215 | pr_err("\tsqnum %llu\n" , aeb->sqnum); |
216 | } |
217 | } |
218 | |
219 | /** |
220 | * ubi_dump_mkvol_req - dump a &struct ubi_mkvol_req object. |
221 | * @req: the object to dump |
222 | */ |
223 | void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req) |
224 | { |
225 | char nm[17]; |
226 | |
227 | pr_err("Volume creation request dump:\n" ); |
228 | pr_err("\tvol_id %d\n" , req->vol_id); |
229 | pr_err("\talignment %d\n" , req->alignment); |
230 | pr_err("\tbytes %lld\n" , (long long)req->bytes); |
231 | pr_err("\tvol_type %d\n" , req->vol_type); |
232 | pr_err("\tname_len %d\n" , req->name_len); |
233 | |
234 | memcpy(nm, req->name, 16); |
235 | nm[16] = 0; |
236 | pr_err("\t1st 16 characters of name: %s\n" , nm); |
237 | } |
238 | |
239 | /* |
240 | * Root directory for UBI stuff in debugfs. Contains sub-directories which |
241 | * contain the stuff specific to particular UBI devices. |
242 | */ |
243 | static struct dentry *dfs_rootdir; |
244 | |
245 | #ifdef CONFIG_MTD_UBI_FAULT_INJECTION |
246 | static void dfs_create_fault_entry(struct dentry *parent) |
247 | { |
248 | struct dentry *dir; |
249 | |
250 | dir = debugfs_create_dir(name: "fault_inject" , parent); |
251 | if (IS_ERR_OR_NULL(ptr: dir)) { |
252 | int err = dir ? PTR_ERR(ptr: dir) : -ENODEV; |
253 | |
254 | pr_warn("UBI error: cannot create \"fault_inject\" debugfs directory, error %d\n" , |
255 | err); |
256 | return; |
257 | } |
258 | |
259 | fault_create_debugfs_attr(name: "emulate_eccerr" , parent: dir, |
260 | attr: &fault_eccerr_attr); |
261 | |
262 | fault_create_debugfs_attr(name: "emulate_read_failure" , parent: dir, |
263 | attr: &fault_read_failure_attr); |
264 | |
265 | fault_create_debugfs_attr(name: "emulate_bitflips" , parent: dir, |
266 | attr: &fault_bitflips_attr); |
267 | |
268 | fault_create_debugfs_attr(name: "emulate_write_failure" , parent: dir, |
269 | attr: &fault_write_failure_attr); |
270 | |
271 | fault_create_debugfs_attr(name: "emulate_erase_failure" , parent: dir, |
272 | attr: &fault_erase_failure_attr); |
273 | |
274 | fault_create_debugfs_attr(name: "emulate_power_cut" , parent: dir, |
275 | attr: &fault_power_cut_attr); |
276 | |
277 | fault_create_debugfs_attr(name: "emulate_io_ff" , parent: dir, |
278 | attr: &fault_io_ff_attr); |
279 | |
280 | fault_create_debugfs_attr(name: "emulate_io_ff_bitflips" , parent: dir, |
281 | attr: &fault_io_ff_bitflips_attr); |
282 | |
283 | fault_create_debugfs_attr(name: "emulate_bad_hdr" , parent: dir, |
284 | attr: &fault_bad_hdr_attr); |
285 | |
286 | fault_create_debugfs_attr(name: "emulate_bad_hdr_ebadmsg" , parent: dir, |
287 | attr: &fault_bad_hdr_ebadmsg_attr); |
288 | } |
289 | #endif |
290 | |
291 | /** |
292 | * ubi_debugfs_init - create UBI debugfs directory. |
293 | * |
294 | * Create UBI debugfs directory. Returns zero in case of success and a negative |
295 | * error code in case of failure. |
296 | */ |
297 | int ubi_debugfs_init(void) |
298 | { |
299 | if (!IS_ENABLED(CONFIG_DEBUG_FS)) |
300 | return 0; |
301 | |
302 | dfs_rootdir = debugfs_create_dir(name: "ubi" , NULL); |
303 | if (IS_ERR_OR_NULL(ptr: dfs_rootdir)) { |
304 | int err = dfs_rootdir ? PTR_ERR(ptr: dfs_rootdir) : -ENODEV; |
305 | |
306 | pr_err("UBI error: cannot create \"ubi\" debugfs directory, error %d\n" , |
307 | err); |
308 | return err; |
309 | } |
310 | |
311 | #ifdef CONFIG_MTD_UBI_FAULT_INJECTION |
312 | dfs_create_fault_entry(parent: dfs_rootdir); |
313 | #endif |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | /** |
319 | * ubi_debugfs_exit - remove UBI debugfs directory. |
320 | */ |
321 | void ubi_debugfs_exit(void) |
322 | { |
323 | if (IS_ENABLED(CONFIG_DEBUG_FS)) |
324 | debugfs_remove(dentry: dfs_rootdir); |
325 | } |
326 | |
327 | /* Read an UBI debugfs file */ |
328 | static ssize_t dfs_file_read(struct file *file, char __user *user_buf, |
329 | size_t count, loff_t *ppos) |
330 | { |
331 | unsigned long ubi_num = (unsigned long)file->private_data; |
332 | struct dentry *dent = file->f_path.dentry; |
333 | struct ubi_device *ubi; |
334 | struct ubi_debug_info *d; |
335 | char buf[16]; |
336 | int val; |
337 | |
338 | ubi = ubi_get_device(ubi_num); |
339 | if (!ubi) |
340 | return -ENODEV; |
341 | d = &ubi->dbg; |
342 | |
343 | if (dent == d->dfs_chk_gen) |
344 | val = d->chk_gen; |
345 | else if (dent == d->dfs_chk_io) |
346 | val = d->chk_io; |
347 | else if (dent == d->dfs_chk_fastmap) |
348 | val = d->chk_fastmap; |
349 | else if (dent == d->dfs_disable_bgt) |
350 | val = d->disable_bgt; |
351 | else if (dent == d->dfs_emulate_bitflips) |
352 | val = d->emulate_bitflips; |
353 | else if (dent == d->dfs_emulate_io_failures) |
354 | val = d->emulate_io_failures; |
355 | else if (dent == d->dfs_emulate_failures) { |
356 | snprintf(buf, size: sizeof(buf), fmt: "0x%04x\n" , d->emulate_failures); |
357 | count = simple_read_from_buffer(to: user_buf, count, ppos, |
358 | from: buf, strlen(buf)); |
359 | goto out; |
360 | } else if (dent == d->dfs_emulate_power_cut) { |
361 | snprintf(buf, size: sizeof(buf), fmt: "%u\n" , d->emulate_power_cut); |
362 | count = simple_read_from_buffer(to: user_buf, count, ppos, |
363 | from: buf, strlen(buf)); |
364 | goto out; |
365 | } else if (dent == d->dfs_power_cut_min) { |
366 | snprintf(buf, size: sizeof(buf), fmt: "%u\n" , d->power_cut_min); |
367 | count = simple_read_from_buffer(to: user_buf, count, ppos, |
368 | from: buf, strlen(buf)); |
369 | goto out; |
370 | } else if (dent == d->dfs_power_cut_max) { |
371 | snprintf(buf, size: sizeof(buf), fmt: "%u\n" , d->power_cut_max); |
372 | count = simple_read_from_buffer(to: user_buf, count, ppos, |
373 | from: buf, strlen(buf)); |
374 | goto out; |
375 | } else { |
376 | count = -EINVAL; |
377 | goto out; |
378 | } |
379 | |
380 | if (val) |
381 | buf[0] = '1'; |
382 | else |
383 | buf[0] = '0'; |
384 | buf[1] = '\n'; |
385 | buf[2] = 0x00; |
386 | |
387 | count = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: 2); |
388 | |
389 | out: |
390 | ubi_put_device(ubi); |
391 | return count; |
392 | } |
393 | |
394 | /* Write an UBI debugfs file */ |
395 | static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, |
396 | size_t count, loff_t *ppos) |
397 | { |
398 | unsigned long ubi_num = (unsigned long)file->private_data; |
399 | struct dentry *dent = file->f_path.dentry; |
400 | struct ubi_device *ubi; |
401 | struct ubi_debug_info *d; |
402 | size_t buf_size; |
403 | char buf[16] = {0}; |
404 | int val; |
405 | |
406 | ubi = ubi_get_device(ubi_num); |
407 | if (!ubi) |
408 | return -ENODEV; |
409 | d = &ubi->dbg; |
410 | |
411 | buf_size = min_t(size_t, count, (sizeof(buf) - 1)); |
412 | if (copy_from_user(to: buf, from: user_buf, n: buf_size)) { |
413 | count = -EFAULT; |
414 | goto out; |
415 | } |
416 | |
417 | if (dent == d->dfs_emulate_failures) { |
418 | if (kstrtouint(s: buf, base: 0, res: &d->emulate_failures) != 0) |
419 | count = -EINVAL; |
420 | goto out; |
421 | } else if (dent == d->dfs_power_cut_min) { |
422 | if (kstrtouint(s: buf, base: 0, res: &d->power_cut_min) != 0) |
423 | count = -EINVAL; |
424 | goto out; |
425 | } else if (dent == d->dfs_power_cut_max) { |
426 | if (kstrtouint(s: buf, base: 0, res: &d->power_cut_max) != 0) |
427 | count = -EINVAL; |
428 | goto out; |
429 | } else if (dent == d->dfs_emulate_power_cut) { |
430 | if (kstrtoint(s: buf, base: 0, res: &val) != 0) |
431 | count = -EINVAL; |
432 | else |
433 | d->emulate_power_cut = val; |
434 | goto out; |
435 | } |
436 | |
437 | if (buf[0] == '1') |
438 | val = 1; |
439 | else if (buf[0] == '0') |
440 | val = 0; |
441 | else { |
442 | count = -EINVAL; |
443 | goto out; |
444 | } |
445 | |
446 | if (dent == d->dfs_chk_gen) |
447 | d->chk_gen = val; |
448 | else if (dent == d->dfs_chk_io) |
449 | d->chk_io = val; |
450 | else if (dent == d->dfs_chk_fastmap) |
451 | d->chk_fastmap = val; |
452 | else if (dent == d->dfs_disable_bgt) |
453 | d->disable_bgt = val; |
454 | else if (dent == d->dfs_emulate_bitflips) |
455 | d->emulate_bitflips = val; |
456 | else if (dent == d->dfs_emulate_io_failures) |
457 | d->emulate_io_failures = val; |
458 | else |
459 | count = -EINVAL; |
460 | |
461 | out: |
462 | ubi_put_device(ubi); |
463 | return count; |
464 | } |
465 | |
466 | /* File operations for all UBI debugfs files except |
467 | * detailed_erase_block_info |
468 | */ |
469 | static const struct file_operations dfs_fops = { |
470 | .read = dfs_file_read, |
471 | .write = dfs_file_write, |
472 | .open = simple_open, |
473 | .llseek = no_llseek, |
474 | .owner = THIS_MODULE, |
475 | }; |
476 | |
477 | /* As long as the position is less then that total number of erase blocks, |
478 | * we still have more to print. |
479 | */ |
480 | static void *eraseblk_count_seq_start(struct seq_file *s, loff_t *pos) |
481 | { |
482 | struct ubi_device *ubi = s->private; |
483 | |
484 | if (*pos < ubi->peb_count) |
485 | return pos; |
486 | |
487 | return NULL; |
488 | } |
489 | |
490 | /* Since we are using the position as the iterator, we just need to check if we |
491 | * are done and increment the position. |
492 | */ |
493 | static void *eraseblk_count_seq_next(struct seq_file *s, void *v, loff_t *pos) |
494 | { |
495 | struct ubi_device *ubi = s->private; |
496 | |
497 | (*pos)++; |
498 | |
499 | if (*pos < ubi->peb_count) |
500 | return pos; |
501 | |
502 | return NULL; |
503 | } |
504 | |
505 | static void eraseblk_count_seq_stop(struct seq_file *s, void *v) |
506 | { |
507 | } |
508 | |
509 | static int eraseblk_count_seq_show(struct seq_file *s, void *iter) |
510 | { |
511 | struct ubi_device *ubi = s->private; |
512 | struct ubi_wl_entry *wl; |
513 | int *block_number = iter; |
514 | int erase_count = -1; |
515 | int err; |
516 | |
517 | /* If this is the start, print a header */ |
518 | if (*block_number == 0) |
519 | seq_puts(m: s, s: "physical_block_number\terase_count\n" ); |
520 | |
521 | err = ubi_io_is_bad(ubi, pnum: *block_number); |
522 | if (err) |
523 | return err; |
524 | |
525 | spin_lock(lock: &ubi->wl_lock); |
526 | |
527 | wl = ubi->lookuptbl[*block_number]; |
528 | if (wl) |
529 | erase_count = wl->ec; |
530 | |
531 | spin_unlock(lock: &ubi->wl_lock); |
532 | |
533 | if (erase_count < 0) |
534 | return 0; |
535 | |
536 | seq_printf(m: s, fmt: "%-22d\t%-11d\n" , *block_number, erase_count); |
537 | |
538 | return 0; |
539 | } |
540 | |
541 | static const struct seq_operations eraseblk_count_seq_ops = { |
542 | .start = eraseblk_count_seq_start, |
543 | .next = eraseblk_count_seq_next, |
544 | .stop = eraseblk_count_seq_stop, |
545 | .show = eraseblk_count_seq_show |
546 | }; |
547 | |
548 | static int eraseblk_count_open(struct inode *inode, struct file *f) |
549 | { |
550 | struct seq_file *s; |
551 | int err; |
552 | |
553 | err = seq_open(f, &eraseblk_count_seq_ops); |
554 | if (err) |
555 | return err; |
556 | |
557 | s = f->private_data; |
558 | s->private = ubi_get_device(ubi_num: (unsigned long)inode->i_private); |
559 | |
560 | if (!s->private) |
561 | return -ENODEV; |
562 | else |
563 | return 0; |
564 | } |
565 | |
566 | static int eraseblk_count_release(struct inode *inode, struct file *f) |
567 | { |
568 | struct seq_file *s = f->private_data; |
569 | struct ubi_device *ubi = s->private; |
570 | |
571 | ubi_put_device(ubi); |
572 | |
573 | return seq_release(inode, f); |
574 | } |
575 | |
576 | static const struct file_operations eraseblk_count_fops = { |
577 | .owner = THIS_MODULE, |
578 | .open = eraseblk_count_open, |
579 | .read = seq_read, |
580 | .llseek = seq_lseek, |
581 | .release = eraseblk_count_release, |
582 | }; |
583 | |
584 | /** |
585 | * ubi_debugfs_init_dev - initialize debugfs for an UBI device. |
586 | * @ubi: UBI device description object |
587 | * |
588 | * This function creates all debugfs files for UBI device @ubi. Returns zero in |
589 | * case of success and a negative error code in case of failure. |
590 | */ |
591 | int ubi_debugfs_init_dev(struct ubi_device *ubi) |
592 | { |
593 | unsigned long ubi_num = ubi->ubi_num; |
594 | struct ubi_debug_info *d = &ubi->dbg; |
595 | umode_t mode = S_IRUSR | S_IWUSR; |
596 | int n; |
597 | |
598 | if (!IS_ENABLED(CONFIG_DEBUG_FS)) |
599 | return 0; |
600 | |
601 | n = snprintf(buf: d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME, |
602 | ubi->ubi_num); |
603 | if (n > UBI_DFS_DIR_LEN) { |
604 | /* The array size is too small */ |
605 | return -EINVAL; |
606 | } |
607 | |
608 | d->dfs_dir = debugfs_create_dir(name: d->dfs_dir_name, parent: dfs_rootdir); |
609 | |
610 | d->dfs_chk_gen = debugfs_create_file(name: "chk_gen" , mode, parent: d->dfs_dir, |
611 | data: (void *)ubi_num, fops: &dfs_fops); |
612 | |
613 | d->dfs_chk_io = debugfs_create_file(name: "chk_io" , mode, parent: d->dfs_dir, |
614 | data: (void *)ubi_num, fops: &dfs_fops); |
615 | |
616 | d->dfs_chk_fastmap = debugfs_create_file(name: "chk_fastmap" , mode, |
617 | parent: d->dfs_dir, data: (void *)ubi_num, |
618 | fops: &dfs_fops); |
619 | |
620 | d->dfs_disable_bgt = debugfs_create_file(name: "tst_disable_bgt" , mode, |
621 | parent: d->dfs_dir, data: (void *)ubi_num, |
622 | fops: &dfs_fops); |
623 | |
624 | d->dfs_emulate_bitflips = debugfs_create_file(name: "tst_emulate_bitflips" , |
625 | mode, parent: d->dfs_dir, |
626 | data: (void *)ubi_num, |
627 | fops: &dfs_fops); |
628 | |
629 | d->dfs_emulate_io_failures = debugfs_create_file(name: "tst_emulate_io_failures" , |
630 | mode, parent: d->dfs_dir, |
631 | data: (void *)ubi_num, |
632 | fops: &dfs_fops); |
633 | |
634 | d->dfs_emulate_power_cut = debugfs_create_file(name: "tst_emulate_power_cut" , |
635 | mode, parent: d->dfs_dir, |
636 | data: (void *)ubi_num, |
637 | fops: &dfs_fops); |
638 | |
639 | d->dfs_power_cut_min = debugfs_create_file(name: "tst_emulate_power_cut_min" , |
640 | mode, parent: d->dfs_dir, |
641 | data: (void *)ubi_num, fops: &dfs_fops); |
642 | |
643 | d->dfs_power_cut_max = debugfs_create_file(name: "tst_emulate_power_cut_max" , |
644 | mode, parent: d->dfs_dir, |
645 | data: (void *)ubi_num, fops: &dfs_fops); |
646 | |
647 | debugfs_create_file(name: "detailed_erase_block_info" , S_IRUSR, parent: d->dfs_dir, |
648 | data: (void *)ubi_num, fops: &eraseblk_count_fops); |
649 | |
650 | #ifdef CONFIG_MTD_UBI_FAULT_INJECTION |
651 | d->dfs_emulate_failures = debugfs_create_file(name: "emulate_failures" , |
652 | mode, parent: d->dfs_dir, |
653 | data: (void *)ubi_num, |
654 | fops: &dfs_fops); |
655 | #endif |
656 | return 0; |
657 | } |
658 | |
659 | /** |
660 | * ubi_debugfs_exit_dev - free all debugfs files corresponding to device @ubi |
661 | * @ubi: UBI device description object |
662 | */ |
663 | void ubi_debugfs_exit_dev(struct ubi_device *ubi) |
664 | { |
665 | if (IS_ENABLED(CONFIG_DEBUG_FS)) |
666 | debugfs_remove_recursive(dentry: ubi->dbg.dfs_dir); |
667 | } |
668 | |
669 | /** |
670 | * ubi_dbg_power_cut - emulate a power cut if it is time to do so |
671 | * @ubi: UBI device description object |
672 | * @caller: Flags set to indicate from where the function is being called |
673 | * |
674 | * Returns non-zero if a power cut was emulated, zero if not. |
675 | */ |
676 | int ubi_dbg_power_cut(struct ubi_device *ubi, int caller) |
677 | { |
678 | unsigned int range; |
679 | |
680 | if ((ubi->dbg.emulate_power_cut & caller) == 0) |
681 | return 0; |
682 | |
683 | if (ubi->dbg.power_cut_counter == 0) { |
684 | ubi->dbg.power_cut_counter = ubi->dbg.power_cut_min; |
685 | |
686 | if (ubi->dbg.power_cut_max > ubi->dbg.power_cut_min) { |
687 | range = ubi->dbg.power_cut_max - ubi->dbg.power_cut_min; |
688 | ubi->dbg.power_cut_counter += get_random_u32_below(ceil: range); |
689 | } |
690 | return 0; |
691 | } |
692 | |
693 | ubi->dbg.power_cut_counter--; |
694 | if (ubi->dbg.power_cut_counter) |
695 | return 0; |
696 | |
697 | return 1; |
698 | } |
699 | |