]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/ubi/debug.c
Merge branch 'master' of git://git.denx.de/u-boot-arc
[karo-tx-uboot.git] / drivers / mtd / ubi / debug.c
index 492ab5cc80f9e9233d353b4aea88ea819e653a0a..af254da4883c375cc9dd39efdb4df43c6454ea46 100644 (file)
 /*
  * 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
+ * SPDX-License-Identifier:    GPL-2.0+
  *
  * Author: Artem Bityutskiy (Битюцкий Артём)
  */
 
-/*
- * Here we keep all the UBI debugging stuff which should normally be disabled
- * and compiled-out, but it is extremely helpful when hunting bugs or doing big
- * changes.
- */
 #include <ubi_uboot.h>
+#include "ubi.h"
+#define __UBOOT__
+#ifndef __UBOOT__
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#endif
 
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+/**
+ * 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;
 
-#include "ubi.h"
+       buf = vmalloc(len);
+       if (!buf)
+               return;
+       err = mtd_read(ubi->mtd, addr, len, &read, 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 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;
+}
 
 /**
- * ubi_dbg_dump_ec_hdr - dump an erase counter header.
+ * ubi_dump_ec_hdr - dump an erase counter header.
  * @ec_hdr: the erase counter header to dump
  */
-void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
-{
-       dbg_msg("erase counter header dump:");
-       dbg_msg("magic          %#08x", be32_to_cpu(ec_hdr->magic));
-       dbg_msg("version        %d",    (int)ec_hdr->version);
-       dbg_msg("ec             %llu",  (long long)be64_to_cpu(ec_hdr->ec));
-       dbg_msg("vid_hdr_offset %d",    be32_to_cpu(ec_hdr->vid_hdr_offset));
-       dbg_msg("data_offset    %d",    be32_to_cpu(ec_hdr->data_offset));
-       dbg_msg("hdr_crc        %#08x", be32_to_cpu(ec_hdr->hdr_crc));
-       dbg_msg("erase counter header hexdump:");
+void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
+{
+       pr_err("Erase counter header dump:\n");
+       pr_err("\tmagic          %#08x\n", be32_to_cpu(ec_hdr->magic));
+       pr_err("\tversion        %d\n", (int)ec_hdr->version);
+       pr_err("\tec             %llu\n", (long long)be64_to_cpu(ec_hdr->ec));
+       pr_err("\tvid_hdr_offset %d\n", be32_to_cpu(ec_hdr->vid_hdr_offset));
+       pr_err("\tdata_offset    %d\n", be32_to_cpu(ec_hdr->data_offset));
+       pr_err("\timage_seq      %d\n", be32_to_cpu(ec_hdr->image_seq));
+       pr_err("\thdr_crc        %#08x\n", be32_to_cpu(ec_hdr->hdr_crc));
+       pr_err("erase counter header hexdump:\n");
        print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
                       ec_hdr, UBI_EC_HDR_SIZE, 1);
 }
 
 /**
- * ubi_dbg_dump_vid_hdr - dump a volume identifier header.
+ * ubi_dump_vid_hdr - dump a volume identifier header.
  * @vid_hdr: the volume identifier header to dump
  */
-void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
-{
-       dbg_msg("volume identifier header dump:");
-       dbg_msg("magic     %08x", be32_to_cpu(vid_hdr->magic));
-       dbg_msg("version   %d",   (int)vid_hdr->version);
-       dbg_msg("vol_type  %d",   (int)vid_hdr->vol_type);
-       dbg_msg("copy_flag %d",   (int)vid_hdr->copy_flag);
-       dbg_msg("compat    %d",   (int)vid_hdr->compat);
-       dbg_msg("vol_id    %d",   be32_to_cpu(vid_hdr->vol_id));
-       dbg_msg("lnum      %d",   be32_to_cpu(vid_hdr->lnum));
-       dbg_msg("leb_ver   %u",   be32_to_cpu(vid_hdr->leb_ver));
-       dbg_msg("data_size %d",   be32_to_cpu(vid_hdr->data_size));
-       dbg_msg("used_ebs  %d",   be32_to_cpu(vid_hdr->used_ebs));
-       dbg_msg("data_pad  %d",   be32_to_cpu(vid_hdr->data_pad));
-       dbg_msg("sqnum     %llu",
+void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
+{
+       pr_err("Volume identifier header dump:\n");
+       pr_err("\tmagic     %08x\n", be32_to_cpu(vid_hdr->magic));
+       pr_err("\tversion   %d\n",  (int)vid_hdr->version);
+       pr_err("\tvol_type  %d\n",  (int)vid_hdr->vol_type);
+       pr_err("\tcopy_flag %d\n",  (int)vid_hdr->copy_flag);
+       pr_err("\tcompat    %d\n",  (int)vid_hdr->compat);
+       pr_err("\tvol_id    %d\n",  be32_to_cpu(vid_hdr->vol_id));
+       pr_err("\tlnum      %d\n",  be32_to_cpu(vid_hdr->lnum));
+       pr_err("\tdata_size %d\n",  be32_to_cpu(vid_hdr->data_size));
+       pr_err("\tused_ebs  %d\n",  be32_to_cpu(vid_hdr->used_ebs));
+       pr_err("\tdata_pad  %d\n",  be32_to_cpu(vid_hdr->data_pad));
+       pr_err("\tsqnum     %llu\n",
                (unsigned long long)be64_to_cpu(vid_hdr->sqnum));
-       dbg_msg("hdr_crc   %08x", be32_to_cpu(vid_hdr->hdr_crc));
-       dbg_msg("volume identifier header hexdump:");
+       pr_err("\thdr_crc   %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
+       pr_err("Volume identifier header hexdump:\n");
+       print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+                      vid_hdr, UBI_VID_HDR_SIZE, 1);
 }
 
 /**
- * ubi_dbg_dump_vol_info- dump volume information.
+ * ubi_dump_vol_info - dump volume information.
  * @vol: UBI volume description object
  */
-void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
-{
-       dbg_msg("volume information dump:");
-       dbg_msg("vol_id          %d", vol->vol_id);
-       dbg_msg("reserved_pebs   %d", vol->reserved_pebs);
-       dbg_msg("alignment       %d", vol->alignment);
-       dbg_msg("data_pad        %d", vol->data_pad);
-       dbg_msg("vol_type        %d", vol->vol_type);
-       dbg_msg("name_len        %d", vol->name_len);
-       dbg_msg("usable_leb_size %d", vol->usable_leb_size);
-       dbg_msg("used_ebs        %d", vol->used_ebs);
-       dbg_msg("used_bytes      %lld", vol->used_bytes);
-       dbg_msg("last_eb_bytes   %d", vol->last_eb_bytes);
-       dbg_msg("corrupted       %d", vol->corrupted);
-       dbg_msg("upd_marker      %d", vol->upd_marker);
+void ubi_dump_vol_info(const struct ubi_volume *vol)
+{
+       printf("Volume information dump:\n");
+       printf("\tvol_id          %d\n", vol->vol_id);
+       printf("\treserved_pebs   %d\n", vol->reserved_pebs);
+       printf("\talignment       %d\n", vol->alignment);
+       printf("\tdata_pad        %d\n", vol->data_pad);
+       printf("\tvol_type        %d\n", vol->vol_type);
+       printf("\tname_len        %d\n", vol->name_len);
+       printf("\tusable_leb_size %d\n", vol->usable_leb_size);
+       printf("\tused_ebs        %d\n", vol->used_ebs);
+       printf("\tused_bytes      %lld\n", vol->used_bytes);
+       printf("\tlast_eb_bytes   %d\n", vol->last_eb_bytes);
+       printf("\tcorrupted       %d\n", vol->corrupted);
+       printf("\tupd_marker      %d\n", vol->upd_marker);
 
        if (vol->name_len <= UBI_VOL_NAME_MAX &&
            strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
-               dbg_msg("name            %s", vol->name);
+               printf("\tname            %s\n", vol->name);
        } else {
-               dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c",
-                       vol->name[0], vol->name[1], vol->name[2],
-                       vol->name[3], vol->name[4]);
+               printf("\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]);
        }
 }
 
 /**
- * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
+ * ubi_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
  * @r: the object to dump
  * @idx: volume table index
  */
-void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
+void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
 {
        int name_len = be16_to_cpu(r->name_len);
 
-       dbg_msg("volume table record %d dump:", idx);
-       dbg_msg("reserved_pebs   %d", be32_to_cpu(r->reserved_pebs));
-       dbg_msg("alignment       %d", be32_to_cpu(r->alignment));
-       dbg_msg("data_pad        %d", be32_to_cpu(r->data_pad));
-       dbg_msg("vol_type        %d", (int)r->vol_type);
-       dbg_msg("upd_marker      %d", (int)r->upd_marker);
-       dbg_msg("name_len        %d", name_len);
+       pr_err("Volume table record %d dump:\n", idx);
+       pr_err("\treserved_pebs   %d\n", be32_to_cpu(r->reserved_pebs));
+       pr_err("\talignment       %d\n", be32_to_cpu(r->alignment));
+       pr_err("\tdata_pad        %d\n", be32_to_cpu(r->data_pad));
+       pr_err("\tvol_type        %d\n", (int)r->vol_type);
+       pr_err("\tupd_marker      %d\n", (int)r->upd_marker);
+       pr_err("\tname_len        %d\n", name_len);
 
        if (r->name[0] == '\0') {
-               dbg_msg("name            NULL");
+               pr_err("\tname            NULL\n");
                return;
        }
 
        if (name_len <= UBI_VOL_NAME_MAX &&
            strnlen(&r->name[0], name_len + 1) == name_len) {
-               dbg_msg("name            %s", &r->name[0]);
+               pr_err("\tname            %s\n", &r->name[0]);
        } else {
-               dbg_msg("1st 5 characters of the name: %c%c%c%c%c",
+               pr_err("\t1st 5 characters of name: %c%c%c%c%c\n",
                        r->name[0], r->name[1], r->name[2], r->name[3],
                        r->name[4]);
        }
-       dbg_msg("crc             %#08x", be32_to_cpu(r->crc));
+       pr_err("\tcrc             %#08x\n", be32_to_cpu(r->crc));
 }
 
 /**
- * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object.
- * @sv: the object to dump
+ * ubi_dump_av - dump a &struct ubi_ainf_volume object.
+ * @av: the object to dump
  */
-void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
+void ubi_dump_av(const struct ubi_ainf_volume *av)
 {
-       dbg_msg("volume scanning information dump:");
-       dbg_msg("vol_id         %d", sv->vol_id);
-       dbg_msg("highest_lnum   %d", sv->highest_lnum);
-       dbg_msg("leb_count      %d", sv->leb_count);
-       dbg_msg("compat         %d", sv->compat);
-       dbg_msg("vol_type       %d", sv->vol_type);
-       dbg_msg("used_ebs       %d", sv->used_ebs);
-       dbg_msg("last_data_size %d", sv->last_data_size);
-       dbg_msg("data_pad       %d", sv->data_pad);
+       pr_err("Volume attaching information dump:\n");
+       pr_err("\tvol_id         %d\n", av->vol_id);
+       pr_err("\thighest_lnum   %d\n", av->highest_lnum);
+       pr_err("\tleb_count      %d\n", av->leb_count);
+       pr_err("\tcompat         %d\n", av->compat);
+       pr_err("\tvol_type       %d\n", av->vol_type);
+       pr_err("\tused_ebs       %d\n", av->used_ebs);
+       pr_err("\tlast_data_size %d\n", av->last_data_size);
+       pr_err("\tdata_pad       %d\n", av->data_pad);
 }
 
 /**
- * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object.
- * @seb: the object to dump
+ * ubi_dump_aeb - dump a &struct ubi_ainf_peb object.
+ * @aeb: the object to dump
  * @type: object type: 0 - not corrupted, 1 - corrupted
  */
-void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type)
+void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type)
 {
-       dbg_msg("eraseblock scanning information dump:");
-       dbg_msg("ec       %d", seb->ec);
-       dbg_msg("pnum     %d", seb->pnum);
+       pr_err("eraseblock attaching information dump:\n");
+       pr_err("\tec       %d\n", aeb->ec);
+       pr_err("\tpnum     %d\n", aeb->pnum);
        if (type == 0) {
-               dbg_msg("lnum     %d", seb->lnum);
-               dbg_msg("scrub    %d", seb->scrub);
-               dbg_msg("sqnum    %llu", seb->sqnum);
-               dbg_msg("leb_ver  %u", seb->leb_ver);
+               pr_err("\tlnum     %d\n", aeb->lnum);
+               pr_err("\tscrub    %d\n", aeb->scrub);
+               pr_err("\tsqnum    %llu\n", aeb->sqnum);
        }
 }
 
 /**
- * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
+ * ubi_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
  * @req: the object to dump
  */
-void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req)
+void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req)
 {
        char nm[17];
 
-       dbg_msg("volume creation request dump:");
-       dbg_msg("vol_id    %d",   req->vol_id);
-       dbg_msg("alignment %d",   req->alignment);
-       dbg_msg("bytes     %lld", (long long)req->bytes);
-       dbg_msg("vol_type  %d",   req->vol_type);
-       dbg_msg("name_len  %d",   req->name_len);
+       pr_err("Volume creation request dump:\n");
+       pr_err("\tvol_id    %d\n",   req->vol_id);
+       pr_err("\talignment %d\n",   req->alignment);
+       pr_err("\tbytes     %lld\n", (long long)req->bytes);
+       pr_err("\tvol_type  %d\n",   req->vol_type);
+       pr_err("\tname_len  %d\n",   req->name_len);
 
        memcpy(nm, req->name, 16);
        nm[16] = 0;
-       dbg_msg("the 1st 16 characters of the name: %s", nm);
+       pr_err("\t1st 16 characters of name: %s\n", nm);
+}
+
+#ifndef __UBOOT__
+/*
+ * 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)
+{
+       if (!IS_ENABLED(CONFIG_DEBUG_FS))
+               return 0;
+
+       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)
+{
+       if (IS_ENABLED(CONFIG_DEBUG_FS))
+               debugfs_remove(dfs_rootdir);
+}
+
+/* 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;
+       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;
+       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;
 }
 
-#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
+/* 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;
+       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;
+       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,
+       .open   = simple_open,
+       .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;
+
+       if (!IS_ENABLED(CONFIG_DEBUG_FS))
+               return 0;
+
+       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;
+
+       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;
+
+       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)
+{
+       if (IS_ENABLED(CONFIG_DEBUG_FS))
+               debugfs_remove_recursive(ubi->dbg.dfs_dir);
+}
+#else
+int ubi_debugfs_init(void)
+{
+       return 0;
+}
+
+void ubi_debugfs_exit(void)
+{
+}
+
+int ubi_debugfs_init_dev(struct ubi_device *ubi)
+{
+       return 0;
+}
+
+void ubi_debugfs_exit_dev(struct ubi_device *ubi)
+{
+}
+#endif